use super::{EccPoint, NonIdentityEccPoint};
use group::prime::PrimeCurveAffine;
use halo2_proofs::{
circuit::{AssignedCell, Region, Value},
plonk::{
Advice, Assigned, Column, ConstraintSystem, Constraints, Error, Expression, Selector,
VirtualCells,
},
poly::Rotation,
};
use pasta_curves::{arithmetic::CurveAffine, pallas};
type Coordinates = (
AssignedCell<Assigned<pallas::Base>, pallas::Base>,
AssignedCell<Assigned<pallas::Base>, pallas::Base>,
);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Config {
q_point: Selector,
q_point_non_id: Selector,
pub x: Column<Advice>,
pub y: Column<Advice>,
}
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
}
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.square() - (x.clone().square() * x) - Expression::Constant(pallas::Affine::b())
};
meta.create_gate("witness point", |meta| {
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());
[
("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| {
let q_point_non_id = meta.query_selector(self.q_point_non_id);
Constraints::with_selector(q_point_non_id, Some(("on_curve", curve_eqn(meta))))
});
}
fn assign_xy(
&self,
value: Value<(Assigned<pallas::Base>, Assigned<pallas::Base>)>,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<Coordinates, Error> {
let x_val = value.map(|value| value.0);
let x_var = region.assign_advice(|| "x", self.x, offset, || x_val)?;
let y_val = value.map(|value| value.1);
let y_var = region.assign_advice(|| "y", self.y, offset, || y_val)?;
Ok((x_var, y_var))
}
pub(super) fn point(
&self,
value: Value<pallas::Affine>,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<EccPoint, Error> {
self.q_point.enable(region, offset)?;
let value = value.map(|value| {
if value == pallas::Affine::identity() {
(Assigned::Zero, Assigned::Zero)
} else {
let value = value.coordinates().unwrap();
(value.x().into(), value.y().into())
}
});
self.assign_xy(value, offset, region)
.map(|(x, y)| EccPoint { x, y })
}
pub(super) fn point_non_id(
&self,
value: Value<pallas::Affine>,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<NonIdentityEccPoint, Error> {
self.q_point_non_id.enable(region, offset)?;
value.error_if_known_and(|value| value == &pallas::Affine::identity())?;
let value = value.map(|value| {
let value = value.coordinates().unwrap();
(value.x().into(), value.y().into())
});
self.assign_xy(value, offset, region)
.map(|(x, y)| NonIdentityEccPoint { x, y })
}
}
#[cfg(test)]
pub mod tests {
use halo2_proofs::circuit::Layouter;
use pasta_curves::pallas;
use super::*;
use crate::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>,
) {
NonIdentityPoint::new(
chip,
layouter.namespace(|| "witness identity"),
Value::known(pallas::Affine::identity()),
)
.expect_err("witnessing 𝒪 should return an error");
}
}