use crate::util::arithmetic::{CurveAffine, PrimeField};
use std::{fmt::Debug, ops::Deref};
pub trait IntegerInstructions<F: PrimeField>: Clone + Debug {
type Context: Debug;
type AssignedCell: Clone + Debug;
type AssignedInteger: Clone + Debug;
fn assign_integer(
&self,
ctx: &mut Self::Context,
integer: F, ) -> Self::AssignedInteger;
fn assign_constant(&self, ctx: &mut Self::Context, integer: F) -> Self::AssignedInteger;
fn sum_with_coeff_and_const(
&self,
ctx: &mut Self::Context,
values: &[(F, impl Deref<Target = Self::AssignedInteger>)],
constant: F,
) -> Self::AssignedInteger;
fn sum_products_with_coeff_and_const(
&self,
ctx: &mut Self::Context,
values: &[(
F,
impl Deref<Target = Self::AssignedInteger>,
impl Deref<Target = Self::AssignedInteger>,
)],
constant: F,
) -> Self::AssignedInteger;
fn sub(
&self,
ctx: &mut Self::Context,
lhs: &Self::AssignedInteger,
rhs: &Self::AssignedInteger,
) -> Self::AssignedInteger;
fn neg(&self, ctx: &mut Self::Context, value: &Self::AssignedInteger) -> Self::AssignedInteger;
fn invert(
&self,
ctx: &mut Self::Context,
value: &Self::AssignedInteger,
) -> Self::AssignedInteger;
fn assert_equal(
&self,
ctx: &mut Self::Context,
lhs: &Self::AssignedInteger,
rhs: &Self::AssignedInteger,
);
fn pow_var(
&self,
ctx: &mut Self::Context,
base: &Self::AssignedInteger,
exponent: &Self::AssignedInteger,
max_bits: usize,
) -> Self::AssignedInteger;
}
pub trait EccInstructions<C: CurveAffine>: Clone + Debug {
type Context: Debug + Default;
type ScalarChip: IntegerInstructions<
C::Scalar,
Context = Self::Context,
AssignedCell = Self::AssignedCell,
AssignedInteger = Self::AssignedScalar,
>;
type AssignedCell: Clone + Debug;
type AssignedScalar: Clone + Debug;
type AssignedEcPoint: Clone + Debug;
fn scalar_chip(&self) -> &Self::ScalarChip;
fn assign_constant(&self, ctx: &mut Self::Context, ec_point: C) -> Self::AssignedEcPoint;
fn assign_point(&self, ctx: &mut Self::Context, ec_point: C) -> Self::AssignedEcPoint;
fn sum_with_const(
&self,
ctx: &mut Self::Context,
values: &[impl Deref<Target = Self::AssignedEcPoint>],
constant: C,
) -> Self::AssignedEcPoint;
fn fixed_base_msm(
&mut self,
ctx: &mut Self::Context,
pairs: &[(impl Deref<Target = Self::AssignedScalar>, C)],
) -> Self::AssignedEcPoint;
fn variable_base_msm(
&mut self,
ctx: &mut Self::Context,
pairs: &[(
impl Deref<Target = Self::AssignedScalar>,
impl Deref<Target = Self::AssignedEcPoint>,
)],
) -> Self::AssignedEcPoint;
fn assert_equal(
&self,
ctx: &mut Self::Context,
lhs: &Self::AssignedEcPoint,
rhs: &Self::AssignedEcPoint,
);
}
mod halo2_lib {
use crate::{
loader::halo2::{EccInstructions, IntegerInstructions},
util::arithmetic::{CurveAffine, PrimeField},
};
use halo2_base::{
self,
gates::{
flex_gate::threads::SinglePhaseCoreManager, GateChip, GateInstructions,
RangeInstructions,
},
utils::{BigPrimeField, CurveAffineExt},
AssignedValue,
QuantumCell::{Constant, Existing},
};
use halo2_ecc::{
bigint::ProperCrtUint,
ecc::{BaseFieldEccChip, EcPoint},
fields::FieldChip,
};
use std::ops::Deref;
type AssignedInteger<C> = ProperCrtUint<<C as CurveAffine>::ScalarExt>;
type AssignedEcPoint<C> = EcPoint<<C as CurveAffine>::ScalarExt, AssignedInteger<C>>;
impl<F: BigPrimeField> IntegerInstructions<F> for GateChip<F> {
type Context = SinglePhaseCoreManager<F>;
type AssignedCell = AssignedValue<F>;
type AssignedInteger = AssignedValue<F>;
fn assign_integer(&self, ctx: &mut Self::Context, integer: F) -> Self::AssignedInteger {
ctx.main().load_witness(integer)
}
fn assign_constant(&self, ctx: &mut Self::Context, integer: F) -> Self::AssignedInteger {
ctx.main().load_constant(integer)
}
fn sum_with_coeff_and_const(
&self,
ctx: &mut Self::Context,
values: &[(F, impl Deref<Target = Self::AssignedInteger>)],
constant: F,
) -> Self::AssignedInteger {
let mut a = Vec::with_capacity(values.len() + 1);
let mut b = Vec::with_capacity(values.len() + 1);
if constant != F::ZERO {
a.push(Constant(constant));
b.push(Constant(F::ONE));
}
a.extend(values.iter().map(|(_, a)| Existing(*a.deref())));
b.extend(values.iter().map(|(c, _)| Constant(*c)));
self.inner_product(ctx.main(), a, b)
}
fn sum_products_with_coeff_and_const(
&self,
ctx: &mut Self::Context,
values: &[(
F,
impl Deref<Target = Self::AssignedInteger>,
impl Deref<Target = Self::AssignedInteger>,
)],
constant: F,
) -> Self::AssignedInteger {
match values.len() {
0 => ctx.main().load_constant(constant),
_ => self.sum_products_with_coeff_and_var(
ctx.main(),
values.iter().map(|(c, a, b)| (*c, Existing(*a.deref()), Existing(*b.deref()))),
Constant(constant),
),
}
}
fn sub(
&self,
ctx: &mut Self::Context,
a: &Self::AssignedInteger,
b: &Self::AssignedInteger,
) -> Self::AssignedInteger {
GateInstructions::sub(self, ctx.main(), Existing(*a), Existing(*b))
}
fn neg(&self, ctx: &mut Self::Context, a: &Self::AssignedInteger) -> Self::AssignedInteger {
GateInstructions::neg(self, ctx.main(), Existing(*a))
}
fn invert(
&self,
ctx: &mut Self::Context,
a: &Self::AssignedInteger,
) -> Self::AssignedInteger {
let is_zero = self.is_zero(ctx.main(), *a);
self.assert_is_const(ctx.main(), &is_zero, &F::ZERO);
GateInstructions::div_unsafe(self, ctx.main(), Constant(F::ONE), Existing(*a))
}
fn assert_equal(
&self,
ctx: &mut Self::Context,
a: &Self::AssignedInteger,
b: &Self::AssignedInteger,
) {
ctx.main().constrain_equal(a, b);
}
fn pow_var(
&self,
ctx: &mut Self::Context,
base: &Self::AssignedInteger,
exponent: &Self::AssignedInteger,
max_bits: usize,
) -> Self::AssignedInteger {
GateInstructions::pow_var(self, ctx.main(), *base, *exponent, max_bits)
}
}
impl<'chip, C: CurveAffineExt> EccInstructions<C> for BaseFieldEccChip<'chip, C>
where
C::ScalarExt: BigPrimeField,
C::Base: BigPrimeField,
{
type Context = SinglePhaseCoreManager<C::Scalar>;
type ScalarChip = GateChip<C::Scalar>;
type AssignedCell = AssignedValue<C::Scalar>;
type AssignedScalar = AssignedValue<C::Scalar>;
type AssignedEcPoint = AssignedEcPoint<C>;
fn scalar_chip(&self) -> &Self::ScalarChip {
self.field_chip.range().gate()
}
fn assign_constant(&self, ctx: &mut Self::Context, point: C) -> Self::AssignedEcPoint {
self.assign_constant_point(ctx.main(), point)
}
fn assign_point(&self, ctx: &mut Self::Context, point: C) -> Self::AssignedEcPoint {
self.assign_point(ctx.main(), point)
}
fn sum_with_const(
&self,
ctx: &mut Self::Context,
values: &[impl Deref<Target = Self::AssignedEcPoint>],
constant: C,
) -> Self::AssignedEcPoint {
let constant = if bool::from(constant.is_identity()) {
None
} else {
let constant = EccInstructions::assign_constant(self, ctx, constant);
Some(constant)
};
self.sum::<C>(
ctx.main(),
constant.into_iter().chain(values.iter().map(|v| v.deref().clone())),
)
}
fn variable_base_msm(
&mut self,
builder: &mut Self::Context,
pairs: &[(
impl Deref<Target = Self::AssignedScalar>,
impl Deref<Target = Self::AssignedEcPoint>,
)],
) -> Self::AssignedEcPoint {
let (scalars, points): (Vec<_>, Vec<_>) = pairs
.iter()
.map(|(scalar, point)| (vec![*scalar.deref()], point.deref().clone()))
.unzip();
BaseFieldEccChip::<C>::variable_base_msm::<C>(
self,
builder,
&points,
scalars,
C::Scalar::NUM_BITS as usize,
)
}
fn fixed_base_msm(
&mut self,
builder: &mut Self::Context,
pairs: &[(impl Deref<Target = Self::AssignedScalar>, C)],
) -> Self::AssignedEcPoint {
let (scalars, points): (Vec<_>, Vec<_>) = pairs
.iter()
.filter_map(|(scalar, point)| {
if point.is_identity().into() {
None
} else {
Some((vec![*scalar.deref()], *point))
}
})
.unzip();
BaseFieldEccChip::<C>::fixed_base_msm::<C>(
self,
builder,
&points,
scalars,
C::Scalar::NUM_BITS as usize,
)
}
fn assert_equal(
&self,
ctx: &mut Self::Context,
a: &Self::AssignedEcPoint,
b: &Self::AssignedEcPoint,
) {
self.assert_equal(ctx.main(), a.clone(), b.clone());
}
}
}