2021-12-01 18:51:46 -08:00
|
|
|
|
use super::{EccPoint, NonIdentityEccPoint};
|
2021-06-04 23:08:51 -07:00
|
|
|
|
|
2021-06-11 15:29:05 -07:00
|
|
|
|
use group::prime::PrimeCurveAffine;
|
|
|
|
|
|
2021-06-04 23:08:51 -07:00
|
|
|
|
use halo2::{
|
2021-12-01 18:51:46 -08:00
|
|
|
|
circuit::{AssignedCell, Region},
|
2021-09-28 08:56:33 -07:00
|
|
|
|
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, VirtualCells},
|
2021-06-04 23:08:51 -07:00
|
|
|
|
poly::Rotation,
|
|
|
|
|
};
|
2021-06-11 15:29:05 -07:00
|
|
|
|
use pasta_curves::{arithmetic::CurveAffine, pallas};
|
2021-06-10 10:09:04 -07:00
|
|
|
|
|
2021-12-07 20:06:00 -08:00
|
|
|
|
type Coordinates = (
|
|
|
|
|
AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
|
AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
|
);
|
|
|
|
|
|
2021-11-30 10:47:52 -08:00
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
2021-06-11 15:29:05 -07:00
|
|
|
|
pub struct Config {
|
2021-09-28 08:56:33 -07:00
|
|
|
|
q_point: Selector,
|
2021-09-27 01:44:43 -07:00
|
|
|
|
q_point_non_id: Selector,
|
2021-06-04 23:08:51 -07:00
|
|
|
|
// x-coordinate
|
|
|
|
|
pub x: Column<Advice>,
|
|
|
|
|
// y-coordinate
|
|
|
|
|
pub y: Column<Advice>,
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-30 10:47:52 -08:00
|
|
|
|
impl Config {
|
|
|
|
|
pub(super) fn configure(
|
|
|
|
|
meta: &mut ConstraintSystem<pallas::Base>,
|
|
|
|
|
x: Column<Advice>,
|
|
|
|
|
y: Column<Advice>,
|
|
|
|
|
) -> Self {
|
|
|
|
|
let config = Self {
|
|
|
|
|
q_point: meta.selector(),
|
|
|
|
|
q_point_non_id: meta.selector(),
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
config.create_gate(meta);
|
|
|
|
|
|
|
|
|
|
config
|
2021-06-04 23:08:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-30 10:47:52 -08:00
|
|
|
|
fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
|
2021-09-28 08:56:33 -07:00
|
|
|
|
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());
|
2021-09-27 01:44:43 -07:00
|
|
|
|
|
2021-09-28 08:56:33 -07:00
|
|
|
|
// y^2 = x^3 + b
|
2021-09-28 10:36:54 -07:00
|
|
|
|
y.square() - (x.clone().square() * x) - Expression::Constant(pallas::Affine::b())
|
2021-09-28 08:56:33 -07:00
|
|
|
|
};
|
2021-09-27 01:44:43 -07:00
|
|
|
|
|
2021-09-28 08:56:33 -07:00
|
|
|
|
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);
|
2021-09-27 03:14:01 -07:00
|
|
|
|
let x = meta.query_advice(self.x, Rotation::cur());
|
|
|
|
|
let y = meta.query_advice(self.y, Rotation::cur());
|
2021-09-27 01:44:43 -07:00
|
|
|
|
|
2021-09-28 08:56:33 -07:00
|
|
|
|
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
|
2021-09-27 01:44:43 -07:00
|
|
|
|
|
2021-09-28 08:56:33 -07:00
|
|
|
|
let q_point_non_id = meta.query_selector(self.q_point_non_id);
|
|
|
|
|
|
|
|
|
|
vec![("on_curve", q_point_non_id * curve_eqn(meta))]
|
2021-09-27 01:44:43 -07:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-28 08:56:33 -07:00
|
|
|
|
fn assign_xy(
|
2021-09-27 01:44:43 -07:00
|
|
|
|
&self,
|
2021-09-28 08:56:33 -07:00
|
|
|
|
value: Option<(pallas::Base, pallas::Base)>,
|
2021-09-27 01:44:43 -07:00
|
|
|
|
offset: usize,
|
|
|
|
|
region: &mut Region<'_, pallas::Base>,
|
2021-12-07 20:06:00 -08:00
|
|
|
|
) -> Result<Coordinates, Error> {
|
2021-09-27 03:14:01 -07:00
|
|
|
|
// Assign `x` value
|
|
|
|
|
let x_val = value.map(|value| value.0);
|
2021-11-23 16:21:44 -08:00
|
|
|
|
let x_var =
|
|
|
|
|
region.assign_advice(|| "x", self.x, offset, || x_val.ok_or(Error::Synthesis))?;
|
2021-09-27 03:14:01 -07:00
|
|
|
|
|
|
|
|
|
// Assign `y` value
|
|
|
|
|
let y_val = value.map(|value| value.1);
|
2021-11-23 16:21:44 -08:00
|
|
|
|
let y_var =
|
|
|
|
|
region.assign_advice(|| "y", self.y, offset, || y_val.ok_or(Error::Synthesis))?;
|
2021-09-27 03:14:01 -07:00
|
|
|
|
|
2021-12-01 17:10:00 -08:00
|
|
|
|
Ok((x_var, y_var))
|
2021-09-28 08:56:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
|
value: Option<pallas::Affine>,
|
|
|
|
|
offset: usize,
|
|
|
|
|
region: &mut Region<'_, pallas::Base>,
|
|
|
|
|
) -> Result<NonIdentityEccPoint, Error> {
|
|
|
|
|
// 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() {
|
2021-11-23 16:21:44 -08:00
|
|
|
|
return Err(Error::Synthesis);
|
2021-09-28 08:56:33 -07:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 })
|
2021-09-27 01:44:43 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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<pallas::Affine> + Clone + Eq + std::fmt::Debug,
|
|
|
|
|
>(
|
|
|
|
|
chip: EccChip,
|
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
|
|
|
|
) {
|
|
|
|
|
// 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");
|
2021-06-04 23:08:51 -07:00
|
|
|
|
}
|
|
|
|
|
}
|