use algebra::{
curves::{
bls12::{Bls12Parameters, G1Prepared, G2Prepared, TwistType},
short_weierstrass_jacobian::GroupAffine,
},
fields::Field,
BitIteratorBE, One,
};
use r1cs_core::{Namespace, SynthesisError};
use crate::{
fields::{fp::FpVar, fp2::Fp2Var, FieldVar},
groups::curves::short_weierstrass::*,
Vec,
};
use core::fmt::Debug;
pub type G1Var
=
ProjectiveVar<
::G1Parameters, FpVar<
::Fp>>;
pub type G1AffineVar
=
AffineVar<
::G1Parameters, FpVar<
::Fp>>;
pub type G2Var
= ProjectiveVar<
::G2Parameters, Fp2G
>;
pub type G2AffineVar
= AffineVar<
::G2Parameters, Fp2G
>;
#[derive(Derivative)]
#[derivative(Clone(bound = "G1Var
: Clone"), Debug(bound = "G1Var
: Debug"))]
pub struct G1PreparedVar(pub AffineVar>);
impl G1PreparedVar {
pub fn value(&self) -> Result, SynthesisError> {
let x = self.0.x.value()?;
let y = self.0.y.value()?;
let infinity = self.0.infinity.value()?;
let g = GroupAffine::new(x, y, infinity);
Ok(g.into())
}
pub fn from_group_var(q: &G1Var) -> Result {
let g = q.to_affine()?;
Ok(Self(g))
}
}
impl AllocVar, P::Fp> for G1PreparedVar {
fn new_variable>>(
cs: impl Into>,
f: impl FnOnce() -> Result,
mode: AllocationMode,
) -> Result {
let ns = cs.into();
let cs = ns.cs();
let g1_prep = f().map(|b| b.borrow().0);
let x = FpVar::new_variable(r1cs_core::ns!(cs, "x"), || g1_prep.map(|g| g.x), mode)?;
let y = FpVar::new_variable(r1cs_core::ns!(cs, "y"), || g1_prep.map(|g| g.y), mode)?;
let infinity = Boolean::new_variable(
r1cs_core::ns!(cs, "inf"),
|| g1_prep.map(|g| g.infinity),
mode,
)?;
let g = AffineVar::new(x, y, infinity);
Ok(Self(g))
}
}
impl ToBytesGadget for G1PreparedVar {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result>, SynthesisError> {
let mut bytes = self.0.x.to_bytes()?;
let y_bytes = self.0.y.to_bytes()?;
let inf_bytes = self.0.infinity.to_bytes()?;
bytes.extend_from_slice(&y_bytes);
bytes.extend_from_slice(&inf_bytes);
Ok(bytes)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result>, SynthesisError> {
let mut bytes = self.0.x.to_bytes()?;
let y_bytes = self.0.y.to_bytes()?;
let inf_bytes = self.0.infinity.to_bytes()?;
bytes.extend_from_slice(&y_bytes);
bytes.extend_from_slice(&inf_bytes);
Ok(bytes)
}
}
type Fp2G = Fp2Var<
::Fp2Params>;
type LCoeff
= (Fp2G
, Fp2G
);
#[derive(Derivative)]
#[derivative(
Clone(bound = "Fp2Var: Clone"),
Debug(bound = "Fp2Var: Debug")
)]
pub struct G2PreparedVar {
pub ell_coeffs: Vec>,
}
impl AllocVar, P::Fp> for G2PreparedVar {
#[tracing::instrument(target = "r1cs", skip(cs, f, mode))]
fn new_variable>>(
cs: impl Into>,
f: impl FnOnce() -> Result,
mode: AllocationMode,
) -> Result {
let ns = cs.into();
let cs = ns.cs();
let g2_prep = f().map(|b| {
let projective_coeffs = &b.borrow().ell_coeffs;
let mut z_s = projective_coeffs
.iter()
.map(|(_, _, z)| *z)
.collect::>();
algebra::fields::batch_inversion(&mut z_s);
projective_coeffs
.iter()
.zip(z_s)
.map(|((x, y, _), z_inv)| (*x * &z_inv, *y * &z_inv))
.collect::>()
});
let l = Vec::new_variable(
r1cs_core::ns!(cs, "l"),
|| {
g2_prep
.clone()
.map(|c| c.iter().map(|(l, _)| *l).collect::>())
},
mode,
)?;
let r = Vec::new_variable(
r1cs_core::ns!(cs, "r"),
|| g2_prep.map(|c| c.iter().map(|(_, r)| *r).collect::>()),
mode,
)?;
let ell_coeffs = l.into_iter().zip(r).collect();
Ok(Self { ell_coeffs })
}
}
impl ToBytesGadget for G2PreparedVar {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result>, SynthesisError> {
let mut bytes = Vec::new();
for coeffs in &self.ell_coeffs {
bytes.extend_from_slice(&coeffs.0.to_bytes()?);
bytes.extend_from_slice(&coeffs.1.to_bytes()?);
}
Ok(bytes)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result>, SynthesisError> {
let mut bytes = Vec::new();
for coeffs in &self.ell_coeffs {
bytes.extend_from_slice(&coeffs.0.to_non_unique_bytes()?);
bytes.extend_from_slice(&coeffs.1.to_non_unique_bytes()?);
}
Ok(bytes)
}
}
impl G2PreparedVar {
#[tracing::instrument(target = "r1cs")]
pub fn from_group_var(q: &G2Var
) -> Result {
let q = q.to_affine()?;
let two_inv = P::Fp::one().double().inverse().unwrap();
// Enforce that `q` is not the point at infinity.
q.infinity.enforce_not_equal(&Boolean::Constant(true))?;
let mut ell_coeffs = vec![];
let mut r = q.clone();
for i in BitIteratorBE::new(P::X).skip(1) {
ell_coeffs.push(Self::double(&mut r, &two_inv)?);
if i {
ell_coeffs.push(Self::add(&mut r, &q)?);
}
}
Ok(Self { ell_coeffs })
}
#[tracing::instrument(target = "r1cs")]
fn double(r: &mut G2AffineVar, two_inv: &P::Fp) -> Result, SynthesisError> {
let a = r.y.inverse()?;
let mut b = r.x.square()?;
let b_tmp = b.clone();
b.mul_assign_by_base_field_constant(*two_inv);
b += &b_tmp;
let c = &a * &b;
let d = r.x.double()?;
let x3 = c.square()? - &d;
let e = &c * &r.x - &r.y;
let c_x3 = &c * &x3;
let y3 = &e - &c_x3;
let mut f = c;
f.negate_in_place()?;
r.x = x3;
r.y = y3;
match P::TWIST_TYPE {
TwistType::M => Ok((e, f)),
TwistType::D => Ok((f, e)),
}
}
#[tracing::instrument(target = "r1cs")]
fn add(r: &mut G2AffineVar, q: &G2AffineVar
) -> Result, SynthesisError> {
let a = (&q.x - &r.x).inverse()?;
let b = &q.y - &r.y;
let c = &a * &b;
let d = &r.x + &q.x;
let x3 = c.square()? - &d;
let e = (&r.x - &x3) * &c;
let y3 = e - &r.y;
let g = &c * &r.x - &r.y;
let mut f = c;
f.negate_in_place()?;
r.x = x3;
r.y = y3;
match P::TWIST_TYPE {
TwistType::M => Ok((g, f)),
TwistType::D => Ok((f, g)),
}
}
}