use super::*;
use crate::types::SparseMatrix;

fn example() -> (CCSInstance, CCSWitness, Vec<(usize, F)>) {
    // z[2] is a hidden square root of public z[1].
    let mut a = SparseMatrix::new(2, 4);
    let mut b = a.clone();
    let mut c = a.clone();
    a.set(0, 2, F::ONE);
    b.set(0, 2, F::ONE);
    c.set(0, 1, F::ONE);
    (
        CCSInstance {
            matrices: vec![a, b, c],
            multisets: vec![vec![0, 1], vec![2]],
            coeffs: vec![F::ONE, -F::ONE],
            num_rows: 2,
            num_cols: 4,
        },
        CCSWitness {
            z: vec![F::ONE, F::new(49), F::new(7), F::ZERO],
        },
        vec![(1, F::new(49))],
    )
}
fn seeds() -> Vec<[Seed; 3]> {
    (0..REPETITIONS)
        .map(|r| {
            std::array::from_fn(|i| {
                let mut seed = [0; 32];
                seed[..8].copy_from_slice(&(r as u64).to_le_bytes());
                seed[8] = i as u8;
                seed
            })
        })
        .collect()
}

#[test]
fn private_proofs_bind_relation_statement_and_every_public_coordinate() {
    let (instance, witness, public) = example();
    let proof = prove(&instance, &witness, b"program", &public).unwrap();
    verify(&instance, &proof, b"program", &public).unwrap();
    assert!(verify(&instance, &proof, b"different", &public).is_err());
    assert!(verify(&instance, &proof, b"program", &[(1, F::new(50))]).is_err());
    let mut changed = instance.clone();
    changed.coeffs[1] = F::ONE;
    assert!(verify(&changed, &proof, b"program", &public).is_err());
    let another = prove(&instance, &witness, b"program", &public).unwrap();
    assert_ne!(proof, another);
    verify(&instance, &another, b"program", &public).unwrap();
    let opposite = CCSWitness {
        z: vec![F::ONE, F::new(49), -F::new(7), F::ZERO],
    };
    let opposite_proof = prove(&instance, &opposite, b"program", &public).unwrap();
    verify(&instance, &opposite_proof, b"program", &public).unwrap();
}

#[test]
fn verifier_rejects_unsatisfied_and_zero_constant_witnesses_without_honest_gate() {
    let (instance, mut witness, public) = example();
    let circuit = Circuit::new(&instance, b"program", &public).unwrap();
    witness.z[2] = F::new(8);
    assert_eq!(
        prove(&instance, &witness, b"program", &public),
        Err(PrivateError::InvalidWitness)
    );
    let forged = prove_seeded(&circuit, &witness.z, &seeds());
    assert!(verify(&instance, &forged, b"program", &public).is_err());
    witness.z.fill(F::ZERO);
    assert!(instance.is_satisfied_by(&witness));
    let circuit = Circuit::new(&instance, b"program", &[]).unwrap();
    let forged = prove_seeded(&circuit, &witness.z, &seeds());
    assert!(verify(&instance, &forged, b"program", &[]).is_err());
}

#[test]
fn malformed_or_spliced_transcripts_fail_closed() {
    let (instance, witness, public) = example();
    let circuit = Circuit::new(&instance, b"program", &public).unwrap();
    let proof = prove_seeded(&circuit, &witness.z, &seeds());
    verify(&instance, &proof, b"program", &public).unwrap();
    let bytes = proof.as_bytes();
    for offset in [8, 12, 16, 20, 21, 52, 53, 60, 61, bytes.len() - 1] {
        let mut changed = proof.clone();
        changed.bytes[offset] ^= 1;
        assert!(
            verify(&instance, &changed, b"program", &public).is_err(),
            "offset {offset}"
        );
    }
    let mut noncanonical = proof.clone();
    noncanonical.bytes[53..61].copy_from_slice(&nebu::field::P.to_le_bytes());
    assert!(verify(&instance, &noncanonical, b"program", &public).is_err());
    let mut truncated = proof.clone();
    truncated.bytes.pop();
    assert!(verify(&instance, &truncated, b"program", &public).is_err());
    let mut trailing = proof.clone();
    trailing.bytes.push(0);
    assert!(verify(&instance, &trailing, b"program", &public).is_err());
    let round_length = |offset: usize| {
        97 + 8
            * (circuit.outputs.len()
                + circuit.products.len()
                + if bytes[offset] == 0 {
                    0
                } else {
                    circuit.inputs
                })
    };
    let first_end = 20 + round_length(20);
    let second_end = first_end + round_length(first_end);
    let mut spliced = proof.clone();
    spliced.bytes = [
        &bytes[..20],
        &bytes[first_end..second_end],
        &bytes[20..first_end],
        &bytes[second_end..],
    ]
    .concat();
    assert!(verify(&instance, &spliced, b"program", &public).is_err());
    let mut wrong_challenge = proof;
    wrong_challenge.bytes[20] = 3;
    assert!(verify(&instance, &wrong_challenge, b"program", &public).is_err());
    assert!(PrivateProof::from_bytes(b"ZHMITH01").is_err());
}

