From bd066d0df2801293a26ae89fd3e1f9ade84c001f Mon Sep 17 00:00:00 2001 From: Alessandro Coglio Date: Sun, 15 Oct 2023 12:42:28 -0700 Subject: [PATCH] Refine the new refactored group circuits. In the circuit to enforce that a given point is in the subgroup, tie the witness values of the internal variables to the witness values of the point. This also takes care of the TODO that I had initially left. Adjust variable and constraint counts in tests. --- circuit/types/group/src/helpers/from_bits.rs | 12 +++--- .../group/src/helpers/from_x_coordinate.rs | 6 +-- .../group/src/helpers/from_xy_coordinates.rs | 6 +-- circuit/types/group/src/lib.rs | 40 ++++++++++--------- 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/circuit/types/group/src/helpers/from_bits.rs b/circuit/types/group/src/helpers/from_bits.rs index 8cd203e7b0..7cd06656e2 100644 --- a/circuit/types/group/src/helpers/from_bits.rs +++ b/circuit/types/group/src/helpers/from_bits.rs @@ -77,31 +77,31 @@ mod tests { #[test] fn test_from_bits_le_constant() { - check_from_bits_le(Mode::Constant, 11, 0, 0, 0); + check_from_bits_le(Mode::Constant, 9, 0, 0, 0); } #[test] fn test_from_bits_le_public() { - check_from_bits_le(Mode::Public, 4, 0, 267, 268); + check_from_bits_le(Mode::Public, 4, 0, 265, 266); } #[test] fn test_from_bits_le_private() { - check_from_bits_le(Mode::Private, 4, 0, 267, 268); + check_from_bits_le(Mode::Private, 4, 0, 265, 266); } #[test] fn test_from_bits_be_constant() { - check_from_bits_be(Mode::Constant, 11, 0, 0, 0); + check_from_bits_be(Mode::Constant, 9, 0, 0, 0); } #[test] fn test_from_bits_be_public() { - check_from_bits_be(Mode::Public, 4, 0, 267, 268); + check_from_bits_be(Mode::Public, 4, 0, 265, 266); } #[test] fn test_from_bits_be_private() { - check_from_bits_be(Mode::Private, 4, 0, 267, 268); + check_from_bits_be(Mode::Private, 4, 0, 265, 266); } } diff --git a/circuit/types/group/src/helpers/from_x_coordinate.rs b/circuit/types/group/src/helpers/from_x_coordinate.rs index 8c2116aca2..acf87bf2f9 100644 --- a/circuit/types/group/src/helpers/from_x_coordinate.rs +++ b/circuit/types/group/src/helpers/from_x_coordinate.rs @@ -62,16 +62,16 @@ mod tests { #[test] fn test_from_x_coordinate_constant() { - check_from_x_coordinate(Mode::Constant, 11, 0, 0, 0); + check_from_x_coordinate(Mode::Constant, 9, 0, 0, 0); } #[test] fn test_from_x_coordinate_public() { - check_from_x_coordinate(Mode::Public, 4, 0, 15, 15); + check_from_x_coordinate(Mode::Public, 4, 0, 13, 13); } #[test] fn test_from_x_coordinate_private() { - check_from_x_coordinate(Mode::Private, 4, 0, 15, 15); + check_from_x_coordinate(Mode::Private, 4, 0, 13, 13); } } diff --git a/circuit/types/group/src/helpers/from_xy_coordinates.rs b/circuit/types/group/src/helpers/from_xy_coordinates.rs index b081289a9f..ecce200d4d 100644 --- a/circuit/types/group/src/helpers/from_xy_coordinates.rs +++ b/circuit/types/group/src/helpers/from_xy_coordinates.rs @@ -90,17 +90,17 @@ mod tests { #[test] fn test_from_xy_coordinates_constant() { - check_from_xy_coordinates(Mode::Constant, 10, 0, 0, 0); + check_from_xy_coordinates(Mode::Constant, 8, 0, 0, 0); } #[test] fn test_from_xy_coordinates_public() { - check_from_xy_coordinates(Mode::Public, 4, 0, 14, 15); + check_from_xy_coordinates(Mode::Public, 4, 0, 12, 13); } #[test] fn test_from_xy_coordinates_private() { - check_from_xy_coordinates(Mode::Private, 4, 0, 14, 15); + check_from_xy_coordinates(Mode::Private, 4, 0, 12, 13); } #[test] diff --git a/circuit/types/group/src/lib.rs b/circuit/types/group/src/lib.rs index 8de8a8c636..5d4714e49b 100644 --- a/circuit/types/group/src/lib.rs +++ b/circuit/types/group/src/lib.rs @@ -61,8 +61,6 @@ impl Inject for Group { /// For safety, the resulting point is always enforced to be on the curve with constraints. /// regardless of whether the y-coordinate was recovered. fn new(mode: Mode, group: Self::Primitive) -> Self { - // TODO: check if `group` is in the group (in console-land, not circuit-land) - // Allocate two new variables for the coordinates, with the mode and values given as inputs. let x = Field::new(mode, group.to_x_coordinate()); let y = Field::new(mode, group.to_y_coordinate()); @@ -99,15 +97,7 @@ impl Group { impl Group { /// Enforces that `self` is on the curve and in the largest prime-order subgroup. pub fn enforce_in_group(&self) { - // Postulate a point on the curve. - // The coordinate values are irrelevant; we pick 0 for both. - let point_x = Field::new(Mode::Private, zero()); - let point_y = Field::new(Mode::Private, zero()); - let point = Self { x: point_x, y: point_y }; - point.enforce_on_curve(); - - // (For advanced users) The cofactor for this curve is `4`. Thus doubling is used to be performant. - debug_assert!(E::Affine::cofactor().len() == 1 && E::Affine::cofactor()[0] == 4); + let self_witness = self.eject_value(); // Each point in the subgroup is the quadruple of some point on the curve, // where 'quadruple' refers to the cofactor 4 of the curve. @@ -116,15 +106,27 @@ impl Group { // The point on the curve is existentially quantified, // so the constraints introduce new coordinate variables for that point. - // Postulate another point that is double of the point on the curve above. - // The coordinate values are irrelevant; we pick 0 for both. - let double_point_x = Field::new(Mode::Private, zero()); - let double_point_y = Field::new(Mode::Private, zero()); - let double_point = Self { x: double_point_x, y: double_point_y }; - point.enforce_double(&double_point); + // For the internal variables of this circuit, + // the mode is constant if the input point is constant, otherwise private. + let mode= if self.eject_mode().is_constant() { Mode::Constant } else {Mode::Private}; + + // Postulate a point (two new R1CS variables) on the curve, + // whose witness is the witness of the input point divided by the cofactor. + let point_witness = self_witness.div_by_cofactor(); + let point_x = Field::new(mode, point_witness.to_x_coordinate()); + let point_y = Field::new(mode, point_witness.to_y_coordinate()); + let point = Self {x: point_x, y: point_y}; + point.enforce_on_curve(); + + // (For advanced users) The cofactor for this curve is `4`. Thus doubling is used to be performant. + debug_assert!(E::Affine::cofactor().len() == 1 && E::Affine::cofactor()[0] == 4); + + // Double the point on the curve. + // This introduces two new R1CS variables for the doubled point. + let double_point = point.double(); // Enforce that the input point (self) is double the double of the point on the curve, - // i.e. that it is 4 (cofactor) times the postulated point on the curve. + // i.e. that it is 4 (= cofactor) times the postulated point on the curve. double_point.enforce_double(self); } } @@ -255,7 +257,7 @@ mod tests { Circuit::scope(&format!("Public {i}"), || { let affine = Group::::new(Mode::Public, point); assert_eq!(point, affine.eject_value()); - assert_scope!(4, 2, 14, 14); + assert_scope!(4, 2, 12, 13); }); }