Remove Point::new() API and introduce is_identity() instruction.

Also remove the q_point selector and gate from the circuit.
This commit is contained in:
therealyingtong 2021-09-27 12:14:01 +02:00 committed by Sean Bowe
parent ec27989b9b
commit 8ad3003e27
9 changed files with 106 additions and 192 deletions

View File

@ -57,14 +57,6 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
b: &Self::Point,
) -> Result<(), Error>;
/// Witnesses the given point as a private input to the circuit.
/// This maps the identity to (0, 0) in affine coordinates.
fn witness_point(
&self,
layouter: &mut impl Layouter<C::Base>,
value: Option<C>,
) -> Result<Self::Point, Error>;
/// Witnesses the given point as a private input to the circuit.
/// This returns an error if the point is the identity.
fn witness_point_non_id(
@ -73,6 +65,9 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
value: Option<C>,
) -> Result<Self::NonIdentityPoint, Error>;
/// Checks if a point is the identity.
fn is_identity(point: &Self::Point) -> Option<bool>;
/// Extracts the x-coordinate of a point.
fn extract_p<Point: Into<Self::Point> + Clone>(point: &Point) -> Self::X;
@ -310,17 +305,6 @@ pub struct Point<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + E
}
impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C, EccChip> {
/// Constructs a new point with the given value.
#[cfg(test)]
pub fn new(
chip: EccChip,
mut layouter: impl Layouter<C::Base>,
value: Option<C>,
) -> Result<Self, Error> {
let point = chip.witness_point(&mut layouter, value);
point.map(|inner| Point { chip, inner })
}
/// Constrains this point to be equal in value to another point.
pub fn constrain_equal<Other: Into<Point<C, EccChip>> + Clone>(
&self,
@ -337,6 +321,11 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
&self.inner
}
/// Checks is a point is the identity.
pub fn is_identity(&self) -> Option<bool> {
EccChip::is_identity(&self.inner)
}
/// Extracts the x-coordinate of a point.
pub fn extract_p(&self) -> X<C, EccChip> {
X::from_inner(self.chip.clone(), EccChip::extract_p(&self.inner))
@ -513,7 +502,7 @@ where
#[cfg(test)]
mod tests {
use group::{prime::PrimeCurveAffine, Curve, Group};
use group::{Curve, Group};
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
@ -604,15 +593,6 @@ mod tests {
// Make sure P and Q are not the same point.
assert_ne!(p_val, q_val);
// Generate a (0,0) point to be used in other tests.
let zero = {
super::Point::new(
chip.clone(),
layouter.namespace(|| "identity"),
Some(pallas::Affine::identity()),
)?
};
// Test witness non-identity point
{
super::chip::witness_point::tests::test_witness_non_id(
@ -626,7 +606,6 @@ mod tests {
super::chip::add::tests::test_add(
chip.clone(),
layouter.namespace(|| "complete addition"),
&zero,
p_val,
&p,
q_val,

View File

@ -181,8 +181,6 @@ pub struct EccConfig {
/// when the scalar is a signed short exponent or a base-field element.
pub q_mul_fixed_running_sum: Selector,
/// Witness point
pub q_point: Selector,
/// Witness non-identity point
pub q_point_non_id: Selector,
@ -280,7 +278,6 @@ impl EccChip {
q_mul_fixed_short: meta.selector(),
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,
@ -419,18 +416,6 @@ impl EccInstructions<pallas::Affine> for EccChip {
)
}
fn witness_point(
&self,
layouter: &mut impl Layouter<pallas::Base>,
value: Option<pallas::Affine>,
) -> Result<Self::Point, Error> {
let config: witness_point::Config = self.config().into();
layouter.assign_region(
|| "witness point",
|mut region| config.point(value, 0, &mut region),
)
}
fn witness_point_non_id(
&self,
layouter: &mut impl Layouter<pallas::Base>,
@ -443,6 +428,10 @@ impl EccInstructions<pallas::Affine> for EccChip {
)
}
fn is_identity(point: &Self::Point) -> Option<bool> {
point.is_identity()
}
fn extract_p<Point: Into<Self::Point> + Clone>(point: &Point) -> Self::X {
let point: EccPoint = (point.clone()).into();
point.x()

View File

@ -391,13 +391,12 @@ pub mod tests {
use halo2::{circuit::Layouter, plonk::Error};
use pasta_curves::{arithmetic::CurveExt, pallas};
use crate::circuit::gadget::ecc::{EccInstructions, NonIdentityPoint, Point};
use crate::circuit::gadget::ecc::{EccInstructions, NonIdentityPoint};
#[allow(clippy::too_many_arguments)]
pub fn test_add<EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug>(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
zero: &Point<pallas::Affine, EccChip>,
p_val: pallas::Affine,
p: &NonIdentityPoint<pallas::Affine, EccChip>,
q_val: pallas::Affine,
@ -408,21 +407,22 @@ pub mod tests {
assert_ne!(p_val, q_val);
// Check complete addition P + (-P)
{
let zero = {
let result = p.add(layouter.namespace(|| "P + (-P)"), p_neg)?;
result.constrain_equal(layouter.namespace(|| "P + (-P) = 𝒪"), zero)?;
}
assert!(result.is_identity().unwrap());
result
};
// Check complete addition 𝒪 + 𝒪
{
let result = zero.add(layouter.namespace(|| "𝒪 + 𝒪"), zero)?;
result.constrain_equal(layouter.namespace(|| "𝒪 + 𝒪 = 𝒪"), zero)?;
let result = zero.add(layouter.namespace(|| "𝒪 + 𝒪"), &zero)?;
result.constrain_equal(layouter.namespace(|| "𝒪 + 𝒪 = 𝒪"), &zero)?;
}
// Check P + Q
{
let result = p.add(layouter.namespace(|| "P + Q"), q)?;
let witnessed_result = Point::new(
let witnessed_result = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "witnessed P + Q"),
Some((p_val + q_val).to_affine()),
@ -433,7 +433,7 @@ pub mod tests {
// P + P
{
let result = p.add(layouter.namespace(|| "P + P"), p)?;
let witnessed_result = Point::new(
let witnessed_result = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "witnessed P + P"),
Some((p_val + p_val).to_affine()),
@ -443,7 +443,7 @@ pub mod tests {
// P + 𝒪
{
let result = p.add(layouter.namespace(|| "P + 𝒪"), zero)?;
let result = p.add(layouter.namespace(|| "P + 𝒪"), &zero)?;
result.constrain_equal(layouter.namespace(|| "P + 𝒪 = P"), p)?;
}
@ -455,7 +455,7 @@ pub mod tests {
// (x, y) + (ζx, y) should behave like normal P + Q.
let endo_p = p_val.to_curve().endo();
let endo_p = Point::new(
let endo_p = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "endo(P)"),
Some(endo_p.to_affine()),
@ -464,7 +464,7 @@ pub mod tests {
// (x, y) + (ζx, -y) should also behave like normal P + Q.
let endo_p_neg = (-p_val).to_curve().endo();
let endo_p_neg = Point::new(
let endo_p_neg = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "endo(-P)"),
Some(endo_p_neg.to_affine()),
@ -473,7 +473,7 @@ pub mod tests {
// (x, y) + ((ζ^2)x, y)
let endo_2_p = p_val.to_curve().endo().endo();
let endo_2_p = Point::new(
let endo_2_p = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "endo^2(P)"),
Some(endo_2_p.to_affine()),
@ -482,7 +482,7 @@ pub mod tests {
// (x, y) + ((ζ^2)x, -y)
let endo_2_p_neg = (-p_val).to_curve().endo().endo();
let endo_2_p_neg = Point::new(
let endo_2_p_neg = NonIdentityPoint::new(
chip,
layouter.namespace(|| "endo^2(-P)"),
Some(endo_2_p_neg.to_affine()),

View File

@ -464,7 +464,7 @@ pub mod tests {
) -> Result<(), Error> {
let column = chip.config().advices[0];
fn constrain_equal<
fn constrain_equal_non_id<
EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug,
>(
chip: EccChip,
@ -476,7 +476,7 @@ pub mod tests {
// Move scalar from base field into scalar field (which always fits
// for Pallas).
let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap();
let expected = Point::new(
let expected = NonIdentityPoint::new(
chip,
layouter.namespace(|| "expected point"),
Some((base_val * scalar).to_affine()),
@ -495,7 +495,7 @@ pub mod tests {
)?;
p.mul(layouter.namespace(|| "random [a]B"), &scalar)?
};
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "random [a]B"),
p_val,
@ -513,13 +513,7 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_val))?;
p.mul(layouter.namespace(|| "[0]B"), &scalar)?
};
constrain_equal(
chip.clone(),
layouter.namespace(|| "[0]B"),
p_val,
scalar_val,
result,
)?;
assert!(result.is_identity().unwrap());
}
// [-1]B (the largest possible base field element)
@ -530,7 +524,7 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "-1"), column, Some(scalar_val))?;
p.mul(layouter.namespace(|| "[-1]B"), &scalar)?
};
constrain_equal(
constrain_equal_non_id(
chip,
layouter.namespace(|| "[-1]B"),
p_val,

View File

@ -389,7 +389,7 @@ pub mod tests {
use crate::circuit::gadget::{
ecc::{
chip::{EccChip, NullifierK},
FixedPointBaseField, Point,
FixedPointBaseField, NonIdentityPoint, Point,
},
utilities::UtilitiesInstructions,
};
@ -418,7 +418,7 @@ pub mod tests {
) -> Result<(), Error> {
let column = chip.config().advices[0];
fn constrain_equal(
fn constrain_equal_non_id(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine,
@ -427,7 +427,7 @@ pub mod tests {
) -> Result<(), Error> {
// Move scalar from base field into scalar field (which always fits for Pallas).
let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap();
let expected = Point::new(
let expected = NonIdentityPoint::new(
chip,
layouter.namespace(|| "expected point"),
Some((base_val * scalar).to_affine()),
@ -446,7 +446,7 @@ pub mod tests {
)?;
base.mul(layouter.namespace(|| "random [a]B"), scalar_fixed)?
};
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "random [a]B"),
base_val,
@ -474,7 +474,7 @@ pub mod tests {
)?;
base.mul(layouter.namespace(|| "mul with double"), scalar_fixed)?
};
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "mul with double"),
base_val,
@ -492,13 +492,7 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_fixed))?;
base.mul(layouter.namespace(|| "mul by zero"), scalar_fixed)?
};
constrain_equal(
chip.clone(),
layouter.namespace(|| "mul by zero"),
base_val,
scalar_fixed,
result,
)?;
assert!(result.is_identity().unwrap());
}
// [-1]B is the largest base field element
@ -509,7 +503,7 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "-1"), column, Some(scalar_fixed))?;
base.mul(layouter.namespace(|| "mul by -1"), scalar_fixed)?
};
constrain_equal(
constrain_equal_non_id(
chip,
layouter.namespace(|| "mul by -1"),
base_val,

View File

@ -175,7 +175,7 @@ pub mod tests {
use crate::circuit::gadget::ecc::{
chip::{EccChip, OrchardFixedBasesFull},
FixedPoint, Point,
FixedPoint, NonIdentityPoint, Point,
};
use crate::constants;
@ -229,14 +229,14 @@ pub mod tests {
base: FixedPoint<pallas::Affine, EccChip>,
base_val: pallas::Affine,
) -> Result<(), Error> {
fn constrain_equal(
fn constrain_equal_non_id(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine,
scalar_val: pallas::Scalar,
result: Point<pallas::Affine, EccChip>,
) -> Result<(), Error> {
let expected = Point::new(
let expected = NonIdentityPoint::new(
chip,
layouter.namespace(|| "expected point"),
Some((base_val * scalar_val).to_affine()),
@ -249,7 +249,7 @@ pub mod tests {
let scalar_fixed = pallas::Scalar::rand();
let (result, _) = base.mul(layouter.namespace(|| "random [a]B"), Some(scalar_fixed))?;
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "random [a]B"),
base_val,
@ -272,7 +272,7 @@ pub mod tests {
let (result, _) =
base.mul(layouter.namespace(|| "mul with double"), Some(scalar_fixed))?;
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "mul with double"),
base_val,
@ -286,20 +286,14 @@ pub mod tests {
{
let scalar_fixed = pallas::Scalar::zero();
let (result, _) = base.mul(layouter.namespace(|| "mul by zero"), Some(scalar_fixed))?;
constrain_equal(
chip.clone(),
layouter.namespace(|| "mul by zero"),
base_val,
scalar_fixed,
result,
)?;
assert!(result.is_identity().unwrap());
}
// [-1]B is the largest scalar field element.
{
let scalar_fixed = -pallas::Scalar::one();
let (result, _) = base.mul(layouter.namespace(|| "mul by -1"), Some(scalar_fixed))?;
constrain_equal(
constrain_equal_non_id(
chip,
layouter.namespace(|| "mul by -1"),
base_val,

View File

@ -244,7 +244,7 @@ pub mod tests {
use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::{
ecc::{chip::EccChip, FixedPointShort, Point},
ecc::{chip::EccChip, FixedPointShort, NonIdentityPoint, Point},
utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions},
};
use crate::constants::load::ValueCommitV;
@ -273,14 +273,14 @@ pub mod tests {
Ok((magnitude, sign))
}
fn constrain_equal(
fn constrain_equal_non_id(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine,
scalar_val: pallas::Scalar,
result: Point<pallas::Affine, EccChip>,
) -> Result<(), Error> {
let expected = Point::new(
let expected = NonIdentityPoint::new(
chip,
layouter.namespace(|| "expected point"),
Some((base_val * scalar_val).to_affine()),
@ -289,8 +289,6 @@ pub mod tests {
}
let magnitude_signs = [
("mul by +zero", pallas::Base::zero(), pallas::Base::one()),
("mul by -zero", pallas::Base::zero(), -pallas::Base::one()),
(
"random [a]B",
pallas::Base::from_u64(rand::random::<u64>()),
@ -347,7 +345,7 @@ pub mod tests {
};
magnitude * sign
};
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| *name),
base_val,
@ -356,6 +354,24 @@ pub mod tests {
)?;
}
let zero_magnitude_signs = [
("mul by +zero", pallas::Base::zero(), pallas::Base::one()),
("mul by -zero", pallas::Base::zero(), -pallas::Base::one()),
];
for (name, magnitude, sign) in zero_magnitude_signs.iter() {
let (result, _) = {
let magnitude_sign = load_magnitude_sign(
chip.clone(),
layouter.namespace(|| *name),
*magnitude,
*sign,
)?;
value_commit_v.mul(layouter.namespace(|| *name), magnitude_sign)?
};
assert!(result.is_identity().unwrap());
}
Ok(())
}
@ -489,7 +505,7 @@ pub mod tests {
Err(vec![
VerifyFailure::ConstraintNotSatisfied {
constraint: (
(17, "Short fixed-base mul gate").into(),
(16, "Short fixed-base mul gate").into(),
0,
"last_window_check"
)
@ -521,13 +537,13 @@ pub mod tests {
prover.verify(),
Err(vec![
VerifyFailure::ConstraintNotSatisfied {
constraint: ((17, "Short fixed-base mul gate").into(), 1, "sign_check")
constraint: ((16, "Short fixed-base mul gate").into(), 1, "sign_check")
.into(),
row: 26
},
VerifyFailure::ConstraintNotSatisfied {
constraint: (
(17, "Short fixed-base mul gate").into(),
(16, "Short fixed-base mul gate").into(),
3,
"negation_check"
)

View File

@ -1,17 +1,16 @@
use super::{CellValue, EccConfig, EccPoint, NonIdentityEccPoint, Var};
use super::{CellValue, EccConfig, NonIdentityEccPoint, Var};
use group::prime::PrimeCurveAffine;
use halo2::{
circuit::Region,
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, VirtualCells},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
poly::Rotation,
};
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<Advice>,
@ -22,7 +21,6 @@ pub struct Config {
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],
@ -32,93 +30,23 @@ impl From<&EccConfig> for Config {
impl Config {
pub(super) fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
let curve_eqn = |meta: &mut VirtualCells<pallas::Base>| {
let x = meta.query_advice(self.x, Rotation::cur());
let y = meta.query_advice(self.y, Rotation::cur());
// y^2 = x^3 + b
y.square() - (x.clone().square() * x) - Expression::Constant(pallas::Affine::b())
};
meta.create_gate("witness point", |meta| {
// 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
let q_point = meta.query_selector(self.q_point);
let x = meta.query_advice(self.x, Rotation::cur());
let y = meta.query_advice(self.y, Rotation::cur());
vec![
("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))]
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.square() - (x.clone().square() * x) - Expression::Constant(pallas::Affine::b());
vec![("on_curve", q_point_non_id * curve_eqn)]
});
}
fn assign_xy(
&self,
value: Option<(pallas::Base, pallas::Base)>,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
// Assign `x` value
let x_val = value.map(|value| value.0);
let x_var = region.assign_advice(
|| "x",
self.x,
offset,
|| x_val.ok_or(Error::SynthesisError),
)?;
// Assign `y` value
let y_val = value.map(|value| value.1);
let y_var = region.assign_advice(
|| "y",
self.y,
offset,
|| y_val.ok_or(Error::SynthesisError),
)?;
Ok((
CellValue::<pallas::Base>::new(x_var, x_val),
CellValue::<pallas::Base>::new(y_var, y_val),
))
}
/// Assigns a point that can be the identity.
pub(super) fn point(
&self,
value: Option<pallas::Affine>,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<EccPoint, Error> {
// 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,
@ -139,8 +67,28 @@ impl Config {
(*value.x(), *value.y())
});
self.assign_xy(value, offset, region)
.map(|(x, y)| NonIdentityEccPoint { x, y })
// Assign `x` value
let x_val = value.map(|value| value.0);
let x_var = region.assign_advice(
|| "x",
self.x,
offset,
|| x_val.ok_or(Error::SynthesisError),
)?;
// Assign `y` value
let y_val = value.map(|value| value.1);
let y_var = region.assign_advice(
|| "y",
self.y,
offset,
|| y_val.ok_or(Error::SynthesisError),
)?;
Ok(NonIdentityEccPoint {
x: CellValue::<pallas::Base>::new(x_var, x_val),
y: CellValue::<pallas::Base>::new(y_var, y_val),
})
}
}

View File

@ -426,7 +426,7 @@ mod tests {
circuit::gadget::{
ecc::{
chip::{EccChip, EccConfig},
Point,
NonIdentityPoint,
},
utilities::lookup_range_check::LookupRangeCheckConfig,
},
@ -580,7 +580,7 @@ mod tests {
None
};
Point::new(
NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "Witness expected parent"),
expected_parent,
@ -631,7 +631,7 @@ mod tests {
None
};
Point::new(
NonIdentityPoint::new(
ecc_chip,
layouter.namespace(|| "Witness expected result"),
expected_result,