Skip to content

Commit f594536

Browse files
authored
fix: improve R1CS folding test to verify cross-terms (#311)
* fix: improve R1CS folding test to verify cross-terms The test previously only checked folding of identical instances, missing cross-term verification. Description ----------- Now it: - Tests different R1CS instances (x=3 and x=-2) - Explicitly verifies cross-term computation (Az₁Bz₂ + Az₂Bz₁ - u₁Cz₂ - u₂Cz₁) - Verifies the complete folding operation with different instances Testing the introduced fix -------------------------- clone this pr branch and from the root directory, run: ---- cargo test --package nexus-nova --lib -- r1cs::tests::folded_with_relaxed_instance_is_satisfied --exact --show-output ---- * refact: cargo fmt && cargo clippy
1 parent b2b38d3 commit f594536

File tree

1 file changed

+99
-37
lines changed

1 file changed

+99
-37
lines changed

nova/src/r1cs/mod.rs

+99-37
Original file line numberDiff line numberDiff line change
@@ -725,28 +725,29 @@ pub(crate) mod tests {
725725
let shape = R1CSShape::<G>::new(NUM_CONSTRAINTS, NUM_WITNESS, NUM_PUBLIC, &a, &b, &c)?;
726726
let X = to_field_elements::<G>(&[1, 35]);
727727
let W = to_field_elements::<G>(&[3, 9, 27, 30]);
728-
let commitment_W = PedersenCommitment::<G>::commit(&pp, &W);
728+
let commitment_W = PedersenCommitment::<G>::commit(&pp, &W[..]);
729729

730-
let instance = R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_W, &X)?;
731-
let witness = R1CSWitness::<G>::new(&shape, &W)?;
730+
let instance =
731+
R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_W, &X[..])?;
732+
let witness = R1CSWitness::<G>::new(&shape, &W[..])?;
732733

733734
shape.is_satisfied(&instance, &witness, &pp)?;
734735

735736
// Change commitment.
736737
let invalid_commitment = commitment_W.double();
737738
let instance =
738-
R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &invalid_commitment, &X)?;
739+
R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &invalid_commitment, &X[..])?;
739740
assert_eq!(
740741
shape.is_satisfied(&instance, &witness, &pp),
741742
Err(Error::NotSatisfied)
742743
);
743744

744745
// Provide invalid witness.
745746
let invalid_W = to_field_elements::<G>(&[4, 9, 27, 30]);
746-
let commitment_invalid_W = PedersenCommitment::<G>::commit(&pp, &W);
747+
let commitment_invalid_W = PedersenCommitment::<G>::commit(&pp, &W[..]);
747748
let instance =
748-
R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_invalid_W, &X)?;
749-
let invalid_witness = R1CSWitness::<G>::new(&shape, &invalid_W)?;
749+
R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_invalid_W, &X[..])?;
750+
let invalid_witness = R1CSWitness::<G>::new(&shape, &invalid_W[..])?;
750751
assert_eq!(
751752
shape.is_satisfied(&instance, &invalid_witness, &pp),
752753
Err(Error::NotSatisfied)
@@ -755,7 +756,7 @@ pub(crate) mod tests {
755756
// Provide invalid public input.
756757
let invalid_X = to_field_elements::<G>(&[1, 36]);
757758
let instance =
758-
R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_W, &invalid_X)?;
759+
R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_W, &invalid_X[..])?;
759760
assert_eq!(
760761
shape.is_satisfied(&instance, &witness, &pp),
761762
Err(Error::NotSatisfied)
@@ -784,10 +785,11 @@ pub(crate) mod tests {
784785

785786
let X = to_field_elements::<G>(&[1, 35]);
786787
let W = to_field_elements::<G>(&[3, 9, 27, 30]);
787-
let commitment_W = PedersenCommitment::<G>::commit(&pp, &W);
788+
let commitment_W = PedersenCommitment::<G>::commit(&pp, &W[..]);
788789

789-
let instance = R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_W, &X)?;
790-
let witness = R1CSWitness::<G>::new(&shape, &W)?;
790+
let instance =
791+
R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_W, &X[..])?;
792+
let witness = R1CSWitness::<G>::new(&shape, &W[..])?;
791793