#[test]
fn every_opened_pair_reconstructs_the_original_views_and_outputs() {
    let (instance, witness, public) = example();
    let circuit = Circuit::new(&instance, b"", &public).unwrap();
    let seeds = seeds();
    let originals = views::simulate(&circuit, &witness.z, &seeds[0]);
    for e in 0..3 {
        let n = (e + 1) % 3;
        let mut opened = [
            View {
                seed: originals[e].seed,
                wires: originals[e].wires[..circuit.inputs].to_vec(),
            },
            View {
                seed: originals[n].seed,
                wires: originals[n].wires.clone(),
            },
        ];
        views::replay(&circuit, e, &mut opened);
        assert_eq!(opened[0].wires, originals[e].wires);
        assert_eq!(
            circuit.outputs(&opened[0].wires, e),
            circuit.outputs(&originals[e].wires, e)
        );
    }
}

#[test]
fn relation_admission_precedes_proof_allocation() {
    let (mut instance, witness, public) = example();
    assert_eq!(
        prove(&instance, &witness, b"", &[(0, F::ONE)]),
        Err(PrivateError::InvalidPublicCoordinates)
    );
    assert!(prove(&instance, &witness, b"", &[(1, F::ONE), (1, F::ONE)]).is_err());
    instance.matrices[0].entries[0][0].0 = 4;
    assert_eq!(
        prove(&instance, &witness, b"", &public),
        Err(PrivateError::InvalidRelation)
    );
    instance.num_rows = usize::MAX;
    assert!(prove(&instance, &witness, b"", &public).is_err());
}

#[test]
fn linear_relations_and_higher_degree_ccs_are_supported() {
    let (mut instance, mut witness, _) = example();
    instance.multisets[0] = vec![0, 0, 0];
    witness.z[1] = F::new(343);
    let public = vec![(1, F::new(343))];
    let proof = prove(&instance, &witness, b"cube", &public).unwrap();
    verify(&instance, &proof, b"cube", &public).unwrap();
    instance.multisets = vec![vec![0], vec![0]];
    let proof = prove(&instance, &witness, b"linear", &[]).unwrap();
    verify(&instance, &proof, b"linear", &[]).unwrap();
}

#[cfg(feature = "serde")]
#[test]
fn serde_keeps_the_bounded_fixed_width_payload() {
    let (instance, witness, public) = example();
    let proof = prove(&instance, &witness, b"", &public).unwrap();
    let bytes = postcard::to_allocvec(&proof).unwrap();
    let decoded: PrivateProof = postcard::from_bytes(&bytes).unwrap();
    assert_eq!(decoded, proof);
    assert!(bytes.len() < proof.as_bytes().len() + 8);
    let length_bomb = postcard::to_allocvec(&(MAX_BYTES as u64 + 1)).unwrap();
    assert!(postcard::from_bytes::<PrivateProof>(&length_bomb).is_err());
    let admitted_but_truncated = postcard::to_allocvec(&(MAX_BYTES as u64)).unwrap();
    assert!(postcard::from_bytes::<PrivateProof>(&admitted_but_truncated).is_err());
}

// Exhaustive finite-ring model of the arithmetic decomposition, including the
// exposed tapes. Two distinct roots of xยฒ=1 induce the same two-view distribution
// as the witness-free simulator. This checks privacy beyond searching raw bytes.
#[test]
fn two_view_distribution_matches_a_witness_free_simulator_over_f3() {
    use std::collections::BTreeMap;
    let reduce = |v: i32| v.rem_euclid(3) as u8;
    for e in 0..3 {
        let n = (e + 1) % 3;
        let h = (e + 2) % 3;
        let mut distributions = Vec::new();
        for secret in [1, 2] {
            let mut counts = BTreeMap::new();
            for bits in 0..243 {
                let mut v = bits;
                let mut random = [0i32; 5];
                for r in &mut random {
                    *r = v % 3;
                    v /= 3;
                }
                let shares = [
                    random[0],
                    random[1],
                    (secret - random[0] - random[1]).rem_euclid(3),
                ];
                let tapes = [random[2], random[3], random[4]];
                let products: [u8; 3] = std::array::from_fn(|i| {
                    let j = (i + 1) % 3;
                    reduce(shares[i] * shares[i] + 2 * shares[i] * shares[j] + tapes[i] - tapes[j])
                });
                let outputs: [u8; 3] =
                    std::array::from_fn(|i| reduce(products[i] as i32 - i32::from(i == 0)));
                let key = [
                    shares[e] as u8,
                    shares[n] as u8,
                    tapes[e] as u8,
                    tapes[n] as u8,
                    products[e],
                    products[n],
                    outputs[h],
                ];
                *counts.entry(key).or_insert(0) += 1;
            }
            distributions.push(counts);
        }
        let mut simulated = BTreeMap::new();
        for bits in 0..243 {
            let mut v = bits;
            let mut random = [0i32; 5];
            for r in &mut random {
                *r = v % 3;
                v /= 3;
            }
            let [a, b, ra, rb, neighbor] = random;
            let own = reduce(a * a + 2 * a * b + ra - rb);
            let out_a = reduce(own as i32 - i32::from(e == 0));
            let out_b = reduce(neighbor - i32::from(n == 0));
            let hidden = reduce(-(out_a as i32) - out_b as i32);
            let key = [
                a as u8,
                b as u8,
                ra as u8,
                rb as u8,
                own,
                neighbor as u8,
                hidden,
            ];
            *simulated.entry(key).or_insert(0) += 1;
        }
        assert_eq!(distributions[0], distributions[1]);
        assert_eq!(distributions[0], simulated);
    }
}

