diff --git a/.gitignore b/.gitignore index 7b4eda9..d96bf3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target /Cargo.lock .vscode -.DS_Store \ No newline at end of file +.DS_Store +.claude/ diff --git a/Cargo.toml b/Cargo.toml index 984c8c8..c79b099 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,42 +5,30 @@ include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] edition = "2021" [dependencies] -ark-crypto-primitives = { version = "0.5.0", features = [ +ark-crypto-primitives = { version = "0.6.0", features = [ "merkle_tree", "crh", - "r1cs", + "constraints", "sponge", + "blake3", ] } -ark-ff = "0.5.0" -ark-poly = "0.5.0" -ark-r1cs-std = "0.5.0" -ark-relations = "0.5.0" -ark-serialize = "0.5.0" -ark-std = "0.5.0" +ark-ff = "0.6.0" +ark-poly = "0.6.0" +ark-r1cs-std = "0.6.0" +ark-relations = "0.6.0" +ark-serialize = "0.6.0" +ark-std = "0.6.0" blake3 = "1.5.0" serde = { version = "1.0.0", features = ["derive"] } serde_json = "1.0" -spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "smallfp-support", features = [ - "ark-ff", -] } -efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git" } +spongefish = { version = "0.7.0", features = ["ark-ff"] } +effsc = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git" } thiserror = "2.0.16" -ark-codes = { git = "https://github.com/dmpierre/ark-codes.git" } - - -[patch.crates-io] -ark-crypto-primitives = { git = "https://github.com/benbencik/crypto-primitives.git", branch = "smallfp-absorb-trait" } -ark-ff = { git = "https://github.com/arkworks-rs/algebra.git" } -ark-poly = { git = "https://github.com/arkworks-rs/algebra.git" } -ark-serialize = { git = "https://github.com/arkworks-rs/algebra.git" } - -# resolve transitive pull of spongefish from efficient-sumcheck -[patch."https://github.com/arkworks-rs/spongefish"] -spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "smallfp-support" } +ark-codes = { git = "https://github.com/z-tech/ark-codes.git", branch = "z-tech/arkworks-0.6.0" } [dev-dependencies] -ark-bls12-381 = "0.5.0" -ark-bn254 = "0.5.0" +ark-bls12-381 = "0.6.0" +ark-bn254 = "0.6.0" criterion = "0.8" [features] diff --git a/benches/utils/domainsep.rs b/benches/utils/domainsep.rs index 9c537ab..5ef1355 100644 --- a/benches/utils/domainsep.rs +++ b/benches/utils/domainsep.rs @@ -2,5 +2,5 @@ use spongefish::ProverState; pub fn init_prover_state() -> ProverState { let domainsep = spongefish::domain_separator!("warp::rs"); - domainsep.instance(&0u32).std_prover() + domainsep.without_session().instance(&0u32).std_prover() } diff --git a/src/crypto/merkle/blake3.rs b/src/crypto/merkle/blake3.rs index 963a7d4..d2ef41b 100644 --- a/src/crypto/merkle/blake3.rs +++ b/src/crypto/merkle/blake3.rs @@ -1,7 +1,6 @@ use super::parameters::MerkleTreeParams; -use ark_crypto_primitives::crh::blake3::fields::Blake3F; -use ark_crypto_primitives::crh::blake3::Blake3; -use ark_crypto_primitives::crh::blake3::GenericDigest; +use ark_crypto_primitives::crh::blake3::{Blake3CRH, Blake3TwoToOneCRH}; +use ark_crypto_primitives::crh::ByteDigest; use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, merkle_tree::{Config as MerkleConfig, IdentityDigestConverter}, @@ -15,13 +14,14 @@ pub struct Blake3MerkleConfig { _field: PhantomData, } -pub type Blake3MerkleTreeParams = MerkleTreeParams, Blake3, GenericDigest<32>>; +pub type Blake3MerkleTreeParams = + MerkleTreeParams, Blake3TwoToOneCRH, ByteDigest<32>>; impl MerkleConfig for Blake3MerkleConfig { type Leaf = [F]; type LeafDigest = ::Output; type LeafInnerDigestConverter = IdentityDigestConverter; type InnerDigest = ::Output; - type LeafHash = Blake3F; // blake3, over field elements - type TwoToOneHash = Blake3; + type LeafHash = Blake3CRH; + type TwoToOneHash = Blake3TwoToOneCRH; } diff --git a/src/error.rs b/src/error.rs index 3f016ab..fb6aa08 100644 --- a/src/error.rs +++ b/src/error.rs @@ -73,6 +73,26 @@ impl From for VerifierError { } } +impl From for VerifierError { + fn from(err: effsc::proof::SumcheckError) -> Self { + use effsc::proof::SumcheckError; + match err { + // Round consistency or degree mismatch detected by the library. + SumcheckError::ConsistencyCheck { .. } | SumcheckError::DegreeMismatch { .. } => { + Self::SumcheckRound + } + // Not raised by `sumcheck_verify` any more (the library dropped + // the internal oracle check); warp does the final-claim check + // itself. Retained for enum completeness. + SumcheckError::FinalEvaluation => Self::Target, + // Ran out of transcript or malformed bytes. + SumcheckError::TranscriptError { .. } => Self::SpongeFish, + // Unreachable: warp always passes a noop hook. + SumcheckError::HookError { .. } => Self::SumcheckRound, + } + } +} + #[derive(Error, Debug)] pub enum DeciderError { #[error("Invalid merkle root")] diff --git a/src/lib.rs b/src/lib.rs index 698dbdc..30bf5e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, merkle_tree::{Config, MerkleTree, Path}, }; -use ark_ff::{Field, PrimeField, Zero}; +use ark_ff::{Field, PrimeField}; use ark_poly::{ univariate::DensePolynomial, DenseMultilinearExtension, DenseUVPolynomial, MultilinearExtension, Polynomial, @@ -14,28 +14,28 @@ use ark_poly::{ use ark_std::log2; use crypto::merkle::build_codeword_leaves; use crypto::merkle::compute_auth_paths; -use efficient_sumcheck::{ - accumulate_sparse_evaluations, batched_constraint_poly, - coefficient_sumcheck::coefficient_sumcheck, +use effsc::{ + coefficient_sumcheck::RoundPolyEvaluator, folding::protogalaxy, - hypercube::{compute_hypercube_eq_evals, Hypercube}, - inner_product_sumcheck, - order_strategy::AscendingOrder, + hypercube::{compute_hypercube_eq_evals, eq_poly_non_binary}, + noop_hook, + provers::{coefficient_lsb::CoefficientProverLSB, inner_product::InnerProductProver}, + runner::sumcheck, + verifier::sumcheck_verify, }; -use protocol::domainsep::parse_statement; +use protocol::domainsep::{derive_between_sumchecks, derive_pre_twin_constraint, parse_statement}; +use protocol::EffscVerifierTranscript; use relations::{r1cs::R1CSConstraints, BundledPESAT}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::collections::HashMap; use std::marker::PhantomData; use utils::binary_field_elements_to_usize; use utils::byte_to_binary_field_array; +use utils::concat_slices; use utils::scale_and_sum; -use utils::{ - concat_slices, - poly::{eq_poly, eq_poly_non_binary}, -}; use config::WARPConfig; -use protocol::domainsep::{absorb_accumulated_instances, absorb_instances, derive_randomness}; +use protocol::domainsep::{absorb_accumulated_instances, absorb_instances}; pub mod config; pub mod constraints; @@ -71,56 +71,107 @@ fn eval_r1cs_constraint_poly( DensePolynomial::from_coefficients_vec(vec![a0 * b0 - c0, a0 * b1 + a1 * b0 - c1, a1 * b1]) } -/// Compute the twin-constraint round polynomial `h(X)`. -/// -/// Proves `∑_i τ(i) · (f(i) + ω · p(i)) = 0` via protogalaxy folding, where: -/// - `f(X)` = `fold(α, oracle_evals)` — folded codeword check -/// - `p(X)` = `fold(β, Az·Bz - Cz)` — folded R1CS constraint check -/// - `t(X)` = linear interpolation of τ — equality polynomial -/// -/// Returns `h(X) = Σ (f(X) + ω·p(X)) · t(X)`. -/// -/// `expected_num_coeffs` is the number of coefficients the verifier expects to read. -/// The result is padded with zeros to ensure the prover always writes exactly that many. -fn twin_constraint_round_poly( - tablewise: &[Vec>], - pairwise: &[Vec], - r1cs: &R1CSConstraints, +/// `RoundPolyEvaluator` for the twin-constraint sumcheck. Fuses the α-fold, +/// β-fold, and τ-linear multiplication in a single pass over even/odd pairs +/// of the input tables. Replaces the closure `twin_constraint_round_poly` +/// previously handed to `coefficient_sumcheck`. +struct TwinConstraintEvaluator<'a, F: Field> { + r1cs: &'a R1CSConstraints, omega: F, - expected_num_coeffs: usize, -) -> DensePolynomial { - let (u, z, a, b) = (&tablewise[0], &tablewise[1], &tablewise[2], &tablewise[3]); - let tau = &pairwise[0]; - - let f_iter = u.chunks(2).zip(a.chunks(2)).map(|(u, a)| { - protogalaxy::fold( - a[0].iter().zip(&a[1]).map(|(&l, &r)| (l, r - l)), - u[0].iter() - .zip(&u[1]) + degree: usize, +} + +impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { + fn degree(&self) -> usize { + self.degree + } + + fn accumulate_pair(&self, coeffs: &mut [F], tw: &[(&[F], &[F])], pw: &[(F, F)]) { + // tw[0]=(u_even,u_odd), tw[1]=(z_even,z_odd), + // tw[2]=(a_even,a_odd), tw[3]=(b_even,b_odd); pw[0]=(tau_even,tau_odd). + let (u_even, u_odd) = tw[0]; + let (z_even, z_odd) = tw[1]; + let (a_even, a_odd) = tw[2]; + let (b_even, b_odd) = tw[3]; + let (tau_even, tau_odd) = pw[0]; + + // CoefficientProverLSB::final_value() invokes the evaluator on fully + // reduced singleton tables with empty odd slices. Warp ignores + // `SumcheckProof::final_value` (only `.challenges` is consumed), so + // bail out — leaves `coeffs` zero, `final_value` returns zero, no + // one reads it. + if u_odd.is_empty() { + return; + } + + let f = protogalaxy::fold( + a_even.iter().zip(a_odd).map(|(&l, &r)| (l, r - l)), + u_even + .iter() + .zip(u_odd) .map(|(&l, &r)| linear_poly(l, r)) .collect(), - ) - }); - let p_iter = b.chunks(2).zip(z.chunks(2)).map(|(b, z)| { - protogalaxy::fold( - b[0].iter().zip(&b[1]).map(|(&l, &r)| (l, r - l)), - r1cs.iter() - .map(|c| eval_r1cs_constraint_poly(c, &z[0], &z[1])) + ); + + let p = protogalaxy::fold( + b_even.iter().zip(b_odd).map(|(&l, &r)| (l, r - l)), + self.r1cs + .iter() + .map(|c| eval_r1cs_constraint_poly(c, z_even, z_odd)) .collect(), - ) - }); - let t_iter = tau.chunks(2).map(|t| linear_poly(t[0], t[1])); - - let mut h = f_iter - .zip(p_iter) - .zip(t_iter) - .map(|((f, p), t)| (f + p * omega).naive_mul(&t)) - .fold(DensePolynomial::zero(), |acc, r| acc + r); - - // Pad coefficients to the expected count so the prover writes exactly - // as many field elements as the verifier reads in derive_randomness. - h.coeffs.resize(expected_num_coeffs, F::zero()); - h + ); + + let t = linear_poly(tau_even, tau_odd); + let h = (f + p * self.omega).naive_mul(&t); + + for (c, &hc) in coeffs.iter_mut().zip(h.coeffs.iter()) { + *c += hc; + } + } +} + +/// [CBBZ23] / HyperPlonk sparse-evaluation optimization: for shift-query +/// zetas (indices `1+s..r`), each ζ is a 0/1 vector representing a single +/// hypercube point. Accumulates the corresponding `eq_evals[i]` into a sparse +/// map keyed by that point's index. Reimplemented locally; the equivalent +/// helper in the old effsc API was removed in the rewrite. +fn accumulate_sparse_evaluations( + zetas: Vec<&[F]>, + eq_evals: Vec, + s: usize, + r: usize, +) -> HashMap { + let mut result: HashMap = HashMap::new(); + for i in 1 + s..r { + let index = zetas[i] + .iter() + .enumerate() + .filter_map(|(j, bit)| bit.is_one().then_some(1 << j)) + .sum::(); + *result.entry(index).or_insert_with(F::zero) += eq_evals[i]; + } + result +} + +/// Build the `g` side of the batching inner-product sumcheck: column-sum the +/// dense OOD evaluation matrix and add the sparse shift-query contributions. +fn batched_constraint_poly( + dense_polys: &[Vec], + sparse_polys: &HashMap, +) -> Vec { + if dense_polys.is_empty() { + return Vec::new(); + } + let mut result = vec![F::ZERO; dense_polys[0].len()]; + for row in dense_polys { + for (i, val) in row.iter().enumerate() { + result[i] += *val; + } + } + for (k, v) in sparse_polys.iter() { + result[*k] += *v; + } + result } pub trait BoolResult { @@ -303,10 +354,10 @@ impl< let tau = prover_state.verifier_messages_vec::(log_l); // b. define [...] - // c. sumcheck protocol - let tau_eq_evals = Hypercube::::new(log_l) - .map(|(index, _point)| eq_poly(&tau, index)) - .collect::>(); + // c. sumcheck protocol. `compute_hypercube_eq_evals` builds the full + // `2^log_l` table in O(2^log_l) via the incremental build-up — + // faster than the previous O(log_l · 2^log_l) per-point loop. + let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); let alpha_vecs = concat_slices(&acc_instances.1, &vec![vec![F::zero(); log_n]; l1]); @@ -322,38 +373,42 @@ impl< let beta_vecs: Vec> = acc_instances.3 .0.into_iter().chain(taus).collect(); - // Twin Constraint sumcheck - let mut tablewise = [ + // Twin Constraint sumcheck via the new `SumcheckProver` trait. + // Wire format: `d+1` evaluations per round (vs. the old `d+1` + // coefficient-form emission). `CoefficientProverLSB` keeps the + // LSB/pair-split semantics the `TwinConstraintEvaluator` was built + // for. + let tablewise = vec![ concat_slices(&acc_witnesses.1, &codewords), // u z_vecs, // z alpha_vecs, // a beta_vecs, // b ]; - let mut pw = [tau_eq_evals]; // tau + let pw = vec![tau_eq_evals]; // tau let r1cs = self.p.constraints(); - let expected_num_coeffs = 2 + (log_n + 1).max(log_M + 2); - let sc = coefficient_sumcheck( - |tw, pw| twin_constraint_round_poly(tw, pw, r1cs, omega, expected_num_coeffs), - &mut tablewise, - &mut pw, - log_l, - prover_state, - ); - let gamma = sc.verifier_messages; - + let degree = 1 + (log_n + 1).max(log_M + 2); + let evaluator = TwinConstraintEvaluator { + r1cs, + omega, + degree, + }; + let mut cc = CoefficientProverLSB::new(&evaluator, tablewise, pw); + let gamma = sumcheck(&mut cc, log_l, prover_state, noop_hook).challenges; debug_assert_eq!(gamma.len(), log_l); - // e. new oracle and target — after log_l rounds each group has one table left - let [mut u_red, mut z_red, mut a_red, mut b_red] = tablewise; - let f = u_red.pop().unwrap(); - let z = z_red.pop().unwrap(); - let zeta_0 = a_red.pop().unwrap(); - let beta_tau = b_red.pop().unwrap(); + // e. new oracle and target — after log_l rounds each group has one + // row left; extract by clone. `CoefficientProverLSB` exposes the + // reduced tables read-only. + let reduced_tw = cc.tablewise(); + debug_assert!(reduced_tw.iter().all(|t| t.len() == 1)); + let f = reduced_tw[0][0].clone(); + let z = reduced_tw[1][0].clone(); + let zeta_0 = reduced_tw[2][0].clone(); + let beta_tau = reduced_tw[3][0].clone(); // eval the bundled r1cs - let beta_eq_evals = (0..M).map(|i| eq_poly(&beta_tau, i)).collect::>(); - + let beta_eq_evals = compute_hypercube_eq_evals(log_M, &beta_tau); let eta = self .p .evaluate_bundled(&beta_eq_evals, &z) @@ -427,13 +482,18 @@ impl< // l. sumcheck polynomials // compute evaluations for xi - let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); + // `xis` has `log_r` elements but only the first `r` entries of the + // eq-evals table are consumed (`r ≤ 2^log_r`). + let xi_eq_evals: Vec = compute_hypercube_eq_evals(log_r, &xis) + .into_iter() + .take(r) + .collect(); let ood_evals_vec = (0..1 + self.config.s) .map(|i| { - (0..n) - .map(|a| eq_poly(zetas[i], a) * xi_eq_evals[i]) - .collect::>() + let table = compute_hypercube_eq_evals(log_n, zetas[i]); + let scale = xi_eq_evals[i]; + table.into_iter().map(|v| v * scale).collect::>() }) .collect::>(); @@ -441,13 +501,15 @@ impl< let id_non_0_eval_sums = accumulate_sparse_evaluations(zetas, xi_eq_evals, self.config.s, r); - // call efficient sumcheck for batched_constraint checks - let alpha = inner_product_sumcheck( - &mut f.clone(), - &mut batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), - prover_state, - ) - .verifier_messages; + // Batching inner-product sumcheck. `InnerProductProver` is MSB + // half-split, so the challenge vector is returned in MSB order; + // reverse once to arkworks' LSB-first convention. + let mut ip = InnerProductProver::new( + f.clone(), + batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), + ); + let mut alpha = sumcheck(&mut ip, log_n, prover_state, noop_hook).challenges; + alpha.reverse(); // m. new target let mu = f_hat.fix_variables(&alpha)[0]; @@ -502,147 +564,94 @@ impl< let (l1, l) = (self.config.l1, self.config.l); let l2 = l - l1; - //////////////////////// - // 1. Parsing phase - //////////////////////// - // a. verification key #[allow(non_snake_case)] let (M, N, k) = (vk.0, vk.1, vk.2); #[allow(non_snake_case)] let (log_M, log_l) = (log2(M) as usize, log2(l) as usize); - let n = self.code.code_len(); let log_n = log2(n) as usize; - - // f. absorb parameters - let (l1_xs, (l2_roots, l2_alphas, l2_mus, (l2_taus, l2_xs), l2_etas)) = - parse_statement::(verifier_state, l1, l2, N - k, log_n, log_M)?; - - //////////////////////// - // 2. Derive randomness - //////////////////////// - let ( - rt_0, - l1_mus, - l1_taus, - omega, - tau, - gamma_sumcheck, - coeffs_twinc_sumcheck, - _rt, - eta, - mut nus, - ood_samples, - bytes_shift_queries, - xi, - alpha_sumcheck, - sums_batching_sumcheck, - ) = derive_randomness::( - verifier_state, - l1, - log_n, - log_l, - self.config.s, - self.config.t, - log_M, - )?; - let r = 1 + self.config.s + self.config.t; let log_r = log2(r) as usize; - //////////////////////// - // 3. Derive values - //////////////////////// - // b. - let alpha_vecs = concat_slices(&l2_alphas, &vec![vec![F::zero(); log_n]; l1]); - - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma_sumcheck); - - let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); - - // compute \eta_{s + k} - let mut nu_s_t = vec![F::default(); self.config.t]; - for (i, v_jk) in proof.6.iter().enumerate() { - let res = v_jk - .iter() - .zip(&gamma_eq_evals) - .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); - nu_s_t[i] = res; - } + // 1. Parse statement. + let (l1_xs, (l2_roots, l2_alphas, l2_mus, (l2_taus, l2_xs), l2_etas)) = + parse_statement::(verifier_state, l1, l2, N - k, log_n, log_M)?; - nus.extend(nu_s_t); + // 2. Pre-twin-constraint transcript reads. + let (rt_0, l1_mus, l1_taus, omega, tau) = + derive_pre_twin_constraint::(verifier_state, l1, log_l, log_M)?; - // d. set \sigma^{(1)} and \sigma^{(2)} - // compute eq(\tau, i) and eq(\xi, i) + // 3. σ₁ = Σ_i τ_eq(i) · (μ_i + ω·η_i). let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); - - let etas = concat_slices(&l2_etas, &vec![F::zero(); l1]); - + let etas_chain = concat_slices(&l2_etas, &vec![F::zero(); l1]); let sigma_1 = tau_eq_evals .into_iter() - .zip(l2_mus.into_iter().chain(l1_mus.to_vec()).zip(etas)) - .fold(F::zero(), |acc, (eq_tau, (mu, eta))| { - acc + eq_tau * (mu + omega * eta) + .zip( + l2_mus + .iter() + .copied() + .chain(l1_mus.iter().copied()) + .zip(etas_chain), + ) + .fold(F::zero(), |acc, (eq_tau, (mu, eta_i))| { + acc + eq_tau * (mu + omega * eta_i) }); - let xi_eq_evals = compute_hypercube_eq_evals(log_r, &xi); - - let sigma_2 = xi_eq_evals - .iter() - .zip(&nus) - .fold(F::zero(), |acc, (xi_eq, nu)| acc + *xi_eq * nu); - - //////////////////////// - // 4. Decision phase - //////////////////////// - // a. new code evaluation point - (acc_instance.1[0] == alpha_sumcheck).ok_or_err(VerifierError::CodeEvaluationPoint)?; + // 4. Twin-constraint sumcheck via `effsc::sumcheck_verify`. The + // library enforces round consistency (`q(0)+q(1)==claim`) and + // returns `(challenges, final_claim)`. The oracle check — verifying + // `final_claim == eq(τ,γ)·(ν₀ + ω·η)` — is warp's responsibility + // and runs below once η and ν₀ have been read from the transcript. + let tc_degree = 1 + (log_n + 1).max(log_M + 2); + let (gamma_sumcheck, tc_final_claim) = { + let mut wrap = EffscVerifierTranscript(verifier_state); + let res = sumcheck_verify(sigma_1, tc_degree, log_l, &mut wrap, |_, _| Ok(()))?; + (res.challenges, res.final_claim) + }; + + // 5. Between-sumchecks reads: td, η, ν₀, OOD, shift bytes, ξ. + let (_td, eta, mut nus, ood_samples, bytes_shift_queries, xi) = + derive_between_sumchecks::( + verifier_state, + log_n, + self.config.s, + self.config.t, + log_r, + )?; - // b. new circuit evaluation point - let betas = l2_taus - .into_iter() - .chain(l1_taus) - .zip(l2_xs.clone().into_iter().chain(l1_xs)) - .map(|(tau, x)| concat_slices(&tau, &x)) - .collect::>>(); - let beta = scale_and_sum(&betas, &gamma_eq_evals); - let expected_beta = concat_slices(&acc_instance.3 .0[0], &acc_instance.3 .1[0]); - (expected_beta == beta).ok_or_err(VerifierError::CircuitEvaluationPoint)?; + // 6. Deferred twin-constraint oracle check. + (eq_poly_non_binary(&tau, &gamma_sumcheck) * (nus[0] + omega * eta) == tc_final_claim) + .ok_or_err(VerifierError::Target)?; - // c. check auth paths - let binary_shift_queries = bytes_shift_queries + // 7. Proximity check. Must run *before* the batching sumcheck because + // σ₂ consumes `proof.6` (shift-query answers); a tampered row + // would otherwise surface as a batching-round consistency failure + // rather than the expected ShiftQuery/NumShiftQueries variant. + let binary_shift_queries_flat = bytes_shift_queries .iter() .flat_map(byte_to_binary_field_array) .take(self.config.t * log_n) .collect::>(); - - let binary_shift_queries = binary_shift_queries.chunks(log_n).collect::>(); - + let binary_shift_queries = binary_shift_queries_flat + .chunks(log_n) + .collect::>(); let shift_queries_indexes: Vec = binary_shift_queries .iter() .map(|vals| binary_field_elements_to_usize(vals)) .collect(); - // check: - // that the leaf index corresponds to the shift query - // that the path is correct (proof.6.len() == self.config.t).ok_or_err(VerifierError::NumShiftQueries)?; - - // proof.4 is auth_0 for (i, path) in proof.4.iter().enumerate() { (path.leaf_index == shift_queries_indexes[i]) .ok_or_err(VerifierError::ShiftQueryIndex)?; - let is_valid = path.verify( &self.mt_leaf_hash_params, &self.mt_two_to_one_hash_params, &rt_0, - &proof.6[i][l2..], // leaves are evaluations of the l1 codewords + &proof.6[i][l2..], )?; - is_valid.ok_or_err(VerifierError::ShiftQuery)? + is_valid.ok_or_err(VerifierError::ShiftQuery)?; } - - // proof.5 holds merkle proofs for l2 accumulated instances (proof.5.len() == l2).ok_or_err(VerifierError::NumL2Instances)?; for (i, paths) in proof.5.iter().enumerate() { (paths.len() == self.config.t).ok_or_err(VerifierError::NumShiftQueries)?; @@ -654,66 +663,72 @@ impl< &self.mt_leaf_hash_params, &self.mt_two_to_one_hash_params, root, - [proof.6[j][i]], // proof.6[j][i] holds f_i(x_j) + [proof.6[j][i]], )?; - - is_valid.ok_or_err(VerifierError::ShiftQuery)? + is_valid.ok_or_err(VerifierError::ShiftQuery)?; } } - // d. sumcheck decisions - // twin constraints sumcheck - (coeffs_twinc_sumcheck.len() == log_l).ok_or_err(VerifierError::NumSumcheckRounds)?; - - let mut target_1 = sigma_1; - for (coeffs, gamma) in coeffs_twinc_sumcheck.into_iter().zip(&gamma_sumcheck) { - let h = DensePolynomial::from_coefficients_vec(coeffs); - (h.evaluate(&F::one()) + h.evaluate(&F::zero()) == target_1) - .ok_or_err(VerifierError::SumcheckRound)?; - target_1 = h.evaluate(gamma); - } + // 8. Derive σ₂ and the reduced α/μ expectation. + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma_sumcheck); + let alpha_vecs = concat_slices(&l2_alphas, &vec![vec![F::zero(); log_n]; l1]); + let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); - // multilinear batching sumcheck - (sums_batching_sumcheck.len() == log_n).ok_or_err(VerifierError::NumSumcheckRounds)?; - let mut target_2 = sigma_2; - for ([sum_00, sum_11, sum_0110], alpha) in - sums_batching_sumcheck.into_iter().zip(&alpha_sumcheck) - { - (sum_00 + sum_11 == target_2).ok_or_err(VerifierError::SumcheckRound)?; - target_2 = (target_2 - sum_0110) * alpha.square() - + sum_00 * (F::one() - alpha.double()) - + sum_0110 * alpha; + let mut nu_s_t = vec![F::default(); self.config.t]; + for (i, v_jk) in proof.6.iter().enumerate() { + nu_s_t[i] = v_jk + .iter() + .zip(&gamma_eq_evals) + .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); } + nus.extend(nu_s_t); - // e. new target decision - // build eq^{\star}(\alpha) - (eq_poly_non_binary(&tau, &gamma_sumcheck) * (nus[0] + omega * eta) == target_1) - .ok_or_err(VerifierError::Target)?; - - let mut zeta_eqs = vec![eq_poly_non_binary(&zeta_0, &alpha_sumcheck)]; - - zeta_eqs.extend( - ood_samples - .chunks(log_n) - .map(|zeta| eq_poly_non_binary(zeta, &alpha_sumcheck)) - .collect::>(), - ); - zeta_eqs.extend( - binary_shift_queries - .iter() - .map(|zeta| eq_poly_non_binary(zeta, &alpha_sumcheck)) - .collect::>(), - ); - (zeta_eqs.len() == r).ok_or_err(VerifierError::NumShiftQueries)?; + let xi_eq_evals = compute_hypercube_eq_evals(log_r, &xi); + let sigma_2 = xi_eq_evals + .iter() + .zip(&nus) + .fold(F::zero(), |acc, (xi_eq, nu)| acc + *xi_eq * nu); - // mul by \mu and compare to target_2 - (acc_instance.2[0] + // 9. Batching (inner-product) sumcheck. Library handles round + // consistency; warp performs the oracle check: + // `final_claim == μ · Σ eq(ζ_i, α_lsb)·ξ_eq(i)`. MSB half-split + // → reverse the challenge vector once before feeding to + // eq_poly_non_binary (arkworks MLE convention). + let (alpha_sumcheck, batching_final_claim) = { + let mut wrap = EffscVerifierTranscript(verifier_state); + let mut res = sumcheck_verify(sigma_2, 2, log_n, &mut wrap, |_, _| Ok(()))?; + res.challenges.reverse(); + (res.challenges, res.final_claim) + }; + + let mut zeta_eqs = Vec::with_capacity(r); + zeta_eqs.push(eq_poly_non_binary(&zeta_0, &alpha_sumcheck)); + for chunk in ood_samples.chunks(log_n) { + zeta_eqs.push(eq_poly_non_binary(chunk, &alpha_sumcheck)); + } + for zeta in &binary_shift_queries { + zeta_eqs.push(eq_poly_non_binary(zeta, &alpha_sumcheck)); + } + debug_assert_eq!(zeta_eqs.len(), r); + let expected_batching = acc_instance.2[0] * zeta_eqs .into_iter() - .zip(xi_eq_evals) - .fold(F::zero(), |acc, (a, b)| acc + a * b) - == target_2) - .ok_or_err(VerifierError::Target)?; + .zip(&xi_eq_evals) + .fold(F::zero(), |acc, (a, b)| acc + a * *b); + (expected_batching == batching_final_claim).ok_or_err(VerifierError::Target)?; + + // 10. Accumulator consistency: new α and β. + (acc_instance.1[0] == alpha_sumcheck).ok_or_err(VerifierError::CodeEvaluationPoint)?; + + let betas = l2_taus + .into_iter() + .chain(l1_taus) + .zip(l2_xs.into_iter().chain(l1_xs)) + .map(|(tau_i, x)| concat_slices(&tau_i, &x)) + .collect::>>(); + let beta = scale_and_sum(&betas, &gamma_eq_evals); + let expected_beta = concat_slices(&acc_instance.3 .0[0], &acc_instance.3 .1[0]); + (expected_beta == beta).ok_or_err(VerifierError::CircuitEvaluationPoint)?; Ok(()) } @@ -733,7 +748,6 @@ impl< )?; (rt[0] == computed_mt.root()).ok_or_err(DeciderError::MerkleRoot)?; (mt[0].root() == computed_mt.root()).ok_or_err(DeciderError::MerkleTrapDoor)?; - (mt[0].leaf_nodes == computed_mt.leaf_nodes).ok_or_err(DeciderError::MerkleRoot)?; let f_hat = DenseMultilinearExtension::from_evaluations_slice( log2(self.code.code_len()) as usize, @@ -743,9 +757,7 @@ impl< let tau = &beta.0[0]; - let tau_zero_evader = Hypercube::::new(tau.len()) - .map(|(index, _point)| eq_poly(tau, index)) - .collect::>(); + let tau_zero_evader = compute_hypercube_eq_evals(tau.len(), tau); let mut z = beta.1[0].clone(); z.extend(w[0].clone()); @@ -853,7 +865,7 @@ pub mod test { for _ in 0..l1 { let domainsep = spongefish::domain_separator!("test::warp"); - let mut prover_state = domainsep.instance(&0u32).std_prover(); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), _pf) = hash_chain_warp .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -889,7 +901,7 @@ pub mod test { warp_config.clone(), code.clone(), r1cs.clone(), (), () ); - let mut prover_state = domainsep.instance(&0u32).std_prover(); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -903,7 +915,10 @@ pub mod test { let narg_str = prover_state.narg_string().to_vec(); let domainsep_v = spongefish::domain_separator!("test::warp"); - let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&narg_str); + let mut verifier_state = domainsep_v + .without_session() + .instance(&0u32) + .std_verifier(&narg_str); hash_chain_warp .verify( (r1cs.m, r1cs.n, r1cs.k), @@ -1001,7 +1016,7 @@ pub mod test { for _ in 0..l1 { let domainsep = spongefish::domain_separator!("test::warp"); - let mut prover_state = domainsep.instance(&0u32).std_prover(); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), _pf) = hash_chain_warp .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -1038,7 +1053,7 @@ pub mod test { warp_config.clone(), code.clone(), r1cs.clone(), (), () ); - let mut prover_state = domainsep.instance(&0u32).std_prover(); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -1052,7 +1067,10 @@ pub mod test { let narg_str = prover_state.narg_string().to_vec(); let domainsep_v = spongefish::domain_separator!("test::warp"); - let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&narg_str); + let mut verifier_state = domainsep_v + .without_session() + .instance(&0u32) + .std_verifier(&narg_str); hash_chain_warp .verify( (r1cs.m, r1cs.n, r1cs.k), diff --git a/src/protocol/domainsep/mod.rs b/src/protocol/domainsep/mod.rs index 4bd0d1f..32d1386 100644 --- a/src/protocol/domainsep/mod.rs +++ b/src/protocol/domainsep/mod.rs @@ -1,6 +1,5 @@ use ark_crypto_primitives::merkle_tree::Config; use ark_ff::Field; -use ark_std::log2; use spongefish::{ Decoding, Encoding, NargDeserialize, ProverState, VerificationResult, VerifierState, @@ -125,123 +124,49 @@ pub fn parse_statement< )) } -pub type DerivedRandomness = ( - ::InnerDigest, - Vec, - Vec>, - F, - Vec, - Vec, - Vec>, - ::InnerDigest, - F, - Vec, - Vec, - Vec, - Vec, - Vec, - Vec<[F; 3]>, -); - -pub fn derive_randomness< +/// Read `rt_0 + l1_mus`, squeeze `l1_taus + ω + τ`. Runs before the +/// twin-constraint sumcheck on the verifier side. +#[allow(clippy::type_complexity)] +pub fn derive_pre_twin_constraint< F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, MT: Config + From<[u8; 32]>>, >( - verifier_state: &mut VerifierState<'_>, + vs: &mut VerifierState<'_>, l1: usize, - log_n: usize, log_l: usize, - s: usize, - t: usize, #[allow(non_snake_case)] log_M: usize, -) -> VerificationResult> { - // read commitment digest - let rt_0_bytes: [u8; 32] = verifier_state.prover_message()?; - let rt_0: MT::InnerDigest = rt_0_bytes.into(); - - // read mus - let l1_mus: Vec = verifier_state.prover_messages_vec(l1)?; - - // challenge taus - let mut l1_taus = Vec::with_capacity(l1); - for _ in 0..l1 { - let tau: Vec = (0..log_M) - .map(|_| verifier_state.verifier_message::()) - .collect(); - l1_taus.push(tau); - } - - let omega: F = verifier_state.verifier_message(); - let tau: Vec = (0..log_l) - .map(|_| verifier_state.verifier_message::()) +) -> VerificationResult<(MT::InnerDigest, Vec, Vec>, F, Vec)> { + let rt_0: MT::InnerDigest = <[u8; 32]>::into(vs.prover_message()?); + let l1_mus = vs.prover_messages_vec(l1)?; + let l1_taus = (0..l1) + .map(|_| (0..log_M).map(|_| vs.verifier_message::()).collect()) .collect(); + let omega = vs.verifier_message(); + let tau = (0..log_l).map(|_| vs.verifier_message::()).collect(); + Ok((rt_0, l1_mus, l1_taus, omega, tau)) +} - // e. twin constraints sumcheck - let mut gamma_sumcheck = Vec::new(); - let mut coeffs_twinc_sumcheck = Vec::new(); - for _ in 0..log_l { - let h_coeffs: Vec = - verifier_state.prover_messages_vec(2 + (log_n + 1).max(log_M + 2))?; - let c: F = verifier_state.verifier_message(); - gamma_sumcheck.push(c); - coeffs_twinc_sumcheck.push(h_coeffs); - } - - // read td digest - let td_bytes: [u8; 32] = verifier_state.prover_message()?; - let _td: MT::InnerDigest = td_bytes.into(); - - // read eta and nu_0 - let eta: F = verifier_state.prover_message()?; - let nu_0: F = verifier_state.prover_message()?; - let mut nus = vec![nu_0]; - - // g. ood samples - let n_ood_samples = s * log_n; - let ood_samples: Vec = (0..n_ood_samples) - .map(|_| verifier_state.verifier_message::()) - .collect(); - - // h. ood answers - let ood_answers: Vec = verifier_state.prover_messages_vec(s)?; - nus.extend(ood_answers); - - // i. shift queries and zero check - let r = 1 + s + t; - let log_r = log2(r) as usize; - let n_shift_queries = (t * log_n).div_ceil(8); - let bytes_shift_queries: Vec = (0..n_shift_queries) - .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) - .collect(); - let xi: Vec = (0..log_r) - .map(|_| verifier_state.verifier_message::()) +/// Read `td + η + ν₀`, squeeze OOD points, read OOD answers, squeeze shift +/// query bytes and `ξ`. Runs between the two sumchecks on the verifier side. +#[allow(clippy::type_complexity)] +pub fn derive_between_sumchecks< + F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, + MT: Config + From<[u8; 32]>>, +>( + vs: &mut VerifierState<'_>, + log_n: usize, + s: usize, + t: usize, + log_r: usize, +) -> VerificationResult<(MT::InnerDigest, F, Vec, Vec, Vec, Vec)> { + let td: MT::InnerDigest = <[u8; 32]>::into(vs.prover_message()?); + let eta = vs.prover_message()?; + let mut nus = vec![vs.prover_message::()?]; + let ood_samples = (0..s * log_n).map(|_| vs.verifier_message::()).collect(); + nus.extend(vs.prover_messages_vec::(s)?); + let bytes_shift_queries = (0..(t * log_n).div_ceil(8)) + .map(|_| vs.verifier_message::<[u8; 1]>()[0]) .collect(); - - // j. batching sumcheck - let mut alpha_sumcheck = Vec::new(); - let mut sums_batching_sumcheck = Vec::new(); - for _ in 0..log_n { - let sums: [F; 3] = verifier_state.prover_messages()?; - let c: F = verifier_state.verifier_message(); - alpha_sumcheck.push(c); - sums_batching_sumcheck.push(sums); - } - - Ok(( - rt_0, - l1_mus, - l1_taus, - omega, - tau, - gamma_sumcheck, - coeffs_twinc_sumcheck, - _td, - eta, - nus, - ood_samples, - bytes_shift_queries, - xi, - alpha_sumcheck, - sums_batching_sumcheck, - )) + let xi = (0..log_r).map(|_| vs.verifier_message::()).collect(); + Ok((td, eta, nus, ood_samples, bytes_shift_queries, xi)) } diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 6aa899d..6a13b4e 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1 +1,23 @@ pub mod domainsep; + +use effsc::transcript::VerifierTranscript; +use spongefish::{Decoding, Encoding, NargDeserialize, VerifierState}; + +/// Adapter plugging spongefish's [`VerifierState`] into effsc's +/// [`VerifierTranscript`] so the verifier can call +/// [`effsc::verifier::sumcheck_verify`]. Prover side is covered by a blanket +/// impl inside effsc (on `spongefish::ProverState`). +pub struct EffscVerifierTranscript<'s, 'a>(pub &'s mut VerifierState<'a>); + +impl VerifierTranscript for EffscVerifierTranscript<'_, '_> +where + F: ark_ff::Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, +{ + type Error = spongefish::VerificationError; + fn receive(&mut self) -> Result { + self.0.prover_message::() + } + fn challenge(&mut self) -> F { + self.0.verifier_message::() + } +} diff --git a/src/relations/description.rs b/src/relations/description.rs index 032dfeb..ded163e 100644 --- a/src/relations/description.rs +++ b/src/relations/description.rs @@ -1,5 +1,5 @@ use ark_ff::Field; -use ark_relations::r1cs::{ConstraintMatrices, ConstraintSynthesizer, ConstraintSystem}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem, R1CS_PREDICATE_LABEL}; use serde::Serialize; #[derive(Serialize)] @@ -37,22 +37,27 @@ impl SerializableConstraintMatrices { .generate_constraints(constraint_system.clone()) .unwrap(); constraint_system.finalize(); - let matrices: ConstraintMatrices = constraint_system.to_matrices().unwrap(); - let serializable = SerializableConstraintMatrices::from(matrices); + + let num_instance_variables = constraint_system.num_instance_variables(); + let num_witness_variables = constraint_system.num_witness_variables(); + let num_constraints = constraint_system.num_constraints(); + + let mut matrices = constraint_system.to_matrices().unwrap(); + let mut r1cs = matrices.remove(R1CS_PREDICATE_LABEL).unwrap(); + let mut r1cs_iter = r1cs.drain(..); + let a = r1cs_iter.next().unwrap(); + let b = r1cs_iter.next().unwrap(); + let c = r1cs_iter.next().unwrap(); + + let serializable = SerializableConstraintMatrices { + num_instance_variables, + num_witness_variables, + num_constraints, + a: SerializableConstraintMatrices::serialize_nested_field(a), + b: SerializableConstraintMatrices::serialize_nested_field(b), + c: SerializableConstraintMatrices::serialize_nested_field(c), + }; let serialized = serde_json::to_string(&serializable).unwrap(); serialized.into_bytes() } } - -impl From> for SerializableConstraintMatrices { - fn from(m: ConstraintMatrices) -> Self { - Self { - num_instance_variables: m.num_instance_variables, - num_witness_variables: m.num_witness_variables, - num_constraints: m.num_constraints, - a: SerializableConstraintMatrices::serialize_nested_field(m.a), - b: SerializableConstraintMatrices::serialize_nested_field(m.b), - c: SerializableConstraintMatrices::serialize_nested_field(m.c), - } - } -} diff --git a/src/relations/r1cs/hashchain/mod.rs b/src/relations/r1cs/hashchain/mod.rs index 45c664c..4f34fe3 100644 --- a/src/relations/r1cs/hashchain/mod.rs +++ b/src/relations/r1cs/hashchain/mod.rs @@ -12,7 +12,7 @@ use ark_crypto_primitives::{ }; use ark_ff::PrimeField; use ark_r1cs_std::fields::fp::FpVar; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem}; pub use config::HashChainConfig; pub use instance::HashChainInstance; pub use relation::compute_hash_chain; diff --git a/src/relations/r1cs/hashchain/relation.rs b/src/relations/r1cs/hashchain/relation.rs index 6742e0a..2bd4999 100644 --- a/src/relations/r1cs/hashchain/relation.rs +++ b/src/relations/r1cs/hashchain/relation.rs @@ -4,7 +4,7 @@ use ark_crypto_primitives::{ }; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::fields::fp::FpVar; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef}; use ark_serialize::CanonicalSerialize; use ark_std::marker::PhantomData; @@ -95,13 +95,15 @@ where constraint_system.finalize(); let cs = constraint_system.into_inner().unwrap(); + let x = cs.instance_assignment().unwrap().to_vec(); + let w = cs.witness_assignment().unwrap().to_vec(); Self { - constraint_system: ConstraintSystemRef::new(cs.clone()), + constraint_system: ConstraintSystemRef::new(cs), config: hash_config, instance, witness, - x: cs.instance_assignment, - w: cs.witness_assignment, + x, + w, _crhs_scheme: PhantomData, _crhs_scheme_gadget: PhantomData, } diff --git a/src/relations/r1cs/hashchain/synthesizer.rs b/src/relations/r1cs/hashchain/synthesizer.rs index beb4a90..511b1ff 100644 --- a/src/relations/r1cs/hashchain/synthesizer.rs +++ b/src/relations/r1cs/hashchain/synthesizer.rs @@ -1,7 +1,7 @@ use ark_crypto_primitives::crh::{CRHScheme, CRHSchemeGadget}; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::fp::FpVar}; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use ark_std::marker::PhantomData; use crate::relations::r1cs::hashchain::{HashChainInstance, HashChainWitness}; diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index 0232a7e..b6bd5d9 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -1,8 +1,8 @@ pub mod hashchain; use ark_ff::Field; -use ark_relations::r1cs::ConstraintSystemRef; -use efficient_sumcheck::{hypercube::Hypercube, order_strategy::AscendingOrder}; +use ark_relations::gr1cs::{ConstraintSystemRef, R1CS_PREDICATE_LABEL}; +use effsc::hypercube::Ascending; use crate::error::WARPError; @@ -27,21 +27,30 @@ impl TryFrom> for R1CS { type Error = WARPError; fn try_from(cs: ConstraintSystemRef) -> Result { - let matrices = cs.to_matrices().unwrap(); + let mut matrices = cs.to_matrices().unwrap(); + let mut r1cs = matrices.remove(R1CS_PREDICATE_LABEL).unwrap(); + let mut r1cs_iter = r1cs.drain(..); + let a_mat = r1cs_iter.next().unwrap(); + let b_mat = r1cs_iter.next().unwrap(); + let c_mat = r1cs_iter.next().unwrap(); + + let num_constraints = cs.num_constraints(); + let num_instance_variables = cs.num_instance_variables(); + let num_witness_variables = cs.num_witness_variables(); // number of constraints should be to be power of 2 - let m = matrices.num_constraints.next_power_of_two(); - let n = matrices.num_instance_variables + matrices.num_witness_variables; - let k = matrices.num_witness_variables; + let m = num_constraints.next_power_of_two(); + let n = num_instance_variables + num_witness_variables; + let k = num_witness_variables; // both `unwrap()` calls below are safe since warp/lib.rs forbids compiling on platforms // with 16-bits pointers width let log_m = m.ilog2().try_into().unwrap(); let log_n = n.ilog2().try_into().unwrap(); - let mut a = matrices.a.into_iter(); - let mut b = matrices.b.into_iter(); - let mut c = matrices.c.into_iter(); + let mut a = a_mat.into_iter(); + let mut b = b_mat.into_iter(); + let mut c = c_mat.into_iter(); let mut p = vec![]; for _ in 0..m { // when there are no constraints left, we store an empty one @@ -89,10 +98,9 @@ impl BundledPESAT for R1CS { type Constraints = R1CSConstraints; fn evaluate_bundled(&self, zero_evader_evals: &[F], z: &[F]) -> Result { - let mut cube = Hypercube::::new(self.log_m); - // TODO: multithread this - cube.try_fold(F::ZERO, |acc, (index, _point)| { + Ascending::new(self.log_m).try_fold(F::ZERO, |acc, p| { + let index = p.index; let eq_tau_i = *zero_evader_evals .get(index) .ok_or(WARPError::ZeroEvaderSize(zero_evader_evals.len(), index))?; diff --git a/src/serialize.rs b/src/serialize.rs index e03ceff..d9846ff 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -29,7 +29,7 @@ pub struct AccWitnessSerializer< F: Field + PrimeField, MT: Config + From<[u8; 32]>>, > { - pub td: Vec, + pub rt: MT::InnerDigest, pub f: Vec, pub w: Vec, } @@ -42,10 +42,9 @@ impl + Fr assert_eq!(acc_witness.1.len(), 1); assert_eq!(acc_witness.2.len(), 1); let f = acc_witness.1[0].clone(); - assert_eq!(f.len(), acc_witness.0[0].leaf_nodes.len()); let w = acc_witness.2[0].clone(); Self { - td: acc_witness.0[0].clone().leaf_nodes, + rt: acc_witness.0[0].root(), f, w, } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index eb85d34..d134388 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,7 +1,6 @@ use ark_ff::{Field, PrimeField}; pub mod fields; -pub mod poly; pub mod poseidon; pub const fn chunk_size_bytes(modulus_bit_size: u32) -> usize { diff --git a/src/utils/poly.rs b/src/utils/poly.rs deleted file mode 100644 index b477c1c..0000000 --- a/src/utils/poly.rs +++ /dev/null @@ -1,25 +0,0 @@ -use ark_ff::Field; -use efficient_sumcheck::{ - hypercube::HypercubeMember, interpolation::LagrangePolynomial, order_strategy::AscendingOrder, -}; - -pub fn eq_poly(original_tau: &[F], point: usize) -> F { - // TODO (z-tech): will fix and get rid of this function - let num_variables = original_tau.len(); - let mut tau = original_tau.to_vec(); - tau.reverse(); - let tau_hat: Vec = tau.iter().map(|t| F::ONE - *t).collect(); - LagrangePolynomial::::lag_poly( - tau, - tau_hat, - HypercubeMember::new(num_variables, point), - ) -} - -pub fn eq_poly_non_binary(x: &[F], y: &[F]) -> F { - assert_eq!(x.len(), y.len()); - let res = x.iter().zip(y).fold(F::one(), |acc, (x_i, y_i)| { - acc * (*x_i * *y_i + (F::one() - x_i) * (F::one() - y_i)) - }); - res -}