Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Frame pointers needed for samply/Instruments to get accurate/deep call stacks on macOS.
[build]
rustflags = ["-C", "force-frame-pointers=yes"]

[target.aarch64-unknown-linux-gnu]
rustflags = ["-C", "force-frame-pointers=yes", "-C", "target-feature=+aes"]
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -D warnings
CARGO_PROFILE_TEST_OPT_LEVEL: 3

jobs:
fmt:
Expand Down
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,10 @@ debug = true
split-debuginfo = "packed"
lto = false
codegen-units = 16

# Tests run the dev profile.
# The Fiat–Shamir sponge is pure-Rust Keccak, which is ~20x slower unoptimized.
[profile.dev]
package.keccak.opt-level = 3
package.sha3.opt-level = 3
package.spongefish.opt-level = 3
59 changes: 45 additions & 14 deletions crates/tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,36 +33,32 @@ pub fn packed_witness(shape: Shape, rng: &mut impl Rng) -> Vec<F128> {
.collect()
}

/// An instance whose claim actually holds, committed under a real scheme.
pub struct Instance {
/// A claim that actually holds, with the witness it is about, before any
/// commitment. What the fold round needs and nothing the opening does.
#[derive(Debug, Clone)]
pub struct HonestClaim {
pub params: BitZParams<Q>,
pub prover: prover::BitZProver<Q>,
pub verifier: verifier::BitZVerifier<Q>,
pub claim: LinearClaim<field::Fq<Q>>,
pub pcs: Pcs,
pub com: Root,
pub data: ProverData,
pub packed: Vec<F128>,
}

impl Instance {
impl HonestClaim {
/// Builds a random witness and the target its own fold produces, so the
/// claim is true by construction rather than by asserting the code agrees
/// with itself.
///
/// `mu` comes straight from the definition — `sum_j v^(2)_j pi_q(eta_j)`
/// with `eta_j` read bit by bit — not from the reconstruction the verifier
/// runs.
pub fn honest(shape: Shape, seed: u64) -> Self {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
pub fn new(shape: Shape, rng: &mut impl Rng) -> Self {
let params = BitZParams::<Q>::new(shape, smallest_generator()).unwrap();

let packed = packed_witness(shape, &mut rng);
let packed = packed_witness(shape, rng);
let row_weights: Vec<Fq<Q>> = (0..shape.rows())
.map(|_| Fq::from(sample_below_q(&mut rng)))
.map(|_| Fq::from(sample_below_q(rng)))
.collect();
let column_weights: Vec<Fq<Q>> = (0..shape.columns())
.map(|_| Fq::from(sample_below_q(&mut rng)))
.map(|_| Fq::from(sample_below_q(rng)))
.collect();

let table = params.table(&packed).unwrap();
Expand All @@ -79,6 +75,40 @@ impl Instance {

let claim = LinearClaim::new(&params, row_weights, column_weights, target).unwrap();

Self {
params,
claim,
packed,
}
}

pub fn table(&self) -> BitTable<'_> {
self.params.table(&self.packed).unwrap()
}
}

/// An instance whose claim actually holds, committed under a real scheme.
pub struct Instance {
pub params: BitZParams<Q>,
pub prover: prover::BitZProver<Q>,
pub verifier: verifier::BitZVerifier<Q>,
pub claim: LinearClaim<field::Fq<Q>>,
pub pcs: Pcs,
pub com: Root,
pub data: ProverData,
pub packed: Vec<F128>,
}

impl Instance {
/// [`HonestClaim::new`] under the `Fast` profile, with a setup per role.
pub fn honest(shape: Shape, seed: u64) -> Self {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let HonestClaim {
params,
claim,
packed,
} = HonestClaim::new(shape, &mut rng);

let pcs = Pcs::new(&shape, LigeritoProfile::Fast, HashKind::Blake3).unwrap();
let (com, data) = pcs.commit(&packed).unwrap();

Expand Down Expand Up @@ -112,7 +142,7 @@ impl Instance {

/// A uniform integer in `[0, q)`, rejection sampled so the weights are not
/// biased toward the low end of the range.
fn sample_below_q(rng: &mut ChaCha8Rng) -> u128 {
fn sample_below_q(rng: &mut impl Rng) -> u128 {
loop {
let candidate =
(u128::from(rng.next_u64()) << 64 | u128::from(rng.next_u64())) & ((1u128 << 100) - 1);
Expand All @@ -135,6 +165,7 @@ pub fn wide_shape() -> Shape {
/// `m = 28` at the reference split, `(t, s) = (17, 11)`: 131072 rows over
/// 2048 columns, a 32 MiB witness. Every other fixture sits at the floor, so
/// this is the only one whose cost scales the way a real instance does.
/// It's too big to go through the opening in CI-enabled test - see `tests/large.rs`.
pub fn large_shape() -> Shape {
reference_shape(28)
}
Expand Down
66 changes: 66 additions & 0 deletions crates/tests/tests/large.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//! Tests for the large fixture, that does NOT do the opening.
//!
//! At `m = 28` the round trip is out of reach for a test. The reduction
//! materializes one field element per committed bit and every product-tree
//! layer above them, some 10 GiB, and an unoptimised build needs over an hour
//! for it.
//!
//! This test is designed to test what's possible in under a minute.

use common::LinearClaim;
use field::Fq;
use num_traits::ConstOne;
use prover::BitZProver;
use rand_chacha::ChaCha8Rng;
use rand_core::SeedableRng;
use tests::{HonestClaim, WINDOW, large_shape, prover_transcript, verifier_transcript};
use verifier::{BitZVerifier, ReceiveError};

#[test]
fn the_fold_round_trips_on_the_large_shape() {
let shape = large_shape();
let honest = HonestClaim::new(shape, &mut ChaCha8Rng::seed_from_u64(31));
let prover = BitZProver::new(honest.params, WINDOW);
let verifier = BitZVerifier::new(honest.params, WINDOW);

let mut transcript = prover_transcript();
let sent = prover
.send_fold(&honest.claim, &honest.table(), &mut transcript)
.unwrap();
let proof = transcript.finish();
assert_eq!(proof.narg_string.len(), 16 * shape.columns());
assert!(proof.hints.is_empty());

let mut transcript = verifier_transcript(&proof);
let received = verifier
.receive_fold(&honest.claim, &mut transcript)
.expect("honest proof");
assert_eq!(sent, received);
assert_eq!(received.row_images.len(), shape.rows());
assert_eq!(received.zeta.len(), shape.log_columns());
transcript.check_eof().expect("both streams exhausted");

// `k_1 (q - 1)` is 117 bits wide here; one past it is still refused.
let mut transcript = prover_transcript();
for _ in 0..shape.columns() {
transcript.prover_message(&(verifier.fold_bound() + 1).to_le_bytes());
}
let over = transcript.finish();
assert_eq!(
verifier.receive_fold(&honest.claim, &mut verifier_transcript(&over)),
Err(ReceiveError::FoldOutOfRange)
);

// The honest folds against a claim off by one.
let retargeted = LinearClaim::new(
&honest.params,
honest.claim.row_weights().to_vec(),
honest.claim.column_weights().to_vec(),
honest.claim.target() + Fq::ONE,
)
.unwrap();
assert_eq!(
verifier.receive_fold(&retargeted, &mut verifier_transcript(&proof)),
Err(ReceiveError::TargetMismatch)
);
}
8 changes: 3 additions & 5 deletions crates/tests/tests/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ use field::{F128, Fq};
use num_traits::{ConstOne, ConstZero};
use pcs::{HashKind, LigeritoProfile, Pcs, VerifyError as PcsVerifyError};
use prover::ProveError;
use tests::{
Instance, large_shape, narrow_shape, prover_transcript, verifier_transcript, wide_shape,
};
use tests::{Instance, narrow_shape, prover_transcript, verifier_transcript, wide_shape};
use transcript::Proof;
use verifier::{ReceiveError, VerifyError};

Expand All @@ -27,8 +25,8 @@ fn prove(instance: &Instance) -> Proof {
}

#[test]
fn an_honest_proof_verifies_on_every_shape_the_profile_admits() {
for shape in [narrow_shape(), wide_shape(), large_shape()] {
fn an_honest_proof_verifies_on_both_floor_shapes() {
for shape in [narrow_shape(), wide_shape()] {
let instance = Instance::honest(shape, 31);
let proof = prove(&instance);

Expand Down
Loading