2021-07-07 00:53:43 -07:00
|
|
|
|
use std::{array, collections::HashSet};
|
2021-06-04 23:11:40 -07:00
|
|
|
|
|
2021-12-01 18:10:00 -08:00
|
|
|
|
use super::NonIdentityEccPoint;
|
2021-12-01 04:59:37 -08:00
|
|
|
|
use ff::Field;
|
2021-06-04 23:11:40 -07:00
|
|
|
|
use group::Curve;
|
2022-01-27 15:28:02 -08:00
|
|
|
|
use halo2_proofs::{
|
2021-06-04 23:11:40 -07:00
|
|
|
|
circuit::Region,
|
2022-04-24 15:13:38 -07:00
|
|
|
|
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Selector},
|
2021-06-04 23:11:40 -07:00
|
|
|
|
poly::Rotation,
|
|
|
|
|
};
|
2021-06-11 15:29:05 -07:00
|
|
|
|
use pasta_curves::{arithmetic::CurveAffine, pallas};
|
2021-06-04 23:11:40 -07:00
|
|
|
|
|
2021-11-30 10:29:52 -08:00
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
2021-06-11 15:29:05 -07:00
|
|
|
|
pub struct Config {
|
2021-06-04 23:11:40 -07:00
|
|
|
|
q_add_incomplete: Selector,
|
|
|
|
|
// x-coordinate of P in P + Q = R
|
|
|
|
|
pub x_p: Column<Advice>,
|
|
|
|
|
// y-coordinate of P in P + Q = R
|
|
|
|
|
pub y_p: Column<Advice>,
|
|
|
|
|
// x-coordinate of Q or R in P + Q = R
|
|
|
|
|
pub x_qr: Column<Advice>,
|
|
|
|
|
// y-coordinate of Q or R in P + Q = R
|
|
|
|
|
pub y_qr: Column<Advice>,
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-30 10:29:52 -08:00
|
|
|
|
impl Config {
|
|
|
|
|
pub(super) fn configure(
|
|
|
|
|
meta: &mut ConstraintSystem<pallas::Base>,
|
|
|
|
|
x_p: Column<Advice>,
|
|
|
|
|
y_p: Column<Advice>,
|
|
|
|
|
x_qr: Column<Advice>,
|
|
|
|
|
y_qr: Column<Advice>,
|
|
|
|
|
) -> Self {
|
2022-01-04 21:28:16 -08:00
|
|
|
|
meta.enable_equality(x_p);
|
|
|
|
|
meta.enable_equality(y_p);
|
|
|
|
|
meta.enable_equality(x_qr);
|
|
|
|
|
meta.enable_equality(y_qr);
|
2021-11-30 10:29:52 -08:00
|
|
|
|
|
|
|
|
|
let config = Self {
|
|
|
|
|
q_add_incomplete: meta.selector(),
|
|
|
|
|
x_p,
|
|
|
|
|
y_p,
|
|
|
|
|
x_qr,
|
|
|
|
|
y_qr,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
config.create_gate(meta);
|
|
|
|
|
|
|
|
|
|
config
|
2021-06-04 23:11:40 -07:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 00:53:43 -07:00
|
|
|
|
pub(crate) fn advice_columns(&self) -> HashSet<Column<Advice>> {
|
|
|
|
|
core::array::IntoIter::new([self.x_p, self.y_p, self.x_qr, self.y_qr]).collect()
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-30 10:29:52 -08:00
|
|
|
|
fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
|
2021-06-04 23:11:40 -07:00
|
|
|
|
meta.create_gate("incomplete addition gates", |meta| {
|
|
|
|
|
let q_add_incomplete = meta.query_selector(self.q_add_incomplete);
|
|
|
|
|
let x_p = meta.query_advice(self.x_p, Rotation::cur());
|
|
|
|
|
let y_p = meta.query_advice(self.y_p, Rotation::cur());
|
|
|
|
|
let x_q = meta.query_advice(self.x_qr, Rotation::cur());
|
|
|
|
|
let y_q = meta.query_advice(self.y_qr, Rotation::cur());
|
|
|
|
|
let x_r = meta.query_advice(self.x_qr, Rotation::next());
|
|
|
|
|
let y_r = meta.query_advice(self.y_qr, Rotation::next());
|
|
|
|
|
|
|
|
|
|
// (x_r + x_q + x_p)⋅(x_p − x_q)^2 − (y_p − y_q)^2 = 0
|
|
|
|
|
let poly1 = {
|
|
|
|
|
(x_r.clone() + x_q.clone() + x_p.clone())
|
|
|
|
|
* (x_p.clone() - x_q.clone())
|
|
|
|
|
* (x_p.clone() - x_q.clone())
|
2021-06-13 09:19:21 -07:00
|
|
|
|
- (y_p.clone() - y_q.clone()).square()
|
2021-06-04 23:11:40 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// (y_r + y_q)(x_p − x_q) − (y_p − y_q)(x_q − x_r) = 0
|
|
|
|
|
let poly2 = (y_r + y_q.clone()) * (x_p - x_q.clone()) - (y_p - y_q) * (x_q - x_r);
|
|
|
|
|
|
2022-04-24 15:13:38 -07:00
|
|
|
|
Constraints::with_selector(
|
|
|
|
|
q_add_incomplete,
|
|
|
|
|
array::IntoIter::new([("x_r", poly1), ("y_r", poly2)]),
|
|
|
|
|
)
|
2021-06-04 23:11:40 -07:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-10 10:09:04 -07:00
|
|
|
|
pub(super) fn assign_region(
|
2021-06-04 23:11:40 -07:00
|
|
|
|
&self,
|
2021-09-27 01:49:08 -07:00
|
|
|
|
p: &NonIdentityEccPoint,
|
|
|
|
|
q: &NonIdentityEccPoint,
|
2021-06-04 23:11:40 -07:00
|
|
|
|
offset: usize,
|
2021-06-11 15:29:05 -07:00
|
|
|
|
region: &mut Region<'_, pallas::Base>,
|
2021-09-27 01:49:08 -07:00
|
|
|
|
) -> Result<NonIdentityEccPoint, Error> {
|
2021-06-04 23:11:40 -07:00
|
|
|
|
// Enable `q_add_incomplete` selector
|
|
|
|
|
self.q_add_incomplete.enable(region, offset)?;
|
|
|
|
|
|
|
|
|
|
// Handle exceptional cases
|
|
|
|
|
let (x_p, y_p) = (p.x.value(), p.y.value());
|
|
|
|
|
let (x_q, y_q) = (q.x.value(), q.y.value());
|
|
|
|
|
x_p.zip(y_p)
|
|
|
|
|
.zip(x_q)
|
|
|
|
|
.zip(y_q)
|
|
|
|
|
.map(|(((x_p, y_p), x_q), y_q)| {
|
|
|
|
|
// P is point at infinity
|
2021-12-01 04:59:37 -08:00
|
|
|
|
if (x_p.is_zero_vartime() && y_p.is_zero_vartime())
|
2021-06-04 23:11:40 -07:00
|
|
|
|
// Q is point at infinity
|
2021-12-01 04:59:37 -08:00
|
|
|
|
|| (x_q.is_zero_vartime() && y_q.is_zero_vartime())
|
2021-06-04 23:11:40 -07:00
|
|
|
|
// x_p = x_q
|
|
|
|
|
|| (x_p == x_q)
|
|
|
|
|
{
|
2021-11-23 16:21:44 -08:00
|
|
|
|
Err(Error::Synthesis)
|
2021-06-04 23:11:40 -07:00
|
|
|
|
} else {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.transpose()?;
|
|
|
|
|
|
|
|
|
|
// Copy point `p` into `x_p`, `y_p` columns
|
2021-12-01 18:10:00 -08:00
|
|
|
|
p.x.copy_advice(|| "x_p", region, self.x_p, offset)?;
|
|
|
|
|
p.y.copy_advice(|| "y_p", region, self.y_p, offset)?;
|
2021-06-04 23:11:40 -07:00
|
|
|
|
|
|
|
|
|
// Copy point `q` into `x_qr`, `y_qr` columns
|
2021-12-01 18:10:00 -08:00
|
|
|
|
q.x.copy_advice(|| "x_q", region, self.x_qr, offset)?;
|
|
|
|
|
q.y.copy_advice(|| "y_q", region, self.y_qr, offset)?;
|
2021-06-04 23:11:40 -07:00
|
|
|
|
|
|
|
|
|
// Compute the sum `P + Q = R`
|
|
|
|
|
let r = {
|
|
|
|
|
let p = p.point();
|
|
|
|
|
let q = q.point();
|
|
|
|
|
let r = p
|
|
|
|
|
.zip(q)
|
|
|
|
|
.map(|(p, q)| (p + q).to_affine().coordinates().unwrap());
|
|
|
|
|
let r_x = r.map(|r| *r.x());
|
|
|
|
|
let r_y = r.map(|r| *r.y());
|
|
|
|
|
|
|
|
|
|
(r_x, r_y)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Assign the sum to `x_qr`, `y_qr` columns in the next row
|
|
|
|
|
let x_r = r.0;
|
|
|
|
|
let x_r_var = region.assign_advice(
|
|
|
|
|
|| "x_r",
|
|
|
|
|
self.x_qr,
|
|
|
|
|
offset + 1,
|
2021-11-23 16:21:44 -08:00
|
|
|
|
|| x_r.ok_or(Error::Synthesis),
|
2021-06-04 23:11:40 -07:00
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
let y_r = r.1;
|
|
|
|
|
let y_r_var = region.assign_advice(
|
|
|
|
|
|| "y_r",
|
|
|
|
|
self.y_qr,
|
|
|
|
|
offset + 1,
|
2021-11-23 16:21:44 -08:00
|
|
|
|
|| y_r.ok_or(Error::Synthesis),
|
2021-06-04 23:11:40 -07:00
|
|
|
|
)?;
|
|
|
|
|
|
2021-09-27 01:49:08 -07:00
|
|
|
|
let result = NonIdentityEccPoint {
|
2021-12-01 17:10:00 -08:00
|
|
|
|
x: x_r_var,
|
|
|
|
|
y: y_r_var,
|
2021-06-04 23:11:40 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub mod tests {
|
2021-06-13 09:19:21 -07:00
|
|
|
|
use group::Curve;
|
2022-01-27 15:28:02 -08:00
|
|
|
|
use halo2_proofs::{circuit::Layouter, plonk::Error};
|
2021-06-13 09:19:21 -07:00
|
|
|
|
use pasta_curves::pallas;
|
2021-06-04 23:11:40 -07:00
|
|
|
|
|
2022-01-27 13:53:10 -08:00
|
|
|
|
use crate::ecc::{EccInstructions, NonIdentityPoint};
|
2021-06-04 23:11:40 -07:00
|
|
|
|
|
2021-06-13 09:19:21 -07:00
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
2021-06-04 23:11:40 -07:00
|
|
|
|
pub fn test_add_incomplete<
|
2021-06-13 09:19:21 -07:00
|
|
|
|
EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug,
|
2021-06-04 23:11:40 -07:00
|
|
|
|
>(
|
2021-06-13 09:19:21 -07:00
|
|
|
|
chip: EccChip,
|
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
|
|
|
|
p_val: pallas::Affine,
|
2021-09-27 01:49:08 -07:00
|
|
|
|
p: &NonIdentityPoint<pallas::Affine, EccChip>,
|
2021-06-13 09:19:21 -07:00
|
|
|
|
q_val: pallas::Affine,
|
2021-09-27 01:49:08 -07:00
|
|
|
|
q: &NonIdentityPoint<pallas::Affine, EccChip>,
|
|
|
|
|
p_neg: &NonIdentityPoint<pallas::Affine, EccChip>,
|
2021-12-08 16:49:01 -08:00
|
|
|
|
test_errors: bool,
|
2021-06-04 23:11:40 -07:00
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
// P + Q
|
2021-06-13 09:19:21 -07:00
|
|
|
|
{
|
|
|
|
|
let result = p.add_incomplete(layouter.namespace(|| "P + Q"), q)?;
|
2021-09-27 01:49:08 -07:00
|
|
|
|
let witnessed_result = NonIdentityPoint::new(
|
2021-06-13 09:19:21 -07:00
|
|
|
|
chip,
|
|
|
|
|
layouter.namespace(|| "witnessed P + Q"),
|
|
|
|
|
Some((p_val + q_val).to_affine()),
|
|
|
|
|
)?;
|
|
|
|
|
result.constrain_equal(layouter.namespace(|| "constrain P + Q"), &witnessed_result)?;
|
|
|
|
|
}
|
2021-06-04 23:11:40 -07:00
|
|
|
|
|
2021-12-08 16:49:01 -08:00
|
|
|
|
if test_errors {
|
|
|
|
|
// P + P should return an error
|
|
|
|
|
p.add_incomplete(layouter.namespace(|| "P + P"), p)
|
|
|
|
|
.expect_err("P + P should return an error");
|
2021-06-04 23:11:40 -07:00
|
|
|
|
|
2021-12-08 16:49:01 -08:00
|
|
|
|
// P + (-P) should return an error
|
|
|
|
|
p.add_incomplete(layouter.namespace(|| "P + (-P)"), p_neg)
|
|
|
|
|
.expect_err("P + (-P) should return an error");
|
|
|
|
|
}
|
2021-06-04 23:11:40 -07:00
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|