Homonyms

neural/trident/src/neural/tests.rs
cyb/honeycrisp/aruminium/src/tests.rs
soft3/radio/iroh-blobs/src/tests.rs
warriors/trisha/rs/lower/tests.rs
neural/trident/src/deploy/tests.rs
soft3/crate/src/wallet/tests.rs
soft3/lens/cli/src/tests.rs
soft3/crate/src/genesis/tests.rs
neural/trident/src/verify/solve/tests.rs
neural/trident/src/package/hash/tests.rs
cyb/wysm/crates/wasmi/src/tests.rs
soft3/zheng/rs/src/accumulate/tests.rs
soft3/zheng/rs/src/air/tests.rs
neural/trident/src/package/registry/tests.rs
neural/trident/src/verify/sym/tests.rs
cyb/honeycrisp/aruminium/src/render/tests.rs
neural/trident/src/syntax/format/tests.rs
neural/trident/src/verify/smt/tests.rs
neural/trident/src/syntax/grammar/tests.rs
neural/trident/src/config/scaffold/tests.rs
warriors/trisha/rs/cost/stack_verifier/tests.rs
soft3/radio/quinn/quinn-udp/tests/tests.rs
soft3/radio/cyber-bao/src/tree/tests.rs
cyb/honeycrisp/unimem/src/block/tests.rs
neural/trident/src/lsp/semantic/tests.rs
neural/trident/src/package/manifest/tests.rs
soft3/radio/cyber-bao/src/group/tests.rs
cyb/wysm/crates/cli/src/tests.rs
soft3/crate/src/chain/pool/tests.rs
soft3/cybernode/rs/src/genesis/tests.rs
soft3/zheng/rs/src/envelope/tests.rs
soft3/zheng/rs/src/machine/tests.rs
soft3/cybernode/rs/src/wallet/tests.rs
neural/trident/src/verify/synthesize/tests.rs
warriors/trisha/rs/lower/legalize/tests.rs
neural/trident/src/syntax/lexer/tests.rs
cyb/wysm/crates/collections/src/tests.rs
neural/trident/src/verify/equiv/tests.rs
neural/trident/src/lsp/util/tests.rs
neural/trident/src/verify/report/tests.rs
soft3/crate/src/chain/economy/tests.rs
soft3/radio/quinn/quinn/src/tests.rs
neural/trident/src/config/resolve/tests.rs
neural/trident/src/package/store/tests.rs
soft3/zheng/rs/src/execution/veil/tests.rs
warriors/erga/rs/mine-bench/src/texture_ffi/tests.rs
neural/trident/src/ir/tir/stack/tests.rs
cyb/wysm/crates/core/src/memory/tests.rs
cyb/evy/forks/bevy_ecs/src/bundle/tests.rs
warriors/erga/rs/rtable-bench/src/probe/tests.rs
soft3/radio/quinn/quinn-proto/src/range_set/tests.rs
cyb/wysm/crates/core/src/table/tests.rs
warriors/erga/rs/blake-bench/src/batch/tests.rs
soft3/crate/src/chain/net/kernel/tests.rs
soft3/zheng/rs/src/recursion/circuit/tests.rs
soft3/zheng/rs/src/recursion/whir/tests.rs
soft3/zheng/rs/src/recursion/wrap/tests.rs
soft3/cybernode/rs/src/chain/economy/tests.rs
neural/trident/src/neural/data/tir_graph/tests.rs
neural/trident/src/ir/tir/optimize/tests.rs
soft3/zheng/rs/src/execution/tagged/tests.rs
warriors/erga/rs/rtable-bench/src/table/tests.rs
warriors/erga/rs/mine-bench/src/owner/tests.rs
soft3/cybernode/rs/src/chain/pool/tests.rs
soft3/zheng/rs/src/ccs/universal/tests.rs
cyb/wysm/crates/wasmi/src/instance/tests.rs
soft3/zheng/rs/src/execution/disclosed/stream/tests.rs
cyb/evy/forks/naga/src/front/wgsl/tests.rs
cyb/wysm/crates/wasmi/src/module/instantiate/tests.rs
soft3/bbg/rs/src/storage/application/transfer/tests.rs
soft3/zheng/rs/src/execution/disclosed/evaluation/tests.rs
warriors/erga/rs/mine-bench/src/cli/timed/tests.rs
cyb/wysm/crates/wasmi/src/engine/limits/tests.rs
soft3/cybernode/rs/src/chain/net/kernel/tests.rs
soft3/zheng/rs/src/execution/veil/hiding/tests.rs
soft3/zheng/rs/src/execution/disclosed/memory/tests.rs

Graph