use ark_ec::mnt4::{ g2::{AteAdditionCoefficients, AteDoubleCoefficients}, G1Prepared, G2Prepared, MNT4Config, }; use ark_ff::Field; use ark_relations::r1cs::{Namespace, SynthesisError}; use crate::{ convert::ToBytesGadget, fields::{fp::FpVar, fp2::Fp2Var, FieldVar}, groups::curves::short_weierstrass::ProjectiveVar, pairing::mnt4::PairingVar, prelude::*, Vec, }; use core::borrow::Borrow; /// Represents a projective point in G1. pub type G1Var

= ProjectiveVar<

::G1Config, FpVar<

::Fp>>; /// Represents a projective point in G2. pub type G2Var

= ProjectiveVar<

::G2Config, Fp2G

>; /// Represents the cached precomputation that can be performed on a G1 element /// which enables speeding up pairing computation. #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Config"), Debug(bound = "P: MNT4Config"))] pub struct G1PreparedVar { #[doc(hidden)] pub x: FpVar, #[doc(hidden)] pub y: FpVar, #[doc(hidden)] pub x_twist: Fp2Var, #[doc(hidden)] pub y_twist: Fp2Var, } impl AllocVar, P::Fp> for G1PreparedVar

{ #[tracing::instrument(target = "r1cs", skip(cs, f))] 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()); let x = FpVar::new_variable(ark_relations::ns!(cs, "x"), || g1_prep.map(|g| g.x), mode)?; let y = FpVar::new_variable(ark_relations::ns!(cs, "y"), || g1_prep.map(|g| g.y), mode)?; let x_twist = Fp2Var::new_variable( ark_relations::ns!(cs, "x_twist"), || g1_prep.map(|g| g.x_twist), mode, )?; let y_twist = Fp2Var::new_variable( ark_relations::ns!(cs, "y_twist"), || g1_prep.map(|g| g.y_twist), mode, )?; Ok(Self { x, y, x_twist, y_twist, }) } } impl G1PreparedVar

{ /// Returns the value assigned to `self` in the underlying constraint /// system. pub fn value(&self) -> Result, SynthesisError> { let (x, y, x_twist, y_twist) = ( self.x.value()?, self.y.value()?, self.x_twist.value()?, self.y_twist.value()?, ); Ok(G1Prepared { x, y, x_twist, y_twist, }) } /// Constructs `Self` from a `G1Var`. #[tracing::instrument(target = "r1cs")] pub fn from_group_var(q: &G1Var

) -> Result { let q = q.to_affine()?; let x_twist = Fp2Var::new(&q.x * P::TWIST.c0, &q.x * P::TWIST.c1); let y_twist = Fp2Var::new(&q.y * P::TWIST.c0, &q.y * P::TWIST.c1); Ok(G1PreparedVar { x: q.x, y: q.y, x_twist, y_twist, }) } } impl ToBytesGadget for G1PreparedVar