792794
let relaxed_instance = RelaxedR1CSInstance::<G, PedersenCommitment<G>>::from(&instance);
793795
let relaxed_witness = RelaxedR1CSWitness::<G>::from_r1cs_witness(&shape, &witness);
@@ -818,10 +820,10 @@ pub(crate) mod tests {
818820

819821
let X = to_field_elements::<G>(&[1, 35]);
820822
let W = to_field_elements::<G>(&[3, 9, 27, 30]);
821-
let commitment_W = PedersenCommitment::<G>::commit(&pp, &W);
823+
let commitment_W = PedersenCommitment::<G>::commit(&pp, &W[..]);
822824

823-
let U2 = R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_W, &X)?;
824-
let W2 = R1CSWitness::<G>::new(&shape, &W)?;
825+
let U2 = R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_W, &X[..])?;
826+
let W2 = R1CSWitness::<G>::new(&shape, &W[..])?;
825827

826828
let U1 = RelaxedR1CSInstance::<G, PedersenCommitment<G>>::from(&U2);
827829
let W1 = RelaxedR1CSWitness::<G>::from_r1cs_witness(&shape, &W2);
@@ -845,7 +847,7 @@ pub(crate) mod tests {
845847

846848
#[test]
847849
fn folded_with_relaxed_instance_is_satisfied() -> Result<(), Error> {
848-
let (a, b, c) = {
850+
let (matrix_a, matrix_b, matrix_c) = {
849851
(
850852
to_field_sparse::<G>(A),
851853
to_field_sparse::<G>(B),
@@ -856,35 +858,95 @@ pub(crate) mod tests {
856858
const NUM_CONSTRAINTS: usize = 4;
857859
const NUM_WITNESS: usize = 4;
858860
const NUM_PUBLIC: usize = 2;
859-
const r: Scalar = Scalar::ONE;
861+
let folding_scalar = Scalar::from(2u64);
860862

861863
let pp = PedersenCommitment::<G>::setup(NUM_WITNESS, b"test", &());
862-
let shape = R1CSShape::<G>::new(NUM_CONSTRAINTS, NUM_WITNESS, NUM_PUBLIC, &a, &b, &c)?;
863-
864-
let X = to_field_elements::<G>(&[1, 35]);
865-
let W = to_field_elements::<G>(&[3, 9, 27, 30]);
866-
let commitment_W = PedersenCommitment::<G>::commit(&pp, &W);
867-
868-
let u = R1CSInstance::<G, PedersenCommitment<G>>::new(&shape, &commitment_W, &X)?;
869-
let w = R1CSWitness::<G>::new(&shape, &W)?;
864+
let shape = R1CSShape::<G>::new(
865+
NUM_CONSTRAINTS,
866+
NUM_WITNESS,
867+
NUM_PUBLIC,
868+
&matrix_a,
869+
&matrix_b,
870+
&matrix_c,
871+
)?;
872+
873+
// Create two different R1CS instances for the equation x³ + x + 5 = output
874+
let create_instance = |input: i64| -> Result<
875+
(R1CSInstance<G, PedersenCommitment<G>>, R1CSWitness<G>),
876+
Error,
877+
> {
878+
let output = input * input * input + input + 5;
879+
let witness_square = input * input;
880+
let witness_cube = witness_square * input;
881+
let witness_sum = witness_cube + input;
882+
883+
let public_inputs = to_field_elements::<G>(&[1, output]);
884+
let witness_values =
885+
to_field_elements::<G>(&[input, witness_square, witness_cube, witness_sum]);
886+
let commitment = PedersenCommitment::<G>::commit(&pp, &witness_values[..]);
887+
888+
let instance = R1CSInstance::<G, PedersenCommitment<G>>::new(
889+
&shape,
890+
&commitment,
891+
&public_inputs[..],
892+
)?;
893+
let witness = R1CSWitness::<G>::new(&shape, &witness_values[..])?;
894+
Ok((instance, witness))
895+
};
870896

871-
let mut U1 = RelaxedR1CSInstance::<G, PedersenCommitment<G>>::from(&u);
872-
let mut W1 = RelaxedR1CSWitness::<G>::from_r1cs_witness(&shape, &w);
897+
let (instance_1, witness_1) = create_instance(3)?;
898+
let (instance_2, witness_2) = create_instance(-2)?;
899+
900+
let relaxed_1 = RelaxedR1CSInstance::<G, PedersenCommitment<G>>::from(&instance_1);
901+
let relaxed_witness_1 = RelaxedR1CSWitness::<G>::from_r1cs_witness(&shape, &witness_1);
902+
let relaxed_2 = RelaxedR1CSInstance::<G, PedersenCommitment<G>>::from(&instance_2);
903+
let relaxed_witness_2 = RelaxedR1CSWitness::<G>::from_r1cs_witness(&shape, &witness_2);
904+
905+
let (cross_terms, commitment_t) = commit_T_with_relaxed(
906+
&shape,
907+
&pp,
908+
&relaxed_1,
909+
&relaxed_witness_1,
910+
&relaxed_2,
911+
&relaxed_witness_2,
912+
)?;
913+
914+
let inputs_1: Vec<Scalar> =
915+
[relaxed_1.X.as_slice(), relaxed_witness_1.W.as_slice()].concat();
916+
let inputs_2: Vec<Scalar> =
917+
[relaxed_2.X.as_slice(), relaxed_witness_2.W.as_slice()].concat();
918+
919+
let multiply_matrices = |inputs: &[Scalar]| {
920+
(
921+
shape.A.multiply_vec(inputs),
922+
shape.B.multiply_vec(inputs),
923+
shape.C.multiply_vec(inputs),
924+
)
925+
};
873926

874-
for _ in 0..3 {
875-
let (T, comm_T) = commit_T(&shape, &pp, &U1, &W1, &u, &w)?;
876-
U1 = U1.fold(&u, &comm_T, &r)?;
877-
W1 = W1.fold(&w, &T, &r)?;
927+
let (az1, bz1, cz1) = multiply_matrices(&inputs_1);
928+
let (az2, bz2, cz2) = multiply_matrices(&inputs_2);
929+
930+
for i in 0..shape.num_constraints {
931+
let expected = az1[i] * bz2[i] + az2[i] * bz1[i]
932+
- relaxed_1.X[0] * cz2[i]
933+
- relaxed_2.X[0] * cz1[i];
934+
assert_eq!(
935+
cross_terms[i], expected,
936+
"Cross-term computation incorrect at index {}",
937+
i
938+
);
878939
}
879940

880-
let U2 = U1.clone();
881-
let W2 = W1.clone();
882-
883-
let (T, comm_T) = commit_T_with_relaxed(&shape, &pp, &U1, &W1, &U2, &W2)?;
884-
let folded_U = U1.fold_with_relaxed(&U2, &comm_T, &r)?;
885-
let folded_W = W1.fold_with_relaxed(&W2, &T, &r)?;
941+
let folded_instance =
942+
relaxed_1.fold_with_relaxed(&relaxed_2, &commitment_t, &folding_scalar)?;
943+
let folded_witness = relaxed_witness_1.fold_with_relaxed(
944+
&relaxed_witness_2,
945+
&cross_terms[..],
946+
&folding_scalar,
947+
)?;
948+
shape.is_relaxed_satisfied(&folded_instance, &folded_witness, &pp)?;
886949

887-
shape.is_relaxed_satisfied(&folded_U, &folded_W, &pp)?;
888950
Ok(())
889951
}
890952
}

0 commit comments

Comments
 (0)