You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1407 lines
50 KiB

use algebra::{
curves::{
twisted_edwards_extended::GroupAffine as TEAffine, MontgomeryModelParameters,
TEModelParameters,
},
BitIterator, Field,
};
use num_traits::{One, Zero};
use r1cs_core::{ConstraintSystem, SynthesisError};
use crate::prelude::*;
use std::{borrow::Borrow, marker::PhantomData};
pub mod edwards_bls12;
pub mod edwards_sw6;
pub mod jubjub;
#[cfg(test)]
mod test;
#[derive(Derivative)]
#[derivative(Debug, Clone)]
#[derivative(Debug(bound = "P: TEModelParameters, ConstraintF: Field"))]
#[must_use]
pub struct MontgomeryAffineGadget<
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
> {
pub x: F,
pub y: F,
#[derivative(Debug = "ignore")]
_params: PhantomData<P>,
#[derivative(Debug = "ignore")]
_engine: PhantomData<ConstraintF>,
}
mod montgomery_affine_impl {
use super::*;
use crate::Assignment;
use algebra::{twisted_edwards_extended::GroupAffine, Field};
use std::ops::{AddAssign, MulAssign, SubAssign};
impl<P: TEModelParameters, ConstraintF: Field, F: FieldGadget<P::BaseField, ConstraintF>>
MontgomeryAffineGadget<P, ConstraintF, F>
{
pub fn new(x: F, y: F) -> Self {
Self {
x,
y,
_params: PhantomData,
_engine: PhantomData,
}
}
pub fn from_edwards_to_coords(
p: &TEAffine<P>,
) -> Result<(P::BaseField, P::BaseField), SynthesisError> {
let montgomery_point: GroupAffine<P> = if p.y == P::BaseField::one() {
GroupAffine::zero()
} else if p.x == P::BaseField::zero() {
GroupAffine::new(P::BaseField::zero(), P::BaseField::zero())
} else {
let u = (P::BaseField::one() + &p.y)
* &(P::BaseField::one() - &p.y).inverse().unwrap();
let v = u * &p.x.inverse().unwrap();
GroupAffine::new(u, v)
};
Ok((montgomery_point.x, montgomery_point.y))
}
pub fn from_edwards<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
p: &TEAffine<P>,
) -> Result<Self, SynthesisError> {
let montgomery_coords = Self::from_edwards_to_coords(p)?;
let u = F::alloc(cs.ns(|| "u"), || Ok(montgomery_coords.0))?;
let v = F::alloc(cs.ns(|| "v"), || Ok(montgomery_coords.1))?;
Ok(Self::new(u, v))
}
pub fn into_edwards<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<AffineGadget<P, ConstraintF, F>, SynthesisError> {
// Compute u = x / y
let u = F::alloc(cs.ns(|| "u"), || {
let mut t0 = self.x.get_value().get()?;
match self.y.get_value().get()?.inverse() {
Some(invy) => {
t0.mul_assign(&invy);
Ok(t0)
},
None => Err(SynthesisError::DivisionByZero),
}
})?;
u.mul_equals(cs.ns(|| "u equals"), &self.y, &self.x)?;
let v = F::alloc(cs.ns(|| "v"), || {
let mut t0 = self.x.get_value().get()?;
let mut t1 = t0.clone();
t0.sub_assign(&P::BaseField::one());
t1.add_assign(&P::BaseField::one());
match t1.inverse() {
Some(t1) => {
t0.mul_assign(&t1);
Ok(t0)
},
None => Err(SynthesisError::DivisionByZero),
}
})?;
let xplusone = self
.x
.add_constant(cs.ns(|| "x plus one"), &P::BaseField::one())?;
let xminusone = self
.x
.sub_constant(cs.ns(|| "x minus one"), &P::BaseField::one())?;
v.mul_equals(cs.ns(|| "v equals"), &xplusone, &xminusone)?;
Ok(AffineGadget::new(u, v))
}
pub fn add<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
other: &Self,
) -> Result<Self, SynthesisError> {
let lambda = F::alloc(cs.ns(|| "lambda"), || {
let mut n = other.y.get_value().get()?;
n.sub_assign(&self.y.get_value().get()?);
let mut d = other.x.get_value().get()?;
d.sub_assign(&self.x.get_value().get()?);
match d.inverse() {
Some(d) => {
n.mul_assign(&d);
Ok(n)
},
None => Err(SynthesisError::DivisionByZero),
}
})?;
let lambda_n = other.y.sub(cs.ns(|| "other.y - self.y"), &self.y)?;
let lambda_d = other.x.sub(cs.ns(|| "other.x - self.x"), &self.x)?;
lambda_d.mul_equals(cs.ns(|| "lambda equals"), &lambda, &lambda_n)?;
// Compute x'' = B*lambda^2 - A - x - x'
let xprime = F::alloc(cs.ns(|| "xprime"), || {
Ok(
lambda.get_value().get()?.square() * &P::MontgomeryModelParameters::COEFF_B
- &P::MontgomeryModelParameters::COEFF_A
- &self.x.get_value().get()?
- &other.x.get_value().get()?,
)
})?;
let xprime_lc = self
.x
.add(cs.ns(|| "self.x + other.x"), &other.x)?
.add(cs.ns(|| "+ xprime"), &xprime)?
.add_constant(cs.ns(|| "+ A"), &P::MontgomeryModelParameters::COEFF_A)?;
// (lambda) * (lambda) = (A + x + x' + x'')
let lambda_b = lambda.mul_by_constant(
cs.ns(|| "lambda * b"),
&P::MontgomeryModelParameters::COEFF_B,
)?;
lambda_b.mul_equals(cs.ns(|| "xprime equals"), &lambda, &xprime_lc)?;
let yprime = F::alloc(cs.ns(|| "yprime"), || {
Ok(-(self.y.get_value().get()?
+ &(lambda.get_value().get()?
* &(xprime.get_value().get()? - &self.x.get_value().get()?))))
})?;
let xres = self.x.sub(cs.ns(|| "xres"), &xprime)?;
let yres = self.y.add(cs.ns(|| "yres"), &yprime)?;
lambda.mul_equals(cs.ns(|| "yprime equals"), &xres, &yres)?;
Ok(MontgomeryAffineGadget::new(xprime, yprime))
}
}
}
#[derive(Derivative)]
#[derivative(Debug, Clone)]
#[derivative(Debug(bound = "P: TEModelParameters, ConstraintF: Field"))]
#[must_use]
pub struct AffineGadget<
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
> {
pub x: F,
pub y: F,
#[derivative(Debug = "ignore")]
_params: PhantomData<P>,
#[derivative(Debug = "ignore")]
_engine: PhantomData<ConstraintF>,
}
impl<P: TEModelParameters, ConstraintF: Field, F: FieldGadget<P::BaseField, ConstraintF>>
AffineGadget<P, ConstraintF, F>
{
pub fn new(x: F, y: F) -> Self {
Self {
x,
y,
_params: PhantomData,
_engine: PhantomData,
}
}
pub fn alloc_without_check<FN, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<TEAffine<P>, SynthesisError>,
{
let (x, y) = match value_gen() {
Ok(fe) => (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<P, ConstraintF, F> PartialEq for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y
}
}
impl<P, ConstraintF, F> Eq for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
}
mod affine_impl {
use super::*;
use crate::Assignment;
use algebra::{curves::AffineCurve, Field, PrimeField};
use std::ops::Neg;
impl<P, ConstraintF, F> GroupGadget<TEAffine<P>, ConstraintF> for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
type Value = TEAffine<P>;
type Variable = (F::Variable, F::Variable);
#[inline]
fn get_value(&self) -> Option<Self::Value> {
match (self.x.get_value(), self.y.get_value()) {
(Some(x), Some(y)) => Some(TEAffine::new(x, y)),
(..) => None,
}
}
#[inline]
fn get_variable(&self) -> Self::Variable {
(self.x.get_variable(), self.y.get_variable())
}
#[inline]
fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
Ok(Self::new(
F::zero(cs.ns(|| "zero"))?,
F::one(cs.ns(|| "one"))?,
))
}
/// Optimized constraints for checking Edwards point addition from ZCash
/// developers Daira Hopwood and Sean Bowe. Requires only 6 constraints
/// compared to 7 for the straightforward version we had earlier.
fn add<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
other: &Self,
) -> Result<Self, SynthesisError> {
let a = P::COEFF_A;
let d = P::COEFF_D;
// Compute U = (x1 + y1) * (x2 + y2)
let u1 = self
.x
.mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())?
.add(cs.ns(|| "-A * x1 + y1"), &self.y)?;
let u2 = other.x.add(cs.ns(|| "x2 + y2"), &other.y)?;
let u = u1.mul(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?;
// Compute v0 = x1 * y2
let v0 = other.y.mul(&mut cs.ns(|| "v0"), &self.x)?;
// Compute v1 = x2 * y1
let v1 = other.x.mul(&mut cs.ns(|| "v1"), &self.y)?;
// Compute C = d*v0*v1
let v2 = v0
.mul(cs.ns(|| "v0 * v1"), &v1)?
.mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?;
// Compute x3 = (v0 + v1) / (1 + v2)
let x3 = F::alloc(&mut cs.ns(|| "x3"), || {
let t0 = v0.get_value().get()? + &v1.get_value().get()?;
let t1 = P::BaseField::one() + &v2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let one = P::BaseField::one();
let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?;
let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?;
x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?;
// Compute y3 = (U + a * v0 - v1) / (1 - v2)
let y3 = F::alloc(&mut cs.ns(|| "y3"), || {
let t0 =
u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?;
let t1 = P::BaseField::one() - &v2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let one_minus_v2 = v2
.add_constant(cs.ns(|| "v2 - 1"), &(-one))?
.negate(cs.ns(|| "1 - v2"))?;
let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?;
let u_plus_a_v0_minus_v1 = u
.add(cs.ns(|| "u + a * v0"), &a_v0)?
.sub(cs.ns(|| "u + a * v0 - v1"), &v1)?;
y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?;
Ok(Self::new(x3, y3))
}
fn add_constant<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
other: &TEAffine<P>,
) -> Result<Self, SynthesisError> {
let a = P::COEFF_A;
let d = P::COEFF_D;
let other_x = other.x;
let other_y = other.y;
// Compute U = (x1 + y1) * (x2 + y2)
let u1 = self
.x
.mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())?
.add(cs.ns(|| "-A * x1 + y1"), &self.y)?;
let u2 = other_x + &other_y;
let u = u1.mul_by_constant(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?;
// Compute v0 = x1 * y2
let v0 = self.x.mul_by_constant(&mut cs.ns(|| "v0"), &other_y)?;
// Compute v1 = x2 * y1
let v1 = self.y.mul_by_constant(&mut cs.ns(|| "v1"), &other.x)?;
// Compute C = d*v0*v1
let v2 = v0
.mul(cs.ns(|| "v0 * v1"), &v1)?
.mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?;
// Compute x3 = (v0 + v1) / (1 + v2)
let x3 = F::alloc(&mut cs.ns(|| "x3"), || {
let t0 = v0.get_value().get()? + &v1.get_value().get()?;
let t1 = P::BaseField::one() + &v2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let one = P::BaseField::one();
let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?;
let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?;
x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?;
// Compute y3 = (U + a * v0 - v1) / (1 - v2)
let y3 = F::alloc(&mut cs.ns(|| "y3"), || {
let t0 =
u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?;
let t1 = P::BaseField::one() - &v2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let one_minus_v2 = v2
.add_constant(cs.ns(|| "v2 - 1"), &(-one))?
.negate(cs.ns(|| "1 - v2"))?;
let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?;
let u_plus_a_v0_minus_v1 = u
.add(cs.ns(|| "u + a * v0"), &a_v0)?
.sub(cs.ns(|| "u + a * v0 - v1"), &v1)?;
y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?;
Ok(Self::new(x3, y3))
}
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
&mut self,
mut cs: CS,
) -> Result<(), SynthesisError> {
let a = P::COEFF_A;
// xy
let xy = self.x.mul(cs.ns(|| "x * y"), &self.y)?;
let x2 = self.x.square(cs.ns(|| "x * x"))?;
let y2 = self.y.square(cs.ns(|| "y * y"))?;
let a_x2 = x2.mul_by_constant(cs.ns(|| "a * x^2"), &a)?;
// Compute x3 = (2xy) / (ax^2 + y^2)
let x3 = F::alloc(&mut cs.ns(|| "x3"), || {
let t0 = xy.get_value().get()?.double();
let t1 = a * &x2.get_value().get()? + &y2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let a_x2_plus_y2 = a_x2.add(cs.ns(|| "v2 + 1"), &y2)?;
let two_xy = xy.double(cs.ns(|| "2xy"))?;
x3.mul_equals(cs.ns(|| "check x3"), &a_x2_plus_y2, &two_xy)?;
// Compute y3 = (y^2 - ax^2) / (2 - ax^2 - y^2)
let two = P::BaseField::one().double();
let y3 = F::alloc(&mut cs.ns(|| "y3"), || {
let a_x2 = a * &x2.get_value().get()?;
let t0 = y2.get_value().get()? - &a_x2;
let t1 = two - &a_x2 - &y2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let y2_minus_a_x2 = y2.sub(cs.ns(|| "y^2 - ax^2"), &a_x2)?;
let two_minus_ax2_minus_y2 = a_x2
.add(cs.ns(|| "ax2 + y2"), &y2)?
.negate(cs.ns(|| "-ax2 - y2"))?
.add_constant(cs.ns(|| "2 -ax2 - y2"), &two)?;
y3.mul_equals(
cs.ns(|| "check y3"),
&two_minus_ax2_minus_y2,
&y2_minus_a_x2,
)?;
self.x = x3;
self.y = y3;
Ok(())
}
fn negate<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Self, SynthesisError> {
Ok(Self::new(
self.x.negate(cs.ns(|| "negate x"))?,
self.y.clone(),
))
}
fn cost_of_add() -> usize {
4 + 2 * F::cost_of_mul()
}
fn cost_of_double() -> usize {
4 + F::cost_of_mul()
}
}
impl<P, ConstraintF, F> AllocGadget<TEAffine<P>, ConstraintF> for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
Self: GroupGadget<TEAffine<P>, ConstraintF>,
{
fn alloc<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TEAffine<P>>,
{
let (x, y) = match value_gen() {
Ok(ge) => {
let ge = *ge.borrow();
(Ok(ge.x), Ok(ge.y))
},
_ => (
Err(SynthesisError::AssignmentMissing),
Err(SynthesisError::AssignmentMissing),
),
};
let d = P::COEFF_D;
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 ax^2 + y^2 = 1 + dx^2y^2
// We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1)
let x2 = x.square(&mut cs.ns(|| "x^2"))?;
let y2 = y.square(&mut cs.ns(|| "y^2"))?;
let one = P::BaseField::one();
let d_x2_minus_one = x2
.mul_by_constant(cs.ns(|| "d * x^2"), &d)?
.add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?;
let a_x2_minus_one = x2
.mul_by_constant(cs.ns(|| "a * x^2"), &a)?
.add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?;
d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?;
Ok(Self::new(x, y))
}
fn alloc_checked<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TEAffine<P>>,
{
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().mul_by_cofactor_inv())
})?;
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)
}
}
fn alloc_input<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TEAffine<P>>,
{
let (x, y) = match value_gen() {
Ok(ge) => {
let ge = *ge.borrow();
(Ok(ge.x), Ok(ge.y))
},
_ => (
Err(SynthesisError::AssignmentMissing),
Err(SynthesisError::AssignmentMissing),
),
};
let d = P::COEFF_D;
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 ax^2 + y^2 = 1 + dx^2y^2
// We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1)
let x2 = x.square(&mut cs.ns(|| "x^2"))?;
let y2 = y.square(&mut cs.ns(|| "y^2"))?;
let one = P::BaseField::one();
let d_x2_minus_one = x2
.mul_by_constant(cs.ns(|| "d * x^2"), &d)?
.add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?;
let a_x2_minus_one = x2
.mul_by_constant(cs.ns(|| "a * x^2"), &a)?
.add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?;
d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?;
Ok(Self::new(x, y))
}
}
}
mod projective_impl {
use super::*;
use crate::Assignment;
use algebra::{
curves::twisted_edwards_extended::GroupProjective as TEProjective, AffineCurve, Field,
PrimeField, ProjectiveCurve,
};
use std::ops::Neg;
impl<P, ConstraintF, F> GroupGadget<TEProjective<P>, ConstraintF>
for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
type Value = TEProjective<P>;
type Variable = (F::Variable, F::Variable);
#[inline]
fn get_value(&self) -> Option<Self::Value> {
match (self.x.get_value(), self.y.get_value()) {
(Some(x), Some(y)) => Some(TEAffine::new(x, y).into()),
(..) => None,
}
}
#[inline]
fn get_variable(&self) -> Self::Variable {
(self.x.get_variable(), self.y.get_variable())
}
#[inline]
fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
Ok(Self::new(
F::zero(cs.ns(|| "zero"))?,
F::one(cs.ns(|| "one"))?,
))
}
/// Optimized constraints for checking Edwards point addition from ZCash
/// developers Daira Hopwood and Sean Bowe. Requires only 6 constraints
/// compared to 7 for the straightforward version we had earlier.
fn add<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
other: &Self,
) -> Result<Self, SynthesisError> {
let a = P::COEFF_A;
let d = P::COEFF_D;
// Compute U = (x1 + y1) * (x2 + y2)
let u1 = self
.x
.mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())?
.add(cs.ns(|| "-A * x1 + y1"), &self.y)?;
let u2 = other.x.add(cs.ns(|| "x2 + y2"), &other.y)?;
let u = u1.mul(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?;
// Compute v0 = x1 * y2
let v0 = other.y.mul(&mut cs.ns(|| "v0"), &self.x)?;
// Compute v1 = x2 * y1
let v1 = other.x.mul(&mut cs.ns(|| "v1"), &self.y)?;
// Compute C = d*v0*v1
let v2 = v0
.mul(cs.ns(|| "v0 * v1"), &v1)?
.mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?;
// Compute x3 = (v0 + v1) / (1 + v2)
let x3 = F::alloc(&mut cs.ns(|| "x3"), || {
let t0 = v0.get_value().get()? + &v1.get_value().get()?;
let t1 = P::BaseField::one() + &v2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let one = P::BaseField::one();
let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?;
let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?;
x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?;
// Compute y3 = (U + a * v0 - v1) / (1 - v2)
let y3 = F::alloc(&mut cs.ns(|| "y3"), || {
let t0 =
u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?;
let t1 = P::BaseField::one() - &v2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let one_minus_v2 = v2
.add_constant(cs.ns(|| "v2 - 1"), &(-one))?
.negate(cs.ns(|| "1 - v2"))?;
let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?;
let u_plus_a_v0_minus_v1 = u
.add(cs.ns(|| "u + a * v0"), &a_v0)?
.sub(cs.ns(|| "u + a * v0 - v1"), &v1)?;
y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?;
Ok(Self::new(x3, y3))
}
fn add_constant<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
other: &TEProjective<P>,
) -> Result<Self, SynthesisError> {
let a = P::COEFF_A;
let d = P::COEFF_D;
let other = other.into_affine();
let other_x = other.x;
let other_y = other.y;
// Compute U = (x1 + y1) * (x2 + y2)
let u1 = self
.x
.mul_by_constant(cs.ns(|| "-A * x1"), &a.neg())?
.add(cs.ns(|| "-A * x1 + y1"), &self.y)?;
let u2 = other_x + &other_y;
let u = u1.mul_by_constant(cs.ns(|| "(-A * x1 + y1) * (x2 + y2)"), &u2)?;
// Compute v0 = x1 * y2
let v0 = self.x.mul_by_constant(&mut cs.ns(|| "v0"), &other_y)?;
// Compute v1 = x2 * y1
let v1 = self.y.mul_by_constant(&mut cs.ns(|| "v1"), &other.x)?;
// Compute C = d*v0*v1
let v2 = v0
.mul(cs.ns(|| "v0 * v1"), &v1)?
.mul_by_constant(cs.ns(|| "D * v0 * v1"), &d)?;
// Compute x3 = (v0 + v1) / (1 + v2)
let x3 = F::alloc(&mut cs.ns(|| "x3"), || {
let t0 = v0.get_value().get()? + &v1.get_value().get()?;
let t1 = P::BaseField::one() + &v2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let one = P::BaseField::one();
let v2_plus_one = v2.add_constant(cs.ns(|| "v2 + 1"), &one)?;
let v0_plus_v1 = v0.add(cs.ns(|| "v0 + v1"), &v1)?;
x3.mul_equals(cs.ns(|| "check x3"), &v2_plus_one, &v0_plus_v1)?;
// Compute y3 = (U + a * v0 - v1) / (1 - v2)
let y3 = F::alloc(&mut cs.ns(|| "y3"), || {
let t0 =
u.get_value().get()? + &(a * &v0.get_value().get()?) - &v1.get_value().get()?;
let t1 = P::BaseField::one() - &v2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let one_minus_v2 = v2
.add_constant(cs.ns(|| "v2 - 1"), &(-one))?
.negate(cs.ns(|| "1 - v2"))?;
let a_v0 = v0.mul_by_constant(cs.ns(|| "a * v0"), &a)?;
let u_plus_a_v0_minus_v1 = u
.add(cs.ns(|| "u + a * v0"), &a_v0)?
.sub(cs.ns(|| "u + a * v0 - v1"), &v1)?;
y3.mul_equals(cs.ns(|| "check y3"), &one_minus_v2, &u_plus_a_v0_minus_v1)?;
Ok(Self::new(x3, y3))
}
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
&mut self,
mut cs: CS,
) -> Result<(), SynthesisError> {
let a = P::COEFF_A;
// xy
let xy = self.x.mul(cs.ns(|| "x * y"), &self.y)?;
let x2 = self.x.square(cs.ns(|| "x * x"))?;
let y2 = self.y.square(cs.ns(|| "y * y"))?;
let a_x2 = x2.mul_by_constant(cs.ns(|| "a * x^2"), &a)?;
// Compute x3 = (2xy) / (ax^2 + y^2)
let x3 = F::alloc(&mut cs.ns(|| "x3"), || {
let t0 = xy.get_value().get()?.double();
let t1 = a * &x2.get_value().get()? + &y2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let a_x2_plus_y2 = a_x2.add(cs.ns(|| "v2 + 1"), &y2)?;
let two_xy = xy.double(cs.ns(|| "2xy"))?;
x3.mul_equals(cs.ns(|| "check x3"), &a_x2_plus_y2, &two_xy)?;
// Compute y3 = (y^2 - ax^2) / (2 - ax^2 - y^2)
let two = P::BaseField::one().double();
let y3 = F::alloc(&mut cs.ns(|| "y3"), || {
let a_x2 = a * &x2.get_value().get()?;
let t0 = y2.get_value().get()? - &a_x2;
let t1 = two - &a_x2 - &y2.get_value().get()?;
Ok(t0 * &t1.inverse().get()?)
})?;
let y2_minus_a_x2 = y2.sub(cs.ns(|| "y^2 - ax^2"), &a_x2)?;
let two_minus_ax2_minus_y2 = a_x2
.add(cs.ns(|| "ax2 + y2"), &y2)?
.negate(cs.ns(|| "-ax2 - y2"))?
.add_constant(cs.ns(|| "2 -ax2 - y2"), &two)?;
y3.mul_equals(
cs.ns(|| "check y3"),
&two_minus_ax2_minus_y2,
&y2_minus_a_x2,
)?;
self.x = x3;
self.y = y3;
Ok(())
}
fn negate<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Self, SynthesisError> {
Ok(Self::new(
self.x.negate(cs.ns(|| "negate x"))?,
self.y.clone(),
))
}
fn precomputed_base_scalar_mul<'a, CS, I, B>(
&mut self,
mut cs: CS,
scalar_bits_with_base_powers: I,
) -> Result<(), SynthesisError>
where
CS: ConstraintSystem<ConstraintF>,
I: Iterator<Item = (B, &'a TEProjective<P>)>,
B: Borrow<Boolean>,
{
let scalar_bits_with_base_powers: Vec<_> = scalar_bits_with_base_powers
.map(|(bit, base)| (bit.borrow().clone(), base.clone()))
.collect();
let zero = TEProjective::zero();
for (i, bits_base_powers) in scalar_bits_with_base_powers.chunks(2).enumerate() {
let mut cs = cs.ns(|| format!("Chunk {}", i));
if bits_base_powers.len() == 2 {
let bits = [bits_base_powers[0].0, bits_base_powers[1].0];
let base_powers = [bits_base_powers[0].1, bits_base_powers[1].1];
let mut table = [
zero,
base_powers[0],
base_powers[1],
base_powers[0] + &base_powers[1],
];
TEProjective::batch_normalization(&mut table);
let x_s = [table[0].x, table[1].x, table[2].x, table[3].x];
let y_s = [table[0].y, table[1].y, table[2].y, table[3].y];
let x: F = F::two_bit_lookup(cs.ns(|| "Lookup x"), &bits, &x_s)?;
let y: F = F::two_bit_lookup(cs.ns(|| "Lookup y"), &bits, &y_s)?;
let adder: Self = Self::new(x, y);
*self = <Self as GroupGadget<TEProjective<P>, ConstraintF>>::add(
self,
&mut cs.ns(|| "Add"),
&adder,
)?;
} else if bits_base_powers.len() == 1 {
let bit = bits_base_powers[0].0;
let base_power = bits_base_powers[0].1;
let new_encoded =
self.add_constant(&mut cs.ns(|| "Add base power"), &base_power)?;
*self = Self::conditionally_select(
&mut cs.ns(|| "Conditional Select"),
&bit,
&new_encoded,
&self,
)?;
}
}
Ok(())
}
fn precomputed_base_3_bit_signed_digit_scalar_mul<'a, CS, I, J, B>(
mut cs: CS,
bases: &[B],
scalars: &[J],
) -> Result<Self, SynthesisError>
where
CS: ConstraintSystem<ConstraintF>,
I: Borrow<[Boolean]>,
J: Borrow<[I]>,
B: Borrow<[TEProjective<P>]>,
{
const CHUNK_SIZE: usize = 3;
let mut edwards_result: Option<AffineGadget<P, ConstraintF, F>> = None;
let mut result: Option<MontgomeryAffineGadget<P, ConstraintF, F>> = None;
let mut process_segment_result =
|mut cs: r1cs_core::Namespace<_, _>,
result: &MontgomeryAffineGadget<P, ConstraintF, F>|
-> Result<(), SynthesisError> {
let segment_result = result.into_edwards(cs.ns(|| "segment result"))?;
match edwards_result {
None => {
edwards_result = Some(segment_result);
},
Some(ref mut edwards_result) => {
*edwards_result = GroupGadget::<TEAffine<P>, ConstraintF>::add(
&segment_result,
cs.ns(|| "edwards addition"),
edwards_result,
)?;
},
}
Ok(())
};
// Compute ∏(h_i^{m_i}) for all i.
for (segment_i, (segment_bits_chunks, segment_powers)) in
scalars.iter().zip(bases.iter()).enumerate()
{
for (i, (bits, base_power)) in segment_bits_chunks
.borrow()
.iter()
.zip(segment_powers.borrow().iter())
.enumerate()
{
let base_power = base_power.borrow();
let mut acc_power = *base_power;
let mut coords = vec![];
for _ in 0..4 {
coords.push(acc_power);
acc_power += base_power;
}
let bits = bits.borrow().to_bits(
&mut cs.ns(|| format!("Convert Scalar {}, {} to bits", segment_i, i)),
)?;
if bits.len() != CHUNK_SIZE {
return Err(SynthesisError::Unsatisfiable);
}
let coords = coords
.iter()
.map(|p| {
let p = p.into_affine();
MontgomeryAffineGadget::<P, ConstraintF, F>::from_edwards_to_coords(&p)
.unwrap()
})
.collect::<Vec<_>>();
let x_coeffs = coords.iter().map(|p| p.0).collect::<Vec<_>>();
let y_coeffs = coords.iter().map(|p| p.1).collect::<Vec<_>>();
let precomp = Boolean::and(
cs.ns(|| format!("precomp in window {}, {}", segment_i, i)),
&bits[0],
&bits[1],
)?;
let x = F::zero(cs.ns(|| format!("x in window {}, {}", segment_i, i)))?
.conditionally_add_constant(
cs.ns(|| format!("add bool 00 in window {}, {}", segment_i, i)),
&Boolean::constant(true),
x_coeffs[0],
)?
.conditionally_add_constant(
cs.ns(|| format!("add bool 01 in window {}, {}", segment_i, i)),
&bits[0],
x_coeffs[1] - &x_coeffs[0],
)?
.conditionally_add_constant(
cs.ns(|| format!("add bool 10 in window {}, {}", segment_i, i)),
&bits[1],
x_coeffs[2] - &x_coeffs[0],
)?
.conditionally_add_constant(
cs.ns(|| format!("add bool 11 in window {}, {}", segment_i, i)),
&precomp,
x_coeffs[3] - &x_coeffs[2] - &x_coeffs[1] + &x_coeffs[0],
)?;
let y = F::three_bit_cond_neg_lookup(
cs.ns(|| format!("y lookup in window {}, {}", segment_i, i)),
&bits,
&precomp,
&y_coeffs,
)?;
let tmp = MontgomeryAffineGadget::new(x, y);
match result {
None => {
result = Some(tmp);
},
Some(ref mut result) => {
*result = tmp.add(
cs.ns(|| format!("addition of window {}, {}", segment_i, i)),
result,
)?;
},
}
}
process_segment_result(
cs.ns(|| format!("window {}", segment_i)),
&result.unwrap(),
)?;
result = None;
}
if result.is_some() {
process_segment_result(cs.ns(|| "leftover"), &result.unwrap())?;
}
Ok(edwards_result.unwrap())
}
fn cost_of_add() -> usize {
4 + 2 * F::cost_of_mul()
}
fn cost_of_double() -> usize {
4 + F::cost_of_mul()
}
}
impl<P, ConstraintF, F> AllocGadget<TEProjective<P>, ConstraintF>
for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
Self: GroupGadget<TEProjective<P>, ConstraintF>,
{
fn alloc<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TEProjective<P>>,
{
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 d = P::COEFF_D;
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 ax^2 + y^2 = 1 + dx^2y^2
// We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1)
let x2 = x.square(&mut cs.ns(|| "x^2"))?;
let y2 = y.square(&mut cs.ns(|| "y^2"))?;
let one = P::BaseField::one();
let d_x2_minus_one = x2
.mul_by_constant(cs.ns(|| "d * x^2"), &d)?
.add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?;
let a_x2_minus_one = x2
.mul_by_constant(cs.ns(|| "a * x^2"), &a)?
.add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?;
d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?;
Ok(Self::new(x, y))
}
fn alloc_checked<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TEProjective<P>>,
{
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)
}
}
fn alloc_input<FN, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
value_gen: FN,
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TEProjective<P>>,
{
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 d = P::COEFF_D;
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 ax^2 + y^2 = 1 + dx^2y^2
// We do this by checking that ax^2 - 1 = y^2 * (dx^2 - 1)
let x2 = x.square(&mut cs.ns(|| "x^2"))?;
let y2 = y.square(&mut cs.ns(|| "y^2"))?;
let one = P::BaseField::one();
let d_x2_minus_one = x2
.mul_by_constant(cs.ns(|| "d * x^2"), &d)?
.add_constant(cs.ns(|| "d * x^2 - 1"), &one.neg())?;
let a_x2_minus_one = x2
.mul_by_constant(cs.ns(|| "a * x^2"), &a)?
.add_constant(cs.ns(|| "a * x^2 - 1"), &one.neg())?;
d_x2_minus_one.mul_equals(cs.ns(|| "on curve check"), &y2, &a_x2_minus_one)?;
Ok(Self::new(x, y))
}
}
}
impl<P, ConstraintF, F> CondSelectGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
#[inline]
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
cond: &Boolean,
first: &Self,
second: &Self,
) -> Result<Self, SynthesisError> {
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 * <F as CondSelectGadget<ConstraintF>>::cost()
}
}
impl<P, ConstraintF, F> EqGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
}
impl<P, ConstraintF, F> ConditionalEqGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
#[inline]
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
&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 * <F as ConditionalEqGadget<ConstraintF>>::cost()
}
}
impl<P, ConstraintF, F> NEqGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
#[inline]
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
&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 * <F as NEqGadget<ConstraintF>>::cost()
}
}
impl<P, ConstraintF, F> ToBitsGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
fn to_bits<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Vec<Boolean>, SynthesisError> {
let mut x_bits = self.x.to_bits(cs.ns(|| "X Coordinate To Bits"))?;
let y_bits = self.y.to_bits(cs.ns(|| "Y Coordinate To Bits"))?;
x_bits.extend_from_slice(&y_bits);
Ok(x_bits)
}
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Vec<Boolean>, SynthesisError> {
let mut x_bits = self.x.to_bits_strict(cs.ns(|| "X Coordinate To Bits"))?;
let y_bits = self.y.to_bits_strict(cs.ns(|| "Y Coordinate To Bits"))?;
x_bits.extend_from_slice(&y_bits);
Ok(x_bits)
}
}
impl<P, ConstraintF, F> ToBytesGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
where
P: TEModelParameters,
ConstraintF: Field,
F: FieldGadget<P::BaseField, ConstraintF>,
{
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
let mut x_bytes = self.x.to_bytes(cs.ns(|| "x"))?;
let y_bytes = self.y.to_bytes(cs.ns(|| "y"))?;
x_bytes.extend_from_slice(&y_bytes);
Ok(x_bytes)
}
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
&self,
mut cs: CS,
) -> Result<Vec<UInt8>, SynthesisError> {
let mut x_bytes = self.x.to_bytes_strict(cs.ns(|| "x"))?;
let y_bytes = self.y.to_bytes_strict(cs.ns(|| "y"))?;
x_bytes.extend_from_slice(&y_bytes);
Ok(x_bytes)
}
}