{ #[inline] #[tracing::instrument(target = "r1cs")] fn to_bytes_le(&self) -> Result>, SynthesisError> { let mut x = self.x.to_bytes_le()?; let mut y = self.y.to_bytes_le()?; let mut x_twist = self.x_twist.to_bytes_le()?; let mut y_twist = self.y_twist.to_bytes_le()?; x.append(&mut y); x.append(&mut x_twist); x.append(&mut y_twist); Ok(x) } #[tracing::instrument(target = "r1cs")] fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { let mut x = self.x.to_non_unique_bytes_le()?; let mut y = self.y.to_non_unique_bytes_le()?; let mut x_twist = self.x_twist.to_non_unique_bytes_le()?; let mut y_twist = self.y_twist.to_non_unique_bytes_le()?; x.append(&mut y); x.append(&mut x_twist); x.append(&mut y_twist); Ok(x) } } type Fp2G

= Fp2Var<

::Fp2Config>; /// Represents the cached precomputation that can be performed on a G2 element /// which enables speeding up pairing computation. #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Config"), Debug(bound = "P: MNT4Config"))] pub struct G2PreparedVar { #[doc(hidden)] pub x: Fp2Var, #[doc(hidden)] pub y: Fp2Var, #[doc(hidden)] pub x_over_twist: Fp2Var, #[doc(hidden)] pub y_over_twist: Fp2Var, #[doc(hidden)] pub double_coefficients: Vec>, #[doc(hidden)] pub addition_coefficients: Vec>, } impl AllocVar, P::Fp> for G2PreparedVar

{ #[tracing::instrument(target = "r1cs", skip(cs, f))] 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| b.borrow().clone()); let g2 = g2_prep.as_ref().map_err(|e| *e); let x = Fp2Var::new_variable(ark_relations::ns!(cs, "x"), || g2.map(|g| g.x), mode)?; let y = Fp2Var::new_variable(ark_relations::ns!(cs, "y"), || g2.map(|g| g.y), mode)?; let x_over_twist = Fp2Var::new_variable( ark_relations::ns!(cs, "x_over_twist"), || g2.map(|g| g.x_over_twist), mode, )?; let y_over_twist = Fp2Var::new_variable( ark_relations::ns!(cs, "y_over_twist"), || g2.map(|g| g.y_over_twist), mode, )?; let double_coefficients = Vec::new_variable( ark_relations::ns!(cs, "double coeffs"), || g2.map(|g| g.double_coefficients.clone()), mode, )?; let addition_coefficients = Vec::new_variable( ark_relations::ns!(cs, "add coeffs"), || g2.map(|g| g.addition_coefficients.clone()), mode, )?; Ok(Self { x, y, x_over_twist, y_over_twist, double_coefficients, addition_coefficients, }) } } impl ToBytesGadget for G2PreparedVar

{ #[inline] #[tracing::instrument(target = "r1cs")] fn to_bytes_le(&self) -> Result>, SynthesisError> { let mut x = self.x.to_bytes_le()?; let mut y = self.y.to_bytes_le()?; let mut x_over_twist = self.x_over_twist.to_bytes_le()?; let mut y_over_twist = self.y_over_twist.to_bytes_le()?; x.append(&mut y); x.append(&mut x_over_twist); x.append(&mut y_over_twist); for coeff in &self.double_coefficients { x.extend_from_slice(&coeff.to_bytes_le()?); } for coeff in &self.addition_coefficients { x.extend_from_slice(&coeff.to_bytes_le()?); } Ok(x) } #[tracing::instrument(target = "r1cs")] fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { let mut x = self.x.to_non_unique_bytes_le()?; let mut y = self.y.to_non_unique_bytes_le()?; let mut x_over_twist = self.x_over_twist.to_non_unique_bytes_le()?; let mut y_over_twist = self.y_over_twist.to_non_unique_bytes_le()?; x.append(&mut y); x.append(&mut x_over_twist); x.append(&mut y_over_twist); for coeff in &self.double_coefficients { x.extend_from_slice(&coeff.to_non_unique_bytes_le()?); } for coeff in &self.addition_coefficients { x.extend_from_slice(&coeff.to_non_unique_bytes_le()?); } Ok(x) } } impl G2PreparedVar

{ /// Returns the value assigned to `self` in the underlying constraint /// system. pub fn value(&self) -> Result, SynthesisError> { let x = self.x.value()?; let y = self.y.value()?; let x_over_twist = self.x_over_twist.value()?; let y_over_twist = self.y_over_twist.value()?; let double_coefficients = self .double_coefficients .iter() .map(|coeff| coeff.value()) .collect::>, _>>()?; let addition_coefficients = self .addition_coefficients .iter() .map(|coeff| coeff.value()) .collect::>, _>>()?; Ok(G2Prepared { x, y, x_over_twist, y_over_twist, double_coefficients, addition_coefficients, }) } /// Constructs `Self` from a `G2Var`. #[tracing::instrument(target = "r1cs")] pub fn from_group_var(q: &G2Var

) -> Result { let twist_inv = P::TWIST.inverse().unwrap(); let q = q.to_affine()?; let mut g2p = G2PreparedVar { x: q.x.clone(), y: q.y.clone(), x_over_twist: &q.x * twist_inv, y_over_twist: &q.y * twist_inv, double_coefficients: vec![], addition_coefficients: vec![], }; let mut r = G2ProjectiveExtendedVar { x: q.x.clone(), y: q.y.clone(), z: Fp2G::

::one(), t: Fp2G::

::one(), }; for bit in P::ATE_LOOP_COUNT.iter().skip(1) { let (r2, coeff) = PairingVar::

::doubling_step_for_flipped_miller_loop(&r)?; g2p.double_coefficients.push(coeff); r = r2; let add_coeff; let r_temp; match bit { 1 => { (r_temp, add_coeff) = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( &q.x, &q.y, &r, )?; }, -1 => { (r_temp, add_coeff) = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( &q.x, &q.y.negate()?, &r, )?; }, _ => continue, } g2p.addition_coefficients.push(add_coeff); r = r_temp; } if P::ATE_IS_LOOP_COUNT_NEG { let rz_inv = r.z.inverse()?; let rz2_inv = rz_inv.square()?; let rz3_inv = &rz_inv * &rz2_inv; let minus_r_affine_x = &r.x * &rz2_inv; let minus_r_affine_y = r.y.negate()? * &rz3_inv; let add_result = PairingVar::

::mixed_addition_step_for_flipped_miller_loop( &minus_r_affine_x, &minus_r_affine_y, &r, )?; g2p.addition_coefficients.push(add_result.1); } Ok(g2p) } } #[doc(hidden)] #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Config"), Debug(bound = "P: MNT4Config"))] pub struct AteDoubleCoefficientsVar { pub c_h: Fp2Var, pub c_4c: Fp2Var, pub c_j: Fp2Var, pub c_l: Fp2Var, } impl AllocVar, P::Fp> for AteDoubleCoefficientsVar

{ #[tracing::instrument(target = "r1cs", skip(cs, f))] fn new_variable>>( cs: impl Into>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { let ns = cs.into(); let cs = ns.cs(); let c_prep = f().map(|c| c.borrow().clone()); let c = c_prep.as_ref().map_err(|e| *e); let c_h = Fp2Var::new_variable(ark_relations::ns!(cs, "c_h"), || c.map(|c| c.c_h), mode)?; let c_4c = Fp2Var::new_variable(ark_relations::ns!(cs, "c_4c"), || c.map(|c| c.c_4c), mode)?; let c_j = Fp2Var::new_variable(ark_relations::ns!(cs, "c_j"), || c.map(|c| c.c_j), mode)?; let c_l = Fp2Var::new_variable(ark_relations::ns!(cs, "c_l"), || c.map(|c| c.c_l), mode)?; Ok(Self { c_h, c_4c, c_j, c_l, }) } } impl ToBytesGadget for AteDoubleCoefficientsVar

{ #[inline] #[tracing::instrument(target = "r1cs")] fn to_bytes_le(&self) -> Result>, SynthesisError> { let mut c_h = self.c_h.to_bytes_le()?; let mut c_4c = self.c_4c.to_bytes_le()?; let mut c_j = self.c_j.to_bytes_le()?; let mut c_l = self.c_l.to_bytes_le()?; c_h.append(&mut c_4c); c_h.append(&mut c_j); c_h.append(&mut c_l); Ok(c_h) } #[tracing::instrument(target = "r1cs")] fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { let mut c_h = self.c_h.to_non_unique_bytes_le()?; let mut c_4c = self.c_4c.to_non_unique_bytes_le()?; let mut c_j = self.c_j.to_non_unique_bytes_le()?; let mut c_l = self.c_l.to_non_unique_bytes_le()?; c_h.append(&mut c_4c); c_h.append(&mut c_j); c_h.append(&mut c_l); Ok(c_h) } } impl AteDoubleCoefficientsVar

{ /// Returns the value assigned to `self` in the underlying constraint /// system. pub fn value(&self) -> Result, SynthesisError> { let (c_h, c_4c, c_j, c_l) = ( self.c_l.value()?, self.c_4c.value()?, self.c_j.value()?, self.c_l.value()?, ); Ok(AteDoubleCoefficients { c_h, c_4c, c_j, c_l, }) } } #[doc(hidden)] #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Config"), Debug(bound = "P: MNT4Config"))] pub struct AteAdditionCoefficientsVar { pub c_l1: Fp2Var, pub c_rz: Fp2Var, } impl AllocVar, P::Fp> for AteAdditionCoefficientsVar

{ #[tracing::instrument(target = "r1cs", skip(cs, f))] fn new_variable>>( cs: impl Into>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { let ns = cs.into(); let cs = ns.cs(); let c_prep = f().map(|c| c.borrow().clone()); let c = c_prep.as_ref().map_err(|e| *e); let c_l1 = Fp2Var::new_variable(ark_relations::ns!(cs, "c_l1"), || c.map(|c| c.c_l1), mode)?; let c_rz = Fp2Var::new_variable(ark_relations::ns!(cs, "c_rz"), || c.map(|c| c.c_rz), mode)?; Ok(Self { c_l1, c_rz }) } } impl ToBytesGadget for AteAdditionCoefficientsVar

{ #[inline] #[tracing::instrument(target = "r1cs")] fn to_bytes_le(&self) -> Result>, SynthesisError> { let mut c_l1 = self.c_l1.to_bytes_le()?; let mut c_rz = self.c_rz.to_bytes_le()?; c_l1.append(&mut c_rz); Ok(c_l1) } #[tracing::instrument(target = "r1cs")] fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { let mut c_l1 = self.c_l1.to_non_unique_bytes_le()?; let mut c_rz = self.c_rz.to_non_unique_bytes_le()?; c_l1.append(&mut c_rz); Ok(c_l1) } } impl AteAdditionCoefficientsVar

{ /// Returns the value assigned to `self` in the underlying constraint /// system. pub fn value(&self) -> Result, SynthesisError> { let (c_l1, c_rz) = (self.c_l1.value()?, self.c_rz.value()?); Ok(AteAdditionCoefficients { c_l1, c_rz }) } } #[doc(hidden)] pub struct G2ProjectiveExtendedVar { pub x: Fp2Var, pub y: Fp2Var, pub z: Fp2Var, pub t: Fp2Var, }