use algebra::{ curves::{ short_weierstrass_jacobian::{GroupAffine as SWAffine, GroupProjective as SWProjective}, SWModelParameters, }, AffineCurve, BitIterator, Field, PrimeField, ProjectiveCurve, }; use r1cs_core::{ConstraintSystem, SynthesisError}; use std::{borrow::Borrow, marker::PhantomData, ops::Neg}; use crate::{prelude::*, Assignment}; pub mod bls12; #[derive(Derivative)] #[derivative(Debug, Clone)] #[must_use] pub struct AffineGadget< P: SWModelParameters, ConstraintF: Field, F: FieldGadget, > { pub x: F, pub y: F, _params: PhantomData

, _engine: PhantomData, } impl> AffineGadget { pub fn new(x: F, y: F) -> Self { Self { x, y, _params: PhantomData, _engine: PhantomData, } } pub fn alloc_without_check>( mut cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, SynthesisError>, { let (x, y) = match value_gen() { Ok(fe) => { let fe = fe.into_affine(); (Ok(fe.x), Ok(fe.y)) }, _ => ( Err(SynthesisError::AssignmentMissing), Err(SynthesisError::AssignmentMissing), ), }; let x = F::alloc(&mut cs.ns(|| "x"), || x)?; let y = F::alloc(&mut cs.ns(|| "y"), || y)?; Ok(Self::new(x, y)) } } impl PartialEq for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { fn eq(&self, other: &Self) -> bool { self.x == other.x && self.y == other.y } } impl Eq for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { } impl GroupGadget, ConstraintF> for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { type Value = SWProjective

; type Variable = (F::Variable, F::Variable); #[inline] fn get_value(&self) -> Option { match (self.x.get_value(), self.y.get_value()) { (Some(x), Some(y)) => { let is_zero = x.is_zero() && y.is_one(); Some(SWAffine::new(x, y, is_zero).into_projective()) }, (None, None) => None, _ => unreachable!(), } } #[inline] fn get_variable(&self) -> Self::Variable { (self.x.get_variable(), self.y.get_variable()) } #[inline] fn zero>(mut cs: CS) -> Result { Ok(Self::new( F::zero(cs.ns(|| "zero"))?, F::one(cs.ns(|| "one"))?, )) } #[inline] /// Incomplete addition: neither `self` nor `other` can be the neutral /// element. fn add>( &self, mut cs: CS, other: &Self, ) -> Result { // lambda = (B.y - A.y)/(B.x - A.x) // C.x = lambda^2 - A.x - B.x // C.y = lambda(A.x - C.x) - A.y // // Special cases: // // doubling: if B.y = A.y and B.x = A.x then lambda is unbound and // C = (lambda^2, lambda^3) // // addition of negative point: if B.y = -A.y and B.x = A.x then no // lambda can satisfy the first equation unless B.y - A.y = 0. But // then this reduces to doubling. // // So we need to check that A.x - B.x != 0, which can be done by // enforcing I * (B.x - A.x) = 1 let x2_minus_x1 = other.x.sub(cs.ns(|| "x2 - x1"), &self.x)?; let y2_minus_y1 = other.y.sub(cs.ns(|| "y2 - y1"), &self.y)?; let inv = x2_minus_x1.inverse(cs.ns(|| "compute inv"))?; let lambda = F::alloc(cs.ns(|| "lambda"), || { Ok(y2_minus_y1.get_value().get()? * &inv.get_value().get()?) })?; let x_3 = F::alloc(&mut cs.ns(|| "x_3"), || { let lambda_val = lambda.get_value().get()?; let x1 = self.x.get_value().get()?; let x2 = other.x.get_value().get()?; Ok((lambda_val.square() - &x1) - &x2) })?; let y_3 = F::alloc(&mut cs.ns(|| "y_3"), || { let lambda_val = lambda.get_value().get()?; let x_1 = self.x.get_value().get()?; let y_1 = self.y.get_value().get()?; let x_3 = x_3.get_value().get()?; Ok(lambda_val * &(x_1 - &x_3) - &y_1) })?; // Check lambda lambda.mul_equals(cs.ns(|| "check lambda"), &x2_minus_x1, &y2_minus_y1)?; // Check x3 let x3_plus_x1_plus_x2 = x_3 .add(cs.ns(|| "x3 + x1"), &self.x)? .add(cs.ns(|| "x3 + x1 + x2"), &other.x)?; lambda.mul_equals(cs.ns(|| "check x3"), &lambda, &x3_plus_x1_plus_x2)?; // Check y3 let y3_plus_y1 = y_3.add(cs.ns(|| "y3 + y1"), &self.y)?; let x1_minus_x3 = self.x.sub(cs.ns(|| "x1 - x3"), &x_3)?; lambda.mul_equals(cs.ns(|| ""), &x1_minus_x3, &y3_plus_y1)?; Ok(Self::new(x_3, y_3)) } /// Incomplete addition: neither `self` nor `other` can be the neutral /// element. fn add_constant>( &self, mut cs: CS, other: &SWProjective

, ) -> Result { // lambda = (B.y - A.y)/(B.x - A.x) // C.x = lambda^2 - A.x - B.x // C.y = lambda(A.x - C.x) - A.y // // Special cases: // // doubling: if B.y = A.y and B.x = A.x then lambda is unbound and // C = (lambda^2, lambda^3) // // addition of negative point: if B.y = -A.y and B.x = A.x then no // lambda can satisfy the first equation unless B.y - A.y = 0. But // then this reduces to doubling. // // So we need to check that A.x - B.x != 0, which can be done by // enforcing I * (B.x - A.x) = 1 if other.is_zero() { return Err(SynthesisError::AssignmentMissing); } let other = other.into_affine(); let other_x = other.x; let other_y = other.y; let x2_minus_x1 = self .x .sub_constant(cs.ns(|| "x2 - x1"), &other_x)? .negate(cs.ns(|| "neg1"))?; let y2_minus_y1 = self .y .sub_constant(cs.ns(|| "y2 - y1"), &other_y)? .negate(cs.ns(|| "neg2"))?; let inv = x2_minus_x1.inverse(cs.ns(|| "compute inv"))?; let lambda = F::alloc(cs.ns(|| "lambda"), || { Ok(y2_minus_y1.get_value().get()? * &inv.get_value().get()?) })?; let x_3 = F::alloc(&mut cs.ns(|| "x_3"), || { let lambda_val = lambda.get_value().get()?; let x1 = self.x.get_value().get()?; let x2 = other_x; Ok((lambda_val.square() - &x1) - &x2) })?; let y_3 = F::alloc(&mut cs.ns(|| "y_3"), || { let lambda_val = lambda.get_value().get()?; let x_1 = self.x.get_value().get()?; let y_1 = self.y.get_value().get()?; let x_3 = x_3.get_value().get()?; Ok(lambda_val * &(x_1 - &x_3) - &y_1) })?; // Check lambda lambda.mul_equals(cs.ns(|| "check lambda"), &x2_minus_x1, &y2_minus_y1)?; // Check x3 let x3_plus_x1_plus_x2 = x_3 .add(cs.ns(|| "x3 + x1"), &self.x)? .add_constant(cs.ns(|| "x3 + x1 + x2"), &other_x)?; lambda.mul_equals(cs.ns(|| "check x3"), &lambda, &x3_plus_x1_plus_x2)?; // Check y3 let y3_plus_y1 = y_3.add(cs.ns(|| "y3 + y1"), &self.y)?; let x1_minus_x3 = self.x.sub(cs.ns(|| "x1 - x3"), &x_3)?; lambda.mul_equals(cs.ns(|| ""), &x1_minus_x3, &y3_plus_y1)?; Ok(Self::new(x_3, y_3)) } #[inline] fn double_in_place>( &mut self, mut cs: CS, ) -> Result<(), SynthesisError> { let a = P::COEFF_A; let x_squared = self.x.square(cs.ns(|| "x^2"))?; let one = P::BaseField::one(); let two = one.double(); let three = two + &one; let three_x_squared = x_squared.mul_by_constant(cs.ns(|| "3 * x^2"), &three)?; let three_x_squared_plus_a = three_x_squared.add_constant(cs.ns(|| "3 * x^2 + a"), &a)?; let two_y = self.y.double(cs.ns(|| "2y"))?; let lambda = F::alloc(cs.ns(|| "lambda"), || { let y_doubled_inv = two_y.get_value().get()?.inverse().get()?; Ok(three_x_squared_plus_a.get_value().get()? * &y_doubled_inv) })?; // Check lambda lambda.mul_equals(cs.ns(|| "check lambda"), &two_y, &three_x_squared_plus_a)?; let x = lambda .square(cs.ns(|| "lambda^2"))? .sub(cs.ns(|| "lambda^2 - x"), &self.x)? .sub(cs.ns(|| "lambda^2 - 2x"), &self.x)?; let y = self .x .sub(cs.ns(|| "x - self.x"), &x)? .mul(cs.ns(|| "times lambda"), &lambda)? .sub(cs.ns(|| "plus self.y"), &self.y)?; *self = Self::new(x, y); Ok(()) } fn negate>( &self, mut cs: CS, ) -> Result { Ok(Self::new( self.x.clone(), self.y.negate(cs.ns(|| "negate y"))?, )) } fn cost_of_add() -> usize { 3 * F::cost_of_mul() + F::cost_of_inv() } fn cost_of_double() -> usize { 4 * F::cost_of_mul() } } impl CondSelectGadget for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { #[inline] fn conditionally_select>( mut cs: CS, cond: &Boolean, first: &Self, second: &Self, ) -> Result { let x = F::conditionally_select(&mut cs.ns(|| "x"), cond, &first.x, &second.x)?; let y = F::conditionally_select(&mut cs.ns(|| "y"), cond, &first.y, &second.y)?; Ok(Self::new(x, y)) } fn cost() -> usize { 2 * >::cost() } } impl EqGadget for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { } impl ConditionalEqGadget for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { #[inline] fn conditional_enforce_equal>( &self, mut cs: CS, other: &Self, condition: &Boolean, ) -> Result<(), SynthesisError> { self.x.conditional_enforce_equal( &mut cs.ns(|| "X Coordinate Conditional Equality"), &other.x, condition, )?; self.y.conditional_enforce_equal( &mut cs.ns(|| "Y Coordinate Conditional Equality"), &other.y, condition, )?; Ok(()) } fn cost() -> usize { 2 * >::cost() } } impl NEqGadget for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { #[inline] fn enforce_not_equal>( &self, mut cs: CS, other: &Self, ) -> Result<(), SynthesisError> { self.x .enforce_not_equal(&mut cs.ns(|| "X Coordinate Inequality"), &other.x)?; self.y .enforce_not_equal(&mut cs.ns(|| "Y Coordinate Inequality"), &other.y)?; Ok(()) } fn cost() -> usize { 2 * >::cost() } } impl AllocGadget, ConstraintF> for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { #[inline] fn alloc>( mut cs: CS, value_gen: FN, ) -> Result where FN: FnOnce() -> Result, T: Borrow>, { let (x, y) = match value_gen() { Ok(ge) => { let ge = ge.borrow().into_affine(); (Ok(ge.x), Ok(ge.y)) }, _ => ( Err(SynthesisError::AssignmentMissing), Err(SynthesisError::AssignmentMissing), ), }; // Perform on-curve check. let b = P::COEFF_B; let a = P::COEFF_A; let x = F::alloc(&mut cs.ns(|| "x"), || x)?; let y = F::alloc(&mut cs.ns(|| "y"), || y)?; // Check that y^2 = x^3 + ax +b // We do this by checking that y^2 - b = x * (x^2 +a) let x2 = x.square(&mut cs.ns(|| "x^2"))?; let y2 = y.square(&mut cs.ns(|| "y^2"))?; let x2_plus_a = x2.add_constant(cs.ns(|| "x^2 + a"), &a)?; let y2_minus_b = y2.add_constant(cs.ns(|| "y^2 - b"), &b.neg())?; x2_plus_a.mul_equals(cs.ns(|| "on curve check"), &x, &y2_minus_b)?; Ok(Self::new(x, y)) } #[inline] fn alloc_checked>( mut cs: CS, value_gen: FN, ) -> Result where FN: FnOnce() -> Result, T: Borrow>, { let cofactor_weight = BitIterator::new(P::COFACTOR).filter(|b| *b).count(); // If we multiply by r, we actually multiply by r - 2. let r_minus_1 = (-P::ScalarField::one()).into_repr(); let r_weight = BitIterator::new(&r_minus_1).filter(|b| *b).count(); // We pick the most efficient method of performing the prime order check: // If the cofactor has lower hamming weight than the scalar field's modulus, // we first multiply by the inverse of the cofactor, and then, after allocating, // multiply by the cofactor. This ensures the resulting point has no cofactors // // Else, we multiply by the scalar field's modulus and ensure that the result // is zero. if cofactor_weight < r_weight { let ge = Self::alloc(cs.ns(|| "Alloc checked"), || { value_gen().map(|ge| { ge.borrow() .into_affine() .mul_by_cofactor_inv() .into_projective() }) })?; let mut seen_one = false; let mut result = Self::zero(cs.ns(|| "result"))?; for (i, b) in BitIterator::new(P::COFACTOR).enumerate() { let mut cs = cs.ns(|| format!("Iteration {}", i)); let old_seen_one = seen_one; if seen_one { result.double_in_place(cs.ns(|| "Double"))?; } else { seen_one = b; } if b { result = if old_seen_one { result.add(cs.ns(|| "Add"), &ge)? } else { ge.clone() }; } } Ok(result) } else { let ge = Self::alloc(cs.ns(|| "Alloc checked"), value_gen)?; let mut seen_one = false; let mut result = Self::zero(cs.ns(|| "result"))?; // Returns bits in big-endian order for (i, b) in BitIterator::new(r_minus_1).enumerate() { let mut cs = cs.ns(|| format!("Iteration {}", i)); let old_seen_one = seen_one; if seen_one { result.double_in_place(cs.ns(|| "Double"))?; } else { seen_one = b; } if b { result = if old_seen_one { result.add(cs.ns(|| "Add"), &ge)? } else { ge.clone() }; } } let neg_ge = ge.negate(cs.ns(|| "Negate ge"))?; neg_ge.enforce_equal(cs.ns(|| "Check equals"), &result)?; Ok(ge) } } #[inline] fn alloc_input>( mut cs: CS, value_gen: FN, ) -> Result where FN: FnOnce() -> Result, T: Borrow>, { let (x, y) = match value_gen() { Ok(ge) => { let ge = ge.borrow().into_affine(); (Ok(ge.x), Ok(ge.y)) }, _ => ( Err(SynthesisError::AssignmentMissing), Err(SynthesisError::AssignmentMissing), ), }; let b = P::COEFF_B; let a = P::COEFF_A; let x = F::alloc_input(&mut cs.ns(|| "x"), || x)?; let y = F::alloc_input(&mut cs.ns(|| "y"), || y)?; // Check that y^2 = x^3 + ax +b // We do this by checking that y^2 - b = x * (x^2 +a) let x2 = x.square(&mut cs.ns(|| "x^2"))?; let y2 = y.square(&mut cs.ns(|| "y^2"))?; let x2_plus_a = x2.add_constant(cs.ns(|| "x^2 + a"), &a)?; let y2_minus_b = y2.add_constant(cs.ns(|| "y^2 - b"), &b.neg())?; x2_plus_a.mul_equals(cs.ns(|| "on curve check"), &x, &y2_minus_b)?; Ok(Self::new(x, y)) } } impl ToBitsGadget for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { fn to_bits>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut x_bits = self.x.to_bits(&mut cs.ns(|| "X Coordinate To Bits"))?; let y_bits = self.y.to_bits(&mut cs.ns(|| "Y Coordinate To Bits"))?; x_bits.extend_from_slice(&y_bits); Ok(x_bits) } fn to_bits_strict>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut x_bits = self .x .to_bits_strict(&mut cs.ns(|| "X Coordinate To Bits"))?; let y_bits = self .y .to_bits_strict(&mut cs.ns(|| "Y Coordinate To Bits"))?; x_bits.extend_from_slice(&y_bits); Ok(x_bits) } } impl ToBytesGadget for AffineGadget where P: SWModelParameters, ConstraintF: Field, F: FieldGadget, { fn to_bytes>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut x_bytes = self.x.to_bytes(&mut cs.ns(|| "X Coordinate To Bytes"))?; let y_bytes = self.y.to_bytes(&mut cs.ns(|| "Y Coordinate To Bytes"))?; x_bytes.extend_from_slice(&y_bytes); Ok(x_bytes) } fn to_bytes_strict>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut x_bytes = self .x .to_bytes_strict(&mut cs.ns(|| "X Coordinate To Bytes"))?; let y_bytes = self .y .to_bytes_strict(&mut cs.ns(|| "Y Coordinate To Bytes"))?; x_bytes.extend_from_slice(&y_bytes); Ok(x_bytes) } }