From df26a6c67446a1a96b835fe8238c90c9050c9d2d Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Mon, 27 Sep 2021 10:44:43 +0200 Subject: [PATCH] chip::witness_point.rs: Constraints for non-identity point. The point_non_id() method returns an error if the given point is the identity. Co-authored-by: Daira Hopwood --- src/circuit/gadget/ecc.rs | 8 ++ src/circuit/gadget/ecc/chip.rs | 43 ++++-- src/circuit/gadget/ecc/chip/witness_point.rs | 137 +++++++++++++++---- 3 files changed, 146 insertions(+), 42 deletions(-) diff --git a/src/circuit/gadget/ecc.rs b/src/circuit/gadget/ecc.rs index 922a374a..c0194520 100644 --- a/src/circuit/gadget/ecc.rs +++ b/src/circuit/gadget/ecc.rs @@ -615,6 +615,14 @@ mod tests { )? }; + // Test witness non-identity point + { + super::chip::witness_point::tests::test_witness_non_id( + chip.clone(), + layouter.namespace(|| "witness non-identity point"), + ) + } + // Test complete addition { super::chip::add::tests::test_add( diff --git a/src/circuit/gadget/ecc/chip.rs b/src/circuit/gadget/ecc/chip.rs index fdcedd60..c7dad1ac 100644 --- a/src/circuit/gadget/ecc/chip.rs +++ b/src/circuit/gadget/ecc/chip.rs @@ -183,6 +183,8 @@ pub struct EccConfig { /// Witness point pub q_point: Selector, + /// Witness non-identity point + pub q_point_non_id: Selector, /// Lookup range check using 10-bit lookup table pub lookup_config: LookupRangeCheckConfig, @@ -279,11 +281,12 @@ impl EccChip { q_mul_fixed_base_field: meta.selector(), q_mul_fixed_running_sum, q_point: meta.selector(), + q_point_non_id: meta.selector(), lookup_config: range_check, running_sum_config, }; - // Create witness point gate + // Create witness point gates { let config: witness_point::Config = (&config).into(); config.create_gate(meta); @@ -393,6 +396,7 @@ impl EccInstructions for EccChip { type ScalarFixedShort = EccScalarFixedShort; type ScalarVar = CellValue; type Point = EccPoint; + type NonIdentityPoint = NonIdentityEccPoint; type X = CellValue; type FixedPoints = OrchardFixedBasesFull; type FixedPointsBaseField = NullifierK; @@ -423,20 +427,33 @@ impl EccInstructions for EccChip { let config: witness_point::Config = self.config().into(); layouter.assign_region( || "witness point", - |mut region| config.assign_region(value, 0, &mut region), + |mut region| config.point(value, 0, &mut region), ) } - fn extract_p(point: &Self::Point) -> &Self::X { - &point.x + fn witness_point_non_id( + &self, + layouter: &mut impl Layouter, + value: Option, + ) -> Result { + let config: witness_point::Config = self.config().into(); + layouter.assign_region( + || "witness non-identity point", + |mut region| config.point_non_id(value, 0, &mut region), + ) + } + + fn extract_p + Clone>(point: &Point) -> Self::X { + let point: EccPoint = (point.clone()).into(); + point.x() } fn add_incomplete( &self, layouter: &mut impl Layouter, - a: &Self::Point, - b: &Self::Point, - ) -> Result { + a: &Self::NonIdentityPoint, + b: &Self::NonIdentityPoint, + ) -> Result { let config: add_incomplete::Config = self.config().into(); layouter.assign_region( || "incomplete point addition", @@ -444,16 +461,18 @@ impl EccInstructions for EccChip { ) } - fn add( + fn add + Clone, B: Into + Clone>( &self, layouter: &mut impl Layouter, - a: &Self::Point, - b: &Self::Point, + a: &A, + b: &B, ) -> Result { let config: add::Config = self.config().into(); layouter.assign_region( || "complete point addition", - |mut region| config.assign_region(a, b, 0, &mut region), + |mut region| { + config.assign_region(&(a.clone()).into(), &(b.clone()).into(), 0, &mut region) + }, ) } @@ -461,7 +480,7 @@ impl EccInstructions for EccChip { &self, layouter: &mut impl Layouter, scalar: &Self::Var, - base: &Self::Point, + base: &Self::NonIdentityPoint, ) -> Result<(Self::Point, Self::ScalarVar), Error> { let config: mul::Config = self.config().into(); config.assign( diff --git a/src/circuit/gadget/ecc/chip/witness_point.rs b/src/circuit/gadget/ecc/chip/witness_point.rs index d3edad49..3f1f0fb9 100644 --- a/src/circuit/gadget/ecc/chip/witness_point.rs +++ b/src/circuit/gadget/ecc/chip/witness_point.rs @@ -1,10 +1,10 @@ -use super::{CellValue, EccConfig, EccPoint, Var}; +use super::{CellValue, EccConfig, EccPoint, NonIdentityEccPoint, Var}; use group::prime::PrimeCurveAffine; use halo2::{ circuit::Region, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, VirtualCells}, poly::Rotation, }; use pasta_curves::{arithmetic::CurveAffine, pallas}; @@ -12,6 +12,7 @@ use pasta_curves::{arithmetic::CurveAffine, pallas}; #[derive(Clone, Debug)] pub struct Config { q_point: Selector, + q_point_non_id: Selector, // x-coordinate pub x: Column, // y-coordinate @@ -22,6 +23,7 @@ impl From<&EccConfig> for Config { fn from(ecc_config: &EccConfig) -> Self { Self { q_point: ecc_config.q_point, + q_point_non_id: ecc_config.q_point_non_id, x: ecc_config.advices[0], y: ecc_config.advices[1], } @@ -30,8 +32,18 @@ impl From<&EccConfig> for Config { impl Config { pub(super) fn create_gate(&self, meta: &mut ConstraintSystem) { + let curve_eqn = |meta: &mut VirtualCells| { + let x = meta.query_advice(self.x, Rotation::cur()); + let y = meta.query_advice(self.y, Rotation::cur()); + + // y^2 = x^3 + b + y.clone().square() + - (x.clone().square() * x.clone()) + - Expression::Constant(pallas::Affine::b()) + }; + meta.create_gate("witness point", |meta| { - // Check that either the point being witness is either: + // Check that the point being witnessed is either: // - the identity, which is mapped to (0, 0) in affine coordinates; or // - a valid curve point y^2 = x^3 + b, where b = 5 in the Pallas equation @@ -39,37 +51,28 @@ impl Config { let x = meta.query_advice(self.x, Rotation::cur()); let y = meta.query_advice(self.y, Rotation::cur()); - // y^2 = x^3 + b - let curve_eqn = y.clone().square() - - (x.clone().square() * x.clone()) - - Expression::Constant(pallas::Affine::b()); - vec![ - ("x == 0 ∨ on_curve", q_point.clone() * x * curve_eqn.clone()), - ("y == 0 ∨ on_curve", q_point * y * curve_eqn), + ("x == 0 v on_curve", q_point.clone() * x * curve_eqn(meta)), + ("y == 0 v on_curve", q_point * y * curve_eqn(meta)), ] }); + + meta.create_gate("witness non-identity point", |meta| { + // Check that the point being witnessed is a valid curve point y^2 = x^3 + b, + // where b = 5 in the Pallas equation + + let q_point_non_id = meta.query_selector(self.q_point_non_id); + + vec![("on_curve", q_point_non_id * curve_eqn(meta))] + }); } - pub(super) fn assign_region( + fn assign_xy( &self, - value: Option, + value: Option<(pallas::Base, pallas::Base)>, offset: usize, region: &mut Region<'_, pallas::Base>, - ) -> Result { - // Enable `q_point` selector - self.q_point.enable(region, offset)?; - - let value = value.map(|value| { - // Map the identity to (0, 0). - if value == pallas::Affine::identity() { - (pallas::Base::zero(), pallas::Base::zero()) - } else { - let value = value.coordinates().unwrap(); - (*value.x(), *value.y()) - } - }); - + ) -> Result<(CellValue, CellValue), Error> { // Assign `x` value let x_val = value.map(|value| value.0); let x_var = region.assign_advice( @@ -88,9 +91,83 @@ impl Config { || y_val.ok_or(Error::SynthesisError), )?; - Ok(EccPoint { - x: CellValue::::new(x_var, x_val), - y: CellValue::::new(y_var, y_val), - }) + Ok(( + CellValue::::new(x_var, x_val), + CellValue::::new(y_var, y_val), + )) + } + + /// Assigns a point that can be the identity. + pub(super) fn point( + &self, + value: Option, + offset: usize, + region: &mut Region<'_, pallas::Base>, + ) -> Result { + // Enable `q_point` selector + self.q_point.enable(region, offset)?; + + let value = value.map(|value| { + // Map the identity to (0, 0). + if value == pallas::Affine::identity() { + (pallas::Base::zero(), pallas::Base::zero()) + } else { + let value = value.coordinates().unwrap(); + (*value.x(), *value.y()) + } + }); + + self.assign_xy(value, offset, region) + .map(|(x, y)| EccPoint { x, y }) + } + + /// Assigns a non-identity point. + pub(super) fn point_non_id( + &self, + value: Option, + offset: usize, + region: &mut Region<'_, pallas::Base>, + ) -> Result { + // Enable `q_point_non_id` selector + self.q_point_non_id.enable(region, offset)?; + + if let Some(value) = value { + // Return an error if the point is the identity. + if value == pallas::Affine::identity() { + return Err(Error::SynthesisError); + } + }; + + let value = value.map(|value| { + let value = value.coordinates().unwrap(); + (*value.x(), *value.y()) + }); + + self.assign_xy(value, offset, region) + .map(|(x, y)| NonIdentityEccPoint { x, y }) + } +} + +#[cfg(test)] +pub mod tests { + use halo2::circuit::Layouter; + use pasta_curves::pallas; + + use super::*; + use crate::circuit::gadget::ecc::{EccInstructions, NonIdentityPoint}; + + pub fn test_witness_non_id< + EccChip: EccInstructions + Clone + Eq + std::fmt::Debug, + >( + chip: EccChip, + mut layouter: impl Layouter, + ) { + // Witnessing the identity should return an error. + NonIdentityPoint::new( + chip, + layouter.namespace(|| "witness identity"), + Some(pallas::Affine::identity()), + ) + .expect_err("witnessing 𝒪 should return an error"); } }