feat: structural zero wire in R1CS layout (z = [w, 1, 0, x])#492
feat: structural zero wire in R1CS layout (z = [w, 1, 0, x])#492sai-deng wants to merge 1 commit intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the project’s R1CS witness vector layout to include a structural zero wire, shifting from z = [w, 1, x] to z = [w, 0, 1, x]. It threads the resulting index/length shift through the frontend constraint systems, R1CS core operations, Spartan SNARK verification logic, and updates affected tests/expected digests accordingly.
Changes:
- Introduces
CS::zero()asInput(0)and shiftsCS::one()toInput(1), with CS implementations initializing built-in inputs(ZERO, ONE). - Updates R1CS core logic to account for the extra structural wire (vector length +2, index shifts, padding/regularity checks).
- Removes
alloc_zero()and migrates call sites toAllocatedNum::zero()(zero-cost), plus updates Spartan/Neutron/Nova components and test vectors/digests.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/spartan/snark.rs | Adjusts witness concatenation and verifier-side public IO handling to include the new structural zero wire. |
| src/spartan/ppsnark.rs | Same as above for the preprocessing SNARK path. |
| src/r1cs/mod.rs | Updates core R1CS shape validation, witness length checks, satisfiability checks, padding, and random sampling for the new layout. |
| src/nova/nifs.rs | Updates hand-built R1CS test shapes to match the shifted column layout. |
| src/nova/mod.rs | Updates expected public-parameter digest test vectors impacted by the layout change. |
| src/nova/circuit/r1cs.rs | Replaces alloc_zero usage with AllocatedNum::zero() in absorption/selection paths. |
| src/nova/circuit/mod.rs | Replaces base-case zero allocation with the structural zero wire; updates expected constraint counts. |
| src/neutron/relation.rs | Updates satisfiability witness concatenation to include the structural zero wire. |
| src/neutron/nifs.rs | Updates folding computations and benchmark matrix sizing for the new z length. |
| src/neutron/mod.rs | Updates expected public-parameter digest test vectors impacted by the layout change. |
| src/neutron/circuit/relation.rs | Replaces alloc_zero with AllocatedNum::zero() for allocating T = 0. |
| src/neutron/circuit/mod.rs | Replaces base-case zero allocation with structural zero; updates expected constraint counts. |
| src/gadgets/utils.rs | Removes alloc_zero() helper (previously created an aux + constraint per call). |
| src/gadgets/ecc.rs | Migrates off alloc_zero(); uses structural AllocatedNum::zero() in relevant code paths. |
| src/frontend/util_cs/witness_cs.rs | Initializes witness CS inputs with (ZERO, ONE) and updates extend() to skip both built-ins. |
| src/frontend/util_cs/test_cs.rs | Updates test CS default inputs/names to include (ZERO, ONE). |
| src/frontend/test_shape_cs.rs | Updates default test shape CS to include named (ZERO, ONE) inputs. |
| src/frontend/shape_cs.rs | Updates shape CS default built-in input count to 2. |
| src/frontend/r1cs.rs | Frontend bridge now skips 2 built-in inputs and adjusts shape num_io accordingly; updates input index mapping comments. |
| src/frontend/gadgets/num.rs | Adds AllocatedNum::zero() wrapper for the new structural zero wire. |
| src/frontend/gadgets/multieq.rs | Ensures MultiEq forwards CS::zero() properly. |
| src/frontend/constraint_system.rs | Adds default CS::zero() and shifts default CS::one() to Input(1); forwards in wrappers. |
Comments suppressed due to low confidence (1)
src/frontend/gadgets/num.rs:56
- Doc comment for
AllocatedNum::one()says it uses the "input-0 wire", butCS::one()now maps toInput(1)(withInput(0)reserved for ZERO). Update the comment to avoid the stale index and prevent confusion about the newz = [w, 0, 1, x]layout.
/// Returns an `AllocatedNum` wrapping the built-in `CS::one()` variable.
/// Costs zero constraints since it uses the input-0 wire directly.
pub fn one<CS: ConstraintSystem<Scalar>>() -> Self {
AllocatedNum {
value: Some(Scalar::ONE),
variable: CS::one(),
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/r1cs/mod.rs
Outdated
| .map(|_| E::Scalar::random(&mut OsRng)) | ||
| .collect::<Vec<E::Scalar>>(); | ||
|
|
||
| // The zero wire must be zero |
There was a problem hiding this comment.
E is computed from Z here, but commit_T and is_sat_relaxed hardcode 0 at this position. A non-zero value would make E inconsistent with the folded equation.
There was a problem hiding this comment.
Reordered the layout from [W, 0, 1, X] to [W, 1, 0, X] so the ONE wire stays at its historical position num_vars. This avoids changing every place that extracts u = Z[num_vars] and keeps the Spartan MLE split untouched. The zero wire is now at num_vars + 1 instead. Force-pushed with the updated commit.
94a981e to
2878fa6
Compare
2878fa6 to
0bd685a
Compare
Add a built-in zero wire at Input(1) alongside the existing one wire at Input(0). The R1CS z-vector layout changes from [W, u, X] to [W, u, 0, X], keeping the ONE wire at its historical position num_vars. This gives every circuit a free CS::zero() / AllocatedNum::zero() without allocating any auxiliary variables or enforcement constraints. Each alloc_zero() call previously cost 1 aux variable + 1 constraint; this change saves 8 constraints across the Nova augmented circuit. Key changes: - ConstraintSystem trait: add zero() returning Input(1) - AllocatedNum::zero() wraps CS::zero(), zero cost - Remove alloc_zero() and all 8 call sites - R1CS z-vectors: [W, u, 0, X] with Z[num_vars+1] = 0 - Spartan MLE: public IO is (u, 0, X) - Frontend bridge: skip first 2 inputs (ONE, ZERO) for X - Update test R1CS column indices for I0/I1 (shifted by +1) - Update constraint counts and PP digests
0bd685a to
62e3780
Compare
Change the R1CS vector layout from
z = [w, 1, x]toz = [w, 1, 0, x],making zero a structural part of the vector like the existing one-wire.
The ONE wire stays at its historical position
num_vars(unchanged).CS::one()returnsInput(0)(unchanged),CS::zero()returnsInput(1)(new)num_vars + 2 + num_io, column validation adjusted(u, 0, X)instead of(u, X)AllocatedNum::zero()wrappingCS::zero()(zero constraints)alloc_zero()(was 1 aux + 1 constraint per call, 8 call sites)