spectral_vm 0.1.6

HYPERION: Production-ready zero-knowledge virtual machine with spectral analysis
Documentation
/*
 * ═══════════════════════════════════════════════════════════════════════════
 * UNIT TESTS: Fast Reed-Solomon Interactive Oracle Proofs (FRI)
 * ═══════════════════════════════════════════════════════════════════════════
 */

use spectral_vm::field::Goldilocks;
use spectral_vm::fri::{FriParams, FriProver, FriVerifier, FriProof};
use spectral_vm::transcript::Transcript;

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

    #[test]
    fn test_fri_params_creation() {
        let params = FriParams {
            codeword_size: 1024,
            blowup_factor: 2,
            num_queries: 32,
            final_degree: 1,
        };

        assert_eq!(params.codeword_size, 1024);
        assert_eq!(params.blowup_factor, 2);
        assert_eq!(params.num_queries, 32);
    }

    #[test]
    fn test_fri_params_validation() {
        // Valid parameters
        let params = FriParams {
            codeword_size: 1024,
            blowup_factor: 2,
            num_queries: 32,
            final_degree: 1,
        };

        assert!(params.codeword_size.is_power_of_two());
        assert!(params.num_queries > 0);
    }

    #[test]
    fn test_fri_prover_creation() {
        // FriProver is a unit struct, just test that it exists
        let _prover = FriProver;
    }

    #[test]
    fn test_fri_basic_proof_generation() {
        let params = FriParams {
            codeword_size: 256, // Smaller for testing
            blowup_factor: 2,
            num_queries: 16,
            final_degree: 1,
        };

        let message = vec![Goldilocks::from_i64(42); params.codeword_size / params.blowup_factor];
        let mut transcript = Transcript::new(b"test_fri");

        let proof = FriProver::prove_from_message(&message, &params, &mut transcript);

        // Check that proof was generated
        assert!(!proof.commitment.roots.is_empty());
        assert!(!proof.queries.is_empty());
    }

    #[test]
    fn test_fri_proof_structure() {
        let params = FriParams {
            codeword_size: 256,
            blowup_factor: 2,
            num_queries: 16,
            final_degree: 1,
        };

        let message = vec![Goldilocks::from_i64(123); params.codeword_size / params.blowup_factor];
        let mut transcript = Transcript::new(b"test_structure");

        let proof = FriProver::prove_from_message(&message, &params, &mut transcript);

        // Check commitment structure
        assert!(proof.commitment.roots.len() > 0);
        assert!(proof.commitment.challenges.len() > 0);
        assert_eq!(proof.commitment.roots.len(), proof.commitment.challenges.len() + 1);

        // Check queries
        assert_eq!(proof.queries.len(), params.num_queries as usize);
        for query in &proof.queries {
            assert!(query.layers.len() > 0);
        }
    }

    #[test]
    fn test_fri_commitment_consistency() {
        let params = FriParams {
            codeword_size: 256,
            blowup_factor: 2,
            num_queries: 8,
            final_degree: 1,
        };

        let message = vec![Goldilocks::from_i64(999); params.codeword_size / params.blowup_factor];
        let mut transcript1 = Transcript::new(b"test_consistency_1");
        let mut transcript2 = Transcript::new(b"test_consistency_1"); // Same seed

        let proof1 = FriProver::prove_from_message(&message, &params, &mut transcript1);
        let proof2 = FriProver::prove_from_message(&message, &params, &mut transcript2);

        // Same inputs should produce same commitment roots
        assert_eq!(proof1.commitment.roots, proof2.commitment.roots);
    }

    #[test]
    fn test_fri_different_inputs_different_proofs() {
        let params = FriParams {
            codeword_size: 256,
            blowup_factor: 2,
            num_queries: 8,
            final_degree: 1,
        };

        let message1 = vec![Goldilocks::from_i64(111); params.codeword_size / params.blowup_factor];
        let message2 = vec![Goldilocks::from_i64(222); params.codeword_size / params.blowup_factor];

        let mut transcript1 = Transcript::new(b"test_diff_1");
        let mut transcript2 = Transcript::new(b"test_diff_2");

        let proof1 = FriProver::prove_from_message(&message1, &params, &mut transcript1);
        let proof2 = FriProver::prove_from_message(&message2, &params, &mut transcript2);

        // Different inputs should produce different commitments
        assert_ne!(proof1.commitment.roots, proof2.commitment.roots);
    }

    #[test]
    fn test_fri_verifier_creation() {
        // FriVerifier is a unit struct
        let _verifier = FriVerifier;
    }

    #[test]
    fn test_fri_proof_serialization() {
        let params = FriParams {
            codeword_size: 256,
            blowup_factor: 2,
            num_queries: 8,
            final_degree: 1,
        };

        let message = vec![Goldilocks::from_i64(456); params.codeword_size / params.blowup_factor];
        let mut transcript = Transcript::new(b"test_serialization");

        let proof = FriProver::prove_from_message(&message, &params, &mut transcript);

        // Test serialization
        let serialized = serde_json::to_string(&proof).unwrap();
        let deserialized: FriProof = serde_json::from_str(&serialized).unwrap();

        // Check that key properties are preserved
        assert_eq!(proof.commitment.roots.len(), deserialized.commitment.roots.len());
        assert_eq!(proof.queries.len(), deserialized.queries.len());
    }

    #[test]
    fn test_fri_params_serialization() {
        let params = FriParams {
            codeword_size: 1024,
            blowup_factor: 4,
            num_queries: 64,
            final_degree: 2,
        };

        let serialized = serde_json::to_string(&params).unwrap();
        let deserialized: FriParams = serde_json::from_str(&serialized).unwrap();

        assert_eq!(params.codeword_size, deserialized.codeword_size);
        assert_eq!(params.blowup_factor, deserialized.blowup_factor);
        assert_eq!(params.num_queries, deserialized.num_queries);
        assert_eq!(params.final_degree, deserialized.final_degree);
    }

    #[test]
    fn test_fri_small_parameters() {
        // Test with minimal valid parameters
        let params = FriParams {
            codeword_size: 64, // Very small for testing
            blowup_factor: 2,
            num_queries: 4,
            final_degree: 1,
        };

        let message = vec![Goldilocks::from_i64(777); params.codeword_size / params.blowup_factor];
        let mut transcript = Transcript::new(b"test_small");

        let proof = FriProver::prove_from_message(&message, &params, &mut transcript);

        assert!(!proof.commitment.roots.is_empty());
        assert!(!proof.queries.is_empty());
    }

    #[test]
    fn test_fri_edge_case_empty_message() {
        // Test with minimal message
        let params = FriParams {
            codeword_size: 64,
            blowup_factor: 2,
            num_queries: 4,
            final_degree: 1,
        };

        let message = vec![Goldilocks::from_i64(0); 1]; // Very small message
        let mut transcript = Transcript::new(b"test_empty");

        let proof = FriProver::prove_from_message(&message, &params, &mut transcript);

        // Should still produce a valid proof structure
        assert!(!proof.commitment.roots.is_empty());
    }
}