diff --git a/Cargo.lock b/Cargo.lock index 24bddaf31..08375811c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,7 +169,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -180,7 +180,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1515,7 +1515,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1732,7 +1732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2606,7 +2606,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3726,7 +3726,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4529,7 +4529,6 @@ dependencies = [ "postcard", "provekit-backend-bn254", "provekit-common", - "provekit-gnark", "provekit-r1cs-compiler", "provekit-spark", "rayon", @@ -5265,7 +5264,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5934,7 +5933,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -6156,7 +6155,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6186,7 +6185,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6862,7 +6861,6 @@ dependencies = [ "chrono", "provekit-backend-bn254", "provekit-common", - "provekit-gnark", "reqwest", "serde", "serde_json", @@ -7049,7 +7047,7 @@ dependencies = [ [[package]] name = "whir" version = "0.1.0" -source = "git+https://github.com/worldfnd/whir.git?rev=8804e80e8e890d01bb585f2bd5e5b564ac0fd80d#8804e80e8e890d01bb585f2bd5e5b564ac0fd80d" +source = "git+https://github.com/worldfnd/whir.git?rev=78e9460420da0954768feb1e52f581a0529c2124#78e9460420da0954768feb1e52f581a0529c2124" dependencies = [ "ark-ff", "ark-serialize", @@ -7099,7 +7097,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2818cfc73..44073997b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,9 @@ reqwest = "0.12.23" ruint = { version = "1.12.3", features = ["num-traits", "rand"] } seq-macro = "0.3.6" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +# `float_roundtrip`: zook configs carry f64s; the default fast parser is off +# by one ULP on read-back, breaking scheme JSON roundtrips. +serde_json = { version = "1.0", features = ["float_roundtrip"] } # Pinned: blake3 1.8.4+ depends on digest 0.11, but the `whir` crate's # blake3 engine is built against digest 0.10. Bumping breaks compilation. blake3 = "=1.8.3" @@ -212,4 +214,4 @@ spongefish = { git = "https://github.com/arkworks-rs/spongefish", features = [ "sha2", ], rev = "fcc277f8a857fdeeadd7cca92ab08de63b1ff1a1" } spongefish-pow = { git = "https://github.com/arkworks-rs/spongefish", rev = "fcc277f8a857fdeeadd7cca92ab08de63b1ff1a1" } -whir = { git = "https://github.com/worldfnd/whir.git", rev = "8804e80e8e890d01bb585f2bd5e5b564ac0fd80d", features = ["tracing", "rs_in_order"] } +whir = { git = "https://github.com/worldfnd/whir.git", rev = "78e9460420da0954768feb1e52f581a0529c2124", features = ["tracing", "rs_in_order"] } diff --git a/provekit/common/src/prefix_covector.rs b/provekit/common/src/prefix_covector.rs index e48717254..cfdb79cd3 100644 --- a/provekit/common/src/prefix_covector.rs +++ b/provekit/common/src/prefix_covector.rs @@ -186,6 +186,12 @@ pub fn make_public_weight(x: F, num_public_inputs: usize, m: usize) -> PrefixCovector::new(public_weights, domain_size) } +/// Collect `&dyn LinearForm` references to a slice of covectors. +#[must_use] +pub fn linear_form_refs>(weights: &[W]) -> Vec<&dyn LinearForm> { + weights.iter().map(|w| w as &dyn LinearForm).collect() +} + /// Build [`PrefixCovector`] weights from alpha vectors, consuming the alphas. /// /// Each alpha vector is padded to a power-of-two length (min 2) and wrapped diff --git a/provekit/common/src/whir_r1cs.rs b/provekit/common/src/whir_r1cs.rs index 8c8c36612..8cb43283e 100644 --- a/provekit/common/src/whir_r1cs.rs +++ b/provekit/common/src/whir_r1cs.rs @@ -18,11 +18,40 @@ use { }, serde::{Deserialize, Serialize}, whir::{ - algebra::embedding::Identity, engines::EngineId, parameters::ProtocolParameters, - protocols::whir::Config as GenericWhirConfig, transcript, + algebra::embedding::Identity, + engines::EngineId, + parameters::ProtocolParameters, + protocols::{ + params::{ + DecodingRegime, FoldingFactor, KneeWeight, Mode, PowBudget, RateSchedule, + SecuritySpec, TuningSpec, + }, + whir::Config as GenericWhirConfig, + zook::ProtocolConfig as ZookConfig, + }, + transcript, }, }; +/// Zook mode for the witness commitment; [`Mode::ZeroKnowledge`] makes it +/// hiding. +const ZOOK_MODE: Mode = Mode::Standard; + +/// Adaptive per-round rate schedule at the balanced (0.5) prover-time / +/// proof-size knee. +const ZOOK_RATE_SCHEDULE: RateSchedule = RateSchedule::Adaptive { + knee_weight: KneeWeight::DEFAULT, +}; + +/// Target security level for the witness and blinding commitments, in bits. +const WHIR_SECURITY_BITS: u32 = 128; + +/// Per-slot proof-of-work budget for both commitments, in bits. +const WHIR_POW_BITS: u32 = 10; + +/// Starting log inverse rate shared by both commitments. +const WHIR_STARTING_LOG_INV_RATE: u32 = 2; + /// WHIR witness-domain floor: prover work is flat at or below `2^13` variables, /// so smaller commitments are padded up to this many variables. pub const MIN_WHIR_NUM_VARIABLES: usize = 13; @@ -82,11 +111,13 @@ impl R1csHash { /// /// # Zero-knowledge /// -/// The ZK posture is fixed, not configurable: /// - Sumcheck ZK is always on: the Spartan sumcheck rounds are masked by a /// blinding polynomial `g`, committed separately in `whir_blinding`. -/// - Witness ZK is off: the witness is committed non-hiding in `whir_witness`, -/// whose WHIR openings leak witness values. It will be enabled by zkWHIR 3.0. +/// - Witness ZK follows the [`Mode`] recorded in `whir_witness` (new schemes +/// use `ZOOK_MODE`): [`Mode::Standard`] is non-hiding, +/// [`Mode::ZeroKnowledge`] hiding. A hiding commitment alone is not full +/// proof ZK — the alpha, public-input, and challenge evaluations are still +/// sent in the clear. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(bound = "")] pub struct WhirR1CSScheme { @@ -97,10 +128,8 @@ pub struct WhirR1CSScheme { pub num_challenges: usize, pub challenge_offsets: Vec, pub has_public_inputs: bool, - /// Base-field witness commitment. Non-hiding — WHIR openings leak witness - /// values, so this provides sumcheck ZK only, not witness ZK. - /// TODO: make the witness commitment hiding for full witness ZK. - pub whir_witness: GenericWhirConfig, + /// Base-field witness commitment, via zook. + pub whir_witness: ZookConfig, /// Separate ext-field commitment to the Spartan blinding polynomial `g`; /// masking the ext-valued sumcheck rounds needs ext randomness, so `g` /// cannot ride on the base witness commitment. @@ -152,7 +181,7 @@ impl WhirR1CSScheme

{ challenge_offsets: Vec, has_public_inputs: bool, hash_config: HashConfig, - ) -> Self { + ) -> anyhow::Result { let mut scheme = Self::new_from_dimensions( r1cs.num_witnesses(), r1cs.num_constraints(), @@ -162,9 +191,9 @@ impl WhirR1CSScheme

{ challenge_offsets, has_public_inputs, hash_config, - ); + )?; scheme.r1cs_hash = r1cs.hash(); - scheme + Ok(scheme) } /// Build a scheme from raw dimensions, leaving `r1cs_hash` unset (the @@ -179,14 +208,13 @@ impl WhirR1CSScheme

{ challenge_offsets: Vec, has_public_inputs: bool, hash_config: HashConfig, - ) -> Self { - assert_eq!( - num_challenges, - challenge_offsets.len(), + ) -> anyhow::Result { + anyhow::ensure!( + num_challenges == challenge_offsets.len(), "num_challenges ({num_challenges}) != challenge_offsets.len() ({})", challenge_offsets.len() ); - assert!(w1_size <= num_witnesses, "w1_size exceeds total witnesses"); + anyhow::ensure!(w1_size <= num_witnesses, "w1_size exceeds total witnesses"); let w2_size = num_witnesses - w1_size; let m1_raw = next_power_of_two(w1_size); @@ -196,11 +224,11 @@ impl WhirR1CSScheme

{ let m = m1_raw.max(m2_raw).max(MIN_WHIR_NUM_VARIABLES); let m_0 = m0_raw.max(MIN_SUMCHECK_NUM_VARIABLES); - Self { + Ok(Self { m, m_0, a_num_terms: next_power_of_two(a_num_entries), - whir_witness: Self::new_witness_config_for_size(m, hash_config.engine_id()), + whir_witness: Self::new_witness_config_for_size(m, hash_config.engine_id())?, whir_blinding: Self::new_blinding_config_for_size(m_0, hash_config.engine_id()), w1_size, num_challenges, @@ -208,18 +236,36 @@ impl WhirR1CSScheme

{ has_public_inputs, r1cs_hash: R1csHash::UNSET, hash_config, - } + }) } - /// Build the non-ZK witness WHIR config: commits in `P`'s base field, opens - /// at extension-field points. + /// Build the zook witness config: commits in `P`'s base field, opens at + /// extension-field points. pub fn new_witness_config_for_size( num_variables: usize, hash_id: EngineId, - ) -> GenericWhirConfig { + ) -> anyhow::Result> { P::register(); let nv = num_variables.max(MIN_WHIR_NUM_VARIABLES); - GenericWhirConfig::::new(1 << nv, &whir_protocol_params(hash_id, 1)) + let security = SecuritySpec { + mode: ZOOK_MODE, + decoding_regime: DecodingRegime::Johnson, + target_security_bits: WHIR_SECURITY_BITS, + pow_budget: PowBudget::per_slot(WHIR_POW_BITS), + hash_id, + }; + let tuning = TuningSpec { + vector_size: 1 << nv, + starting_log_inv_rate: WHIR_STARTING_LOG_INV_RATE, + folding_factor: FoldingFactor::ConstantFromSecondRound { + initial: WHIR_INITIAL_FOLDING_FACTOR, + rest: WHIR_FOLDING_FACTOR, + }, + rate_schedule: ZOOK_RATE_SCHEDULE, + }; + ZookConfig::::derive(security, tuning).map_err(|e| { + anyhow::anyhow!("zook witness config derivation failed at {nv} variables: {e}") + }) } /// Build the WHIR config for the blinding polynomial `g`: its `4 * m_0` diff --git a/provekit/prover/src/whir_r1cs.rs b/provekit/prover/src/whir_r1cs.rs index 24adf5308..bab35462f 100644 --- a/provekit/prover/src/whir_r1cs.rs +++ b/provekit/prover/src/whir_r1cs.rs @@ -9,8 +9,8 @@ use { provekit_common::{ prefix_covector::{ build_prefix_covectors, compute_alpha_evals, compute_challenge_eval, - compute_public_eval, expand_powers, make_challenge_weight, make_public_weight, - OffsetCovector, + compute_public_eval, expand_powers, linear_form_refs, make_challenge_weight, + make_public_weight, OffsetCovector, }, utils::{ pad_to_power_of_two, @@ -31,7 +31,7 @@ use { mixed_dot, }, buffer::{Buffer, BufferOps}, - protocols::whir::Witness as WhirWitness, + protocols::{whir::Witness as WhirWitness, zook::CommittedWitness as ZookCommittedWitness}, transcript::{Codec, DuplexSpongeInterface, ProverState, VerifierMessage}, }, }; @@ -48,7 +48,7 @@ pub struct BlindingState { } pub struct WhirR1CSCommitment { - pub witness: WhirWitness, P::Embedding>, + pub witness: ZookCommittedWitness, pub polynomial: Buffer>, pub blinding: Option>, } @@ -135,18 +135,22 @@ where "R1CS constraints exceed scheme capacity" ); - let num_vars = self.whir_witness.initial_num_variables(); - let target_len = 1usize << num_vars; + let target_len = self.whir_witness.tuning().vector_size; let mut padded_witness = pad_to_power_of_two(witness); if padded_witness.len() < target_len { padded_witness.resize(target_len, >::zero()); } + ensure!( + padded_witness.len() == target_len, + "witness length exceeds the zook commitment size; scheme dimensions and witness \ + config are inconsistent" + ); - // Commit the base-field witness directly (non-hiding — openings leak - // witness values; see the `whir_witness` field docs). + // zook's `commit` consumes the buffer; the prove stage still needs + // the message for the covector evaluations, so pass a clone. let padded_witness = Buffer::from(padded_witness); - let witness_commitment = self.whir_witness.commit(merlin, &[&padded_witness]); + let witness_commitment = self.whir_witness.commit(merlin, padded_witness.clone()); // Commit the Spartan sumcheck blinding `g` separately, natively in the // extension field. Transcript order: this commitment is absorbed @@ -391,18 +395,13 @@ where let evaluations = compute_evaluations(&embedding, &weights, &commitment.polynomial); - let boxed_weights: Vec>>> = weights - .into_iter() - .map(|w| Box::new(w) as Box>>) - .collect(); + let WhirR1CSCommitment { witness, .. } = commitment; - let final_claim = scheme.whir_witness.prove( - &mut merlin, - &[&commitment.polynomial], - vec![&commitment.witness], - boxed_weights, - Buffer::from(evaluations), - ); + let form_refs = linear_form_refs(&weights); + + let final_claim = scheme + .whir_witness + .prove(&mut merlin, witness, &form_refs, &evaluations); spark_row.zip(spark_weights).map(|(row, spark_weights)| { let [claimed_a, claimed_b, claimed_c] = @@ -470,11 +469,9 @@ where None }; - let WhirR1CSCommitment { - witness: w1, - polynomial: p1, - .. - } = c1; + let WhirR1CSCommitment { witness: w1, .. } = c1; + let WhirR1CSCommitment { witness: w2, .. } = c2; + let (final_claim_1, claimed_1) = { let mut weights = build_prefix_covectors(scheme.m, alphas_1); @@ -495,31 +492,17 @@ where } evaluations.extend_from_slice(&evals_1); - let boxed_weights: Vec>>> = weights - .into_iter() - .map(|w| Box::new(w) as Box>>) - .collect(); - - let final_claim = scheme.whir_witness.prove( - &mut merlin, - &[&p1], - vec![&w1], - boxed_weights, - Buffer::from(evaluations), - ); + let form_refs = linear_form_refs(&weights); + + let final_claim = scheme + .whir_witness + .prove(&mut merlin, w1, &form_refs, &evaluations); let claimed = spark_weights.map(|sw| evaluate_spark_weights(sw, &final_claim.evaluation_point)); (final_claim, claimed) }; - drop(p1); - drop(w1); - - let WhirR1CSCommitment { - witness: w2, - polynomial: p2, - .. - } = c2; + let (final_claim_2, claimed_2) = { let weights = build_prefix_covectors(scheme.m, alphas_2); @@ -534,24 +517,19 @@ where let mut evaluations: Vec> = evals_2; - let mut boxed_weights: Vec>>> = weights - .into_iter() - .map(|w| Box::new(w) as Box>>) - .collect(); - - if let Some(ce) = challenge_eval { - let cw = make_challenge_weight(x, &scheme.challenge_offsets, scheme.m); + let challenge_covector = challenge_eval.map(|ce| { evaluations.push(ce); - boxed_weights.push(Box::new(cw)); + make_challenge_weight(x, &scheme.challenge_offsets, scheme.m) + }); + + let mut form_refs = linear_form_refs(&weights); + if let Some(ref cw) = challenge_covector { + form_refs.push(cw as &dyn LinearForm>); } - let final_claim = scheme.whir_witness.prove( - &mut merlin, - &[&p2], - vec![&w2], - boxed_weights, - Buffer::from(evaluations), - ); + let final_claim = scheme + .whir_witness + .prove(&mut merlin, w2, &form_refs, &evaluations); let claimed = spark_weights.map(|sw| evaluate_spark_weights(sw, &final_claim.evaluation_point)); diff --git a/provekit/r1cs-compiler/src/noir_proof_scheme.rs b/provekit/r1cs-compiler/src/noir_proof_scheme.rs index 6c802dedb..02f767fee 100644 --- a/provekit/r1cs-compiler/src/noir_proof_scheme.rs +++ b/provekit/r1cs-compiler/src/noir_proof_scheme.rs @@ -165,7 +165,7 @@ impl NoirCompiler { challenge_offsets, has_public_inputs, hash_config, - ); + )?; Ok(NoirProofScheme::Noir(NoirSchemeData { program: program.bytecode, @@ -237,7 +237,7 @@ impl MavrosCompiler { challenge_offsets, num_public_inputs > 0, hash_config, - ); + )?; whir_for_witness.r1cs_hash = r1cs.hash(); Ok(NoirProofScheme::Mavros(MavrosSchemeData { diff --git a/provekit/r1cs-compiler/src/whir_r1cs.rs b/provekit/r1cs-compiler/src/whir_r1cs.rs index e49b833f3..d8d78600d 100644 --- a/provekit/r1cs-compiler/src/whir_r1cs.rs +++ b/provekit/r1cs-compiler/src/whir_r1cs.rs @@ -22,7 +22,9 @@ pub trait MavrosSchemeBuilder { challenge_offsets: Vec, has_public_inputs: bool, hash_config: HashConfig, - ) -> Self; + ) -> anyhow::Result + where + Self: Sized; } impl MavrosSchemeBuilder for WhirR1CSScheme { @@ -33,7 +35,9 @@ impl MavrosSchemeBuilder for WhirR1CSScheme { challenge_offsets: Vec, has_public_inputs: bool, hash_config: HashConfig, - ) -> Self { + ) -> anyhow::Result { + provekit_backend_bn254::register(); + let num_witnesses = r1cs.witness_layout.size(); let num_constraints = r1cs.constraints.len(); let a_num_entries: usize = r1cs.constraints.iter().map(|c| c.a.len()).sum(); @@ -82,7 +86,8 @@ mod tests { vec![], false, HashConfig::Sha256, - ); + ) + .expect("scheme from dimensions"); assert_eq!(from_dimensions.m, expected_m); assert_eq!(from_dimensions.m_0, expected_m_0); assert_eq!(from_dimensions.w1_size, w1_size); @@ -95,7 +100,8 @@ mod tests { vec![], false, HashConfig::Sha256, - ); + ) + .expect("scheme from r1cs"); assert_eq!(from_r1cs.m, expected_m); assert_eq!(from_r1cs.m_0, expected_m_0); assert_eq!(from_r1cs.w1_size, w1_size); @@ -105,17 +111,24 @@ mod tests { } /// Assert both WHIR commitments reach 128-bit security for field `P`. - fn assert_configs_secure(size: usize) { let field = std::any::type_name::

(); - let witness = WhirR1CSScheme::

::new_witness_config_for_size(size, whir::hash::SHA2); + let witness = WhirR1CSScheme::

::new_witness_config_for_size(size, whir::hash::SHA2) + .expect("witness config derivation"); let blinding = WhirR1CSScheme::

::new_blinding_config_for_size(size, whir::hash::SHA2); - let sec_witness = witness.security_level(witness.initial_committer.num_vectors(), 1); - let sec_blinding = blinding.security_level(blinding.initial_committer.num_vectors(), 1); - assert!( - sec_witness >= 128.0, - "Witness commitment security {sec_witness:.2} < 128 bits at size {size} for {field}" + + // zook's `validate` checks the plan against its own `SecuritySpec`. + witness.validate().unwrap_or_else(|e| { + panic!("Witness commitment fails zook validation at size {size} for {field}: {e:?}") + }); + assert_eq!( + witness.security().target_security_bits, + 128, + "Witness commitment target must stay at 128 bits at size {size} for {field}" ); + + // The blinding commitment is still a plain WHIR config. + let sec_blinding = blinding.security_level(blinding.initial_committer.num_vectors(), 1); assert!( sec_blinding >= 128.0, "Blinding commitment security {sec_blinding:.2} < 128 bits at size {size} for {field}" @@ -145,7 +158,8 @@ mod tests { vec![0, 1], false, HashConfig::Sha256, - ); + ) + .expect("mavros-sized scheme"); assert_eq!(scheme.m, 19); } diff --git a/provekit/verifier/src/whir_r1cs.rs b/provekit/verifier/src/whir_r1cs.rs index c4ea7ddac..0ff7b8e6a 100644 --- a/provekit/verifier/src/whir_r1cs.rs +++ b/provekit/verifier/src/whir_r1cs.rs @@ -1,11 +1,14 @@ use { anyhow::{ensure, Context, Result}, ark_ff::Field, - ark_std::One, + ark_std::{ + rand::distributions::{Distribution, Standard}, + One, + }, provekit_common::{ prefix_covector::{ - build_prefix_covectors, expand_powers, make_challenge_weight, make_public_weight, - OffsetCovector, + build_prefix_covectors, expand_powers, linear_form_refs, make_challenge_weight, + make_public_weight, OffsetCovector, }, utils::sumcheck::{ calculate_eq, eval_cubic_poly, multiply_transposed_by_eq_alpha, transpose_r1cs_matrices, @@ -36,7 +39,10 @@ pub trait WhirR1CSVerifier { ) -> Result<()>; } -impl WhirR1CSVerifier

for WhirR1CSScheme

{ +impl WhirR1CSVerifier

for WhirR1CSScheme

+where + Standard: Distribution>, +{ #[instrument(skip_all)] fn verify( &self, @@ -184,30 +190,24 @@ impl WhirR1CSVerifier

for WhirR1CSScheme

{ None }; - let weight_refs_1: Vec<&dyn LinearForm>> = weights_1 - .iter() - .map(|w| w as &dyn LinearForm>) - .collect(); + let weight_refs_1 = linear_form_refs(&weights_1); let fc_1 = self .whir_witness - .verify(&mut arthur, &[&commitment_1], &evaluations_1) + .verify(&mut arthur, commitment_1, &weight_refs_1, &evaluations_1) .map_err(|_| anyhow::anyhow!("WHIR verification failed for c1"))?; - fc_1.verify(weight_refs_1.iter().copied()) + fc_1.verify(&weight_refs_1) .map_err(|_| anyhow::anyhow!("WHIR final-claim check failed for c1"))?; - let mut weight_refs_2: Vec<&dyn LinearForm>> = weights_2 - .iter() - .map(|w| w as &dyn LinearForm>) - .collect(); + let mut weight_refs_2 = linear_form_refs(&weights_2); if let Some(ref cw) = challenge_covector { weight_refs_2.push(cw as &dyn LinearForm>); } let fc_2 = self .whir_witness - .verify(&mut arthur, &[&commitment_2], &evaluations_2) + .verify(&mut arthur, commitment_2, &weight_refs_2, &evaluations_2) .map_err(|_| anyhow::anyhow!("WHIR verification failed for c2"))?; - fc_2.verify(weight_refs_2.iter().copied()) + fc_2.verify(&weight_refs_2) .map_err(|_| anyhow::anyhow!("WHIR final-claim check failed for c2"))?; ( @@ -241,16 +241,13 @@ impl WhirR1CSVerifier

for WhirR1CSScheme

{ evals.to_vec() }; - let weight_refs: Vec<&dyn LinearForm>> = weights - .iter() - .map(|w| w as &dyn LinearForm>) - .collect(); + let weight_refs = linear_form_refs(&weights); let fc = self .whir_witness - .verify(&mut arthur, &[&commitment_1], &evaluations) + .verify(&mut arthur, commitment_1, &weight_refs, &evaluations) .map_err(|_| anyhow::anyhow!("WHIR verification failed"))?; - fc.verify(weight_refs.iter().copied()) + fc.verify(&weight_refs) .map_err(|_| anyhow::anyhow!("WHIR final-claim check failed"))?; (evals[0], evals[1], evals[2]) diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index aa95e19d5..f5a75094b 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -13,7 +13,6 @@ repository.workspace = true mavros-artifacts.workspace = true provekit-common.workspace = true provekit-backend-bn254 = { workspace = true, features = ["witness-generation", "parallel"] } -provekit-gnark.workspace = true provekit-r1cs-compiler.workspace = true provekit-spark.workspace = true diff --git a/tooling/cli/src/cmd/generate_gnark_inputs.rs b/tooling/cli/src/cmd/generate_gnark_inputs.rs index fd04de451..31bcd4041 100644 --- a/tooling/cli/src/cmd/generate_gnark_inputs.rs +++ b/tooling/cli/src/cmd/generate_gnark_inputs.rs @@ -1,13 +1,4 @@ -use { - crate::Command, - anyhow::{Context, Result}, - argh::FromArgs, - provekit_backend_bn254::{Bn254Field, ProvekitProof, Verifier}, - provekit_common::file::read, - provekit_gnark::write_gnark_parameters_to_file, - std::{fs::File, io::Write, path::PathBuf}, - tracing::{info, instrument}, -}; +use {crate::Command, anyhow::Result, argh::FromArgs, std::path::PathBuf, tracing::instrument}; /// Generate input compatible with gnark. #[derive(FromArgs, PartialEq, Eq, Debug)] @@ -37,43 +28,12 @@ pub struct Args { impl Command for Args { #[instrument(skip_all)] fn run(&self) -> Result<()> { - let verifier: Verifier = - read(&self.verifier_path).context("while reading Verifier data")?; - let (constraints, witnesses) = ( - verifier.r1cs.num_constraints(), - verifier.r1cs.num_witnesses(), + // The gnark exporter cannot describe zook's `ProtocolConfig`; the Go + // recursive verifier needs a paired update first. + anyhow::bail!( + "cannot write gnark parameters to {}: gnark parameter export is not supported with \ + the zook witness commitment; the Go recursive verifier needs a paired update", + self.params_for_recursive_verifier ); - info!(constraints, witnesses, "Read verifier data"); - - // Read the proof - let proof: ProvekitProof = - read(&self.proof_path).context("while reading proof")?; - - let wfw = verifier - .whir_for_witness - .as_ref() - .context("verifier is missing whir_for_witness config")?; - - write_gnark_parameters_to_file( - &verifier.whir_for_witness.clone().unwrap(), - &wfw.whir_witness, - &wfw.whir_blinding, - &proof.whir_r1cs_proof, - wfw.m_0, - wfw.m, - wfw.a_num_terms, - wfw.num_challenges, - wfw.w1_size, - &proof.public_inputs, - &self.params_for_recursive_verifier, - ); - - let json = - serde_json::to_string(&verifier.r1cs).context("while serializing R1CS to JSON")?; - let mut file = File::create(&self.r1cs_path).context("while creating R1CS file")?; - file.write_all(json.as_bytes()) - .context("while writing R1CS file")?; - - Ok(()) } } diff --git a/tooling/provekit-fixtures/src/harness.rs b/tooling/provekit-fixtures/src/harness.rs index 8aec25db9..e909424cd 100644 --- a/tooling/provekit-fixtures/src/harness.rs +++ b/tooling/provekit-fixtures/src/harness.rs @@ -48,7 +48,7 @@ where Vec::new(), !public_inputs.is_empty(), HASH, - ); + )?; let instance = public_inputs.hash_bytes::

(HASH); let ds = scheme.create_domain_separator().instance(&instance); @@ -182,7 +182,7 @@ where challenge_offsets.clone(), !public.is_empty(), hash, - ); + )?; let num_witnesses = r1cs.num_witnesses(); let num_constraints = r1cs.num_constraints(); @@ -265,7 +265,7 @@ where challenge_offsets, false, HASH, - ); + )?; let num_witnesses = r1cs.num_witnesses(); let num_constraints = r1cs.num_constraints(); @@ -302,18 +302,18 @@ pub struct ProveInputs { /// Build the scheme and owned witness copies for a single-commit prove. Kept /// separate from [`time_prove_core`] so a profiler can install its subscriber /// between setup and the timed region (only the latter is captured). -pub fn prove_setup

(r1cs: &R1CS>, witness: &[Base

]) -> ProveInputs

+pub fn prove_setup

(r1cs: &R1CS>, witness: &[Base

]) -> Result> where P: FieldHash, Standard: Distribution> + Distribution>, { - let scheme = WhirR1CSScheme::

::new_for_r1cs(r1cs, witness.len(), 0, Vec::new(), true, HASH); - ProveInputs { + let scheme = WhirR1CSScheme::

::new_for_r1cs(r1cs, witness.len(), 0, Vec::new(), true, HASH)?; + Ok(ProveInputs { scheme, r1cs: r1cs.clone(), witness_commit: witness.to_vec(), witness_prove: witness.to_vec(), - } + }) } /// Time the single-commit proving core — `commit` + `prove_noir` — from diff --git a/tooling/provekit-fixtures/tests/profile.rs b/tooling/provekit-fixtures/tests/profile.rs index 70f333452..1c7d61c2b 100644 --- a/tooling/provekit-fixtures/tests/profile.rs +++ b/tooling/provekit-fixtures/tests/profile.rs @@ -40,7 +40,7 @@ where let public_inputs = PublicInputs::from_vec(vec![w[1]]); // Only commit + prove_noir are timed (setup excluded above). - let inp = prove_setup::

(&r1cs, &w); + let inp = prove_setup::

(&r1cs, &w).expect("prove setup"); let (prove_t, proof, scheme) = time_prove_core::

(inp, &public_inputs).expect("prove"); let t = Instant::now(); @@ -141,7 +141,8 @@ fn flamegraph_2pow20_bf() { let depth = (1usize << log_size) - 2; let (r1cs, w) = squaring_chain::>(2, depth); let public_inputs = PublicInputs::from_vec(vec![w[1]]); - let inp = prove_setup::(&r1cs, &w); + let inp = prove_setup::(&r1cs, &w) + .expect("prove setup"); // Global subscriber required: prove() uses rayon, and only the global // default captures spans created on rayon worker threads. diff --git a/tooling/verifier-server/Cargo.toml b/tooling/verifier-server/Cargo.toml index 66435cb27..6fc5cc3dd 100644 --- a/tooling/verifier-server/Cargo.toml +++ b/tooling/verifier-server/Cargo.toml @@ -12,7 +12,6 @@ repository.workspace = true # Workspace crates provekit-common.workspace = true provekit-backend-bn254.workspace = true -provekit-gnark.workspace = true # 3rd party anyhow.workspace = true diff --git a/tooling/verifier-server/src/services/verification.rs b/tooling/verifier-server/src/services/verification.rs index f1b7a6736..f4768261f 100644 --- a/tooling/verifier-server/src/services/verification.rs +++ b/tooling/verifier-server/src/services/verification.rs @@ -10,7 +10,6 @@ use { services::artifact::ArtifactPaths, }, provekit_backend_bn254::{Bn254Field, ProvekitProof, Verifier}, - provekit_gnark::write_gnark_parameters_to_file, std::time::Instant, tokio_util::sync::CancellationToken, tracing::{info, warn}, @@ -35,6 +34,9 @@ impl VerificationService { } /// Perform complete proof verification + /// + /// Currently always fails: gnark parameters cannot be exported with the + /// zook witness commitment (see `Self::prepare_gnark_parameters`). pub async fn verify_proof( &self, request: &VerifyRequest, @@ -66,43 +68,20 @@ impl VerificationService { } /// Prepare gnark parameters file for verification + /// + /// The gnark exporter cannot describe zook's `ProtocolConfig`; the Go + /// recursive verifier needs a paired update before this can be restored. fn prepare_gnark_parameters( &self, - proof: &ProvekitProof, - verifier: &Verifier, - paths: &ArtifactPaths, + _proof: &ProvekitProof, + _verifier: &Verifier, + _paths: &ArtifactPaths, ) -> AppResult<()> { - info!( - gnark_params_file = %paths.gnark_params_file.display(), - "Preparing gnark parameters" - ); - - let gnark_params_path = paths - .gnark_params_file - .to_str() - .ok_or_else(|| AppError::Internal("Invalid gnark params path".to_string()))?; - - let whir_scheme = verifier - .whir_for_witness - .as_ref() - .ok_or_else(|| AppError::Internal("WHIR scheme not found in verifier".to_string()))?; - - write_gnark_parameters_to_file( - whir_scheme, - &whir_scheme.whir_witness, - &whir_scheme.whir_blinding, - &proof.whir_r1cs_proof, - whir_scheme.m_0, - whir_scheme.m, - whir_scheme.a_num_terms, - whir_scheme.num_challenges, - whir_scheme.w1_size, - &proof.public_inputs, - gnark_params_path, - ); - - info!("Gnark parameters prepared successfully"); - Ok(()) + Err(AppError::Internal( + "gnark parameter export is not supported with the zook witness commitment; the Go \ + recursive verifier needs a paired update" + .to_string(), + )) } /// Execute the external verifier binary @@ -181,7 +160,6 @@ impl VerificationService { tokio::select! { _ = token.cancelled() => { info!("Stdout logging task cancelled"); - return; } _ = async { while let Ok(Some(line)) = lines.next_line().await { @@ -206,7 +184,6 @@ impl VerificationService { tokio::select! { _ = token.cancelled() => { warn!("Stderr logging task cancelled"); - return; } _ = async { while let Ok(Some(line)) = lines.next_line().await {