#![allow(non_snake_case)]
use crate::ff::Field;
use crate::fields::{fp::FpChip, FieldChip, Selectable};
use crate::group::{Curve, Group};
use crate::halo2_proofs::arithmetic::CurveAffine;
use halo2_base::gates::flex_gate::threads::SinglePhaseCoreManager;
use halo2_base::utils::{modulus, BigPrimeField};
use halo2_base::{
gates::{GateInstructions, RangeInstructions},
utils::CurveAffineExt,
AssignedValue, Context,
};
use itertools::Itertools;
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use std::marker::PhantomData;
pub mod ecdsa;
pub mod fixed_base;
pub mod pippenger;
#[derive(Debug)]
pub struct EcPoint<F: BigPrimeField, FieldPoint> {
pub x: FieldPoint,
pub y: FieldPoint,
_marker: PhantomData<F>,
}
impl<F: BigPrimeField, FieldPoint: Clone> Clone for EcPoint<F, FieldPoint> {
fn clone(&self) -> Self {
Self { x: self.x.clone(), y: self.y.clone(), _marker: PhantomData }
}
}
impl<'a, F: BigPrimeField, FieldPoint: Clone> From<&'a EcPoint<F, FieldPoint>>
for EcPoint<F, FieldPoint>
{
fn from(value: &'a EcPoint<F, FieldPoint>) -> Self {
value.clone()
}
}
impl<F: BigPrimeField, FieldPoint> EcPoint<F, FieldPoint> {
pub fn new(x: FieldPoint, y: FieldPoint) -> Self {
Self { x, y, _marker: PhantomData }
}
pub fn x(&self) -> &FieldPoint {
&self.x
}
pub fn y(&self) -> &FieldPoint {
&self.y
}
}
#[derive(Clone, Debug)]
pub struct StrictEcPoint<F: BigPrimeField, FC: FieldChip<F>> {
pub x: FC::ReducedFieldPoint,
pub y: FC::FieldPoint,
_marker: PhantomData<F>,
}
impl<F: BigPrimeField, FC: FieldChip<F>> StrictEcPoint<F, FC> {
pub fn new(x: FC::ReducedFieldPoint, y: FC::FieldPoint) -> Self {
Self { x, y, _marker: PhantomData }
}
}
impl<F: BigPrimeField, FC: FieldChip<F>> From<StrictEcPoint<F, FC>> for EcPoint<F, FC::FieldPoint> {
fn from(value: StrictEcPoint<F, FC>) -> Self {
Self::new(value.x.into(), value.y)
}
}
impl<'a, F: BigPrimeField, FC: FieldChip<F>> From<&'a StrictEcPoint<F, FC>>
for EcPoint<F, FC::FieldPoint>
{
fn from(value: &'a StrictEcPoint<F, FC>) -> Self {
value.clone().into()
}
}
#[derive(Clone, Debug)]
pub enum ComparableEcPoint<F: BigPrimeField, FC: FieldChip<F>> {
Strict(StrictEcPoint<F, FC>),
NonStrict(EcPoint<F, FC::FieldPoint>),
}
impl<F: BigPrimeField, FC: FieldChip<F>> From<StrictEcPoint<F, FC>> for ComparableEcPoint<F, FC> {
fn from(pt: StrictEcPoint<F, FC>) -> Self {
Self::Strict(pt)
}
}
impl<F: BigPrimeField, FC: FieldChip<F>> From<EcPoint<F, FC::FieldPoint>>
for ComparableEcPoint<F, FC>
{
fn from(pt: EcPoint<F, FC::FieldPoint>) -> Self {
Self::NonStrict(pt)
}
}
impl<'a, F: BigPrimeField, FC: FieldChip<F>> From<&'a StrictEcPoint<F, FC>>
for ComparableEcPoint<F, FC>
{
fn from(pt: &'a StrictEcPoint<F, FC>) -> Self {
Self::Strict(pt.clone())
}
}
impl<'a, F: BigPrimeField, FC: FieldChip<F>> From<&'a EcPoint<F, FC::FieldPoint>>
for ComparableEcPoint<F, FC>
{
fn from(pt: &'a EcPoint<F, FC::FieldPoint>) -> Self {
Self::NonStrict(pt.clone())
}
}
impl<F: BigPrimeField, FC: FieldChip<F>> From<ComparableEcPoint<F, FC>>
for EcPoint<F, FC::FieldPoint>
{
fn from(pt: ComparableEcPoint<F, FC>) -> Self {
match pt {
ComparableEcPoint::Strict(pt) => Self::new(pt.x.into(), pt.y),
ComparableEcPoint::NonStrict(pt) => pt,
}
}
}
pub fn ec_add_unequal<F: BigPrimeField, FC: FieldChip<F>>(
chip: &FC,
ctx: &mut Context<F>,
P: impl Into<ComparableEcPoint<F, FC>>,
Q: impl Into<ComparableEcPoint<F, FC>>,
is_strict: bool,
) -> EcPoint<F, FC::FieldPoint> {
let (P, Q) = check_points_are_unequal(chip, ctx, P, Q, is_strict);
let dx = chip.sub_no_carry(ctx, &Q.x, &P.x);
let dy = chip.sub_no_carry(ctx, Q.y, &P.y);
let lambda = chip.divide_unsafe(ctx, dy, dx);
let lambda_sq = chip.mul_no_carry(ctx, &lambda, &lambda);
let lambda_sq_minus_px = chip.sub_no_carry(ctx, lambda_sq, &P.x);
let x_3_no_carry = chip.sub_no_carry(ctx, lambda_sq_minus_px, Q.x);
let x_3 = chip.carry_mod(ctx, x_3_no_carry);
let dx_13 = chip.sub_no_carry(ctx, P.x, &x_3);
let lambda_dx_13 = chip.mul_no_carry(ctx, lambda, dx_13);
let y_3_no_carry = chip.sub_no_carry(ctx, lambda_dx_13, P.y);
let y_3 = chip.carry_mod(ctx, y_3_no_carry);
EcPoint::new(x_3, y_3)
}
fn check_points_are_unequal<F: BigPrimeField, FC: FieldChip<F>>(
chip: &FC,
ctx: &mut Context<F>,
P: impl Into<ComparableEcPoint<F, FC>>,
Q: impl Into<ComparableEcPoint<F, FC>>,
do_check: bool,
) -> (EcPoint<F, FC::FieldPoint> , EcPoint<F, FC::FieldPoint> ) {
let P = P.into();
let Q = Q.into();
if do_check {
let [x1, x2] = [&P, &Q].map(|pt| match pt {
ComparableEcPoint::Strict(pt) => pt.x.clone(),
ComparableEcPoint::NonStrict(pt) => chip.enforce_less_than(ctx, pt.x.clone()),
});
let x_is_equal = chip.is_equal_unenforced(ctx, x1, x2);
chip.gate().assert_is_const(ctx, &x_is_equal, &F::ZERO);
}
(EcPoint::from(P), EcPoint::from(Q))
}
pub fn ec_sub_unequal<F: BigPrimeField, FC: FieldChip<F>>(
chip: &FC,
ctx: &mut Context<F>,
P: impl Into<ComparableEcPoint<F, FC>>,
Q: impl Into<ComparableEcPoint<F, FC>>,
is_strict: bool,
) -> EcPoint<F, FC::FieldPoint> {
let (P, Q) = check_points_are_unequal(chip, ctx, P, Q, is_strict);
let dx = chip.sub_no_carry(ctx, &Q.x, &P.x);
let sy = chip.add_no_carry(ctx, Q.y, &P.y);
let lambda = chip.neg_divide_unsafe(ctx, sy, dx);
let lambda_sq = chip.mul_no_carry(ctx, &lambda, &lambda);
let lambda_sq_minus_px = chip.sub_no_carry(ctx, lambda_sq, &P.x);
let x_3_no_carry = chip.sub_no_carry(ctx, lambda_sq_minus_px, Q.x);
let x_3 = chip.carry_mod(ctx, x_3_no_carry);
let dx_13 = chip.sub_no_carry(ctx, P.x, &x_3);
let lambda_dx_13 = chip.mul_no_carry(ctx, lambda, dx_13);
let y_3_no_carry = chip.sub_no_carry(ctx, lambda_dx_13, P.y);
let y_3 = chip.carry_mod(ctx, y_3_no_carry);
EcPoint::new(x_3, y_3)
}
pub fn ec_sub_strict<F: BigPrimeField, FC: FieldChip<F>>(
chip: &FC,
ctx: &mut Context<F>,
P: impl Into<EcPoint<F, FC::FieldPoint>>,
Q: impl Into<EcPoint<F, FC::FieldPoint>>,
) -> EcPoint<F, FC::FieldPoint>
where
FC: Selectable<F, FC::FieldPoint>,
{
let mut P = P.into();
let Q = Q.into();
let x_is_eq = chip.is_equal(ctx, P.x(), Q.x());
let y_is_eq = chip.is_equal(ctx, P.y(), Q.y());
let is_identity = chip.gate().and(ctx, x_is_eq, y_is_eq);
ctx.constrain_equal(&x_is_eq, &is_identity);
let mut rng = ChaCha20Rng::from_entropy();
let [rand_x, rand_y] = [(); 2].map(|_| FC::FieldType::random(&mut rng));
let [rand_x, rand_y] = [rand_x, rand_y].map(|x| chip.load_private(ctx, x));
let rand_pt = EcPoint::new(rand_x, rand_y);
P = ec_select(chip, ctx, rand_pt, P, is_identity);
let out = ec_sub_unequal(chip, ctx, P, Q, false);
let zero = chip.load_constant(ctx, FC::FieldType::ZERO);
ec_select(chip, ctx, EcPoint::new(zero.clone(), zero), out, is_identity)
}
pub fn ec_double<F: BigPrimeField, FC: FieldChip<F>>(
chip: &FC,
ctx: &mut Context<F>,
P: impl Into<EcPoint<F, FC::FieldPoint>>,
) -> EcPoint<F, FC::FieldPoint> {
let P = P.into();
let two_y = chip.scalar_mul_no_carry(ctx, &P.y, 2);
let three_x = chip.scalar_mul_no_carry(ctx, &P.x, 3);
let three_x_sq = chip.mul_no_carry(ctx, three_x, &P.x);
let lambda = chip.divide_unsafe(ctx, three_x_sq, two_y);
let lambda_sq = chip.mul_no_carry(ctx, &lambda, &lambda);
let two_x = chip.scalar_mul_no_carry(ctx, &P.x, 2);
let x_3_no_carry = chip.sub_no_carry(ctx, lambda_sq, two_x);
let x_3 = chip.carry_mod(ctx, x_3_no_carry);
let dx = chip.sub_no_carry(ctx, P.x, &x_3);
let lambda_dx = chip.mul_no_carry(ctx, lambda, dx);
let y_3_no_carry = chip.sub_no_carry(ctx, lambda_dx, P.y);
let y_3 = chip.carry_mod(ctx, y_3_no_carry);
EcPoint::new(x_3, y_3)
}
pub fn ec_double_and_add_unequal<F: BigPrimeField, FC: FieldChip<F>>(
chip: &FC,
ctx: &mut Context<F>,
P: impl Into<ComparableEcPoint<F, FC>>,
Q: impl Into<ComparableEcPoint<F, FC>>,
is_strict: bool,
) -> EcPoint<F, FC::FieldPoint> {
let P = P.into();
let Q = Q.into();
let mut x_0 = None;
if is_strict {
let [x0, x1] = [&P, &Q].map(|pt| match pt {
ComparableEcPoint::Strict(pt) => pt.x.clone(),
ComparableEcPoint::NonStrict(pt) => chip.enforce_less_than(ctx, pt.x.clone()),
});
let x_is_equal = chip.is_equal_unenforced(ctx, x0.clone(), x1);
chip.gate().assert_is_const(ctx, &x_is_equal, &F::ZERO);
x_0 = Some(x0);
}
let P = EcPoint::from(P);
let Q = EcPoint::from(Q);
let dx = chip.sub_no_carry(ctx, &Q.x, &P.x);
let dy = chip.sub_no_carry(ctx, Q.y, &P.y);
let lambda_0 = chip.divide_unsafe(ctx, dy, dx);
let lambda_0_sq = chip.mul_no_carry(ctx, &lambda_0, &lambda_0);
let lambda_0_sq_minus_x_0 = chip.sub_no_carry(ctx, lambda_0_sq, &P.x);
let x_2_no_carry = chip.sub_no_carry(ctx, lambda_0_sq_minus_x_0, Q.x);
let x_2 = chip.carry_mod(ctx, x_2_no_carry);
if is_strict {
let x_2 = chip.enforce_less_than(ctx, x_2.clone());
let x_is_equal = chip.is_equal_unenforced(ctx, x_0.unwrap(), x_2);
chip.range().gate().assert_is_const(ctx, &x_is_equal, &F::ZERO);
}
let two_y_0 = chip.scalar_mul_no_carry(ctx, &P.y, 2);
let x_2_minus_x_0 = chip.sub_no_carry(ctx, &x_2, &P.x);
let lambda_1_minus_lambda_0 = chip.divide_unsafe(ctx, two_y_0, x_2_minus_x_0);
let lambda_1_no_carry = chip.add_no_carry(ctx, lambda_0, lambda_1_minus_lambda_0);
let lambda_1_sq_nc = chip.mul_no_carry(ctx, &lambda_1_no_carry, &lambda_1_no_carry);
let lambda_1_sq_minus_x_0 = chip.sub_no_carry(ctx, lambda_1_sq_nc, &P.x);
let x_res_no_carry = chip.sub_no_carry(ctx, lambda_1_sq_minus_x_0, x_2);
let x_res = chip.carry_mod(ctx, x_res_no_carry);
let x_res_minus_x_0 = chip.sub_no_carry(ctx, &x_res, P.x);
let lambda_1_x_res_minus_x_0 = chip.mul_no_carry(ctx, lambda_1_no_carry, x_res_minus_x_0);
let y_res_no_carry = chip.sub_no_carry(ctx, lambda_1_x_res_minus_x_0, P.y);
let y_res = chip.carry_mod(ctx, y_res_no_carry);
EcPoint::new(x_res, y_res)
}
pub fn ec_select<F: BigPrimeField, FC>(
chip: &FC,
ctx: &mut Context<F>,
P: EcPoint<F, FC::FieldPoint>,
Q: EcPoint<F, FC::FieldPoint>,
sel: AssignedValue<F>,
) -> EcPoint<F, FC::FieldPoint>
where
FC: FieldChip<F> + Selectable<F, FC::FieldPoint>,
{
let Rx = chip.select(ctx, P.x, Q.x, sel);
let Ry = chip.select(ctx, P.y, Q.y, sel);
EcPoint::new(Rx, Ry)
}
pub fn ec_select_by_indicator<F: BigPrimeField, FC, Pt>(
chip: &FC,
ctx: &mut Context<F>,
points: &[Pt],
coeffs: &[AssignedValue<F>],
) -> EcPoint<F, FC::FieldPoint>
where
FC: FieldChip<F> + Selectable<F, FC::FieldPoint>,
Pt: Into<EcPoint<F, FC::FieldPoint>> + Clone,
{
let (x, y): (Vec<_>, Vec<_>) = points
.iter()
.map(|P| {
let P: EcPoint<_, _> = P.clone().into();
(P.x, P.y)
})
.unzip();
let Rx = chip.select_by_indicator(ctx, &x, coeffs);
let Ry = chip.select_by_indicator(ctx, &y, coeffs);
EcPoint::new(Rx, Ry)
}
pub fn ec_select_from_bits<F: BigPrimeField, FC, Pt>(
chip: &FC,
ctx: &mut Context<F>,
points: &[Pt],
sel: &[AssignedValue<F>],
) -> EcPoint<F, FC::FieldPoint>
where
FC: FieldChip<F> + Selectable<F, FC::FieldPoint>,
Pt: Into<EcPoint<F, FC::FieldPoint>> + Clone,
{
let w = sel.len();
assert_eq!(1 << w, points.len());
let coeffs = chip.range().gate().bits_to_indicator(ctx, sel);
ec_select_by_indicator(chip, ctx, points, &coeffs)
}
pub fn strict_ec_select_from_bits<F: BigPrimeField, FC>(
chip: &FC,
ctx: &mut Context<F>,
points: &[StrictEcPoint<F, FC>],
sel: &[AssignedValue<F>],
) -> StrictEcPoint<F, FC>
where
FC: FieldChip<F> + Selectable<F, FC::FieldPoint> + Selectable<F, FC::ReducedFieldPoint>,
{
let w = sel.len();
assert_eq!(1 << w, points.len());
let coeffs = chip.range().gate().bits_to_indicator(ctx, sel);
let (x, y): (Vec<_>, Vec<_>) = points.iter().map(|pt| (pt.x.clone(), pt.y.clone())).unzip();
let x = chip.select_by_indicator(ctx, &x, &coeffs);
let y = chip.select_by_indicator(ctx, &y, &coeffs);
StrictEcPoint::new(x, y)
}
pub fn scalar_multiply<F: BigPrimeField, FC, C>(
chip: &FC,
ctx: &mut Context<F>,
P: EcPoint<F, FC::FieldPoint>,
scalar: Vec<AssignedValue<F>>,
max_bits: usize,
window_bits: usize,
) -> EcPoint<F, FC::FieldPoint>
where
FC: FieldChip<F> + Selectable<F, FC::FieldPoint>,
C: CurveAffineExt<Base = FC::FieldType>,
{
assert!(!scalar.is_empty());
assert!((max_bits as u64) <= modulus::<F>().bits());
assert!(window_bits != 0);
multi_scalar_multiply::<F, FC, C>(chip, ctx, &[P], vec![scalar], max_bits, window_bits)
}
pub fn check_is_on_curve<F, FC, C>(chip: &FC, ctx: &mut Context<F>, P: &EcPoint<F, FC::FieldPoint>)
where
F: BigPrimeField,
FC: FieldChip<F>,
C: CurveAffine<Base = FC::FieldType>,
{
let lhs = chip.mul_no_carry(ctx, &P.y, &P.y);
let mut rhs = chip.mul(ctx, &P.x, &P.x).into();
rhs = chip.mul_no_carry(ctx, rhs, &P.x);
rhs = chip.add_constant_no_carry(ctx, rhs, C::b());
let diff = chip.sub_no_carry(ctx, lhs, rhs);
chip.check_carry_mod_to_zero(ctx, diff)
}
pub fn load_random_point<F, FC, C>(chip: &FC, ctx: &mut Context<F>) -> EcPoint<F, FC::FieldPoint>
where
F: BigPrimeField,
FC: FieldChip<F>,
C: CurveAffineExt<Base = FC::FieldType>,
{
let base_point: C = C::CurveExt::random(ChaCha20Rng::from_entropy()).to_affine();
let (x, y) = base_point.into_coordinates();
let base = {
let x_overflow = chip.load_private(ctx, x);
let y_overflow = chip.load_private(ctx, y);
EcPoint::new(x_overflow, y_overflow)
};
check_is_on_curve::<F, FC, C>(chip, ctx, &base);
base
}
pub fn into_strict_point<F, FC>(
chip: &FC,
ctx: &mut Context<F>,
pt: EcPoint<F, FC::FieldPoint>,
) -> StrictEcPoint<F, FC>
where
F: BigPrimeField,
FC: FieldChip<F>,
{
let x = chip.enforce_less_than(ctx, pt.x);
StrictEcPoint::new(x, pt.y)
}
pub fn multi_scalar_multiply<F: BigPrimeField, FC, C>(
chip: &FC,
ctx: &mut Context<F>,
P: &[EcPoint<F, FC::FieldPoint>],
scalars: Vec<Vec<AssignedValue<F>>>,
max_bits: usize,
window_bits: usize,
) -> EcPoint<F, FC::FieldPoint>
where
FC: FieldChip<F> + Selectable<F, FC::FieldPoint>,
C: CurveAffineExt<Base = FC::FieldType>,
{
let k = P.len();
assert_eq!(k, scalars.len());
assert_ne!(k, 0);
assert!(!scalars[0].is_empty());
assert!((max_bits as u32) <= F::NUM_BITS);
let scalar_len = scalars[0].len();
let total_bits = max_bits * scalar_len;
let num_windows = (total_bits + window_bits - 1) / window_bits;
let rounded_bitlen = num_windows * window_bits;
let zero_cell = ctx.load_zero();
let rounded_bits = scalars
.into_iter()
.flat_map(|scalar| {
debug_assert_eq!(scalar.len(), scalar_len);
scalar
.into_iter()
.flat_map(|scalar_chunk| chip.gate().num_to_bits(ctx, scalar_chunk, max_bits))
.chain(std::iter::repeat(zero_cell).take(rounded_bitlen - total_bits))
.collect_vec()
})
.collect_vec();
let base = load_random_point::<F, FC, C>(chip, ctx);
let mut rand_start_vec = Vec::with_capacity(k + window_bits);
rand_start_vec.push(base);
for idx in 1..(k + window_bits) {
let base_mult = ec_double(chip, ctx, &rand_start_vec[idx - 1]);
rand_start_vec.push(base_mult);
}
assert!(rand_start_vec.len() >= k + window_bits);
let cache_size = 1usize << window_bits;
let mut cached_points = Vec::with_capacity(k * cache_size);
for (idx, point) in P.iter().enumerate() {
let is_infinity = chip.is_zero(ctx, &point.y);
let neg_mult_rand_start = ec_sub_unequal(
chip,
ctx,
&rand_start_vec[idx],
&rand_start_vec[idx + window_bits],
true, );
let point = into_strict_point(chip, ctx, point.clone());
let neg_mult_rand_start = into_strict_point(chip, ctx, neg_mult_rand_start);
cached_points.push(neg_mult_rand_start);
for _ in 0..(cache_size - 1) {
let prev = cached_points.last().unwrap().clone();
let mut new_point = ec_add_unequal(chip, ctx, &prev, &point, true);
new_point = ec_select(chip, ctx, prev.into(), new_point, is_infinity);
let new_point = into_strict_point(chip, ctx, new_point);
cached_points.push(new_point);
}
}
let start_point = ec_sub_unequal(
chip,
ctx,
&rand_start_vec[k],
&rand_start_vec[0],
true, );
let mut curr_point = start_point.clone();
for idx in 0..num_windows {
for _ in 0..window_bits {
curr_point = ec_double(chip, ctx, curr_point);
}
for (cached_points, rounded_bits) in
cached_points.chunks(cache_size).zip(rounded_bits.chunks(rounded_bitlen))
{
let add_point = ec_select_from_bits(
chip,
ctx,
cached_points,
&rounded_bits
[rounded_bitlen - window_bits * (idx + 1)..rounded_bitlen - window_bits * idx],
);
curr_point = ec_add_unequal(chip, ctx, curr_point, add_point, true);
}
}
ec_sub_strict(chip, ctx, curr_point, start_point)
}
pub fn get_naf(mut exp: Vec<u64>) -> Vec<i8> {
let mut naf: Vec<i8> = Vec::with_capacity(64 * exp.len());
let len = exp.len();
for idx in 0..len {
let mut e: u64 = exp[idx];
for _ in 0..64 {
if e & 1 == 1 {
let z = 2i8 - (e % 4) as i8;
e /= 2;
if z == -1 {
e += 1;
}
naf.push(z);
} else {
naf.push(0);
e /= 2;
}
}
if e != 0 {
assert_eq!(e, 1);
let mut j = idx + 1;
while j < exp.len() && exp[j] == u64::MAX {
exp[j] = 0;
j += 1;
}
if j < exp.len() {
exp[j] += 1;
} else {
exp.push(1);
}
}
}
if exp.len() != len {
assert_eq!(len, exp.len() + 1);
assert!(exp[len] == 1);
naf.push(1);
}
naf
}
pub type BaseFieldEccChip<'chip, C> = EccChip<
'chip,
<C as CurveAffine>::ScalarExt,
FpChip<'chip, <C as CurveAffine>::ScalarExt, <C as CurveAffine>::Base>,
>;
#[derive(Clone, Debug)]
pub struct EccChip<'chip, F: BigPrimeField, FC: FieldChip<F>> {
pub field_chip: &'chip FC,
_marker: PhantomData<F>,
}
impl<'chip, F: BigPrimeField, FC: FieldChip<F>> EccChip<'chip, F, FC> {
pub fn new(field_chip: &'chip FC) -> Self {
Self { field_chip, _marker: PhantomData }
}
pub fn field_chip(&self) -> &FC {
self.field_chip
}
pub fn load_private<C>(
&self,
ctx: &mut Context<F>,
(x, y): (FC::FieldType, FC::FieldType),
) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt<Base = FC::FieldType>,
{
let pt = self.load_private_unchecked(ctx, (x, y));
self.assert_is_on_curve::<C>(ctx, &pt);
pt
}
pub fn load_private_unchecked(
&self,
ctx: &mut Context<F>,
(x, y): (FC::FieldType, FC::FieldType),
) -> EcPoint<F, FC::FieldPoint> {
let x_assigned = self.field_chip.load_private(ctx, x);
let y_assigned = self.field_chip.load_private(ctx, y);
EcPoint::new(x_assigned, y_assigned)
}
pub fn assign_point<C>(&self, ctx: &mut Context<F>, g: C) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt<Base = FC::FieldType>,
C::Base: crate::ff::PrimeField,
{
let pt = self.assign_point_unchecked(ctx, g);
let is_on_curve = self.is_on_curve_or_infinity::<C>(ctx, &pt);
self.field_chip.gate().assert_is_const(ctx, &is_on_curve, &F::ONE);
pt
}
pub fn assign_point_unchecked<C>(
&self,
ctx: &mut Context<F>,
g: C,
) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt<Base = FC::FieldType>,
{
let (x, y) = g.into_coordinates();
self.load_private_unchecked(ctx, (x, y))
}
pub fn assign_constant_point<C>(&self, ctx: &mut Context<F>, g: C) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt<Base = FC::FieldType>,
{
let (x, y) = g.into_coordinates();
let x = self.field_chip.load_constant(ctx, x);
let y = self.field_chip.load_constant(ctx, y);
EcPoint::new(x, y)
}
pub fn load_random_point<C>(&self, ctx: &mut Context<F>) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt<Base = FC::FieldType>,
{
load_random_point::<F, FC, C>(self.field_chip(), ctx)
}
pub fn assert_is_on_curve<C>(&self, ctx: &mut Context<F>, P: &EcPoint<F, FC::FieldPoint>)
where
C: CurveAffine<Base = FC::FieldType>,
{
check_is_on_curve::<F, FC, C>(self.field_chip, ctx, P)
}
pub fn is_on_curve_or_infinity<C>(
&self,
ctx: &mut Context<F>,
P: &EcPoint<F, FC::FieldPoint>,
) -> AssignedValue<F>
where
C: CurveAffine<Base = FC::FieldType>,
{
let lhs = self.field_chip.mul_no_carry(ctx, &P.y, &P.y);
let mut rhs = self.field_chip.mul(ctx, &P.x, &P.x).into();
rhs = self.field_chip.mul_no_carry(ctx, rhs, &P.x);
rhs = self.field_chip.add_constant_no_carry(ctx, rhs, C::b());
let diff = self.field_chip.sub_no_carry(ctx, lhs, rhs);
let diff = self.field_chip.carry_mod(ctx, diff);
let is_on_curve = self.field_chip.is_zero(ctx, diff);
let x_is_zero = self.field_chip.is_zero(ctx, &P.x);
let y_is_zero = self.field_chip.is_zero(ctx, &P.y);
self.field_chip.range().gate().or_and(ctx, is_on_curve, x_is_zero, y_is_zero)
}
pub fn negate(
&self,
ctx: &mut Context<F>,
P: impl Into<EcPoint<F, FC::FieldPoint>>,
) -> EcPoint<F, FC::FieldPoint> {
let P = P.into();
EcPoint::new(P.x, self.field_chip.negate(ctx, P.y))
}
pub fn add_unequal(
&self,
ctx: &mut Context<F>,
P: impl Into<ComparableEcPoint<F, FC>>,
Q: impl Into<ComparableEcPoint<F, FC>>,
is_strict: bool,
) -> EcPoint<F, FC::FieldPoint> {
ec_add_unequal(self.field_chip, ctx, P, Q, is_strict)
}
pub fn sub_unequal(
&self,
ctx: &mut Context<F>,
P: impl Into<ComparableEcPoint<F, FC>>,
Q: impl Into<ComparableEcPoint<F, FC>>,
is_strict: bool,
) -> EcPoint<F, FC::FieldPoint> {
ec_sub_unequal(self.field_chip, ctx, P, Q, is_strict)
}
pub fn double(
&self,
ctx: &mut Context<F>,
P: impl Into<EcPoint<F, FC::FieldPoint>>,
) -> EcPoint<F, FC::FieldPoint> {
ec_double(self.field_chip, ctx, P)
}
pub fn is_equal(
&self,
ctx: &mut Context<F>,
P: EcPoint<F, FC::FieldPoint>,
Q: EcPoint<F, FC::FieldPoint>,
) -> AssignedValue<F> {
let x_is_equal = self.field_chip.is_equal(ctx, P.x, Q.x);
let y_is_equal = self.field_chip.is_equal(ctx, P.y, Q.y);
self.field_chip.range().gate().and(ctx, x_is_equal, y_is_equal)
}
pub fn assert_equal(
&self,
ctx: &mut Context<F>,
P: EcPoint<F, FC::FieldPoint>,
Q: EcPoint<F, FC::FieldPoint>,
) {
self.field_chip.assert_equal(ctx, P.x, Q.x);
self.field_chip.assert_equal(ctx, P.y, Q.y);
}
pub fn sum<C>(
&self,
ctx: &mut Context<F>,
points: impl IntoIterator<Item = EcPoint<F, FC::FieldPoint>>,
) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt<Base = FC::FieldType>,
{
let rand_point = self.load_random_point::<C>(ctx);
let rand_point = into_strict_point(self.field_chip, ctx, rand_point);
let mut acc = rand_point.clone();
for point in points {
let _acc = self.add_unequal(ctx, acc, point, true);
acc = into_strict_point(self.field_chip, ctx, _acc);
}
self.sub_unequal(ctx, acc, rand_point, true)
}
}
impl<'chip, F: BigPrimeField, FC: FieldChip<F>> EccChip<'chip, F, FC>
where
FC: Selectable<F, FC::FieldPoint>,
{
pub fn select(
&self,
ctx: &mut Context<F>,
P: EcPoint<F, FC::FieldPoint>,
Q: EcPoint<F, FC::FieldPoint>,
condition: AssignedValue<F>,
) -> EcPoint<F, FC::FieldPoint> {
ec_select(self.field_chip, ctx, P, Q, condition)
}
pub fn scalar_mult<C>(
&self,
ctx: &mut Context<F>,
P: EcPoint<F, FC::FieldPoint>,
scalar: Vec<AssignedValue<F>>,
max_bits: usize,
window_bits: usize,
) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt<Base = FC::FieldType>,
{
scalar_multiply::<F, FC, C>(self.field_chip, ctx, P, scalar, max_bits, window_bits)
}
pub fn variable_base_msm<C>(
&self,
thread_pool: &mut SinglePhaseCoreManager<F>,
P: &[EcPoint<F, FC::FieldPoint>],
scalars: Vec<Vec<AssignedValue<F>>>,
max_bits: usize,
) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt<Base = FC::FieldType>,
FC: Selectable<F, FC::ReducedFieldPoint>,
{
self.variable_base_msm_custom::<C>(thread_pool, P, scalars, max_bits, 4)
}
pub fn variable_base_msm_custom<C>(
&self,
builder: &mut SinglePhaseCoreManager<F>,
P: &[EcPoint<F, FC::FieldPoint>],
scalars: Vec<Vec<AssignedValue<F>>>,
max_bits: usize,
window_bits: usize,
) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt<Base = FC::FieldType>,
FC: Selectable<F, FC::ReducedFieldPoint>,
{
#[cfg(feature = "display")]
println!("computing length {} MSM", P.len());
if P.len() <= 25 {
multi_scalar_multiply::<F, FC, C>(
self.field_chip,
builder.main(),
P,
scalars,
max_bits,
window_bits,
)
} else {
pippenger::multi_exp_par::<F, FC, C>(
self.field_chip,
builder,
P,
scalars,
max_bits,
window_bits, )
}
}
}
impl<'chip, F: BigPrimeField, FC: FieldChip<F>> EccChip<'chip, F, FC> {
pub fn fixed_base_scalar_mult<C>(
&self,
ctx: &mut Context<F>,
point: &C,
scalar: Vec<AssignedValue<F>>,
max_bits: usize,
window_bits: usize,
) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt,
FC: FieldChip<F, FieldType = C::Base> + Selectable<F, FC::FieldPoint>,
{
fixed_base::scalar_multiply::<F, _, _>(
self.field_chip,
ctx,
point,
scalar,
max_bits,
window_bits,
)
}
pub fn fixed_base_msm<C>(
&self,
builder: &mut SinglePhaseCoreManager<F>,
points: &[C],
scalars: Vec<Vec<AssignedValue<F>>>,
max_scalar_bits_per_cell: usize,
) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt,
FC: FieldChip<F, FieldType = C::Base> + Selectable<F, FC::FieldPoint>,
{
self.fixed_base_msm_custom::<C>(builder, points, scalars, max_scalar_bits_per_cell, 4)
}
pub fn fixed_base_msm_custom<C>(
&self,
builder: &mut SinglePhaseCoreManager<F>,
points: &[C],
scalars: Vec<Vec<AssignedValue<F>>>,
max_scalar_bits_per_cell: usize,
clump_factor: usize,
) -> EcPoint<F, FC::FieldPoint>
where
C: CurveAffineExt,
FC: FieldChip<F, FieldType = C::Base> + Selectable<F, FC::FieldPoint>,
{
assert_eq!(points.len(), scalars.len());
#[cfg(feature = "display")]
println!("computing length {} fixed base msm", points.len());
fixed_base::msm_par(self, builder, points, scalars, max_scalar_bits_per_cell, clump_factor)
}
}
#[cfg(test)]
pub(crate) mod tests;