@ -0,0 +1,31 @@ |
|||||
|
[package] |
||||
|
name = "r1cs-std" |
||||
|
version = "0.1.0" |
||||
|
authors = [ |
||||
|
"Sean Bowe", |
||||
|
"Alessandro Chiesa", |
||||
|
"Matthew Green", |
||||
|
"Ian Miers", |
||||
|
"Pratyush Mishra", |
||||
|
"Howard Wu" |
||||
|
] |
||||
|
description = "A standard library for constraint system gadgets" |
||||
|
homepage = "https://libzexe.org" |
||||
|
repository = "https://github.com/scipr/zexe" |
||||
|
documentation = "https://docs.rs/snark-gadgets/" |
||||
|
keywords = ["zero knowledge", "cryptography", "zkSNARK", "SNARK"] |
||||
|
categories = ["cryptography"] |
||||
|
include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] |
||||
|
license = "MIT/Apache-2.0" |
||||
|
edition = "2018" |
||||
|
|
||||
|
################################# Dependencies ################################ |
||||
|
|
||||
|
[dependencies] |
||||
|
algebra = { path = "../algebra" } |
||||
|
r1cs-core = { path = "../r1cs-core" } |
||||
|
derivative = "1" |
||||
|
radix_trie = "0.1" |
||||
|
|
||||
|
[dev-dependencies] |
||||
|
rand = { version = "0.4" } |
@ -0,0 +1 @@ |
|||||
|
../LICENSE-APACHE |
@ -0,0 +1 @@ |
|||||
|
../LICENSE-MIT |
@ -0,0 +1,105 @@ |
|||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use std::borrow::Borrow;
|
||||
|
|
||||
|
pub trait AllocGadget<V, ConstraintF: Field>
|
||||
|
where
|
||||
|
Self: Sized,
|
||||
|
V: ?Sized,
|
||||
|
{
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<V>;
|
||||
|
|
||||
|
fn alloc_checked<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<V>,
|
||||
|
{
|
||||
|
Self::alloc(cs, f)
|
||||
|
}
|
||||
|
|
||||
|
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<V>;
|
||||
|
|
||||
|
fn alloc_input_checked<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
f: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<V>,
|
||||
|
{
|
||||
|
Self::alloc_input(cs, f)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<I, ConstraintF: Field, A: AllocGadget<I, ConstraintF>> AllocGadget<[I], ConstraintF> for Vec<A> {
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(mut cs: CS, f: F) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<[I]>,
|
||||
|
{
|
||||
|
let mut vec = Vec::new();
|
||||
|
for (i, value) in f()?.borrow().iter().enumerate() {
|
||||
|
vec.push(A::alloc(&mut cs.ns(|| format!("value_{}", i)), || {
|
||||
|
Ok(value)
|
||||
|
})?);
|
||||
|
}
|
||||
|
Ok(vec)
|
||||
|
}
|
||||
|
|
||||
|
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(mut cs: CS, f: F) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<[I]>,
|
||||
|
{
|
||||
|
let mut vec = Vec::new();
|
||||
|
for (i, value) in f()?.borrow().iter().enumerate() {
|
||||
|
vec.push(A::alloc_input(
|
||||
|
&mut cs.ns(|| format!("value_{}", i)),
|
||||
|
|| Ok(value),
|
||||
|
)?);
|
||||
|
}
|
||||
|
Ok(vec)
|
||||
|
}
|
||||
|
|
||||
|
fn alloc_checked<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
f: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<[I]>,
|
||||
|
{
|
||||
|
let mut vec = Vec::new();
|
||||
|
for (i, value) in f()?.borrow().iter().enumerate() {
|
||||
|
vec.push(A::alloc_checked(
|
||||
|
&mut cs.ns(|| format!("value_{}", i)),
|
||||
|
|| Ok(value),
|
||||
|
)?);
|
||||
|
}
|
||||
|
Ok(vec)
|
||||
|
}
|
||||
|
|
||||
|
fn alloc_input_checked<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
f: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<[I]>,
|
||||
|
{
|
||||
|
let mut vec = Vec::new();
|
||||
|
for (i, value) in f()?.borrow().iter().enumerate() {
|
||||
|
vec.push(A::alloc_input_checked(
|
||||
|
&mut cs.ns(|| format!("value_{}", i)),
|
||||
|
|| Ok(value),
|
||||
|
)?);
|
||||
|
}
|
||||
|
Ok(vec)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,84 @@ |
|||||
|
use crate::bits::{boolean::Boolean, uint8::UInt8};
|
||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
pub mod boolean;
|
||||
|
pub mod uint32;
|
||||
|
pub mod uint8;
|
||||
|
|
||||
|
pub trait ToBitsGadget<ConstraintF: Field> {
|
||||
|
fn to_bits<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Vec<Boolean>, SynthesisError>;
|
||||
|
|
||||
|
/// Additionally checks if the produced list of booleans is 'valid'.
|
||||
|
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<Vec<Boolean>, SynthesisError>;
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field> ToBitsGadget<ConstraintF> for Boolean {
|
||||
|
fn to_bits<CS: ConstraintSystem<ConstraintF>>(&self, _: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
Ok(vec![self.clone()])
|
||||
|
}
|
||||
|
|
||||
|
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
_: CS,
|
||||
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
Ok(vec![self.clone()])
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field> ToBitsGadget<ConstraintF> for [Boolean] {
|
||||
|
fn to_bits<CS: ConstraintSystem<ConstraintF>>(&self, _cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
Ok(self.to_vec())
|
||||
|
}
|
||||
|
|
||||
|
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
_cs: CS,
|
||||
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
Ok(self.to_vec())
|
||||
|
}
|
||||
|
}
|
||||
|
impl<ConstraintF: Field> ToBitsGadget<ConstraintF> for Vec<Boolean> {
|
||||
|
fn to_bits<CS: ConstraintSystem<ConstraintF>>(&self, _cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
Ok(self.clone())
|
||||
|
}
|
||||
|
|
||||
|
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
_cs: CS,
|
||||
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
Ok(self.clone())
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field> ToBitsGadget<ConstraintF> for [UInt8] {
|
||||
|
fn to_bits<CS: ConstraintSystem<ConstraintF>>(&self, _cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
let mut result = Vec::with_capacity(&self.len() * 8);
|
||||
|
for byte in self {
|
||||
|
result.extend_from_slice(&byte.into_bits_le());
|
||||
|
}
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
self.to_bits(cs)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub trait ToBytesGadget<ConstraintF: Field> {
|
||||
|
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Vec<UInt8>, SynthesisError>;
|
||||
|
|
||||
|
/// Additionally checks if the produced list of booleans is 'valid'.
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError>;
|
||||
|
}
|
||||
|
|
||||
|
|
@ -0,0 +1,534 @@ |
|||||
|
use algebra::{FpParameters, PrimeField, Field};
|
||||
|
|
||||
|
use r1cs_core::{ConstraintSystem, LinearCombination, SynthesisError};
|
||||
|
|
||||
|
use crate::boolean::{AllocatedBit, Boolean};
|
||||
|
use crate::Assignment;
|
||||
|
use crate::prelude::*;
|
||||
|
|
||||
|
/// Represents an interpretation of 32 `Boolean` objects as an
|
||||
|
/// unsigned integer.
|
||||
|
#[derive(Clone, Debug)]
|
||||
|
pub struct UInt32 {
|
||||
|
// Least significant bit_gadget first
|
||||
|
bits: Vec<Boolean>,
|
||||
|
value: Option<u32>,
|
||||
|
}
|
||||
|
|
||||
|
impl UInt32 {
|
||||
|
/// Construct a constant `UInt32` from a `u32`
|
||||
|
pub fn constant(value: u32) -> Self {
|
||||
|
let mut bits = Vec::with_capacity(32);
|
||||
|
|
||||
|
let mut tmp = value;
|
||||
|
for _ in 0..32 {
|
||||
|
if tmp & 1 == 1 {
|
||||
|
bits.push(Boolean::constant(true))
|
||||
|
} else {
|
||||
|
bits.push(Boolean::constant(false))
|
||||
|
}
|
||||
|
|
||||
|
tmp >>= 1;
|
||||
|
}
|
||||
|
|
||||
|
UInt32 {
|
||||
|
bits,
|
||||
|
value: Some(value),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
/// Allocate a `UInt32` in the constraint system
|
||||
|
pub fn alloc<ConstraintF, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
ConstraintF: Field,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
{
|
||||
|
let values = match value {
|
||||
|
Some(mut val) => {
|
||||
|
let mut v = Vec::with_capacity(32);
|
||||
|
|
||||
|
for _ in 0..32 {
|
||||
|
v.push(Some(val & 1 == 1));
|
||||
|
val >>= 1;
|
||||
|
}
|
||||
|
|
||||
|
v
|
||||
|
},
|
||||
|
None => vec![None; 32],
|
||||
|
};
|
||||
|
|
||||
|
let bits = values
|
||||
|
.into_iter()
|
||||
|
.enumerate()
|
||||
|
.map(|(i, v)| {
|
||||
|
Ok(Boolean::from(AllocatedBit::alloc(
|
||||
|
cs.ns(|| format!("allocated bit_gadget {}", i)),
|
||||
|
|| v.get(),
|
||||
|
)?))
|
||||
|
})
|
||||
|
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
||||
|
|
||||
|
Ok(UInt32 { bits, value })
|
||||
|
}
|
||||
|
|
||||
|
/// Turns this `UInt32` into its little-endian byte order representation.
|
||||
|
pub fn to_bits_le(&self) -> Vec<Boolean> {
|
||||
|
self.bits.clone()
|
||||
|
}
|
||||
|
|
||||
|
/// Converts a little-endian byte order representation of bits into a
|
||||
|
/// `UInt32`.
|
||||
|
pub fn from_bits_le(bits: &[Boolean]) -> Self {
|
||||
|
assert_eq!(bits.len(), 32);
|
||||
|
|
||||
|
let bits = bits.to_vec();
|
||||
|
|
||||
|
let mut value = Some(0u32);
|
||||
|
for b in bits.iter().rev() {
|
||||
|
value.as_mut().map(|v| *v <<= 1);
|
||||
|
|
||||
|
match b {
|
||||
|
&Boolean::Constant(b) => {
|
||||
|
if b {
|
||||
|
value.as_mut().map(|v| *v |= 1);
|
||||
|
}
|
||||
|
},
|
||||
|
&Boolean::Is(ref b) => match b.get_value() {
|
||||
|
Some(true) => {
|
||||
|
value.as_mut().map(|v| *v |= 1);
|
||||
|
},
|
||||
|
Some(false) => {},
|
||||
|
None => value = None,
|
||||
|
},
|
||||
|
&Boolean::Not(ref b) => match b.get_value() {
|
||||
|
Some(false) => {
|
||||
|
value.as_mut().map(|v| *v |= 1);
|
||||
|
},
|
||||
|
Some(true) => {},
|
||||
|
None => value = None,
|
||||
|
},
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
Self { value, bits }
|
||||
|
}
|
||||
|
|
||||
|
pub fn rotr(&self, by: usize) -> Self {
|
||||
|
let by = by % 32;
|
||||
|
|
||||
|
let new_bits = self
|
||||
|
.bits
|
||||
|
.iter()
|
||||
|
.skip(by)
|
||||
|
.chain(self.bits.iter())
|
||||
|
.take(32)
|
||||
|
.cloned()
|
||||
|
.collect();
|
||||
|
|
||||
|
UInt32 {
|
||||
|
bits: new_bits,
|
||||
|
value: self.value.map(|v| v.rotate_right(by as u32)),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
/// XOR this `UInt32` with another `UInt32`
|
||||
|
pub fn xor<ConstraintF, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
ConstraintF: Field,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
{
|
||||
|
let new_value = match (self.value, other.value) {
|
||||
|
(Some(a), Some(b)) => Some(a ^ b),
|
||||
|
_ => None,
|
||||
|
};
|
||||
|
|
||||
|
let bits = self
|
||||
|
.bits
|
||||
|
.iter()
|
||||
|
.zip(other.bits.iter())
|
||||
|
.enumerate()
|
||||
|
.map(|(i, (a, b))| Boolean::xor(cs.ns(|| format!("xor of bit_gadget {}", i)), a, b))
|
||||
|
.collect::<Result<_, _>>()?;
|
||||
|
|
||||
|
Ok(UInt32 {
|
||||
|
bits,
|
||||
|
value: new_value,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
/// Perform modular addition of several `UInt32` objects.
|
||||
|
pub fn addmany<ConstraintF, CS>(mut cs: CS, operands: &[Self]) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
ConstraintF: PrimeField,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
{
|
||||
|
// Make some arbitrary bounds for ourselves to avoid overflows
|
||||
|
// in the scalar field
|
||||
|
assert!(ConstraintF::Params::MODULUS_BITS >= 64);
|
||||
|
assert!(operands.len() >= 2); // Weird trivial cases that should never happen
|
||||
|
assert!(operands.len() <= 10);
|
||||
|
|
||||
|
// Compute the maximum value of the sum so we allocate enough bits for
|
||||
|
// the result
|
||||
|
let mut max_value = (operands.len() as u64) * u64::from(u32::max_value());
|
||||
|
|
||||
|
// Keep track of the resulting value
|
||||
|
let mut result_value = Some(0u64);
|
||||
|
|
||||
|
// This is a linear combination that we will enforce to be "zero"
|
||||
|
let mut lc = LinearCombination::zero();
|
||||
|
|
||||
|
let mut all_constants = true;
|
||||
|
|
||||
|
// Iterate over the operands
|
||||
|
for op in operands {
|
||||
|
// Accumulate the value
|
||||
|
match op.value {
|
||||
|
Some(val) => {
|
||||
|
result_value.as_mut().map(|v| *v += u64::from(val));
|
||||
|
},
|
||||
|
None => {
|
||||
|
// If any of our operands have unknown value, we won't
|
||||
|
// know the value of the result
|
||||
|
result_value = None;
|
||||
|
},
|
||||
|
}
|
||||
|
|
||||
|
// Iterate over each bit_gadget of the operand and add the operand to
|
||||
|
// the linear combination
|
||||
|
let mut coeff = ConstraintF::one();
|
||||
|
for bit in &op.bits {
|
||||
|
match *bit {
|
||||
|
Boolean::Is(ref bit) => {
|
||||
|
all_constants = false;
|
||||
|
|
||||
|
// Add coeff * bit_gadget
|
||||
|
lc = lc + (coeff, bit.get_variable());
|
||||
|
},
|
||||
|
Boolean::Not(ref bit) => {
|
||||
|
all_constants = false;
|
||||
|
|
||||
|
// Add coeff * (1 - bit_gadget) = coeff * ONE - coeff * bit_gadget
|
||||
|
lc = lc + (coeff, CS::one()) - (coeff, bit.get_variable());
|
||||
|
},
|
||||
|
Boolean::Constant(bit) => {
|
||||
|
if bit {
|
||||
|
lc = lc + (coeff, CS::one());
|
||||
|
}
|
||||
|
},
|
||||
|
}
|
||||
|
|
||||
|
coeff.double_in_place();
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
// The value of the actual result is modulo 2^32
|
||||
|
let modular_value = result_value.map(|v| v as u32);
|
||||
|
|
||||
|
if all_constants && modular_value.is_some() {
|
||||
|
// We can just return a constant, rather than
|
||||
|
// unpacking the result into allocated bits.
|
||||
|
|
||||
|
return Ok(UInt32::constant(modular_value.unwrap()));
|
||||
|
}
|
||||
|
|
||||
|
// Storage area for the resulting bits
|
||||
|
let mut result_bits = vec![];
|
||||
|
|
||||
|
// Allocate each bit_gadget of the result
|
||||
|
let mut coeff = ConstraintF::one();
|
||||
|
let mut i = 0;
|
||||
|
while max_value != 0 {
|
||||
|
// Allocate the bit_gadget
|
||||
|
let b = AllocatedBit::alloc(cs.ns(|| format!("result bit_gadget {}", i)), || {
|
||||
|
result_value.map(|v| (v >> i) & 1 == 1).get()
|
||||
|
})?;
|
||||
|
|
||||
|
// Subtract this bit_gadget from the linear combination to ensure the sums
|
||||
|
// balance out
|
||||
|
lc = lc - (coeff, b.get_variable());
|
||||
|
|
||||
|
result_bits.push(b.into());
|
||||
|
|
||||
|
max_value >>= 1;
|
||||
|
i += 1;
|
||||
|
coeff.double_in_place();
|
||||
|
}
|
||||
|
|
||||
|
// Enforce that the linear combination equals zero
|
||||
|
cs.enforce(|| "modular addition", |lc| lc, |lc| lc, |_| lc);
|
||||
|
|
||||
|
// Discard carry bits that we don't care about
|
||||
|
result_bits.truncate(32);
|
||||
|
|
||||
|
Ok(UInt32 {
|
||||
|
bits: result_bits,
|
||||
|
value: modular_value,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field> ToBytesGadget<ConstraintF> for UInt32 {
|
||||
|
#[inline]
|
||||
|
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, _cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let value_chunks = match self.value.map(|val| {
|
||||
|
use algebra::bytes::ToBytes;
|
||||
|
let mut bytes = [0u8; 4];
|
||||
|
val.write(bytes.as_mut()).unwrap();
|
||||
|
bytes
|
||||
|
}) {
|
||||
|
Some(chunks) => [
|
||||
|
Some(chunks[0]),
|
||||
|
Some(chunks[1]),
|
||||
|
Some(chunks[2]),
|
||||
|
Some(chunks[3]),
|
||||
|
],
|
||||
|
None => [None, None, None, None],
|
||||
|
};
|
||||
|
let mut bytes = Vec::new();
|
||||
|
for (i, chunk8) in self.to_bits_le().chunks(8).into_iter().enumerate() {
|
||||
|
let byte = UInt8 {
|
||||
|
bits: chunk8.to_vec(),
|
||||
|
value: value_chunks[i],
|
||||
|
};
|
||||
|
bytes.push(byte);
|
||||
|
}
|
||||
|
|
||||
|
Ok(bytes)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
self.to_bytes(cs)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl PartialEq for UInt32 {
|
||||
|
fn eq(&self, other: &Self) -> bool {
|
||||
|
!self.value.is_none() && !other.value.is_none() && self.value == other.value
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl Eq for UInt32 {}
|
||||
|
|
||||
|
impl<ConstraintF: Field> ConditionalEqGadget<ConstraintF> for UInt32 {
|
||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
for (i, (a, b)) in self.bits.iter().zip(&other.bits).enumerate() {
|
||||
|
a.conditional_enforce_equal(
|
||||
|
&mut cs.ns(|| format!("uint32_equal_{}", i)),
|
||||
|
b,
|
||||
|
condition,
|
||||
|
)?;
|
||||
|
}
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
32 * <Boolean as ConditionalEqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use super::UInt32;
|
||||
|
use crate::{bits::boolean::Boolean, test_constraint_system::TestConstraintSystem};
|
||||
|
use algebra::fields::{bls12_381::Fr, Field};
|
||||
|
use rand::{Rng, SeedableRng, XorShiftRng};
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_uint32_from_bits() {
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
|
||||
|
|
||||
|
for _ in 0..1000 {
|
||||
|
let v = (0..32)
|
||||
|
.map(|_| Boolean::constant(rng.gen()))
|
||||
|
.collect::<Vec<_>>();
|
||||
|
|
||||
|
let b = UInt32::from_bits_le(&v);
|
||||
|
|
||||
|
for (i, bit_gadget) in b.bits.iter().enumerate() {
|
||||
|
match bit_gadget {
|
||||
|
&Boolean::Constant(bit_gadget) => {
|
||||
|
assert!(bit_gadget == ((b.value.unwrap() >> i) & 1 == 1));
|
||||
|
},
|
||||
|
_ => unreachable!(),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
let expected_to_be_same = b.to_bits_le();
|
||||
|
|
||||
|
for x in v.iter().zip(expected_to_be_same.iter()) {
|
||||
|
match x {
|
||||
|
(&Boolean::Constant(true), &Boolean::Constant(true)) => {},
|
||||
|
(&Boolean::Constant(false), &Boolean::Constant(false)) => {},
|
||||
|
_ => unreachable!(),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_uint32_xor() {
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
|
||||
|
|
||||
|
for _ in 0..1000 {
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
|
||||
|
let a: u32 = rng.gen();
|
||||
|
let b: u32 = rng.gen();
|
||||
|
let c: u32 = rng.gen();
|
||||
|
|
||||
|
let mut expected = a ^ b ^ c;
|
||||
|
|
||||
|
let a_bit = UInt32::alloc(cs.ns(|| "a_bit"), Some(a)).unwrap();
|
||||
|
let b_bit = UInt32::constant(b);
|
||||
|
let c_bit = UInt32::alloc(cs.ns(|| "c_bit"), Some(c)).unwrap();
|
||||
|
|
||||
|
let r = a_bit.xor(cs.ns(|| "first xor"), &b_bit).unwrap();
|
||||
|
let r = r.xor(cs.ns(|| "second xor"), &c_bit).unwrap();
|
||||
|
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
|
||||
|
assert!(r.value == Some(expected));
|
||||
|
|
||||
|
for b in r.bits.iter() {
|
||||
|
match b {
|
||||
|
&Boolean::Is(ref b) => {
|
||||
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
|
},
|
||||
|
&Boolean::Not(ref b) => {
|
||||
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
|
},
|
||||
|
&Boolean::Constant(b) => {
|
||||
|
assert!(b == (expected & 1 == 1));
|
||||
|
},
|
||||
|
}
|
||||
|
|
||||
|
expected >>= 1;
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_uint32_addmany_constants() {
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
|
||||
|
for _ in 0..1000 {
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
|
||||
|
let a: u32 = rng.gen();
|
||||
|
let b: u32 = rng.gen();
|
||||
|
let c: u32 = rng.gen();
|
||||
|
|
||||
|
let a_bit = UInt32::constant(a);
|
||||
|
let b_bit = UInt32::constant(b);
|
||||
|
let c_bit = UInt32::constant(c);
|
||||
|
|
||||
|
let mut expected = a.wrapping_add(b).wrapping_add(c);
|
||||
|
|
||||
|
let r = UInt32::addmany(cs.ns(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
|
||||
|
|
||||
|
assert!(r.value == Some(expected));
|
||||
|
|
||||
|
for b in r.bits.iter() {
|
||||
|
match b {
|
||||
|
&Boolean::Is(_) => panic!(),
|
||||
|
&Boolean::Not(_) => panic!(),
|
||||
|
&Boolean::Constant(b) => {
|
||||
|
assert!(b == (expected & 1 == 1));
|
||||
|
},
|
||||
|
}
|
||||
|
|
||||
|
expected >>= 1;
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_uint32_addmany() {
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
|
||||
|
for _ in 0..1000 {
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
|
||||
|
let a: u32 = rng.gen();
|
||||
|
let b: u32 = rng.gen();
|
||||
|
let c: u32 = rng.gen();
|
||||
|
let d: u32 = rng.gen();
|
||||
|
|
||||
|
let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d);
|
||||
|
|
||||
|
let a_bit = UInt32::alloc(cs.ns(|| "a_bit"), Some(a)).unwrap();
|
||||
|
let b_bit = UInt32::constant(b);
|
||||
|
let c_bit = UInt32::constant(c);
|
||||
|
let d_bit = UInt32::alloc(cs.ns(|| "d_bit"), Some(d)).unwrap();
|
||||
|
|
||||
|
let r = a_bit.xor(cs.ns(|| "xor"), &b_bit).unwrap();
|
||||
|
let r = UInt32::addmany(cs.ns(|| "addition"), &[r, c_bit, d_bit]).unwrap();
|
||||
|
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
|
||||
|
assert!(r.value == Some(expected));
|
||||
|
|
||||
|
for b in r.bits.iter() {
|
||||
|
match b {
|
||||
|
&Boolean::Is(ref b) => {
|
||||
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
|
},
|
||||
|
&Boolean::Not(ref b) => {
|
||||
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
|
},
|
||||
|
&Boolean::Constant(_) => unreachable!(),
|
||||
|
}
|
||||
|
|
||||
|
expected >>= 1;
|
||||
|
}
|
||||
|
|
||||
|
// Flip a bit_gadget and see if the addition constraint still works
|
||||
|
if cs.get("addition/result bit_gadget 0/boolean").is_zero() {
|
||||
|
cs.set("addition/result bit_gadget 0/boolean", Field::one());
|
||||
|
} else {
|
||||
|
cs.set("addition/result bit_gadget 0/boolean", Field::zero());
|
||||
|
}
|
||||
|
|
||||
|
assert!(!cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_uint32_rotr() {
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
|
||||
|
let mut num = rng.gen();
|
||||
|
|
||||
|
let a = UInt32::constant(num);
|
||||
|
|
||||
|
for i in 0..32 {
|
||||
|
let b = a.rotr(i);
|
||||
|
|
||||
|
assert!(b.value.unwrap() == num);
|
||||
|
|
||||
|
let mut tmp = num;
|
||||
|
for b in &b.bits {
|
||||
|
match b {
|
||||
|
&Boolean::Constant(b) => {
|
||||
|
assert_eq!(b, tmp & 1 == 1);
|
||||
|
},
|
||||
|
_ => unreachable!(),
|
||||
|
}
|
||||
|
|
||||
|
tmp >>= 1;
|
||||
|
}
|
||||
|
|
||||
|
num = num.rotate_right(1);
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,399 @@ |
|||||
|
use algebra::{ToConstraintField, FpParameters, Field, PrimeField};
|
||||
|
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use crate::boolean::AllocatedBit;
|
||||
|
use crate::fields::fp::FpGadget;
|
||||
|
use crate::prelude::*;
|
||||
|
use crate::Assignment;
|
||||
|
use std::borrow::Borrow;
|
||||
|
|
||||
|
/// Represents an interpretation of 8 `Boolean` objects as an
|
||||
|
/// unsigned integer.
|
||||
|
#[derive(Clone, Debug)]
|
||||
|
pub struct UInt8 {
|
||||
|
// Least significant bit_gadget first
|
||||
|
pub(crate) bits: Vec<Boolean>,
|
||||
|
pub(crate) value: Option<u8>,
|
||||
|
}
|
||||
|
|
||||
|
impl UInt8 {
|
||||
|
pub fn get_value(&self) -> Option<u8> {
|
||||
|
self.value
|
||||
|
}
|
||||
|
|
||||
|
/// Construct a constant vector of `UInt8` from a vector of `u8`
|
||||
|
pub fn constant_vec(values: &[u8]) -> Vec<Self> {
|
||||
|
let mut result = Vec::new();
|
||||
|
for value in values {
|
||||
|
result.push(UInt8::constant(*value));
|
||||
|
}
|
||||
|
result
|
||||
|
}
|
||||
|
|
||||
|
/// Construct a constant `UInt8` from a `u8`
|
||||
|
pub fn constant(value: u8) -> Self {
|
||||
|
let mut bits = Vec::with_capacity(8);
|
||||
|
|
||||
|
let mut tmp = value;
|
||||
|
for _ in 0..8 {
|
||||
|
// If last bit is one, push one.
|
||||
|
if tmp & 1 == 1 {
|
||||
|
bits.push(Boolean::constant(true))
|
||||
|
} else {
|
||||
|
bits.push(Boolean::constant(false))
|
||||
|
}
|
||||
|
|
||||
|
tmp >>= 1;
|
||||
|
}
|
||||
|
|
||||
|
Self {
|
||||
|
bits,
|
||||
|
value: Some(value),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn alloc_vec<ConstraintF, CS, T>(mut cs: CS, values: &[T]) -> Result<Vec<Self>, SynthesisError>
|
||||
|
where
|
||||
|
ConstraintF: Field,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
T: Into<Option<u8>> + Copy,
|
||||
|
{
|
||||
|
let mut output_vec = Vec::with_capacity(values.len());
|
||||
|
for (i, value) in values.into_iter().enumerate() {
|
||||
|
let byte: Option<u8> = Into::into(*value);
|
||||
|
let alloc_byte = Self::alloc(&mut cs.ns(|| format!("byte_{}", i)), || byte.get())?;
|
||||
|
output_vec.push(alloc_byte);
|
||||
|
}
|
||||
|
Ok(output_vec)
|
||||
|
}
|
||||
|
|
||||
|
/// Allocates a vector of `u8`'s by first converting (chunks of) them to
|
||||
|
/// `ConstraintF` elements, (thus reducing the number of input allocations), and
|
||||
|
/// then converts this list of `ConstraintF` gadgets back into bytes.
|
||||
|
pub fn alloc_input_vec<ConstraintF, CS>(mut cs: CS, values: &[u8]) -> Result<Vec<Self>, SynthesisError>
|
||||
|
where
|
||||
|
ConstraintF: PrimeField,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
{
|
||||
|
let values_len = values.len();
|
||||
|
let field_elements: Vec<ConstraintF> = ToConstraintField::<ConstraintF>::to_field_elements(values).unwrap();
|
||||
|
|
||||
|
let max_size = 8 * (ConstraintF::Params::CAPACITY / 8) as usize;
|
||||
|
let mut allocated_bits = Vec::new();
|
||||
|
for (i, field_element) in field_elements.into_iter().enumerate() {
|
||||
|
let fe = FpGadget::alloc_input(&mut cs.ns(|| format!("Field element {}", i)), || {
|
||||
|
Ok(field_element)
|
||||
|
})?;
|
||||
|
let mut fe_bits = fe.to_bits(cs.ns(|| format!("Convert fe to bits {}", i)))?;
|
||||
|
// FpGadget::to_bits outputs a big-endian binary representation of
|
||||
|
// fe_gadget's value, so we have to reverse it to get the little-endian
|
||||
|
// form.
|
||||
|
fe_bits.reverse();
|
||||
|
|
||||
|
// Remove the most significant bit, because we know it should be zero
|
||||
|
// because `values.to_field_elements()` only
|
||||
|
// packs field elements up to the penultimate bit.
|
||||
|
// That is, the most significant bit (`ConstraintF::NUM_BITS`-th bit) is
|
||||
|
// unset, so we can just pop it off.
|
||||
|
allocated_bits.extend_from_slice(&fe_bits[0..max_size]);
|
||||
|
}
|
||||
|
|
||||
|
// Chunk up slices of 8 bit into bytes.
|
||||
|
Ok(allocated_bits[0..8 * values_len]
|
||||
|
.chunks(8)
|
||||
|
.map(Self::from_bits_le)
|
||||
|
.collect())
|
||||
|
}
|
||||
|
|
||||
|
/// Turns this `UInt8` into its little-endian byte order representation.
|
||||
|
/// LSB-first means that we can easily get the corresponding field element
|
||||
|
/// via double and add.
|
||||
|
pub fn into_bits_le(&self) -> Vec<Boolean> {
|
||||
|
self.bits.iter().cloned().collect()
|
||||
|
}
|
||||
|
|
||||
|
/// Converts a little-endian byte order representation of bits into a
|
||||
|
/// `UInt8`.
|
||||
|
pub fn from_bits_le(bits: &[Boolean]) -> Self {
|
||||
|
assert_eq!(bits.len(), 8);
|
||||
|
|
||||
|
let bits = bits.to_vec();
|
||||
|
|
||||
|
let mut value = Some(0u8);
|
||||
|
for b in bits.iter().rev() {
|
||||
|
value.as_mut().map(|v| *v <<= 1);
|
||||
|
|
||||
|
match *b {
|
||||
|
Boolean::Constant(b) => {
|
||||
|
if b {
|
||||
|
value.as_mut().map(|v| *v |= 1);
|
||||
|
}
|
||||
|
},
|
||||
|
Boolean::Is(ref b) => match b.get_value() {
|
||||
|
Some(true) => {
|
||||
|
value.as_mut().map(|v| *v |= 1);
|
||||
|
},
|
||||
|
Some(false) => {},
|
||||
|
None => value = None,
|
||||
|
},
|
||||
|
Boolean::Not(ref b) => match b.get_value() {
|
||||
|
Some(false) => {
|
||||
|
value.as_mut().map(|v| *v |= 1);
|
||||
|
},
|
||||
|
Some(true) => {},
|
||||
|
None => value = None,
|
||||
|
},
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
Self { value, bits }
|
||||
|
}
|
||||
|
|
||||
|
/// XOR this `UInt8` with another `UInt8`
|
||||
|
pub fn xor<ConstraintF, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
ConstraintF: Field,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
{
|
||||
|
let new_value = match (self.value, other.value) {
|
||||
|
(Some(a), Some(b)) => Some(a ^ b),
|
||||
|
_ => None,
|
||||
|
};
|
||||
|
|
||||
|
let bits = self
|
||||
|
.bits
|
||||
|
.iter()
|
||||
|
.zip(other.bits.iter())
|
||||
|
.enumerate()
|
||||
|
.map(|(i, (a, b))| Boolean::xor(cs.ns(|| format!("xor of bit_gadget {}", i)), a, b))
|
||||
|
.collect::<Result<_, _>>()?;
|
||||
|
|
||||
|
Ok(Self {
|
||||
|
bits,
|
||||
|
value: new_value,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl PartialEq for UInt8 {
|
||||
|
fn eq(&self, other: &Self) -> bool {
|
||||
|
!self.value.is_none() && !other.value.is_none() && self.value == other.value
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl Eq for UInt8 {}
|
||||
|
|
||||
|
impl<ConstraintF: Field> ConditionalEqGadget<ConstraintF> for UInt8 {
|
||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
for (i, (a, b)) in self.bits.iter().zip(&other.bits).enumerate() {
|
||||
|
a.conditional_enforce_equal(
|
||||
|
&mut cs.ns(|| format!("UInt8 equality check for {}-th bit", i)),
|
||||
|
b,
|
||||
|
condition,
|
||||
|
)?;
|
||||
|
}
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
8 * <Boolean as ConditionalEqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field> EqGadget<ConstraintF> for UInt8 {}
|
||||
|
|
||||
|
impl<ConstraintF: Field> AllocGadget<u8, ConstraintF> for UInt8 {
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<u8>,
|
||||
|
{
|
||||
|
let value = value_gen().map(|val| *val.borrow());
|
||||
|
let values = match value {
|
||||
|
Ok(mut val) => {
|
||||
|
let mut v = Vec::with_capacity(8);
|
||||
|
|
||||
|
for _ in 0..8 {
|
||||
|
v.push(Some(val & 1 == 1));
|
||||
|
val >>= 1;
|
||||
|
}
|
||||
|
|
||||
|
v
|
||||
|
},
|
||||
|
_ => vec![None; 8],
|
||||
|
};
|
||||
|
|
||||
|
let bits = values
|
||||
|
.into_iter()
|
||||
|
.enumerate()
|
||||
|
.map(|(i, v)| {
|
||||
|
Ok(Boolean::from(AllocatedBit::alloc(
|
||||
|
&mut cs.ns(|| format!("allocated bit_gadget {}", i)),
|
||||
|
|| v.ok_or(SynthesisError::AssignmentMissing),
|
||||
|
)?))
|
||||
|
})
|
||||
|
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
||||
|
|
||||
|
Ok(Self {
|
||||
|
bits,
|
||||
|
value: value.ok(),
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<u8>,
|
||||
|
{
|
||||
|
let value = value_gen().map(|val| *val.borrow());
|
||||
|
let values = match value {
|
||||
|
Ok(mut val) => {
|
||||
|
let mut v = Vec::with_capacity(8);
|
||||
|
for _ in 0..8 {
|
||||
|
v.push(Some(val & 1 == 1));
|
||||
|
val >>= 1;
|
||||
|
}
|
||||
|
|
||||
|
v
|
||||
|
},
|
||||
|
_ => vec![None; 8],
|
||||
|
};
|
||||
|
|
||||
|
let bits = values
|
||||
|
.into_iter()
|
||||
|
.enumerate()
|
||||
|
.map(|(i, v)| {
|
||||
|
Ok(Boolean::from(AllocatedBit::alloc_input(
|
||||
|
&mut cs.ns(|| format!("allocated bit_gadget {}", i)),
|
||||
|
|| v.ok_or(SynthesisError::AssignmentMissing),
|
||||
|
)?))
|
||||
|
})
|
||||
|
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
||||
|
|
||||
|
Ok(Self {
|
||||
|
bits,
|
||||
|
value: value.ok(),
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use super::UInt8;
|
||||
|
use crate::{
|
||||
|
bits::boolean::Boolean, test_constraint_system::TestConstraintSystem, utils::AllocGadget,
|
||||
|
};
|
||||
|
use algebra::fields::bls12_381::Fr;
|
||||
|
use rand::{Rng, SeedableRng, XorShiftRng};
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_uint8_from_bits_to_bits() {
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
let byte_val = 0b01110001;
|
||||
|
let byte = UInt8::alloc(cs.ns(|| "alloc value"), || Ok(byte_val)).unwrap();
|
||||
|
let bits = byte.into_bits_le();
|
||||
|
for (i, bit) in bits.iter().enumerate() {
|
||||
|
assert_eq!(bit.get_value().unwrap(), (byte_val >> i) & 1 == 1)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_uint8_alloc_input_vec() {
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
let byte_vals = (64u8..128u8).into_iter().collect::<Vec<_>>();
|
||||
|
let bytes = UInt8::alloc_input_vec(cs.ns(|| "alloc value"), &byte_vals).unwrap();
|
||||
|
for (native_byte, gadget_byte) in byte_vals.into_iter().zip(bytes) {
|
||||
|
let bits = gadget_byte.into_bits_le();
|
||||
|
for (i, bit) in bits.iter().enumerate() {
|
||||
|
assert_eq!(bit.get_value().unwrap(), (native_byte >> i) & 1 == 1)
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_uint8_from_bits() {
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
|
||||
|
|
||||
|
for _ in 0..1000 {
|
||||
|
let v = (0..8)
|
||||
|
.map(|_| Boolean::constant(rng.gen()))
|
||||
|
.collect::<Vec<_>>();
|
||||
|
|
||||
|
let b = UInt8::from_bits_le(&v);
|
||||
|
|
||||
|
for (i, bit_gadget) in b.bits.iter().enumerate() {
|
||||
|
match bit_gadget {
|
||||
|
&Boolean::Constant(bit_gadget) => {
|
||||
|
assert!(bit_gadget == ((b.value.unwrap() >> i) & 1 == 1));
|
||||
|
},
|
||||
|
_ => unreachable!(),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
let expected_to_be_same = b.into_bits_le();
|
||||
|
|
||||
|
for x in v.iter().zip(expected_to_be_same.iter()) {
|
||||
|
match x {
|
||||
|
(&Boolean::Constant(true), &Boolean::Constant(true)) => {},
|
||||
|
(&Boolean::Constant(false), &Boolean::Constant(false)) => {},
|
||||
|
_ => unreachable!(),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_uint8_xor() {
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
|
||||
|
|
||||
|
for _ in 0..1000 {
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
|
||||
|
let a: u8 = rng.gen();
|
||||
|
let b: u8 = rng.gen();
|
||||
|
let c: u8 = rng.gen();
|
||||
|
|
||||
|
let mut expected = a ^ b ^ c;
|
||||
|
|
||||
|
let a_bit = UInt8::alloc(cs.ns(|| "a_bit"), || Ok(a)).unwrap();
|
||||
|
let b_bit = UInt8::constant(b);
|
||||
|
let c_bit = UInt8::alloc(cs.ns(|| "c_bit"), || Ok(c)).unwrap();
|
||||
|
|
||||
|
let r = a_bit.xor(cs.ns(|| "first xor"), &b_bit).unwrap();
|
||||
|
let r = r.xor(cs.ns(|| "second xor"), &c_bit).unwrap();
|
||||
|
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
|
||||
|
assert!(r.value == Some(expected));
|
||||
|
|
||||
|
for b in r.bits.iter() {
|
||||
|
match b {
|
||||
|
&Boolean::Is(ref b) => {
|
||||
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
|
},
|
||||
|
&Boolean::Not(ref b) => {
|
||||
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
|
},
|
||||
|
&Boolean::Constant(b) => {
|
||||
|
assert!(b == (expected & 1 == 1));
|
||||
|
},
|
||||
|
}
|
||||
|
|
||||
|
expected >>= 1;
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,137 @@ |
|||||
|
use crate::prelude::*;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use algebra::Field;
|
||||
|
|
||||
|
/// If `condition == 1`, then enforces that `self` and `other` are equal;
|
||||
|
/// otherwise, it doesn't enforce anything.
|
||||
|
pub trait ConditionalEqGadget<ConstraintF: Field>: Eq {
|
||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> Result<(), SynthesisError>;
|
||||
|
|
||||
|
fn cost() -> usize;
|
||||
|
}
|
||||
|
impl<T: ConditionalEqGadget<ConstraintF>, ConstraintF: Field> ConditionalEqGadget<ConstraintF> for [T] {
|
||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
for (i, (a, b)) in self.iter().zip(other.iter()).enumerate() {
|
||||
|
let mut cs = cs.ns(|| format!("Iteration {}", i));
|
||||
|
a.conditional_enforce_equal(&mut cs, b, condition)?;
|
||||
|
}
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
unimplemented!()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub trait EqGadget<ConstraintF: Field>: Eq |
||||
|
where
|
||||
|
Self: ConditionalEqGadget<ConstraintF>,
|
||||
|
{
|
||||
|
fn enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
self.conditional_enforce_equal(cs, other, &Boolean::constant(true))
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
<Self as ConditionalEqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<T: EqGadget<ConstraintF>, ConstraintF: Field> EqGadget<ConstraintF> for [T] {}
|
||||
|
|
||||
|
pub trait NEqGadget<ConstraintF: Field>: Eq {
|
||||
|
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<(), SynthesisError>;
|
||||
|
|
||||
|
fn cost() -> usize;
|
||||
|
}
|
||||
|
|
||||
|
pub trait OrEqualsGadget<ConstraintF: Field>
|
||||
|
where
|
||||
|
Self: Sized,
|
||||
|
{
|
||||
|
fn enforce_equal_or<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
cond: &Boolean,
|
||||
|
var: &Self,
|
||||
|
first: &Self,
|
||||
|
second: &Self,
|
||||
|
) -> Result<(), SynthesisError>;
|
||||
|
|
||||
|
fn cost() -> usize;
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field, T: Sized + ConditionalOrEqualsGadget<ConstraintF>> OrEqualsGadget<ConstraintF> for T {
|
||||
|
fn enforce_equal_or<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
cond: &Boolean,
|
||||
|
var: &Self,
|
||||
|
first: &Self,
|
||||
|
second: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
Self::conditional_enforce_equal_or(cs, cond, var, first, second, &Boolean::Constant(true))
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
<Self as ConditionalOrEqualsGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub trait ConditionalOrEqualsGadget<ConstraintF: Field>
|
||||
|
where
|
||||
|
Self: Sized,
|
||||
|
{
|
||||
|
fn conditional_enforce_equal_or<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
cond: &Boolean,
|
||||
|
var: &Self,
|
||||
|
first: &Self,
|
||||
|
second: &Self,
|
||||
|
should_enforce: &Boolean,
|
||||
|
) -> Result<(), SynthesisError>;
|
||||
|
|
||||
|
fn cost() -> usize;
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field, T: Sized + ConditionalEqGadget<ConstraintF> + CondSelectGadget<ConstraintF>>
|
||||
|
ConditionalOrEqualsGadget<ConstraintF> for T
|
||||
|
{
|
||||
|
fn conditional_enforce_equal_or<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
cond: &Boolean,
|
||||
|
var: &Self,
|
||||
|
first: &Self,
|
||||
|
second: &Self,
|
||||
|
should_enforce: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
let match_opt = Self::conditionally_select(
|
||||
|
&mut cs.ns(|| "conditional_select_in_or"),
|
||||
|
cond,
|
||||
|
first,
|
||||
|
second,
|
||||
|
)?;
|
||||
|
var.conditional_enforce_equal(&mut cs.ns(|| "equals_in_or"), &match_opt, should_enforce)
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
<Self as ConditionalEqGadget<ConstraintF>>::cost() + <Self as CondSelectGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
@ -0,0 +1,10 @@ |
|||||
|
use algebra::{
|
||||
|
fields::bls12_377::{Fq, Fq12Parameters, Fq2Parameters, Fq6Parameters},
|
||||
|
};
|
||||
|
|
||||
|
use super::{fp::FpGadget, fp12::Fp12Gadget, fp2::Fp2Gadget, fp6_3over2::Fp6Gadget};
|
||||
|
|
||||
|
pub type FqGadget = FpGadget<Fq>;
|
||||
|
pub type Fq2Gadget = Fp2Gadget<Fq2Parameters, Fq>;
|
||||
|
pub type Fq6Gadget = Fp6Gadget<Fq6Parameters, Fq>;
|
||||
|
pub type Fq12Gadget = Fp12Gadget<Fq12Parameters, Fq>;
|
@ -0,0 +1,4 @@ |
|||||
|
use algebra::fields::edwards_bls12::fq::Fq;
|
||||
|
use crate::fields::fp::FpGadget;
|
||||
|
|
||||
|
pub type FqGadget = FpGadget<Fq>;
|
@ -0,0 +1,4 @@ |
|||||
|
use algebra::fields::edwards_sw6::fq::Fq;
|
||||
|
use crate::fields::fp::FpGadget;
|
||||
|
|
||||
|
pub type FqGadget = FpGadget<Fq>;
|
@ -0,0 +1,571 @@ |
|||||
|
use algebra::{bytes::ToBytes, FpParameters, PrimeField};
|
||||
|
use r1cs_core::{ConstraintSystem, LinearCombination, SynthesisError};
|
||||
|
|
||||
|
use std::borrow::Borrow;
|
||||
|
|
||||
|
use super::FieldGadget;
|
||||
|
use crate::{
|
||||
|
boolean::{AllocatedBit, Boolean},
|
||||
|
uint8::UInt8,
|
||||
|
};
|
||||
|
|
||||
|
use crate::prelude::*;
|
||||
|
|
||||
|
use crate::{
|
||||
|
Assignment,
|
||||
|
ConstraintVar::{self, *},
|
||||
|
};
|
||||
|
|
||||
|
#[derive(Debug)]
|
||||
|
pub struct FpGadget<F: PrimeField> {
|
||||
|
pub value: Option<F>,
|
||||
|
pub variable: ConstraintVar<F>,
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> FpGadget<F> {
|
||||
|
#[inline]
|
||||
|
pub fn from<CS: ConstraintSystem<F>>(mut cs: CS, value: &F) -> Self {
|
||||
|
Self::alloc(cs.ns(|| "from"), || Ok(*value)).unwrap()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> FieldGadget<F, F> for FpGadget<F> {
|
||||
|
type Variable = ConstraintVar<F>;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn get_value(&self) -> Option<F> {
|
||||
|
self.value
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn get_variable(&self) -> Self::Variable {
|
||||
|
self.variable.clone()
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn zero<CS: ConstraintSystem<F>>(_cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let value = Some(F::zero());
|
||||
|
Ok(FpGadget {
|
||||
|
value,
|
||||
|
variable: ConstraintVar::zero(),
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn one<CS: ConstraintSystem<F>>(_cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let value = Some(F::one());
|
||||
|
Ok(FpGadget {
|
||||
|
value,
|
||||
|
variable: CS::one().into(),
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
mut _cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let value = match (self.value, other.value) {
|
||||
|
(Some(val1), Some(val2)) => Some(val1 + &val2),
|
||||
|
(..) => None,
|
||||
|
};
|
||||
|
|
||||
|
Ok(FpGadget {
|
||||
|
value,
|
||||
|
variable: &self.variable + &other.variable,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
fn double<CS: ConstraintSystem<F>>(&self, _cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let value = self.value.map(|val| val.double());
|
||||
|
let mut variable = self.variable.clone();
|
||||
|
variable.double_in_place();
|
||||
|
Ok(FpGadget { value, variable })
|
||||
|
}
|
||||
|
|
||||
|
fn double_in_place<CS: ConstraintSystem<F>>(
|
||||
|
&mut self,
|
||||
|
_cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.value.as_mut().map(|val| val.double_in_place());
|
||||
|
self.variable.double_in_place();
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
mut _cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let value = match (self.value, other.value) {
|
||||
|
(Some(val1), Some(val2)) => Some(val1 - &val2),
|
||||
|
(..) => None,
|
||||
|
};
|
||||
|
|
||||
|
Ok(FpGadget {
|
||||
|
value,
|
||||
|
variable: &self.variable - &other.variable,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn negate<CS: ConstraintSystem<F>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let mut result = self.clone();
|
||||
|
result.negate_in_place(cs)?;
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn negate_in_place<CS: ConstraintSystem<F>>(
|
||||
|
&mut self,
|
||||
|
_cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.value.as_mut().map(|val| *val = -(*val));
|
||||
|
self.variable.negate_in_place();
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn mul<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let product = Self::alloc(cs.ns(|| "mul"), || {
|
||||
|
Ok(self.value.get()? * &other.value.get()?)
|
||||
|
})?;
|
||||
|
cs.enforce(
|
||||
|
|| "mul_constraint",
|
||||
|
|lc| &self.variable + lc,
|
||||
|
|lc| &other.variable + lc,
|
||||
|
|lc| &product.variable + lc,
|
||||
|
);
|
||||
|
Ok(product)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add_constant<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
_cs: CS,
|
||||
|
other: &F,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let value = self.value.map(|val| val + other);
|
||||
|
Ok(FpGadget {
|
||||
|
value,
|
||||
|
variable: self.variable.clone() + (*other, CS::one()),
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add_constant_in_place<CS: ConstraintSystem<F>>(
|
||||
|
&mut self,
|
||||
|
_cs: CS,
|
||||
|
other: &F,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.value.as_mut().map(|val| *val += other);
|
||||
|
self.variable += (*other, CS::one());
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn mul_by_constant<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
other: &F,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut result = self.clone();
|
||||
|
result.mul_by_constant_in_place(cs, other)?;
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn mul_by_constant_in_place<CS: ConstraintSystem<F>>(
|
||||
|
&mut self,
|
||||
|
mut _cs: CS,
|
||||
|
other: &F,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.value.as_mut().map(|val| *val *= other);
|
||||
|
self.variable *= *other;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn inverse<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let inverse = Self::alloc(cs.ns(|| "inverse"), || {
|
||||
|
let result = self.value.get()?;
|
||||
|
let inv = result.inverse().expect("Inverse doesn't exist!");
|
||||
|
Ok(inv)
|
||||
|
})?;
|
||||
|
|
||||
|
let one = CS::one();
|
||||
|
cs.enforce(
|
||||
|
|| "inv_constraint",
|
||||
|
|lc| &self.variable + lc,
|
||||
|
|lc| &inverse.variable + lc,
|
||||
|
|lc| lc + one,
|
||||
|
);
|
||||
|
Ok(inverse)
|
||||
|
}
|
||||
|
|
||||
|
fn frobenius_map<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
_: CS,
|
||||
|
_: usize,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
Ok(self.clone())
|
||||
|
}
|
||||
|
|
||||
|
fn frobenius_map_in_place<CS: ConstraintSystem<F>>(
|
||||
|
&mut self,
|
||||
|
_: CS,
|
||||
|
_: usize,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn mul_equals<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
result: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
cs.enforce(
|
||||
|
|| "mul_constraint",
|
||||
|
|lc| &self.variable + lc,
|
||||
|
|lc| &other.variable + lc,
|
||||
|
|lc| &result.variable + lc,
|
||||
|
);
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn square_equals<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
result: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
cs.enforce(
|
||||
|
|| "sqr_constraint",
|
||||
|
|lc| &self.variable + lc,
|
||||
|
|lc| &self.variable + lc,
|
||||
|
|lc| &result.variable + lc,
|
||||
|
);
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_mul() -> usize {
|
||||
|
1
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_inv() -> usize {
|
||||
|
1
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> PartialEq for FpGadget<F> {
|
||||
|
fn eq(&self, other: &Self) -> bool {
|
||||
|
!self.value.is_none() && !other.value.is_none() && self.value == other.value
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> Eq for FpGadget<F> {}
|
||||
|
|
||||
|
impl<F: PrimeField> EqGadget<F> for FpGadget<F> {}
|
||||
|
|
||||
|
impl<F: PrimeField> ConditionalEqGadget<F> for FpGadget<F> {
|
||||
|
#[inline]
|
||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
let difference = self.sub(cs.ns(|| "difference"), other)?;
|
||||
|
let one = CS::one();
|
||||
|
let one_const = F::one();
|
||||
|
cs.enforce(
|
||||
|
|| "conditional_equals",
|
||||
|
|lc| &difference.variable + lc,
|
||||
|
|lc| lc + &condition.lc(one, one_const),
|
||||
|
|lc| lc,
|
||||
|
);
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
1
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> NEqGadget<F> for FpGadget<F> {
|
||||
|
#[inline]
|
||||
|
fn enforce_not_equal<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
let a_minus_b = self.sub(cs.ns(|| "A - B"), other)?;
|
||||
|
a_minus_b.inverse(cs.ns(|| "Enforce inverse exists"))?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
1
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> ToBitsGadget<F> for FpGadget<F> {
|
||||
|
/// Outputs the binary representation of the value in `self` in *big-endian*
|
||||
|
/// form.
|
||||
|
fn to_bits<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
let num_bits = F::Params::MODULUS_BITS;
|
||||
|
use algebra::BitIterator;
|
||||
|
let bit_values = match self.value {
|
||||
|
Some(value) => {
|
||||
|
let mut field_char = BitIterator::new(F::characteristic());
|
||||
|
let mut tmp = Vec::with_capacity(num_bits as usize);
|
||||
|
let mut found_one = false;
|
||||
|
for b in BitIterator::new(value.into_repr()) {
|
||||
|
// Skip leading bits
|
||||
|
found_one |= field_char.next().unwrap();
|
||||
|
if !found_one {
|
||||
|
continue;
|
||||
|
}
|
||||
|
|
||||
|
tmp.push(Some(b));
|
||||
|
}
|
||||
|
|
||||
|
assert_eq!(tmp.len(), num_bits as usize);
|
||||
|
|
||||
|
tmp
|
||||
|
},
|
||||
|
None => vec![None; num_bits as usize],
|
||||
|
};
|
||||
|
|
||||
|
let mut bits = vec![];
|
||||
|
for (i, b) in bit_values.into_iter().enumerate() {
|
||||
|
bits.push(AllocatedBit::alloc(cs.ns(|| format!("bit {}", i)), || {
|
||||
|
b.get()
|
||||
|
})?);
|
||||
|
}
|
||||
|
|
||||
|
let mut lc = LinearCombination::zero();
|
||||
|
let mut coeff = F::one();
|
||||
|
|
||||
|
for bit in bits.iter().rev() {
|
||||
|
lc = lc + (coeff, bit.get_variable());
|
||||
|
|
||||
|
coeff.double_in_place();
|
||||
|
}
|
||||
|
|
||||
|
lc = &self.variable - lc;
|
||||
|
|
||||
|
cs.enforce(|| "unpacking_constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
|
||||
|
Ok(bits.into_iter().map(Boolean::from).collect())
|
||||
|
}
|
||||
|
|
||||
|
fn to_bits_strict<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
let bits = self.to_bits(&mut cs)?;
|
||||
|
Boolean::enforce_in_field::<_, _, F>(&mut cs, &bits)?;
|
||||
|
|
||||
|
Ok(bits)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> ToBytesGadget<F> for FpGadget<F> {
|
||||
|
fn to_bytes<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let byte_values = match self.value {
|
||||
|
Some(value) => to_bytes![&value.into_repr()]?
|
||||
|
.into_iter()
|
||||
|
.map(Some)
|
||||
|
.collect::<Vec<_>>(),
|
||||
|
None => {
|
||||
|
let default = F::default();
|
||||
|
let default_len = to_bytes![&default].unwrap().len();
|
||||
|
vec![None; default_len]
|
||||
|
},
|
||||
|
};
|
||||
|
|
||||
|
let bytes = UInt8::alloc_vec(cs.ns(|| "Alloc bytes"), &byte_values)?;
|
||||
|
|
||||
|
let mut lc = LinearCombination::zero();
|
||||
|
let mut coeff = F::one();
|
||||
|
|
||||
|
for bit in bytes
|
||||
|
.iter()
|
||||
|
.flat_map(|byte_gadget| byte_gadget.bits.clone())
|
||||
|
{
|
||||
|
match bit {
|
||||
|
Boolean::Is(bit) => {
|
||||
|
lc = lc + (coeff, bit.get_variable());
|
||||
|
coeff.double_in_place();
|
||||
|
},
|
||||
|
Boolean::Constant(_) | Boolean::Not(_) => unreachable!(),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
lc = &self.variable - lc;
|
||||
|
|
||||
|
cs.enforce(|| "unpacking_constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
|
||||
|
Ok(bytes)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<F>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let bytes = self.to_bytes(&mut cs)?;
|
||||
|
Boolean::enforce_in_field::<_, _, F>(
|
||||
|
&mut cs,
|
||||
|
&bytes.iter()
|
||||
|
.flat_map(|byte_gadget| byte_gadget.into_bits_le())
|
||||
|
// This reverse maps the bits into big-endian form, as required by `enforce_in_field`.
|
||||
|
.rev()
|
||||
|
.collect::<Vec<_>>(),
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(bytes)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> CondSelectGadget<F> for FpGadget<F> {
|
||||
|
#[inline]
|
||||
|
fn conditionally_select<CS: ConstraintSystem<F>>(
|
||||
|
mut cs: CS,
|
||||
|
cond: &Boolean,
|
||||
|
first: &Self,
|
||||
|
second: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
if let Boolean::Constant(cond) = *cond {
|
||||
|
if cond {
|
||||
|
Ok(first.clone())
|
||||
|
} else {
|
||||
|
Ok(second.clone())
|
||||
|
}
|
||||
|
} else {
|
||||
|
let result = Self::alloc(cs.ns(|| ""), || {
|
||||
|
cond.get_value()
|
||||
|
.and_then(|cond| if cond { first } else { second }.get_value())
|
||||
|
.get()
|
||||
|
})?;
|
||||
|
// a = self; b = other; c = cond;
|
||||
|
//
|
||||
|
// r = c * a + (1 - c) * b
|
||||
|
// r = b + c * (a - b)
|
||||
|
// c * (a - b) = r - b
|
||||
|
let one = CS::one();
|
||||
|
cs.enforce(
|
||||
|
|| "conditionally_select",
|
||||
|
|_| cond.lc(one, F::one()),
|
||||
|
|lc| (&first.variable - &second.variable) + lc,
|
||||
|
|lc| (&result.variable - &second.variable) + lc,
|
||||
|
);
|
||||
|
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
1
|
||||
|
}
|
||||
|
}
|
||||
|
/// Uses two bits to perform a lookup into a table
|
||||
|
/// `b` is little-endian: `b[0]` is LSB.
|
||||
|
impl<F: PrimeField> TwoBitLookupGadget<F> for FpGadget<F> {
|
||||
|
type TableConstant = F;
|
||||
|
fn two_bit_lookup<CS: ConstraintSystem<F>>(
|
||||
|
mut cs: CS,
|
||||
|
b: &[Boolean],
|
||||
|
c: &[Self::TableConstant],
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
debug_assert!(b.len() == 2);
|
||||
|
debug_assert!(c.len() == 4);
|
||||
|
|
||||
|
let result = Self::alloc(cs.ns(|| "Allocate lookup result"), || {
|
||||
|
match (b[0].get_value().get()?, b[1].get_value().get()?) {
|
||||
|
(false, false) => Ok(c[0]),
|
||||
|
(false, true) => Ok(c[2]),
|
||||
|
(true, false) => Ok(c[1]),
|
||||
|
(true, true) => Ok(c[3]),
|
||||
|
}
|
||||
|
})?;
|
||||
|
let one = CS::one();
|
||||
|
cs.enforce(
|
||||
|
|| "Enforce lookup",
|
||||
|
|lc| lc + b[1].lc(one, c[3] - &c[2] - &c[1] + &c[0]) + (c[1] - &c[0], one),
|
||||
|
|lc| lc + b[0].lc(one, F::one()),
|
||||
|
|lc| result.get_variable() + lc + (-c[0], one) + b[1].lc(one, c[0] - &c[2]),
|
||||
|
);
|
||||
|
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
1
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> Clone for FpGadget<F> {
|
||||
|
fn clone(&self) -> Self {
|
||||
|
Self {
|
||||
|
value: self.value.clone(),
|
||||
|
variable: self.variable.clone(),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> AllocGadget<F, F> for FpGadget<F> {
|
||||
|
#[inline]
|
||||
|
fn alloc<FN, T, CS: ConstraintSystem<F>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: FN,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<F>,
|
||||
|
{
|
||||
|
let mut value = None;
|
||||
|
let variable = cs.alloc(
|
||||
|
|| "alloc",
|
||||
|
|| {
|
||||
|
let tmp = *value_gen()?.borrow();
|
||||
|
value = Some(tmp);
|
||||
|
Ok(tmp)
|
||||
|
},
|
||||
|
)?;
|
||||
|
Ok(FpGadget {
|
||||
|
value,
|
||||
|
variable: Var(variable),
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn alloc_input<FN, T, CS: ConstraintSystem<F>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: FN,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<F>,
|
||||
|
{
|
||||
|
let mut value = None;
|
||||
|
let variable = cs.alloc_input(
|
||||
|
|| "alloc",
|
||||
|
|| {
|
||||
|
let tmp = *value_gen()?.borrow();
|
||||
|
value = Some(tmp);
|
||||
|
Ok(tmp)
|
||||
|
},
|
||||
|
)?;
|
||||
|
Ok(FpGadget {
|
||||
|
value,
|
||||
|
variable: Var(variable),
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,854 @@ |
|||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use algebra::{
|
||||
|
PrimeField, Field,
|
||||
|
fields::{
|
||||
|
fp12_2over3over2::{Fp12, Fp12Parameters},
|
||||
|
fp6_3over2::{Fp6, Fp6Parameters},
|
||||
|
Fp2Parameters,
|
||||
|
},
|
||||
|
BitIterator,
|
||||
|
};
|
||||
|
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
|
||||
|
use crate::prelude::*;
|
||||
|
use crate::Assignment;
|
||||
|
|
||||
|
type Fp2Gadget<P, ConstraintF> =
|
||||
|
super::fp2::Fp2Gadget<<<P as Fp12Parameters>::Fp6Params as Fp6Parameters>::Fp2Params, ConstraintF>;
|
||||
|
type Fp6Gadget<P, ConstraintF> = super::fp6_3over2::Fp6Gadget<<P as Fp12Parameters>::Fp6Params, ConstraintF>;
|
||||
|
type Fp6GadgetVariable<P, ConstraintF> =
|
||||
|
<Fp6Gadget<P, ConstraintF> as FieldGadget<Fp6<<P as Fp12Parameters>::Fp6Params>, ConstraintF>>::Variable;
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Debug(bound = "ConstraintF: PrimeField"))]
|
||||
|
#[must_use]
|
||||
|
pub struct Fp12Gadget<P, ConstraintF: PrimeField>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
pub c0: Fp6Gadget<P, ConstraintF>,
|
||||
|
pub c1: Fp6Gadget<P, ConstraintF>,
|
||||
|
#[derivative(Debug = "ignore")]
|
||||
|
_params: PhantomData<P>,
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
pub fn new(c0: Fp6Gadget<P, ConstraintF>, c1: Fp6Gadget<P, ConstraintF>) -> Self {
|
||||
|
Self {
|
||||
|
c0,
|
||||
|
c1,
|
||||
|
_params: PhantomData,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
/// Multiply by quadratic nonresidue v.
|
||||
|
#[inline]
|
||||
|
pub(crate) fn mul_fp6_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
fe: &Fp6Gadget<P, ConstraintF>,
|
||||
|
) -> Result<Fp6Gadget<P, ConstraintF>, SynthesisError> {
|
||||
|
let new_c0 = Fp6Gadget::<P, ConstraintF>::mul_fp2_gadget_by_nonresidue(cs, &fe.c2)?;
|
||||
|
let new_c1 = fe.c0.clone();
|
||||
|
let new_c2 = fe.c1.clone();
|
||||
|
Ok(Fp6Gadget::<P, ConstraintF>::new(new_c0, new_c1, new_c2))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
pub fn conjugate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c1.negate_in_place(cs)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
/// Multiplies by an element of the form (c0 = (c0, c1, 0), c1 = (0, d1, 0))
|
||||
|
#[inline]
|
||||
|
pub fn mul_by_014<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
c0: &Fp2Gadget<P, ConstraintF>,
|
||||
|
c1: &Fp2Gadget<P, ConstraintF>,
|
||||
|
d1: &Fp2Gadget<P, ConstraintF>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let v0 = self.c0.mul_by_c0_c1_0(cs.ns(|| "v0"), &c0, &c1)?;
|
||||
|
let v1 = self.c1.mul_by_0_c1_0(cs.ns(|| "v1"), &d1)?;
|
||||
|
let new_c0 = Self::mul_fp6_by_nonresidue(cs.ns(|| "first mul_by_nr"), &v1)?
|
||||
|
.add(cs.ns(|| "v0 + nonresidue * v1"), &v0)?;
|
||||
|
|
||||
|
let c1 = {
|
||||
|
let tmp = c1.add(cs.ns(|| "c1 + d1"), &d1)?;
|
||||
|
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
a0_plus_a1
|
||||
|
.mul_by_c0_c1_0(cs.ns(|| "(a0 + a1) * (b0 + b1)"), &c0, &tmp)?
|
||||
|
.sub(cs.ns(|| "sub v0"), &v0)?
|
||||
|
.sub(cs.ns(|| "sub v1"), &v1)?
|
||||
|
};
|
||||
|
Ok(Self::new(new_c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
/// Multiplies by an element of the form (c0 = (c0, 0, 0), c1 = (d0, d1, 0))
|
||||
|
#[inline]
|
||||
|
pub fn mul_by_034<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
c0: &Fp2Gadget<P, ConstraintF>,
|
||||
|
d0: &Fp2Gadget<P, ConstraintF>,
|
||||
|
d1: &Fp2Gadget<P, ConstraintF>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let a0 = self.c0.c0.mul(cs.ns(|| "a0"), &c0)?;
|
||||
|
let a1 = self.c0.c1.mul(cs.ns(|| "a1"), &c0)?;
|
||||
|
let a2 = self.c0.c2.mul(cs.ns(|| "a2"), &c0)?;
|
||||
|
let a = Fp6Gadget::<P, ConstraintF>::new(a0, a1, a2);
|
||||
|
let b = self.c1.mul_by_c0_c1_0(cs.ns(|| "b"), &d0, &d1)?;
|
||||
|
|
||||
|
let c0 = c0.add(cs.ns(|| "c0 + d0"), &d0)?;
|
||||
|
let c1 = d1;
|
||||
|
let e = self
|
||||
|
.c0
|
||||
|
.add(cs.ns(|| "self.c0 + self.c1"), &self.c1)?
|
||||
|
.mul_by_c0_c1_0(cs.ns(|| "compute e"), &c0, &c1)?;
|
||||
|
let a_plus_b = a.add(cs.ns(|| "a + b"), &b)?;
|
||||
|
let c1 = e.sub(cs.ns(|| "e - (a + b)"), &a_plus_b)?;
|
||||
|
let c0 = Self::mul_fp6_by_nonresidue(cs.ns(|| "b *nr"), &b)?.add(cs.ns(|| "plus a"), &a)?;
|
||||
|
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
pub fn cyclotomic_square<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut result = Self::zero(cs.ns(|| "alloc result"))?;
|
||||
|
let fp2_nr = <P::Fp6Params as Fp6Parameters>::NONRESIDUE;
|
||||
|
|
||||
|
let z0 = &self.c0.c0;
|
||||
|
let z4 = &self.c0.c1;
|
||||
|
let z3 = &self.c0.c2;
|
||||
|
let z2 = &self.c1.c0;
|
||||
|
let z1 = &self.c1.c1;
|
||||
|
let z5 = &self.c1.c2;
|
||||
|
|
||||
|
// t0 + t1*y = (z0 + z1*y)^2 = a^2
|
||||
|
let tmp = z0.mul(cs.ns(|| "first mul"), &z1)?;
|
||||
|
let t0 = {
|
||||
|
// (z0 + &z1) * &(z0 + &(fp2_nr * &z1)) - &tmp - &(tmp * &fp2_nr);
|
||||
|
let mut cs = cs.ns(|| "t0");
|
||||
|
let tmp1 = z0.add(cs.ns(|| "tmp1"), &z1)?;
|
||||
|
let tmp2 = z1
|
||||
|
.mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)?
|
||||
|
.add(cs.ns(|| "tmp2.2"), &z0)?;
|
||||
|
let tmp4 = tmp
|
||||
|
.mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)?
|
||||
|
.add(cs.ns(|| "tmp4.2"), &tmp)?;
|
||||
|
tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)?
|
||||
|
.sub(cs.ns(|| "tmp3.2"), &tmp4)?
|
||||
|
};
|
||||
|
let t1 = tmp.double(cs.ns(|| "t1"))?;
|
||||
|
|
||||
|
// t2 + t3*y = (z2 + z3*y)^2 = b^2
|
||||
|
let tmp = z2.mul(cs.ns(|| "second mul"), &z3)?;
|
||||
|
let t2 = {
|
||||
|
// (z2 + &z3) * &(z2 + &(fp2_nr * &z3)) - &tmp - &(tmp * &fp2_nr);
|
||||
|
let mut cs = cs.ns(|| "t2");
|
||||
|
let tmp1 = z2.add(cs.ns(|| "tmp1"), &z3)?;
|
||||
|
let tmp2 = z3
|
||||
|
.mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)?
|
||||
|
.add(cs.ns(|| "tmp2.2"), &z2)?;
|
||||
|
let tmp4 = tmp
|
||||
|
.mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)?
|
||||
|
.add(cs.ns(|| "tmp4.2"), &tmp)?;
|
||||
|
tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)?
|
||||
|
.sub(cs.ns(|| "tmp3.2"), &tmp4)?
|
||||
|
};
|
||||
|
let t3 = tmp.double(cs.ns(|| "t3"))?;
|
||||
|
|
||||
|
// t4 + t5*y = (z4 + z5*y)^2 = c^2
|
||||
|
let tmp = z4.mul(cs.ns(|| "third mul"), &z5)?;
|
||||
|
let t4 = {
|
||||
|
// (z4 + &z5) * &(z4 + &(fp2_nr * &z5)) - &tmp - &(tmp * &fp2_nr);
|
||||
|
let mut cs = cs.ns(|| "t4");
|
||||
|
let tmp1 = z4.add(cs.ns(|| "tmp1"), &z5)?;
|
||||
|
let tmp2 = z5
|
||||
|
.mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)?
|
||||
|
.add(cs.ns(|| "tmp2.2"), &z4)?;
|
||||
|
let tmp4 = tmp
|
||||
|
.mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)?
|
||||
|
.add(cs.ns(|| "tmp4.2"), &tmp)?;
|
||||
|
tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)?
|
||||
|
.sub(cs.ns(|| "tmp3.2"), &tmp4)?
|
||||
|
};
|
||||
|
let t5 = tmp.double(cs.ns(|| "t5"))?;
|
||||
|
|
||||
|
// for A
|
||||
|
|
||||
|
// z0 = 3 * t0 - 2 * z0
|
||||
|
result.c0.c0 = {
|
||||
|
let mut cs = cs.ns(|| "result.c0.c0");
|
||||
|
t0.sub(cs.ns(|| "1"), &z0)?
|
||||
|
.double(cs.ns(|| "2"))?
|
||||
|
.add(cs.ns(|| "3"), &t0)?
|
||||
|
};
|
||||
|
|
||||
|
// z1 = 3 * t1 + 2 * z1
|
||||
|
result.c1.c1 = {
|
||||
|
let mut cs = cs.ns(|| "result.c1.c1");
|
||||
|
t1.add(cs.ns(|| "1"), &z1)?
|
||||
|
.double(cs.ns(|| "2"))?
|
||||
|
.add(cs.ns(|| "3"), &t1)?
|
||||
|
};
|
||||
|
|
||||
|
// for B
|
||||
|
|
||||
|
// z2 = 3 * (xi * t5) + 2 * z2
|
||||
|
result.c1.c0 = {
|
||||
|
let mut cs = cs.ns(|| "result.c1.c0");
|
||||
|
let tmp = t5.mul_by_constant(cs.ns(|| "1"), &fp2_nr)?;
|
||||
|
z2.add(cs.ns(|| "2"), &tmp)?
|
||||
|
.double(cs.ns(|| "3"))?
|
||||
|
.add(cs.ns(|| "4"), &tmp)?
|
||||
|
};
|
||||
|
|
||||
|
// z3 = 3 * t4 - 2 * z3
|
||||
|
result.c0.c2 = {
|
||||
|
let mut cs = cs.ns(|| "result.c0.c2");
|
||||
|
t4.sub(cs.ns(|| "1"), &z3)?
|
||||
|
.double(cs.ns(|| "2"))?
|
||||
|
.add(cs.ns(|| "3"), &t4)?
|
||||
|
};
|
||||
|
|
||||
|
// for C
|
||||
|
|
||||
|
// z4 = 3 * t2 - 2 * z4
|
||||
|
result.c0.c1 = {
|
||||
|
let mut cs = cs.ns(|| "result.c0.c1");
|
||||
|
t2.sub(cs.ns(|| "1"), &z4)?
|
||||
|
.double(cs.ns(|| "2"))?
|
||||
|
.add(cs.ns(|| "3"), &t2)?
|
||||
|
};
|
||||
|
|
||||
|
// z5 = 3 * t3 + 2 * z5
|
||||
|
result.c1.c2 = {
|
||||
|
let mut cs = cs.ns(|| "result.c1.c2");
|
||||
|
t3.add(cs.ns(|| "1"), &z5)?
|
||||
|
.double(cs.ns(|| "2"))?
|
||||
|
.add(cs.ns(|| "3"), &t3)?
|
||||
|
};
|
||||
|
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
pub fn cyclotomic_exp<CS: ConstraintSystem<ConstraintF>, S: AsRef<[u64]>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
exp: S,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut res = Self::one(cs.ns(|| "one"))?;
|
||||
|
let mut found_one = false;
|
||||
|
for (j, i) in BitIterator::new(exp).enumerate() {
|
||||
|
if found_one {
|
||||
|
res = res.cyclotomic_square(cs.ns(|| format!("res_square_{:?}", j)))?;
|
||||
|
} else {
|
||||
|
found_one = i;
|
||||
|
}
|
||||
|
if i {
|
||||
|
res.mul_in_place(cs.ns(|| format!("res_mul2_{:?}", j)), self)?;
|
||||
|
}
|
||||
|
}
|
||||
|
Ok(res)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> FieldGadget<Fp12<P>, ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
type Variable = (Fp6GadgetVariable<P, ConstraintF>, Fp6GadgetVariable<P, ConstraintF>);
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn get_value(&self) -> Option<Fp12<P>> {
|
||||
|
Some(Fp12::new(self.c0.get_value()?, self.c1.get_value()?))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn get_variable(&self) -> Self::Variable {
|
||||
|
(self.c0.get_variable(), self.c1.get_variable())
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = Fp6Gadget::<P, ConstraintF>::zero(cs.ns(|| "c0"))?;
|
||||
|
let c1 = Fp6Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = Fp6Gadget::<P, ConstraintF>::one(cs.ns(|| "c0"))?;
|
||||
|
let c1 = Fp6Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.add(cs.ns(|| "c0"), &other.c0)?;
|
||||
|
let c1 = self.c1.add(cs.ns(|| "c1"), &other.c1)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.add_in_place(cs.ns(|| "c0"), &other.c0)?;
|
||||
|
self.c1.add_in_place(cs.ns(|| "c1"), &other.c1)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.sub(cs.ns(|| "c0"), &other.c0)?;
|
||||
|
let c1 = self.c1.sub(cs.ns(|| "c1"), &other.c1)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.sub_in_place(cs.ns(|| "c0"), &other.c0)?;
|
||||
|
self.c1.sub_in_place(cs.ns(|| "c1"), &other.c1)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn negate<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.negate(cs.ns(|| "c0"))?;
|
||||
|
let c1 = self.c1.negate(cs.ns(|| "c1"))?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.negate_in_place(cs.ns(|| "c0"))?;
|
||||
|
self.c1.negate_in_place(cs.ns(|| "c1"))?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn mul<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
// Karatsuba multiplication:
|
||||
|
// v0 = A.c0 * B.c0
|
||||
|
// v1 = A.c1 * B.c1
|
||||
|
// result.c0 = v0 + non_residue * v1
|
||||
|
// result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
|
//
|
||||
|
// Enforced with 3 Fp3_mul_gadget's that ensure that:
|
||||
|
// A.c1 * B.c1 = v1
|
||||
|
// A.c0 * B.c0 = v0
|
||||
|
// (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + v0 + v1
|
||||
|
|
||||
|
let v0 = self.c0.mul(cs.ns(|| "v0"), &other.c0)?;
|
||||
|
let v1 = self.c1.mul(cs.ns(|| "v1"), &other.c1)?;
|
||||
|
let c0 = {
|
||||
|
let non_residue_times_v1 =
|
||||
|
Self::mul_fp6_by_nonresidue(cs.ns(|| "first mul_by_nr"), &v1)?;
|
||||
|
v0.add(cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)?
|
||||
|
};
|
||||
|
let c1 = {
|
||||
|
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
let b0_plus_b1 = other.c0.add(cs.ns(|| "b0 + b1"), &other.c1)?;
|
||||
|
let a0_plus_a1_times_b0_plus_b1 =
|
||||
|
a0_plus_a1.mul(&mut cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?;
|
||||
|
a0_plus_a1_times_b0_plus_b1
|
||||
|
.sub(cs.ns(|| "res - v0"), &v0)?
|
||||
|
.sub(cs.ns(|| "res - v0 - v1"), &v1)?
|
||||
|
};
|
||||
|
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
fn square<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
// From Libsnark/fp2_gadget.tcc
|
||||
|
// Complex multiplication for Fp2:
|
||||
|
// v0 = A.c0 * A.c1
|
||||
|
// result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 +
|
||||
|
// non_residue) * v0 result.c1 = 2 * v0
|
||||
|
// Enforced with 2 constraints:
|
||||
|
// (2*A.c0) * A.c1 = result.c1
|
||||
|
// (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1
|
||||
|
// + non_residue)/2 Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
|
||||
|
let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?;
|
||||
|
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
|
||||
|
let non_residue_a1 = Self::mul_fp6_by_nonresidue(cs.ns(|| "non_residue * a1"), &self.c1)?;
|
||||
|
let a0_plus_non_residue_a1 = self
|
||||
|
.c0
|
||||
|
.add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_a1)?;
|
||||
|
let one_plus_non_residue_v0 =
|
||||
|
Self::mul_fp6_by_nonresidue(cs.ns(|| "non_residue * v0"), &v0)?
|
||||
|
.add(cs.ns(|| "plus v0"), &v0)?;
|
||||
|
|
||||
|
let c0 = a0_plus_a1
|
||||
|
.mul(
|
||||
|
cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"),
|
||||
|
&a0_plus_non_residue_a1,
|
||||
|
)?
|
||||
|
.sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?;
|
||||
|
|
||||
|
v0.double_in_place(cs.ns(|| "2v0"))?;
|
||||
|
let c1 = v0;
|
||||
|
|
||||
|
Ok(Self {
|
||||
|
c0,
|
||||
|
c1,
|
||||
|
_params: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Fp12<P>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.add_constant(cs.ns(|| "c0"), &other.c0)?;
|
||||
|
let c1 = self.c1.add_constant(cs.ns(|| "c1"), &other.c1)?;
|
||||
|
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Fp12<P>,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?;
|
||||
|
self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Fp12<P>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
// Karatsuba multiplication (see mul above).
|
||||
|
// Doesn't need any constraints; returns linear combinations of
|
||||
|
// `self`'s variables.
|
||||
|
//
|
||||
|
// (The operations below are guaranteed to return linear combinations)
|
||||
|
let (a0, a1) = (&self.c0, &self.c1);
|
||||
|
let (b0, b1) = (other.c0, other.c1);
|
||||
|
let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?;
|
||||
|
let mut v1 = Self::mul_fp6_by_nonresidue(&mut cs.ns(|| "v1"), a1)?;
|
||||
|
let beta_v1 = v1.mul_by_constant_in_place(&mut cs.ns(|| "beta * v1"), &b1)?;
|
||||
|
|
||||
|
v0.add_in_place(&mut cs.ns(|| "c0"), beta_v1)?;
|
||||
|
let c0 = v0;
|
||||
|
|
||||
|
let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?;
|
||||
|
let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?;
|
||||
|
a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?;
|
||||
|
let c1 = a0b1;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
power: usize,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut res = self.clone();
|
||||
|
res.frobenius_map_in_place(cs, power)?;
|
||||
|
Ok(res)
|
||||
|
}
|
||||
|
|
||||
|
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
power: usize,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0
|
||||
|
.frobenius_map_in_place(cs.ns(|| "frob_map1"), power)?;
|
||||
|
self.c1
|
||||
|
.frobenius_map_in_place(cs.ns(|| "frob_map2"), power)?;
|
||||
|
|
||||
|
self.c1
|
||||
|
.c0
|
||||
|
.mul_by_constant_in_place(cs.ns(|| "mul1"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?;
|
||||
|
self.c1
|
||||
|
.c1
|
||||
|
.mul_by_constant_in_place(cs.ns(|| "mul2"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?;
|
||||
|
self.c1
|
||||
|
.c2
|
||||
|
.mul_by_constant_in_place(cs.ns(|| "mul3"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn inverse<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
|
||||
|
self.get_value().and_then(|val| val.inverse()).get()
|
||||
|
})?;
|
||||
|
|
||||
|
// Karatsuba multiplication for Fp2 with the inverse:
|
||||
|
// v0 = A.c0 * B.c0
|
||||
|
// v1 = A.c1 * B.c1
|
||||
|
//
|
||||
|
// 1 = v0 + non_residue * v1
|
||||
|
// => v0 = 1 - non_residue * v1
|
||||
|
//
|
||||
|
// 0 = result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
|
// => v0 + v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
|
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
|
// Enforced with 2 constraints:
|
||||
|
// A.c1 * B.c1 = v1
|
||||
|
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
|
// Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
|
||||
|
// Constraint 1
|
||||
|
let v1 = self.c1.mul(cs.ns(|| "inv_constraint_1"), &inverse.c1)?;
|
||||
|
|
||||
|
// Constraint 2
|
||||
|
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
let b0_plus_b1 = inverse.c0.add(cs.ns(|| "b0 + b1"), &inverse.c1)?;
|
||||
|
|
||||
|
let one = Fp6::<P::Fp6Params>::one();
|
||||
|
let rhs = Self::mul_fp6_by_nonresidue(cs.ns(|| "nr * v1"), &v1)?
|
||||
|
.sub(cs.ns(|| "sub v1"), &v1)?
|
||||
|
.negate(cs.ns(|| "negate it"))?
|
||||
|
.add_constant(cs.ns(|| "add one"), &one)?;
|
||||
|
a0_plus_a1.mul_equals(cs.ns(|| "inv_constraint_2"), &b0_plus_b1, &rhs)?;
|
||||
|
Ok(inverse)
|
||||
|
}
|
||||
|
|
||||
|
fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
result: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
// Karatsuba multiplication for Fp2:
|
||||
|
// v0 = A.c0 * B.c0
|
||||
|
// v1 = A.c1 * B.c1
|
||||
|
// result.c0 = v0 + non_residue * v1
|
||||
|
// result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
|
// Enforced with 3 constraints:
|
||||
|
// A.c1 * B.c1 = v1
|
||||
|
// A.c0 * B.c0 = result.c0 - non_residue * v1
|
||||
|
// (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1
|
||||
|
// Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
let mul_cs = &mut cs.ns(|| "mul");
|
||||
|
|
||||
|
// Compute v1
|
||||
|
let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?;
|
||||
|
|
||||
|
// Perform second check
|
||||
|
let non_residue_times_v1 = Self::mul_fp6_by_nonresidue(mul_cs.ns(|| "nr * v1"), &v1)?;
|
||||
|
let rhs = result
|
||||
|
.c0
|
||||
|
.sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?;
|
||||
|
self.c0
|
||||
|
.mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?;
|
||||
|
|
||||
|
// Last check
|
||||
|
let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?;
|
||||
|
let one_minus_non_residue_v1 =
|
||||
|
v1.sub(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?;
|
||||
|
|
||||
|
let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result
|
||||
|
.c1
|
||||
|
.add(mul_cs.ns(|| "c1 + c0"), &result.c0)?
|
||||
|
.add(mul_cs.ns(|| "rest of stuff"), &one_minus_non_residue_v1)?;
|
||||
|
|
||||
|
a0_plus_a1.mul_equals(
|
||||
|
mul_cs.ns(|| "third check"),
|
||||
|
&b0_plus_b1,
|
||||
|
&result_c1_plus_result_c0_plus_one_minus_non_residue_v1,
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_mul() -> usize {
|
||||
|
unimplemented!()
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_inv() -> usize {
|
||||
|
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> PartialEq for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
fn eq(&self, other: &Self) -> bool {
|
||||
|
self.c0 == other.c0 && self.c1 == other.c1
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> Eq for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
self.c0
|
||||
|
.conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?;
|
||||
|
self.c1
|
||||
|
.conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
2 * <Fp6Gadget<P, ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?;
|
||||
|
self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
2 * <Fp6Gadget<P, ConstraintF> as NEqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
fn to_bits<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bits(&mut cs)?;
|
||||
|
let mut c1 = self.c1.to_bits(cs)?;
|
||||
|
c0.append(&mut c1);
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bits_strict(&mut cs)?;
|
||||
|
let mut c1 = self.c1.to_bits_strict(cs)?;
|
||||
|
c0.append(&mut c1);
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?;
|
||||
|
let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?;
|
||||
|
c0.append(&mut c1);
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bytes_strict(cs.ns(|| "c0"))?;
|
||||
|
let mut c1 = self.c1.to_bytes_strict(cs.ns(|| "c1"))?;
|
||||
|
c0.append(&mut c1);
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> Clone for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
fn clone(&self) -> Self {
|
||||
|
Self::new(self.c0.clone(), self.c1.clone())
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
cond: &Boolean,
|
||||
|
first: &Self,
|
||||
|
second: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = Fp6Gadget::<P, ConstraintF>::conditionally_select(
|
||||
|
&mut cs.ns(|| "c0"),
|
||||
|
cond,
|
||||
|
&first.c0,
|
||||
|
&second.c0,
|
||||
|
)?;
|
||||
|
let c1 = Fp6Gadget::<P, ConstraintF>::conditionally_select(
|
||||
|
&mut cs.ns(|| "c1"),
|
||||
|
cond,
|
||||
|
&first.c1,
|
||||
|
&second.c1,
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
2 * <Fp6Gadget<P, ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
type TableConstant = Fp12<P>;
|
||||
|
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
b: &[Boolean],
|
||||
|
c: &[Self::TableConstant],
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
||||
|
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
||||
|
let c0 = Fp6Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
|
||||
|
let c1 = Fp6Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
2 * <Fp6Gadget<P, ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> AllocGadget<Fp12<P>, ConstraintF> for Fp12Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp12Parameters,
|
||||
|
<P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<Fp12<P>>,
|
||||
|
{
|
||||
|
let (c0, c1) = match value_gen() {
|
||||
|
Ok(fe) => {
|
||||
|
let fe = *fe.borrow();
|
||||
|
(Ok(fe.c0), Ok(fe.c1))
|
||||
|
},
|
||||
|
Err(_) => (
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
),
|
||||
|
};
|
||||
|
|
||||
|
let c0 = Fp6Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
|
||||
|
let c1 = Fp6Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<Fp12<P>>,
|
||||
|
{
|
||||
|
let (c0, c1) = match value_gen() {
|
||||
|
Ok(fe) => {
|
||||
|
let fe = *fe.borrow();
|
||||
|
(Ok(fe.c0), Ok(fe.c1))
|
||||
|
},
|
||||
|
Err(_) => (
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
),
|
||||
|
};
|
||||
|
|
||||
|
let c0 = Fp6Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
|
||||
|
let c1 = Fp6Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,627 @@ |
|||||
|
use algebra::{
|
||||
|
fields::{Fp2, Fp2Parameters},
|
||||
|
Field, PrimeField,
|
||||
|
};
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
|
||||
|
use crate::fields::fp::FpGadget;
|
||||
|
use crate::prelude::*;
|
||||
|
use crate::{Assignment, ConstraintVar};
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Debug(bound = "P: Fp2Parameters, ConstraintF: PrimeField"))]
|
||||
|
#[must_use]
|
||||
|
pub struct Fp2Gadget<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> {
|
||||
|
pub c0: FpGadget<ConstraintF>,
|
||||
|
pub c1: FpGadget<ConstraintF>,
|
||||
|
#[derivative(Debug = "ignore")]
|
||||
|
_params: PhantomData<P>,
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> Fp2Gadget<P, ConstraintF> {
|
||||
|
pub fn new(c0: FpGadget<ConstraintF>, c1: FpGadget<ConstraintF>) -> Self {
|
||||
|
Self {
|
||||
|
c0,
|
||||
|
c1,
|
||||
|
_params: PhantomData,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
/// Multiply a FpGadget by quadratic nonresidue P::NONRESIDUE.
|
||||
|
#[inline]
|
||||
|
pub fn mul_fp_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
fe: &FpGadget<ConstraintF>,
|
||||
|
) -> Result<FpGadget<ConstraintF>, SynthesisError> {
|
||||
|
fe.mul_by_constant(cs, &P::NONRESIDUE)
|
||||
|
}
|
||||
|
|
||||
|
/// Multiply a Fp2Gadget by an element of fp.
|
||||
|
#[inline]
|
||||
|
pub fn mul_by_fp_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
fe: &P::Fp,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.mul_by_constant_in_place(cs.ns(|| "c0"), fe)?;
|
||||
|
self.c1.mul_by_constant_in_place(cs.ns(|| "c1"), fe)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
/// Multiply a Fp2Gadget by an element of fp.
|
||||
|
#[inline]
|
||||
|
pub fn mul_by_fp_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
fe: &P::Fp,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut result = self.clone();
|
||||
|
result.mul_by_fp_constant_in_place(cs, fe)?;
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> FieldGadget<Fp2<P>, ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
|
type Variable = (ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>);
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn get_value(&self) -> Option<Fp2<P>> {
|
||||
|
match (self.c0.value, self.c1.value) {
|
||||
|
(Some(c0), Some(c1)) => Some(Fp2::new(c0, c1)),
|
||||
|
(..) => None,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn get_variable(&self) -> Self::Variable {
|
||||
|
(
|
||||
|
self.c0.get_variable().clone(),
|
||||
|
self.c1.get_variable().clone(),
|
||||
|
)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = FpGadget::zero(cs.ns(|| "c0"))?;
|
||||
|
let c1 = FpGadget::zero(cs.ns(|| "c1"))?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = FpGadget::one(cs.ns(|| "c0"))?;
|
||||
|
let c1 = FpGadget::zero(cs.ns(|| "c1"))?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?;
|
||||
|
let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?;
|
||||
|
let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn double<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let mut result = self.clone();
|
||||
|
result.double_in_place(cs)?;
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.double_in_place(&mut cs.ns(|| "double c0"))?;
|
||||
|
self.c1.double_in_place(&mut cs.ns(|| "double c1"))?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn negate<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let mut result = self.clone();
|
||||
|
result.negate_in_place(cs)?;
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?;
|
||||
|
self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn mul<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
// Karatsuba multiplication for Fp2:
|
||||
|
// v0 = A.c0 * B.c0
|
||||
|
// v1 = A.c1 * B.c1
|
||||
|
// result.c0 = v0 + non_residue * v1
|
||||
|
// result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
|
// Enforced with 3 constraints:
|
||||
|
// A.c1 * B.c1 = v1
|
||||
|
// A.c0 * B.c0 = result.c0 - non_residue * v1
|
||||
|
// (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1
|
||||
|
// Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
let mul_cs = &mut cs.ns(|| "mul");
|
||||
|
|
||||
|
let v0 = self.c0.mul(mul_cs.ns(|| "v0"), &other.c0)?;
|
||||
|
let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?;
|
||||
|
let c0 = {
|
||||
|
let non_residue_times_v1 =
|
||||
|
v1.mul_by_constant(mul_cs.ns(|| "non_residue * v0"), &P::NONRESIDUE)?;
|
||||
|
v0.add(mul_cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)?
|
||||
|
};
|
||||
|
let c1 = {
|
||||
|
let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?;
|
||||
|
let a0_plus_a1_times_b0_plus_b1 =
|
||||
|
a0_plus_a1.mul(&mut mul_cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?;
|
||||
|
a0_plus_a1_times_b0_plus_b1
|
||||
|
.sub(mul_cs.ns(|| "res - v0"), &v0)?
|
||||
|
.sub(mul_cs.ns(|| "res - v0 - v1"), &v1)?
|
||||
|
};
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn square<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
// From Libsnark/fp2_gadget.tcc
|
||||
|
// Complex multiplication for Fp2:
|
||||
|
// v0 = A.c0 * A.c1
|
||||
|
// result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 +
|
||||
|
// non_residue) * v0 result.c1 = 2 * v0
|
||||
|
// Enforced with 2 constraints:
|
||||
|
// (2*A.c0) * A.c1 = result.c1
|
||||
|
// (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1
|
||||
|
// + non_residue)/2 Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
|
||||
|
let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?;
|
||||
|
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
|
||||
|
let non_residue_c1 = self
|
||||
|
.c1
|
||||
|
.mul_by_constant(cs.ns(|| "non_residue * a1"), &P::NONRESIDUE)?;
|
||||
|
let a0_plus_non_residue_c1 = self
|
||||
|
.c0
|
||||
|
.add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_c1)?;
|
||||
|
let one_plus_non_residue_v0 = v0.mul_by_constant(
|
||||
|
cs.ns(|| "1 + non_residue * v0"),
|
||||
|
&(P::Fp::one() + &P::NONRESIDUE),
|
||||
|
)?;
|
||||
|
|
||||
|
let c0 = a0_plus_a1
|
||||
|
.mul(
|
||||
|
cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"),
|
||||
|
&a0_plus_non_residue_c1,
|
||||
|
)?
|
||||
|
.sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?;
|
||||
|
|
||||
|
v0.double_in_place(cs.ns(|| "2v0"))?;
|
||||
|
let c1 = v0;
|
||||
|
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn square_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
// From Libsnark/fp2_gadget.tcc
|
||||
|
// Complex multiplication for Fp2:
|
||||
|
// v0 = A.c0 * A.c1
|
||||
|
// result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 +
|
||||
|
// non_residue) * v0 result.c1 = 2 * v0
|
||||
|
// Enforced with 2 constraints:
|
||||
|
// (2*A.c0) * A.c1 = result.c1
|
||||
|
// (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1
|
||||
|
// + non_residue)/2 Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
|
||||
|
let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?;
|
||||
|
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
|
||||
|
let _ = self
|
||||
|
.c1
|
||||
|
.mul_by_constant_in_place(cs.ns(|| "non_residue * a1"), &P::NONRESIDUE)?;
|
||||
|
let a0_plus_non_residue_c1 = self.c0.add(cs.ns(|| "a0 + non_residue * a1"), &self.c1)?;
|
||||
|
let one_plus_non_residue_v0 = v0.mul_by_constant(
|
||||
|
cs.ns(|| "1 + non_residue * v0"),
|
||||
|
&(P::Fp::one() + &P::NONRESIDUE),
|
||||
|
)?;
|
||||
|
|
||||
|
self.c0 = a0_plus_a1
|
||||
|
.mul(
|
||||
|
cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"),
|
||||
|
&a0_plus_non_residue_c1,
|
||||
|
)?
|
||||
|
.sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?;
|
||||
|
|
||||
|
v0.double_in_place(cs.ns(|| "2v0"))?;
|
||||
|
self.c1 = v0;
|
||||
|
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn inverse<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
|
||||
|
self.get_value().and_then(|val| val.inverse()).get()
|
||||
|
})?;
|
||||
|
|
||||
|
// Karatsuba multiplication for Fp2 with the inverse:
|
||||
|
// v0 = A.c0 * B.c0
|
||||
|
// v1 = A.c1 * B.c1
|
||||
|
//
|
||||
|
// 1 = v0 + non_residue * v1
|
||||
|
// => v0 = 1 - non_residue * v1
|
||||
|
//
|
||||
|
// 0 = result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
|
// => v0 + v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
|
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
|
// Enforced with 2 constraints:
|
||||
|
// A.c1 * B.c1 = v1
|
||||
|
// => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
|
||||
|
// Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
|
||||
|
// Constraint 1
|
||||
|
let mut v1 = self.c1.mul(cs.ns(|| "inv_constraint_1"), &inverse.c1)?;
|
||||
|
|
||||
|
// Constraint 2
|
||||
|
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
let b0_plus_b1 = inverse.c0.add(cs.ns(|| "b0 + b1"), &inverse.c1)?;
|
||||
|
|
||||
|
let one = P::Fp::one();
|
||||
|
let rhs = v1
|
||||
|
.mul_by_constant_in_place(cs.ns(|| "(1 - nonresidue) * v1"), &(one - &P::NONRESIDUE))?
|
||||
|
.add_constant_in_place(cs.ns(|| "add one"), &one)?;
|
||||
|
a0_plus_a1.mul_equals(cs.ns(|| "inv_constraint_2"), &b0_plus_b1, rhs)?;
|
||||
|
Ok(inverse)
|
||||
|
}
|
||||
|
|
||||
|
fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
result: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
// Karatsuba multiplication for Fp2:
|
||||
|
// v0 = A.c0 * B.c0
|
||||
|
// v1 = A.c1 * B.c1
|
||||
|
// result.c0 = v0 + non_residue * v1
|
||||
|
// result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
|
||||
|
// Enforced with 3 constraints:
|
||||
|
// A.c1 * B.c1 = v1
|
||||
|
// A.c0 * B.c0 = result.c0 - non_residue * v1
|
||||
|
// (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1
|
||||
|
// Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
let mul_cs = &mut cs.ns(|| "mul");
|
||||
|
|
||||
|
// Compute v1
|
||||
|
let mut v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?;
|
||||
|
|
||||
|
// Perform second check
|
||||
|
let non_residue_times_v1 =
|
||||
|
v1.mul_by_constant(mul_cs.ns(|| "non_residue * v0"), &P::NONRESIDUE)?;
|
||||
|
let rhs = result
|
||||
|
.c0
|
||||
|
.sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?;
|
||||
|
self.c0
|
||||
|
.mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?;
|
||||
|
|
||||
|
// Last check
|
||||
|
let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?;
|
||||
|
let one_minus_non_residue_v1 =
|
||||
|
v1.sub_in_place(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?;
|
||||
|
|
||||
|
let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result
|
||||
|
.c1
|
||||
|
.add(mul_cs.ns(|| "c1 + c0"), &result.c0)?
|
||||
|
.add(mul_cs.ns(|| "rest of stuff"), one_minus_non_residue_v1)?;
|
||||
|
|
||||
|
a0_plus_a1.mul_equals(
|
||||
|
mul_cs.ns(|| "third check"),
|
||||
|
&b0_plus_b1,
|
||||
|
&result_c1_plus_result_c0_plus_one_minus_non_residue_v1,
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
power: usize,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut result = self.clone();
|
||||
|
let _ = result.frobenius_map_in_place(cs, power)?;
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
power: usize,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c1
|
||||
|
.mul_by_constant_in_place(cs, &P::FROBENIUS_COEFF_FP2_C1[power % 2])?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
other: &Fp2<P>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut result = self.clone();
|
||||
|
let _ = result.add_constant_in_place(cs, other)?;
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Fp2<P>,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?;
|
||||
|
self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
fe: &Fp2<P>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
// Karatsuba multiplication (see mul above).
|
||||
|
// Doesn't need any constraints; returns linear combinations of
|
||||
|
// `self`'s variables.
|
||||
|
//
|
||||
|
// (The operations below are guaranteed to return linear combinations)
|
||||
|
let (a0, a1) = (&self.c0, &self.c1);
|
||||
|
let (b0, b1) = (fe.c0, fe.c1);
|
||||
|
let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?;
|
||||
|
let beta_v1 = a1.mul_by_constant(&mut cs.ns(|| "v1"), &(b1 * &P::NONRESIDUE))?;
|
||||
|
|
||||
|
v0.add_in_place(&mut cs.ns(|| "c0"), &beta_v1)?;
|
||||
|
let c0 = v0;
|
||||
|
|
||||
|
let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?;
|
||||
|
let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?;
|
||||
|
a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?;
|
||||
|
let c1 = a0b1;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_mul() -> usize {
|
||||
|
3
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_inv() -> usize {
|
||||
|
2
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> PartialEq for Fp2Gadget<P, ConstraintF> {
|
||||
|
fn eq(&self, other: &Self) -> bool {
|
||||
|
self.c0 == other.c0 && self.c1 == other.c1
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> Eq for Fp2Gadget<P, ConstraintF> {}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
|
#[inline]
|
||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
self.c0
|
||||
|
.conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?;
|
||||
|
self.c1
|
||||
|
.conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
2
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
|
#[inline]
|
||||
|
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?;
|
||||
|
self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
2
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
|
fn to_bits<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bits(&mut cs)?;
|
||||
|
let mut c1 = self.c1.to_bits(cs)?;
|
||||
|
c0.append(&mut c1);
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bits_strict(&mut cs)?;
|
||||
|
let mut c1 = self.c1.to_bits_strict(cs)?;
|
||||
|
c0.append(&mut c1);
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
|
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?;
|
||||
|
let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?;
|
||||
|
c0.append(&mut c1);
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bytes_strict(cs.ns(|| "c0"))?;
|
||||
|
let mut c1 = self.c1.to_bytes_strict(cs.ns(|| "c1"))?;
|
||||
|
c0.append(&mut c1);
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> Clone for Fp2Gadget<P, ConstraintF> {
|
||||
|
fn clone(&self) -> Self {
|
||||
|
Self {
|
||||
|
c0: self.c0.clone(),
|
||||
|
c1: self.c1.clone(),
|
||||
|
_params: PhantomData,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
|
#[inline]
|
||||
|
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
cond: &Boolean,
|
||||
|
first: &Self,
|
||||
|
second: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 =
|
||||
|
FpGadget::<ConstraintF>::conditionally_select(&mut cs.ns(|| "c0"), cond, &first.c0, &second.c0)?;
|
||||
|
let c1 =
|
||||
|
FpGadget::<ConstraintF>::conditionally_select(&mut cs.ns(|| "c1"), cond, &first.c1, &second.c1)?;
|
||||
|
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
2
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
|
type TableConstant = Fp2<P>;
|
||||
|
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
b: &[Boolean],
|
||||
|
c: &[Self::TableConstant],
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
||||
|
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
||||
|
let c0 = FpGadget::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
|
||||
|
let c1 = FpGadget::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
2 * <FpGadget<ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> AllocGadget<Fp2<P>, ConstraintF> for Fp2Gadget<P, ConstraintF> {
|
||||
|
#[inline]
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<Fp2<P>>,
|
||||
|
{
|
||||
|
let (c0, c1) = match value_gen() {
|
||||
|
Ok(fe) => {
|
||||
|
let fe = *fe.borrow();
|
||||
|
(Ok(fe.c0), Ok(fe.c1))
|
||||
|
},
|
||||
|
Err(_) => (
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
),
|
||||
|
};
|
||||
|
|
||||
|
let c0 = FpGadget::alloc(&mut cs.ns(|| "c0"), || c0)?;
|
||||
|
let c1 = FpGadget::alloc(&mut cs.ns(|| "c1"), || c1)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<Fp2<P>>,
|
||||
|
{
|
||||
|
let (c0, c1) = match value_gen() {
|
||||
|
Ok(fe) => {
|
||||
|
let fe = *fe.borrow();
|
||||
|
(Ok(fe.c0), Ok(fe.c1))
|
||||
|
},
|
||||
|
Err(_) => (
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
),
|
||||
|
};
|
||||
|
|
||||
|
let c0 = FpGadget::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
|
||||
|
let c1 = FpGadget::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
|
||||
|
Ok(Self::new(c0, c1))
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,940 @@ |
|||||
|
use algebra::{
|
||||
|
fields::{
|
||||
|
fp6_3over2::{Fp6, Fp6Parameters},
|
||||
|
Field, Fp2Parameters,
|
||||
|
},
|
||||
|
PrimeField,
|
||||
|
};
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
|
||||
|
use crate::prelude::*;
|
||||
|
use crate::ConstraintVar;
|
||||
|
use crate::Assignment;
|
||||
|
|
||||
|
type Fp2Gadget<P, ConstraintF> = super::fp2::Fp2Gadget<<P as Fp6Parameters>::Fp2Params, ConstraintF>;
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Debug(bound = "ConstraintF: PrimeField"))]
|
||||
|
#[must_use]
|
||||
|
pub struct Fp6Gadget<P, ConstraintF: PrimeField>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
pub c0: Fp2Gadget<P, ConstraintF>,
|
||||
|
pub c1: Fp2Gadget<P, ConstraintF>,
|
||||
|
pub c2: Fp2Gadget<P, ConstraintF>,
|
||||
|
#[derivative(Debug = "ignore")]
|
||||
|
_params: PhantomData<P>,
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
pub fn new(c0: Fp2Gadget<P, ConstraintF>, c1: Fp2Gadget<P, ConstraintF>, c2: Fp2Gadget<P, ConstraintF>) -> Self {
|
||||
|
Self {
|
||||
|
c0,
|
||||
|
c1,
|
||||
|
c2,
|
||||
|
_params: PhantomData,
|
||||
|
}
|
||||
|
}
|
||||
|
/// Multiply a Fp2Gadget by cubic nonresidue P::NONRESIDUE.
|
||||
|
#[inline]
|
||||
|
pub fn mul_fp2_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
fe: &Fp2Gadget<P, ConstraintF>,
|
||||
|
) -> Result<Fp2Gadget<P, ConstraintF>, SynthesisError> {
|
||||
|
fe.mul_by_constant(cs, &P::NONRESIDUE)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
pub fn mul_by_0_c1_0<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
c1: &Fp2Gadget<P, ConstraintF>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
// Karatsuba multiplication
|
||||
|
// v0 = a0 * b0 = 0
|
||||
|
|
||||
|
// v1 = a1 * b1
|
||||
|
let v1 = self.c1.mul(cs.ns(|| "first mul"), c1)?;
|
||||
|
|
||||
|
// v2 = a2 * b2 = 0
|
||||
|
|
||||
|
let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?;
|
||||
|
let b1_plus_b2 = c1.clone();
|
||||
|
|
||||
|
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
|
||||
|
// c0 = (NONRESIDUE * ((a1 + a2)*(b1 + b2) - v1 - v2)) + v0
|
||||
|
// = NONRESIDUE * ((a1 + a2) * b1 - v1)
|
||||
|
let c0 = a1_plus_a2
|
||||
|
.mul(cs.ns(|| "second mul"), &b1_plus_b2)?
|
||||
|
.sub(cs.ns(|| "first sub"), &v1)?
|
||||
|
.mul_by_constant(cs.ns(|| "mul_by_nonresidue"), &P::NONRESIDUE)?;
|
||||
|
|
||||
|
// c1 = (a0 + a1) * (b0 + b1) - v0 - v1 + NONRESIDUE * v2
|
||||
|
// = (a0 + a1) * b1 - v1
|
||||
|
let c1 = a0_plus_a1
|
||||
|
.mul(cs.ns(|| "third mul"), &c1)?
|
||||
|
.sub(cs.ns(|| "second sub"), &v1)?;
|
||||
|
// c2 = (a0 + a2) * (b0 + b2) - v0 - v2 + v1
|
||||
|
// = v1
|
||||
|
let c2 = v1;
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
// #[inline]
|
||||
|
pub fn mul_by_c0_c1_0<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
c0: &Fp2Gadget<P, ConstraintF>,
|
||||
|
c1: &Fp2Gadget<P, ConstraintF>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let v0 = self.c0.mul(cs.ns(|| "v0"), c0)?;
|
||||
|
let v1 = self.c1.mul(cs.ns(|| "v1"), c1)?;
|
||||
|
// v2 = 0.
|
||||
|
|
||||
|
let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?;
|
||||
|
let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
|
||||
|
let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?;
|
||||
|
|
||||
|
let b1_plus_b2 = c1.clone();
|
||||
|
let b0_plus_b1 = c0.add(cs.ns(|| "b0 + b1"), &c1)?;
|
||||
|
let b0_plus_b2 = c0.clone();
|
||||
|
|
||||
|
let c0 = {
|
||||
|
let cs = &mut cs.ns(|| "c0");
|
||||
|
a1_plus_a2
|
||||
|
.mul(cs.ns(|| "(a1 + a2) * (b1 + b2)"), &b1_plus_b2)?
|
||||
|
.sub(cs.ns(|| "sub v1"), &v1)?
|
||||
|
.mul_by_constant(cs.ns(|| "First mul_by_nonresidue"), &P::NONRESIDUE)?
|
||||
|
.add(cs.ns(|| "add v0"), &v0)?
|
||||
|
};
|
||||
|
|
||||
|
let c1 = {
|
||||
|
let cs = &mut cs.ns(|| "c1");
|
||||
|
a0_plus_a1
|
||||
|
.mul(cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?
|
||||
|
.sub(cs.ns(|| "sub v0"), &v0)?
|
||||
|
.sub(cs.ns(|| "sub v1"), &v1)?
|
||||
|
};
|
||||
|
|
||||
|
let c2 = {
|
||||
|
a0_plus_a2
|
||||
|
.mul(cs.ns(|| "(a0 + a2) * (b0 + b2)"), &b0_plus_b2)?
|
||||
|
.sub(cs.ns(|| "sub v0"), &v0)?
|
||||
|
.add(cs.ns(|| "add v1"), &v1)?
|
||||
|
};
|
||||
|
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> FieldGadget<Fp6<P>, ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
type Variable = (
|
||||
|
(ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>),
|
||||
|
(ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>),
|
||||
|
(ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>),
|
||||
|
);
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn get_value(&self) -> Option<Fp6<P>> {
|
||||
|
match (
|
||||
|
self.c0.get_value(),
|
||||
|
self.c1.get_value(),
|
||||
|
self.c2.get_value(),
|
||||
|
) {
|
||||
|
(Some(c0), Some(c1), Some(c2)) => Some(Fp6::new(c0, c1, c2)),
|
||||
|
(..) => None,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn get_variable(&self) -> Self::Variable {
|
||||
|
(
|
||||
|
self.c0.get_variable(),
|
||||
|
self.c1.get_variable(),
|
||||
|
self.c2.get_variable(),
|
||||
|
)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c0"))?;
|
||||
|
let c1 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
|
||||
|
let c2 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c2"))?;
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = Fp2Gadget::<P, ConstraintF>::one(cs.ns(|| "c0"))?;
|
||||
|
let c1 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
|
||||
|
let c2 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c2"))?;
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?;
|
||||
|
let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?;
|
||||
|
let c2 = self.c2.add(&mut cs.ns(|| "add c2"), &other.c2)?;
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?;
|
||||
|
let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?;
|
||||
|
let c2 = self.c2.sub(&mut cs.ns(|| "sub c2"), &other.c2)?;
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn negate<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.negate(&mut cs.ns(|| "negate c0"))?;
|
||||
|
let c1 = self.c1.negate(&mut cs.ns(|| "negate c1"))?;
|
||||
|
let c2 = self.c2.negate(&mut cs.ns(|| "negate c2"))?;
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?;
|
||||
|
self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?;
|
||||
|
self.c2.negate_in_place(&mut cs.ns(|| "negate c2"))?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
/// Use the Toom-Cook-3x method to compute multiplication.
|
||||
|
#[inline]
|
||||
|
fn mul<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
// Uses Toom-Cool-3x multiplication from
|
||||
|
//
|
||||
|
// Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
|
||||
|
// v0 = a(0)b(0) = a0 * b0
|
||||
|
let v0 = self.c0.mul(&mut cs.ns(|| "Calc v0"), &other.c0)?;
|
||||
|
|
||||
|
// v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2)
|
||||
|
let v1 = {
|
||||
|
let mut v1_cs = cs.ns(|| "compute v1");
|
||||
|
let a0_plus_a1_plus_a2 = self
|
||||
|
.c0
|
||||
|
.add(v1_cs.ns(|| "a0 + a1"), &self.c1)?
|
||||
|
.add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?;
|
||||
|
let b0_plus_b1_plus_b2 = other
|
||||
|
.c0
|
||||
|
.add(v1_cs.ns(|| "b0 + b1"), &other.c1)?
|
||||
|
.add(v1_cs.ns(|| "b0 + b1 + b2"), &other.c2)?;
|
||||
|
|
||||
|
a0_plus_a1_plus_a2.mul(
|
||||
|
v1_cs.ns(|| "(a0 + a1 + a2)(b0 + b1 + b2)"),
|
||||
|
&b0_plus_b1_plus_b2,
|
||||
|
)?
|
||||
|
};
|
||||
|
|
||||
|
// v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2)
|
||||
|
let v2 = {
|
||||
|
let mut v2_cs = cs.ns(|| "compute v2");
|
||||
|
|
||||
|
let a0_minus_a1_plus_a2 = self
|
||||
|
.c0
|
||||
|
.sub(v2_cs.ns(|| "a0 - a1"), &self.c1)?
|
||||
|
.add(v2_cs.ns(|| "a0 - a1 + a2"), &self.c2)?;
|
||||
|
|
||||
|
let b0_minus_b1_plus_b2 = other
|
||||
|
.c0
|
||||
|
.sub(v2_cs.ns(|| "b0 - b1"), &other.c1)?
|
||||
|
.add(v2_cs.ns(|| "b0 - b1 + b2"), &other.c2)?;
|
||||
|
|
||||
|
a0_minus_a1_plus_a2.mul(
|
||||
|
v2_cs.ns(|| "(a0 - a1 + a2)(b0 - b1 + b2)"),
|
||||
|
&b0_minus_b1_plus_b2,
|
||||
|
)?
|
||||
|
};
|
||||
|
|
||||
|
// v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2)
|
||||
|
let v3 = {
|
||||
|
let v3_cs = &mut cs.ns(|| "compute v3");
|
||||
|
|
||||
|
let a1_double = self.c1.double(v3_cs.ns(|| "2 * a1"))?;
|
||||
|
let a2_quad = self
|
||||
|
.c2
|
||||
|
.double(v3_cs.ns(|| "2 * a2"))?
|
||||
|
.double(v3_cs.ns(|| "4 * a2"))?;
|
||||
|
|
||||
|
let a0_plus_2_a1_plus_4_a2 = self
|
||||
|
.c0
|
||||
|
.add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)?
|
||||
|
.add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?;
|
||||
|
|
||||
|
let b1_double = other.c1.double(v3_cs.ns(|| "2 * b1"))?;
|
||||
|
let b2_quad = other
|
||||
|
.c2
|
||||
|
.double(v3_cs.ns(|| "2 * b2"))?
|
||||
|
.double(v3_cs.ns(|| "4 * b2"))?;
|
||||
|
let b0_plus_2_b1_plus_4_b2 = other
|
||||
|
.c0
|
||||
|
.add(v3_cs.ns(|| "b0 + 2b1"), &b1_double)?
|
||||
|
.add(v3_cs.ns(|| "b0 + 2b1 + 4b2"), &b2_quad)?;
|
||||
|
|
||||
|
a0_plus_2_a1_plus_4_a2.mul(
|
||||
|
v3_cs.ns(|| "(a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2)"),
|
||||
|
&b0_plus_2_b1_plus_4_b2,
|
||||
|
)?
|
||||
|
};
|
||||
|
|
||||
|
// v4 = a(∞)b(∞) = a2 * b2
|
||||
|
let v4 = self.c2.mul(cs.ns(|| "v2: a2 * b2"), &other.c2)?;
|
||||
|
|
||||
|
let two = <P::Fp2Params as Fp2Parameters>::Fp::one().double();
|
||||
|
let six = two.double() + &two;
|
||||
|
let mut two_and_six = [two, six];
|
||||
|
algebra::fields::batch_inversion(&mut two_and_six);
|
||||
|
let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]);
|
||||
|
|
||||
|
let half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?;
|
||||
|
let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?;
|
||||
|
let one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_six"), &six_inverse)?;
|
||||
|
let one_sixth_v3 = v3.mul_by_fp_constant(cs.ns(|| "v3_by_six"), &six_inverse)?;
|
||||
|
let two_v4 = v4.double(cs.ns(|| "2 * v4"))?;
|
||||
|
|
||||
|
// c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4)
|
||||
|
let c0 = {
|
||||
|
let c0_cs = &mut cs.ns(|| "c0");
|
||||
|
|
||||
|
// No constraints, only get a linear combination back.
|
||||
|
let temp = half_v0
|
||||
|
.sub(c0_cs.ns(|| "sub1"), &half_v1)?
|
||||
|
.sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)?
|
||||
|
.add(c0_cs.ns(|| "add3"), &one_sixth_v3)?
|
||||
|
.sub(c0_cs.ns(|| "sub4"), &two_v4)?;
|
||||
|
let non_residue_times_inner =
|
||||
|
temp.mul_by_constant(&mut c0_cs.ns(|| "mul5"), &P::NONRESIDUE)?;
|
||||
|
v0.add(c0_cs.ns(|| "add6"), &non_residue_times_inner)?
|
||||
|
};
|
||||
|
|
||||
|
// −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4
|
||||
|
let c1 = {
|
||||
|
let c1_cs = &mut cs.ns(|| "c1");
|
||||
|
let one_third_v2 = one_sixth_v2.double(&mut c1_cs.ns(|| "v2_by_3"))?;
|
||||
|
let non_residue_v4 =
|
||||
|
v4.mul_by_constant(&mut c1_cs.ns(|| "mul_by_beta"), &P::NONRESIDUE)?;
|
||||
|
|
||||
|
let result = half_v0
|
||||
|
.negate(c1_cs.ns(|| "neg1"))?
|
||||
|
.add(c1_cs.ns(|| "add2"), &v1)?
|
||||
|
.sub(c1_cs.ns(|| "sub3"), &one_third_v2)?
|
||||
|
.sub(c1_cs.ns(|| "sub4"), &one_sixth_v3)?
|
||||
|
.add(c1_cs.ns(|| "sub5"), &two_v4)?
|
||||
|
.add(c1_cs.ns(|| "sub6"), &non_residue_v4)?;
|
||||
|
result
|
||||
|
};
|
||||
|
|
||||
|
// -v0 + (1/2)v1 + (1/2)v2 −v4
|
||||
|
let c2 = {
|
||||
|
let c2_cs = &mut cs.ns(|| "c2");
|
||||
|
let half_v2 = v2.mul_by_fp_constant(&mut c2_cs.ns(|| "mul1"), &two_inverse)?;
|
||||
|
let result = half_v1
|
||||
|
.add(c2_cs.ns(|| "add1"), &half_v2)?
|
||||
|
.sub(c2_cs.ns(|| "sub1"), &v4)?
|
||||
|
.sub(c2_cs.ns(|| "sub2"), &v0)?;
|
||||
|
result
|
||||
|
};
|
||||
|
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
/// Use the Toom-Cook-3x method to compute multiplication.
|
||||
|
#[inline]
|
||||
|
fn square<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
// Uses Toom-Cool-3x multiplication from
|
||||
|
//
|
||||
|
// Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
|
||||
|
// v0 = a(0)^2 = a0^2
|
||||
|
let v0 = self.c0.square(&mut cs.ns(|| "Calc v0"))?;
|
||||
|
|
||||
|
// v1 = a(1)^2 = (a0 + a1 + a2)^2
|
||||
|
let v1 = {
|
||||
|
let a0_plus_a1_plus_a2 = self
|
||||
|
.c0
|
||||
|
.add(cs.ns(|| "a0 + a1"), &self.c1)?
|
||||
|
.add(cs.ns(|| "a0 + a1 + a2"), &self.c2)?;
|
||||
|
a0_plus_a1_plus_a2.square(&mut cs.ns(|| "(a0 + a1 + a2)^2"))?
|
||||
|
};
|
||||
|
|
||||
|
// v2 = a(−1)^2 = (a0 − a1 + a2)^2
|
||||
|
let v2 = {
|
||||
|
let a0_minus_a1_plus_a2 = self
|
||||
|
.c0
|
||||
|
.sub(cs.ns(|| "a0 - a1"), &self.c1)?
|
||||
|
.add(cs.ns(|| "a0 - a2 + a2"), &self.c2)?;
|
||||
|
a0_minus_a1_plus_a2.square(&mut cs.ns(|| "(a0 - a1 + a2)^2"))?
|
||||
|
};
|
||||
|
|
||||
|
// v3 = a(2)^2 = (a0 + 2a1 + 4a2)^2
|
||||
|
let v3 = {
|
||||
|
let a1_double = self.c1.double(cs.ns(|| "2a1"))?;
|
||||
|
let a2_quad = self.c2.double(cs.ns(|| "2a2"))?.double(cs.ns(|| "4a2"))?;
|
||||
|
let a0_plus_2_a1_plus_4_a2 = self
|
||||
|
.c0
|
||||
|
.add(cs.ns(|| "a0 + 2a1"), &a1_double)?
|
||||
|
.add(cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?;
|
||||
|
|
||||
|
a0_plus_2_a1_plus_4_a2.square(&mut cs.ns(|| "(a0 + 2a1 + 4a2)^2"))?
|
||||
|
};
|
||||
|
|
||||
|
// v4 = a(∞)^2 = a2^2
|
||||
|
let v4 = self.c2.square(&mut cs.ns(|| "a2^2"))?;
|
||||
|
|
||||
|
let two = <P::Fp2Params as Fp2Parameters>::Fp::one().double();
|
||||
|
let six = two.double() + &two;
|
||||
|
let mut two_and_six = [two, six];
|
||||
|
algebra::fields::batch_inversion(&mut two_and_six);
|
||||
|
let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]);
|
||||
|
|
||||
|
let half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?;
|
||||
|
let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?;
|
||||
|
let one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "one_sixth_v2"), &six_inverse)?;
|
||||
|
let one_sixth_v3 = v3.mul_by_fp_constant(cs.ns(|| "one_sixth_v3"), &six_inverse)?;
|
||||
|
let two_v4 = v4.double(cs.ns(|| "double_v4"))?;
|
||||
|
|
||||
|
// c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4)
|
||||
|
let c0 = {
|
||||
|
let mut c0_cs = cs.ns(|| "c0");
|
||||
|
// No constraints, only get a linear combination back.
|
||||
|
let inner = half_v0
|
||||
|
.sub(c0_cs.ns(|| "sub1"), &half_v1)?
|
||||
|
.sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)?
|
||||
|
.add(c0_cs.ns(|| "add3"), &one_sixth_v3)?
|
||||
|
.sub(c0_cs.ns(|| "sub4"), &two_v4)?;
|
||||
|
let non_residue_times_inner =
|
||||
|
inner.mul_by_constant(c0_cs.ns(|| "mul_by_res"), &P::NONRESIDUE)?;
|
||||
|
v0.add(c0_cs.ns(|| "add5"), &non_residue_times_inner)?
|
||||
|
};
|
||||
|
|
||||
|
// −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4
|
||||
|
let c1 = {
|
||||
|
let mut c1_cs = cs.ns(|| "c1");
|
||||
|
let one_third_v2 = one_sixth_v2.double(c1_cs.ns(|| "v2_by_3"))?;
|
||||
|
let non_residue_v4 = v4.mul_by_constant(c1_cs.ns(|| "mul_by_res"), &P::NONRESIDUE)?;
|
||||
|
|
||||
|
half_v0
|
||||
|
.negate(c1_cs.ns(|| "neg1"))?
|
||||
|
.add(c1_cs.ns(|| "add1"), &v1)?
|
||||
|
.sub(c1_cs.ns(|| "sub2"), &one_third_v2)?
|
||||
|
.sub(c1_cs.ns(|| "sub3"), &one_sixth_v3)?
|
||||
|
.add(c1_cs.ns(|| "add4"), &two_v4)?
|
||||
|
.add(c1_cs.ns(|| "add5"), &non_residue_v4)?
|
||||
|
};
|
||||
|
|
||||
|
// -v0 + (1/2)v1 + (1/2)v2 −v4
|
||||
|
let c2 = {
|
||||
|
let mut c2_cs = cs.ns(|| "c2");
|
||||
|
let half_v2 = v2.mul_by_fp_constant(c2_cs.ns(|| "half_v2"), &two_inverse)?;
|
||||
|
half_v1
|
||||
|
.add(c2_cs.ns(|| "add1"), &half_v2)?
|
||||
|
.sub(c2_cs.ns(|| "sub1"), &v4)?
|
||||
|
.sub(c2_cs.ns(|| "sub2"), &v0)?
|
||||
|
};
|
||||
|
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
// 18 constaints, we can probably do better but not sure it's worth it.
|
||||
|
#[inline]
|
||||
|
fn inverse<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
|
||||
|
self.get_value().and_then(|val| val.inverse()).get()
|
||||
|
})?;
|
||||
|
let one = Self::one(cs.ns(|| "one"))?;
|
||||
|
inverse.mul_equals(cs.ns(|| "check inverse"), &self, &one)?;
|
||||
|
Ok(inverse)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Fp6<P>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = self.c0.add_constant(cs.ns(|| "c0"), &other.c0)?;
|
||||
|
let c1 = self.c1.add_constant(cs.ns(|| "c1"), &other.c1)?;
|
||||
|
let c2 = self.c2.add_constant(cs.ns(|| "c2"), &other.c2)?;
|
||||
|
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Fp6<P>,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?;
|
||||
|
self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?;
|
||||
|
self.c2.add_constant_in_place(cs.ns(|| "c2"), &other.c2)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
/// Use the Toom-Cook-3x method to compute multiplication.
|
||||
|
#[inline]
|
||||
|
fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Fp6<P>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
// Uses Toom-Cook-3x multiplication from
|
||||
|
//
|
||||
|
// Reference:
|
||||
|
// "Multiplication and Squaring on Pairing-Friendly Fields"
|
||||
|
// Devegili, OhEigeartaigh, Scott, Dahab
|
||||
|
|
||||
|
// v0 = a(0)b(0) = a0 * b0
|
||||
|
let v0 = self.c0.mul_by_constant(cs.ns(|| "v0"), &other.c0)?;
|
||||
|
|
||||
|
// v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2)
|
||||
|
let v1 = {
|
||||
|
let mut v1_cs = cs.ns(|| "v1");
|
||||
|
let mut a0_plus_a1_plus_a2 = self
|
||||
|
.c0
|
||||
|
.add(v1_cs.ns(|| "a0 + a1"), &self.c1)?
|
||||
|
.add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?;
|
||||
|
let b0_plus_b1_plus_b2 = other.c0 + &other.c1 + &other.c2;
|
||||
|
|
||||
|
a0_plus_a1_plus_a2.mul_by_constant_in_place(
|
||||
|
v1_cs.ns(|| "(a0 + a1 + a2)*(b0 + b1 + b2)"),
|
||||
|
&b0_plus_b1_plus_b2,
|
||||
|
)?;
|
||||
|
a0_plus_a1_plus_a2
|
||||
|
};
|
||||
|
|
||||
|
// v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2)
|
||||
|
let mut v2 = {
|
||||
|
let mut v2_cs = cs.ns(|| "v2");
|
||||
|
let mut a0_minus_a1_plus_a2 = self
|
||||
|
.c0
|
||||
|
.sub(v2_cs.ns(|| "sub1"), &self.c1)?
|
||||
|
.add(v2_cs.ns(|| "add2"), &self.c2)?;
|
||||
|
let b0_minus_b1_plus_b2 = other.c0 - &other.c1 + &other.c2;
|
||||
|
a0_minus_a1_plus_a2.mul_by_constant_in_place(
|
||||
|
v2_cs.ns(|| "(a0 - a1 + a2)*(b0 - b1 + b2)"),
|
||||
|
&b0_minus_b1_plus_b2,
|
||||
|
)?;
|
||||
|
a0_minus_a1_plus_a2
|
||||
|
};
|
||||
|
|
||||
|
// v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2)
|
||||
|
let mut v3 = {
|
||||
|
let mut v3_cs = cs.ns(|| "v3");
|
||||
|
let a1_double = self.c1.double(v3_cs.ns(|| "2a1"))?;
|
||||
|
let a2_quad = self
|
||||
|
.c2
|
||||
|
.double(v3_cs.ns(|| "2a2"))?
|
||||
|
.double(v3_cs.ns(|| "4a2"))?;
|
||||
|
let mut a0_plus_2_a1_plus_4_a2 = self
|
||||
|
.c0
|
||||
|
.add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)?
|
||||
|
.add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?;
|
||||
|
|
||||
|
let b1_double = other.c1.double();
|
||||
|
let b2_quad = other.c2.double().double();
|
||||
|
let b0_plus_2_b1_plus_4_b2 = other.c0 + &b1_double + &b2_quad;
|
||||
|
|
||||
|
a0_plus_2_a1_plus_4_a2.mul_by_constant_in_place(
|
||||
|
v3_cs.ns(|| "(a0 + 2a1 + 4a2)*(b0 + 2b1 + 4b2)"),
|
||||
|
&b0_plus_2_b1_plus_4_b2,
|
||||
|
)?;
|
||||
|
a0_plus_2_a1_plus_4_a2
|
||||
|
};
|
||||
|
|
||||
|
// v4 = a(∞)b(∞) = a2 * b2
|
||||
|
let v4 = self.c2.mul_by_constant(cs.ns(|| "v4"), &other.c2)?;
|
||||
|
|
||||
|
let two = <P::Fp2Params as Fp2Parameters>::Fp::one().double();
|
||||
|
let six = two.double() + &two;
|
||||
|
let mut two_and_six = [two, six];
|
||||
|
algebra::fields::batch_inversion(&mut two_and_six);
|
||||
|
let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]);
|
||||
|
|
||||
|
let mut half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?;
|
||||
|
let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?;
|
||||
|
let mut one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_6"), &six_inverse)?;
|
||||
|
let one_sixth_v3 = v3.mul_by_fp_constant_in_place(cs.ns(|| "v3_by_6"), &six_inverse)?;
|
||||
|
let two_v4 = v4.double(cs.ns(|| "2v4"))?;
|
||||
|
|
||||
|
// c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4)
|
||||
|
let c0 = {
|
||||
|
let mut c0_cs = cs.ns(|| "c0");
|
||||
|
|
||||
|
// No constraints, only get a linear combination back.
|
||||
|
let mut inner = half_v0
|
||||
|
.sub(c0_cs.ns(|| "sub1"), &half_v1)?
|
||||
|
.sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)?
|
||||
|
.add(c0_cs.ns(|| "add3"), &one_sixth_v3)?
|
||||
|
.sub(c0_cs.ns(|| "sub4"), &two_v4)?;
|
||||
|
let non_residue_times_inner =
|
||||
|
inner.mul_by_constant_in_place(&mut c0_cs, &P::NONRESIDUE)?;
|
||||
|
v0.add(c0_cs.ns(|| "add5"), non_residue_times_inner)?
|
||||
|
};
|
||||
|
|
||||
|
// −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4
|
||||
|
let c1 = {
|
||||
|
let mut c1_cs = cs.ns(|| "c1");
|
||||
|
let one_third_v2 = one_sixth_v2.double_in_place(c1_cs.ns(|| "double1"))?;
|
||||
|
let non_residue_v4 =
|
||||
|
v4.mul_by_constant(c1_cs.ns(|| "mul_by_const1"), &P::NONRESIDUE)?;
|
||||
|
|
||||
|
half_v0
|
||||
|
.negate_in_place(c1_cs.ns(|| "neg1"))?
|
||||
|
.add(c1_cs.ns(|| "add1"), &v1)?
|
||||
|
.sub(c1_cs.ns(|| "sub2"), one_third_v2)?
|
||||
|
.sub(c1_cs.ns(|| "sub3"), &one_sixth_v3)?
|
||||
|
.add(c1_cs.ns(|| "add4"), &two_v4)?
|
||||
|
.add(c1_cs.ns(|| "add5"), &non_residue_v4)?
|
||||
|
};
|
||||
|
|
||||
|
// -v0 + (1/2)v1 + (1/2)v2 −v4
|
||||
|
let c2 = {
|
||||
|
let mut c2_cs = cs.ns(|| "c2");
|
||||
|
let half_v2 = v2.mul_by_fp_constant_in_place(c2_cs.ns(|| "half_v2"), &two_inverse)?;
|
||||
|
half_v1
|
||||
|
.add(c2_cs.ns(|| "add1"), half_v2)?
|
||||
|
.sub(c2_cs.ns(|| "sub2"), &v4)?
|
||||
|
.sub(c2_cs.ns(|| "sub3"), &v0)?
|
||||
|
};
|
||||
|
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
power: usize,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut result = self.clone();
|
||||
|
result.frobenius_map_in_place(cs, power)?;
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
mut cs: CS,
|
||||
|
power: usize,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.c0.frobenius_map_in_place(&mut cs.ns(|| "c0"), power)?;
|
||||
|
self.c1.frobenius_map_in_place(&mut cs.ns(|| "c1"), power)?;
|
||||
|
self.c2.frobenius_map_in_place(&mut cs.ns(|| "c2"), power)?;
|
||||
|
|
||||
|
self.c1.mul_by_constant_in_place(
|
||||
|
cs.ns(|| "c1_power"),
|
||||
|
&P::FROBENIUS_COEFF_FP6_C1[power % 6],
|
||||
|
)?;
|
||||
|
self.c2.mul_by_constant_in_place(
|
||||
|
cs.ns(|| "c2_power"),
|
||||
|
&P::FROBENIUS_COEFF_FP6_C2[power % 6],
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_mul() -> usize {
|
||||
|
5 * Fp2Gadget::<P, ConstraintF>::cost_of_mul()
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_inv() -> usize {
|
||||
|
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> PartialEq for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
fn eq(&self, other: &Self) -> bool {
|
||||
|
self.c0 == other.c0 && self.c1 == other.c1 && self.c2 == other.c2
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> Eq for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
self.c0
|
||||
|
.conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?;
|
||||
|
self.c1
|
||||
|
.conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?;
|
||||
|
self.c2
|
||||
|
.conditional_enforce_equal(&mut cs.ns(|| "c2"), &other.c2, condition)?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
3 * <Fp2Gadget<P, ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?;
|
||||
|
self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?;
|
||||
|
self.c2.enforce_not_equal(&mut cs.ns(|| "c2"), &other.c2)?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
3 * <Fp2Gadget<P, ConstraintF> as NEqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
fn to_bits<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bits(&mut cs)?;
|
||||
|
let mut c1 = self.c1.to_bits(&mut cs)?;
|
||||
|
let mut c2 = self.c2.to_bits(cs)?;
|
||||
|
|
||||
|
c0.append(&mut c1);
|
||||
|
c0.append(&mut c2);
|
||||
|
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bits_strict(&mut cs)?;
|
||||
|
let mut c1 = self.c1.to_bits_strict(&mut cs)?;
|
||||
|
let mut c2 = self.c2.to_bits_strict(cs)?;
|
||||
|
|
||||
|
c0.append(&mut c1);
|
||||
|
c0.append(&mut c2);
|
||||
|
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?;
|
||||
|
let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?;
|
||||
|
let mut c2 = self.c2.to_bytes(cs.ns(|| "c2"))?;
|
||||
|
|
||||
|
c0.append(&mut c1);
|
||||
|
c0.append(&mut c2);
|
||||
|
|
||||
|
Ok(c0)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
self.to_bytes(cs)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> Clone for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
fn clone(&self) -> Self {
|
||||
|
Self::new(self.c0.clone(), self.c1.clone(), self.c2.clone())
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
cond: &Boolean,
|
||||
|
first: &Self,
|
||||
|
second: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
|
||||
|
&mut cs.ns(|| "c0"),
|
||||
|
cond,
|
||||
|
&first.c0,
|
||||
|
&second.c0,
|
||||
|
)?;
|
||||
|
let c1 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
|
||||
|
&mut cs.ns(|| "c1"),
|
||||
|
cond,
|
||||
|
&first.c1,
|
||||
|
&second.c1,
|
||||
|
)?;
|
||||
|
let c2 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
|
||||
|
&mut cs.ns(|| "c2"),
|
||||
|
cond,
|
||||
|
&first.c2,
|
||||
|
&second.c2,
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
3 * <Fp2Gadget<P, ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
type TableConstant = Fp6<P>;
|
||||
|
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
b: &[Boolean],
|
||||
|
c: &[Self::TableConstant],
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
||||
|
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
||||
|
let c2s = c.iter().map(|f| f.c2).collect::<Vec<_>>();
|
||||
|
let c0 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
|
||||
|
let c1 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
|
||||
|
let c2 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c2"), b, &c2s)?;
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
3 * <Fp2Gadget<P, ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF: PrimeField> AllocGadget<Fp6<P>, ConstraintF> for Fp6Gadget<P, ConstraintF>
|
||||
|
where
|
||||
|
P: Fp6Parameters,
|
||||
|
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<Fp6<P>>,
|
||||
|
{
|
||||
|
let (c0, c1, c2) = match value_gen() {
|
||||
|
Ok(fe) => {
|
||||
|
let fe = *fe.borrow();
|
||||
|
(Ok(fe.c0), Ok(fe.c1), Ok(fe.c2))
|
||||
|
},
|
||||
|
_ => (
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
),
|
||||
|
};
|
||||
|
|
||||
|
let c0 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
|
||||
|
let c1 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
|
||||
|
let c2 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c2"), || c2)?;
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<Fp6<P>>,
|
||||
|
{
|
||||
|
let (c0, c1, c2) = match value_gen() {
|
||||
|
Ok(fe) => {
|
||||
|
let fe = *fe.borrow();
|
||||
|
(Ok(fe.c0), Ok(fe.c1), Ok(fe.c2))
|
||||
|
},
|
||||
|
_ => (
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
Err(SynthesisError::AssignmentMissing),
|
||||
|
),
|
||||
|
};
|
||||
|
|
||||
|
let c0 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
|
||||
|
let c1 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
|
||||
|
let c2 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c2"), || c2)?;
|
||||
|
Ok(Self::new(c0, c1, c2))
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,6 @@ |
|||||
|
use algebra::fields::jubjub::fq::Fq;
|
||||
|
|
||||
|
use crate::fields::fp::FpGadget;
|
||||
|
|
||||
|
// JubJub Fq uses BLS12-381 Fr.
|
||||
|
pub type FqGadget = FpGadget<Fq>;
|
@ -0,0 +1,512 @@ |
|||||
|
// use std::ops::{Mul, MulAssign};
|
||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use std::fmt::Debug;
|
||||
|
|
||||
|
use crate::prelude::*;
|
||||
|
|
||||
|
pub mod fp;
|
||||
|
pub mod fp12;
|
||||
|
pub mod fp2;
|
||||
|
pub mod fp6_3over2;
|
||||
|
|
||||
|
pub mod bls12_377;
|
||||
|
pub mod edwards_bls12;
|
||||
|
pub mod edwards_sw6;
|
||||
|
pub mod jubjub;
|
||||
|
|
||||
|
pub trait FieldGadget<F: Field, ConstraintF: Field>: |
||||
|
Sized
|
||||
|
+ Clone
|
||||
|
+ EqGadget<ConstraintF>
|
||||
|
+ NEqGadget<ConstraintF>
|
||||
|
+ ConditionalEqGadget<ConstraintF>
|
||||
|
+ ToBitsGadget<ConstraintF>
|
||||
|
+ AllocGadget<F, ConstraintF>
|
||||
|
+ ToBytesGadget<ConstraintF>
|
||||
|
+ CondSelectGadget<ConstraintF>
|
||||
|
+ TwoBitLookupGadget<ConstraintF, TableConstant = F>
|
||||
|
+ Debug
|
||||
|
{
|
||||
|
type Variable: Clone + Debug;
|
||||
|
|
||||
|
fn get_value(&self) -> Option<F>;
|
||||
|
|
||||
|
fn get_variable(&self) -> Self::Variable;
|
||||
|
|
||||
|
fn zero<CS: ConstraintSystem<ConstraintF>>(_: CS) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn one<CS: ConstraintSystem<ConstraintF>>(_: CS) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn add<CS: ConstraintSystem<ConstraintF>>(&self, _: CS, _: &Self) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn add_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
*self = self.add(cs, other)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn double<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
self.add(cs, &self)
|
||||
|
}
|
||||
|
|
||||
|
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
*self = self.double(cs)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn sub<CS: ConstraintSystem<ConstraintF>>(&self, _: CS, _: &Self) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn sub_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
*self = self.sub(cs, other)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn negate<CS: ConstraintSystem<ConstraintF>>(&self, _: CS) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
*self = self.negate(cs)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn mul<CS: ConstraintSystem<ConstraintF>>(&self, _: CS, _: &Self) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn mul_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
*self = self.mul(cs, other)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn square<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
self.mul(cs, &self)
|
||||
|
}
|
||||
|
|
||||
|
fn square_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
*self = self.square(cs)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
result: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
let actual_result = self.mul(cs.ns(|| "calc_actual_result"), other)?;
|
||||
|
result.enforce_equal(&mut cs.ns(|| "test_equals"), &actual_result)
|
||||
|
}
|
||||
|
|
||||
|
fn square_equals<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
result: &Self,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
let actual_result = self.square(cs.ns(|| "calc_actual_result"))?;
|
||||
|
result.enforce_equal(&mut cs.ns(|| "test_equals"), &actual_result)
|
||||
|
}
|
||||
|
|
||||
|
fn add_constant<CS: ConstraintSystem<ConstraintF>>(&self, _: CS, _: &F) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
other: &F,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
*self = self.add_constant(cs, other)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn sub_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
fe: &F,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
self.add_constant(cs, &(-(*fe)))
|
||||
|
}
|
||||
|
|
||||
|
fn sub_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
other: &F,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
self.add_constant_in_place(cs, &(-(*other)))
|
||||
|
}
|
||||
|
|
||||
|
fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
_: CS,
|
||||
|
_: &F,
|
||||
|
) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn mul_by_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
other: &F,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
*self = self.mul_by_constant(cs, other)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
fn inverse<CS: ConstraintSystem<ConstraintF>>(&self, _: CS) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
_: CS,
|
||||
|
power: usize,
|
||||
|
) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&mut self,
|
||||
|
cs: CS,
|
||||
|
power: usize,
|
||||
|
) -> Result<&mut Self, SynthesisError> {
|
||||
|
*self = self.frobenius_map(cs, power)?;
|
||||
|
Ok(self)
|
||||
|
}
|
||||
|
|
||||
|
/// Accepts as input a list of bits which, when interpreted in big-endian
|
||||
|
/// form, are a scalar.
|
||||
|
#[inline]
|
||||
|
fn pow<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
bits: &[Boolean],
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut res = Self::one(cs.ns(|| "Alloc result"))?;
|
||||
|
for (i, bit) in bits.into_iter().enumerate() {
|
||||
|
res = res.square(cs.ns(|| format!("Double {}", i)))?;
|
||||
|
let tmp = res.mul(cs.ns(|| format!("Add {}-th base power", i)), self)?;
|
||||
|
res = Self::conditionally_select(
|
||||
|
cs.ns(|| format!("Conditional Select {}", i)),
|
||||
|
bit,
|
||||
|
&tmp,
|
||||
|
&res,
|
||||
|
)?;
|
||||
|
}
|
||||
|
Ok(res)
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_mul() -> usize;
|
||||
|
|
||||
|
fn cost_of_inv() -> usize;
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use rand::{self, thread_rng, Rand, SeedableRng, XorShiftRng};
|
||||
|
|
||||
|
use super::FieldGadget;
|
||||
|
use crate::{
|
||||
|
bits::boolean::Boolean, test_constraint_system::TestConstraintSystem, utils::AllocGadget,
|
||||
|
};
|
||||
|
use algebra::{fields::Field, BitIterator};
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
fn field_test<FE: Field, ConstraintF: Field, F: FieldGadget<FE, ConstraintF>, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
a: F,
|
||||
|
b: F,
|
||||
|
) {
|
||||
|
let a_native = a.get_value().unwrap();
|
||||
|
let b_native = b.get_value().unwrap();
|
||||
|
|
||||
|
let zero = F::zero(cs.ns(|| "zero")).unwrap();
|
||||
|
let zero_native = zero.get_value().unwrap();
|
||||
|
zero.enforce_equal(&mut cs.ns(|| "zero_equals?"), &zero)
|
||||
|
.unwrap();
|
||||
|
assert_eq!(zero, zero);
|
||||
|
|
||||
|
let one = F::one(cs.ns(|| "one")).unwrap();
|
||||
|
let one_native = one.get_value().unwrap();
|
||||
|
assert_eq!(one, one);
|
||||
|
one.enforce_equal(&mut cs.ns(|| "one_equals?"), &one)
|
||||
|
.unwrap();
|
||||
|
assert_ne!(one, zero);
|
||||
|
|
||||
|
let one_dup = zero.add(cs.ns(|| "zero_plus_one"), &one).unwrap();
|
||||
|
one_dup
|
||||
|
.enforce_equal(&mut cs.ns(|| "one_plus_zero_equals"), &one)
|
||||
|
.unwrap();
|
||||
|
assert_eq!(one_dup, one);
|
||||
|
|
||||
|
let two = one.add(cs.ns(|| "one_plus_one"), &one).unwrap();
|
||||
|
two.enforce_equal(&mut cs.ns(|| "two_equals?"), &two)
|
||||
|
.unwrap();
|
||||
|
assert_eq!(two, two);
|
||||
|
assert_ne!(zero, two);
|
||||
|
assert_ne!(one, two);
|
||||
|
|
||||
|
// a == a
|
||||
|
assert_eq!(a, a);
|
||||
|
|
||||
|
// a + 0 = a
|
||||
|
let a_plus_zero = a.add(cs.ns(|| "a_plus_zero"), &zero).unwrap();
|
||||
|
assert_eq!(a_plus_zero, a);
|
||||
|
assert_eq!(a_plus_zero.get_value().unwrap(), a_native);
|
||||
|
a_plus_zero
|
||||
|
.enforce_equal(&mut cs.ns(|| "a_plus_zero_equals?"), &a)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
// a - 0 = a
|
||||
|
let a_minus_zero = a.sub(cs.ns(|| "a_minus_zero"), &zero).unwrap();
|
||||
|
assert_eq!(a_minus_zero, a);
|
||||
|
assert_eq!(a_minus_zero.get_value().unwrap(), a_native);
|
||||
|
a_minus_zero
|
||||
|
.enforce_equal(&mut cs.ns(|| "a_minus_zero_equals?"), &a)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
// a - a = 0
|
||||
|
let a_minus_a = a.sub(cs.ns(|| "a_minus_a"), &a).unwrap();
|
||||
|
assert_eq!(a_minus_a, zero);
|
||||
|
assert_eq!(a_minus_a.get_value().unwrap(), zero_native);
|
||||
|
a_minus_a
|
||||
|
.enforce_equal(&mut cs.ns(|| "a_minus_a_equals?"), &zero)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
// a + b = b + a
|
||||
|
let a_b = a.add(cs.ns(|| "a_plus_b"), &b).unwrap();
|
||||
|
let b_a = b.add(cs.ns(|| "b_plus_a"), &a).unwrap();
|
||||
|
assert_eq!(a_b, b_a);
|
||||
|
assert_eq!(a_b.get_value().unwrap(), a_native + &b_native);
|
||||
|
a_b.enforce_equal(&mut cs.ns(|| "a+b == b+a"), &b_a)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
// (a + b) + a = a + (b + a)
|
||||
|
let ab_a = a_b.add(cs.ns(|| "a_b_plus_a"), &a).unwrap();
|
||||
|
let a_ba = a.add(cs.ns(|| "a_plus_b_a"), &b_a).unwrap();
|
||||
|
assert_eq!(ab_a, a_ba);
|
||||
|
assert_eq!(ab_a.get_value().unwrap(), a_native + &b_native + &a_native);
|
||||
|
ab_a.enforce_equal(&mut cs.ns(|| "a+b + a == a+ b+a"), &a_ba)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
let b_times_a_plus_b = a_b.mul(cs.ns(|| "b * (a + b)"), &b).unwrap();
|
||||
|
let b_times_b_plus_a = b_a.mul(cs.ns(|| "b * (b + a)"), &b).unwrap();
|
||||
|
assert_eq!(b_times_b_plus_a, b_times_a_plus_b);
|
||||
|
assert_eq!(
|
||||
|
b_times_a_plus_b.get_value().unwrap(),
|
||||
|
b_native * &(b_native + &a_native)
|
||||
|
);
|
||||
|
assert_eq!(
|
||||
|
b_times_a_plus_b.get_value().unwrap(),
|
||||
|
(b_native + &a_native) * &b_native
|
||||
|
);
|
||||
|
assert_eq!(
|
||||
|
b_times_a_plus_b.get_value().unwrap(),
|
||||
|
(a_native + &b_native) * &b_native
|
||||
|
);
|
||||
|
b_times_b_plus_a
|
||||
|
.enforce_equal(&mut cs.ns(|| "b*(a+b) == b * (b+a)"), &b_times_a_plus_b)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
// a * 0 = 0
|
||||
|
assert_eq!(a.mul(cs.ns(|| "a_times_zero"), &zero).unwrap(), zero);
|
||||
|
|
||||
|
// a * 1 = a
|
||||
|
assert_eq!(a.mul(cs.ns(|| "a_times_one"), &one).unwrap(), a);
|
||||
|
assert_eq!(
|
||||
|
a.mul(cs.ns(|| "a_times_one2"), &one)
|
||||
|
.unwrap()
|
||||
|
.get_value()
|
||||
|
.unwrap(),
|
||||
|
a_native * &one_native
|
||||
|
);
|
||||
|
|
||||
|
// a * b = b * a
|
||||
|
let ab = a.mul(cs.ns(|| "a_times_b"), &b).unwrap();
|
||||
|
let ba = b.mul(cs.ns(|| "b_times_a"), &a).unwrap();
|
||||
|
assert_eq!(ab, ba);
|
||||
|
assert_eq!(ab.get_value().unwrap(), a_native * &b_native);
|
||||
|
|
||||
|
// (a * b) * a = a * (b * a)
|
||||
|
let ab_a = ab.mul(cs.ns(|| "ab_times_a"), &a).unwrap();
|
||||
|
let a_ba = a.mul(cs.ns(|| "a_times_ba"), &ba).unwrap();
|
||||
|
assert_eq!(ab_a, a_ba);
|
||||
|
assert_eq!(ab_a.get_value().unwrap(), a_native * &b_native * &a_native);
|
||||
|
|
||||
|
let aa = a.mul(cs.ns(|| "a * a"), &a).unwrap();
|
||||
|
let a_squared = a.square(cs.ns(|| "a^2")).unwrap();
|
||||
|
a_squared
|
||||
|
.enforce_equal(&mut cs.ns(|| "a^2 == a*a"), &aa)
|
||||
|
.unwrap();
|
||||
|
assert_eq!(aa, a_squared);
|
||||
|
assert_eq!(aa.get_value().unwrap(), a_native.square());
|
||||
|
|
||||
|
let aa = a
|
||||
|
.mul_by_constant(cs.ns(|| "a * a via mul_by_const"), &a.get_value().unwrap())
|
||||
|
.unwrap();
|
||||
|
a_squared
|
||||
|
.enforce_equal(&mut cs.ns(|| "a^2 == a*a via mul_by_const"), &aa)
|
||||
|
.unwrap();
|
||||
|
assert_eq!(aa, a_squared);
|
||||
|
assert_eq!(aa.get_value().unwrap(), a_native.square());
|
||||
|
|
||||
|
let a_b2 = a
|
||||
|
.add_constant(cs.ns(|| "a + b via add_const"), &b.get_value().unwrap())
|
||||
|
.unwrap();
|
||||
|
a_b.enforce_equal(&mut cs.ns(|| "a + b == a + b via add_const"), &a_b2)
|
||||
|
.unwrap();
|
||||
|
assert_eq!(a_b, a_b2);
|
||||
|
|
||||
|
let a_inv = a.inverse(cs.ns(|| "a_inv")).unwrap();
|
||||
|
a_inv
|
||||
|
.mul_equals(cs.ns(|| "check_equals"), &a, &one)
|
||||
|
.unwrap();
|
||||
|
assert_eq!(
|
||||
|
a_inv.get_value().unwrap(),
|
||||
|
a.get_value().unwrap().inverse().unwrap()
|
||||
|
);
|
||||
|
assert_eq!(a_inv.get_value().unwrap(), a_native.inverse().unwrap());
|
||||
|
// a * a * a = a^3
|
||||
|
let bits = BitIterator::new([0x3])
|
||||
|
.map(|bit| Boolean::constant(bit))
|
||||
|
.collect::<Vec<_>>();
|
||||
|
assert_eq!(
|
||||
|
a_native * &(a_native * &a_native),
|
||||
|
a.pow(cs.ns(|| "test_pow"), &bits)
|
||||
|
.unwrap()
|
||||
|
.get_value()
|
||||
|
.unwrap()
|
||||
|
);
|
||||
|
|
||||
|
// a * a * a = a^3
|
||||
|
let mut constants = [FE::zero(); 4];
|
||||
|
for c in &mut constants {
|
||||
|
*c = rand::random();
|
||||
|
println!("Current c[i]: {:?}", c);
|
||||
|
}
|
||||
|
let bits = [Boolean::constant(false), Boolean::constant(true)];
|
||||
|
let lookup_result =
|
||||
|
F::two_bit_lookup(cs.ns(|| "Lookup"), &bits, constants.as_ref()).unwrap();
|
||||
|
assert_eq!(lookup_result.get_value().unwrap(), constants[2]);
|
||||
|
|
||||
|
let negone: FE = rand::random();
|
||||
|
|
||||
|
let n = F::alloc(&mut cs.ns(|| "alloc new var"), || Ok(negone)).unwrap();
|
||||
|
let _ = n.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap();
|
||||
|
let _ = n.to_bytes_strict(&mut cs.ns(|| "ToBytes Strict")).unwrap();
|
||||
|
}
|
||||
|
|
||||
|
fn random_frobenius_tests<
|
||||
|
FE: Field,
|
||||
|
ConstraintF: Field,
|
||||
|
F: FieldGadget<FE, ConstraintF>,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
>(
|
||||
|
mut cs: CS,
|
||||
|
maxpower: usize,
|
||||
|
) {
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
for i in 0..(maxpower + 1) {
|
||||
|
let mut a = FE::rand(&mut rng);
|
||||
|
let mut a_gadget = F::alloc(cs.ns(|| format!("a_gadget_{:?}", i)), || Ok(a)).unwrap();
|
||||
|
a_gadget = a_gadget
|
||||
|
.frobenius_map(cs.ns(|| format!("frob_map_{}", i)), i)
|
||||
|
.unwrap();
|
||||
|
a.frobenius_map(i);
|
||||
|
|
||||
|
assert_eq!(a_gadget.get_value().unwrap(), a);
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn bls12_377_field_gadgets_test() {
|
||||
|
use crate::fields::bls12_377::{Fq12Gadget, Fq2Gadget, Fq6Gadget, FqGadget};
|
||||
|
use algebra::fields::bls12_377::{Fq, Fq12, Fq2, Fq6};
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
|
||||
|
|
||||
|
let a = FqGadget::alloc(&mut cs.ns(|| "generate_a"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
|
let b = FqGadget::alloc(&mut cs.ns(|| "generate_b"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
|
field_test(cs.ns(|| "test_fq"), a, b);
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
|
}
|
||||
|
|
||||
|
let c = Fq2Gadget::alloc(&mut cs.ns(|| "generate_c"), || Ok(Fq2::rand(&mut rng))).unwrap();
|
||||
|
let d = Fq2Gadget::alloc(&mut cs.ns(|| "generate_d"), || Ok(Fq2::rand(&mut rng))).unwrap();
|
||||
|
field_test(cs.ns(|| "test_fq2"), c, d);
|
||||
|
random_frobenius_tests::<Fq2, _, Fq2Gadget, _>(cs.ns(|| "test_frob_fq2"), 13);
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
|
}
|
||||
|
|
||||
|
let a = Fq6Gadget::alloc(&mut cs.ns(|| "generate_e"), || Ok(Fq6::rand(&mut rng))).unwrap();
|
||||
|
let b = Fq6Gadget::alloc(&mut cs.ns(|| "generate_f"), || Ok(Fq6::rand(&mut rng))).unwrap();
|
||||
|
field_test(cs.ns(|| "test_fq6"), a, b);
|
||||
|
random_frobenius_tests::<Fq6, _, Fq6Gadget, _>(cs.ns(|| "test_frob_fq6"), 13);
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
|
}
|
||||
|
|
||||
|
let c =
|
||||
|
Fq12Gadget::alloc(&mut cs.ns(|| "generate_g"), || Ok(Fq12::rand(&mut rng))).unwrap();
|
||||
|
let d =
|
||||
|
Fq12Gadget::alloc(&mut cs.ns(|| "generate_h"), || Ok(Fq12::rand(&mut rng))).unwrap();
|
||||
|
field_test(cs.ns(|| "test_fq12"), c, d);
|
||||
|
random_frobenius_tests::<Fq12, _, Fq12Gadget, _>(cs.ns(|| "test_frob_fq12"), 13);
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("Here!");
|
||||
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
|
}
|
||||
|
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn jubjub_field_gadgets_test() {
|
||||
|
use crate::fields::jubjub::FqGadget;
|
||||
|
use algebra::fields::jubjub::fq::Fq;
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
let mut rng = thread_rng();
|
||||
|
|
||||
|
let a = FqGadget::alloc(&mut cs.ns(|| "generate_a"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
|
let b = FqGadget::alloc(&mut cs.ns(|| "generate_b"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
|
field_test(cs.ns(|| "test_fq"), a, b);
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
|
}
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn edwards_field_gadgets_test() {
|
||||
|
use crate::fields::edwards_bls12::FqGadget;
|
||||
|
use algebra::fields::edwards_bls12::fq::Fq;
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
let mut rng = thread_rng();
|
||||
|
|
||||
|
let a = FqGadget::alloc(&mut cs.ns(|| "generate_a"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
|
let b = FqGadget::alloc(&mut cs.ns(|| "generate_b"), || Ok(Fq::rand(&mut rng))).unwrap();
|
||||
|
field_test(cs.ns(|| "test_fq"), a, b);
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
|
}
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,2 @@ |
|||||
|
pub mod short_weierstrass;
|
||||
|
pub mod twisted_edwards;
|
@ -0,0 +1,213 @@ |
|||||
|
use crate::groups::bls12::{
|
||||
|
G1Gadget as Bls12G1Gadget, G1PreparedGadget as Bls12G1PreparedGadget,
|
||||
|
G2Gadget as Bls12G2Gadget, G2PreparedGadget as Bls12G2PreparedGadget,
|
||||
|
};
|
||||
|
use algebra::curves::bls12_377::Bls12_377Parameters;
|
||||
|
|
||||
|
pub type G1Gadget = Bls12G1Gadget<Bls12_377Parameters>;
|
||||
|
pub type G2Gadget = Bls12G2Gadget<Bls12_377Parameters>;
|
||||
|
|
||||
|
pub type G1PreparedGadget = Bls12G1PreparedGadget<Bls12_377Parameters>;
|
||||
|
pub type G2PreparedGadget = Bls12G2PreparedGadget<Bls12_377Parameters>;
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use crate::fields::FieldGadget;
|
||||
|
use rand;
|
||||
|
|
||||
|
use super::{G1Gadget, G2Gadget};
|
||||
|
use crate::{
|
||||
|
boolean::Boolean,
|
||||
|
groups::GroupGadget,
|
||||
|
test_constraint_system::TestConstraintSystem,
|
||||
|
utils::{AllocGadget, CondSelectGadget, EqGadget},
|
||||
|
};
|
||||
|
use algebra::{
|
||||
|
curves::bls12_377::{G1Projective as G1, G2Projective as G2},
|
||||
|
fields::bls12_377::Fr,
|
||||
|
fields::bls12_377::Fq,
|
||||
|
AffineCurve, BitIterator, PrimeField, ProjectiveCurve,
|
||||
|
};
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn bls12_g1_constraint_costs() {
|
||||
|
use crate::boolean::AllocatedBit;
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
let bit = AllocatedBit::alloc(&mut cs.ns(|| "bool"), || Ok(true))
|
||||
|
.unwrap()
|
||||
|
.into();
|
||||
|
|
||||
|
let a: G1 = rand::random();
|
||||
|
let b: G1 = rand::random();
|
||||
|
let gadget_a = G1Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap();
|
||||
|
let gadget_b = G1Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap();
|
||||
|
let alloc_cost = cs.num_constraints();
|
||||
|
let _ = G1Gadget::conditionally_select(
|
||||
|
&mut cs.ns(|| "cond_select"),
|
||||
|
&bit,
|
||||
|
&gadget_a,
|
||||
|
&gadget_b,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
let cond_select_cost = cs.num_constraints() - alloc_cost;
|
||||
|
|
||||
|
let _ = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap();
|
||||
|
let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost;
|
||||
|
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
assert_eq!(
|
||||
|
cond_select_cost,
|
||||
|
<G1Gadget as CondSelectGadget<Fq>>::cost()
|
||||
|
);
|
||||
|
assert_eq!(add_cost, G1Gadget::cost_of_add());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn bls12_g2_constraint_costs() {
|
||||
|
use crate::boolean::AllocatedBit;
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
let bit = AllocatedBit::alloc(&mut cs.ns(|| "bool"), || Ok(true))
|
||||
|
.unwrap()
|
||||
|
.into();
|
||||
|
|
||||
|
let a: G2 = rand::random();
|
||||
|
let b: G2 = rand::random();
|
||||
|
let gadget_a = G2Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap();
|
||||
|
let gadget_b = G2Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap();
|
||||
|
let alloc_cost = cs.num_constraints();
|
||||
|
let _ = G2Gadget::conditionally_select(
|
||||
|
&mut cs.ns(|| "cond_select"),
|
||||
|
&bit,
|
||||
|
&gadget_a,
|
||||
|
&gadget_b,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
let cond_select_cost = cs.num_constraints() - alloc_cost;
|
||||
|
|
||||
|
let _ = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap();
|
||||
|
let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost;
|
||||
|
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
assert_eq!(
|
||||
|
cond_select_cost,
|
||||
|
<G2Gadget as CondSelectGadget<Fq>>::cost()
|
||||
|
);
|
||||
|
assert_eq!(add_cost, G2Gadget::cost_of_add());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn bls12_g1_gadget_test() {
|
||||
|
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
let a = G1::rand(&mut rng);
|
||||
|
let b = G1::rand(&mut rng);
|
||||
|
let a_affine = a.into_affine();
|
||||
|
let b_affine = b.into_affine();
|
||||
|
let mut gadget_a = G1Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap();
|
||||
|
let gadget_b = G1Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap();
|
||||
|
assert_eq!(gadget_a.x.value.unwrap(), a_affine.x);
|
||||
|
assert_eq!(gadget_a.y.value.unwrap(), a_affine.y);
|
||||
|
assert_eq!(gadget_b.x.value.unwrap(), b_affine.x);
|
||||
|
assert_eq!(gadget_b.y.value.unwrap(), b_affine.y);
|
||||
|
|
||||
|
// Check addition
|
||||
|
let ab = a + &b;
|
||||
|
let ab_affine = ab.into_affine();
|
||||
|
let gadget_ab = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap();
|
||||
|
let gadget_ba = gadget_b.add(&mut cs.ns(|| "ba"), &gadget_a).unwrap();
|
||||
|
gadget_ba
|
||||
|
.enforce_equal(&mut cs.ns(|| "b + a == a + b?"), &gadget_ab)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
let ab_val = gadget_ab
|
||||
|
.get_value()
|
||||
|
.expect("Doubling should be successful")
|
||||
|
.into_affine();
|
||||
|
assert_eq!(ab_val, ab_affine, "Result of addition is unequal");
|
||||
|
|
||||
|
// Check doubling
|
||||
|
let aa = a.double();
|
||||
|
let aa_affine = aa.into_affine();
|
||||
|
gadget_a.double_in_place(&mut cs.ns(|| "2a")).unwrap();
|
||||
|
let aa_val = gadget_a
|
||||
|
.get_value()
|
||||
|
.expect("Doubling should be successful")
|
||||
|
.into_affine();
|
||||
|
assert_eq!(
|
||||
|
aa_val, aa_affine,
|
||||
|
"Gadget and native values are unequal after double."
|
||||
|
);
|
||||
|
|
||||
|
// Check mul_bits
|
||||
|
let scalar = Fr::rand(&mut rng);
|
||||
|
let native_result = aa.into_affine().mul(scalar) + &b;
|
||||
|
let native_result = native_result.into_affine();
|
||||
|
|
||||
|
let mut scalar: Vec<bool> = BitIterator::new(scalar.into_repr()).collect();
|
||||
|
// Get the scalar bits into little-endian form.
|
||||
|
scalar.reverse();
|
||||
|
let input = Vec::<Boolean>::alloc(cs.ns(|| "Input"), || Ok(scalar)).unwrap();
|
||||
|
let result = gadget_a
|
||||
|
.mul_bits(cs.ns(|| "mul_bits"), &gadget_b, input.iter())
|
||||
|
.unwrap();
|
||||
|
let result_val = result.get_value().unwrap().into_affine();
|
||||
|
assert_eq!(
|
||||
|
result_val, native_result,
|
||||
|
"gadget & native values are diff. after scalar mul"
|
||||
|
);
|
||||
|
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
|
}
|
||||
|
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn bls12_g2_gadget_test() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
let a: G2 = rand::random();
|
||||
|
let b: G2 = rand::random();
|
||||
|
let a_affine = a.into_affine();
|
||||
|
let b_affine = b.into_affine();
|
||||
|
|
||||
|
let mut gadget_a = G2Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap();
|
||||
|
let gadget_b = G2Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap();
|
||||
|
assert_eq!(gadget_a.x.get_value().unwrap(), a_affine.x);
|
||||
|
assert_eq!(gadget_a.y.get_value().unwrap(), a_affine.y);
|
||||
|
assert_eq!(gadget_b.x.get_value().unwrap(), b_affine.x);
|
||||
|
assert_eq!(gadget_b.y.get_value().unwrap(), b_affine.y);
|
||||
|
|
||||
|
let ab = a + &b;
|
||||
|
let ab_affine = ab.into_affine();
|
||||
|
let gadget_ab = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap();
|
||||
|
let gadget_ba = gadget_b.add(&mut cs.ns(|| "ba"), &gadget_a).unwrap();
|
||||
|
gadget_ba
|
||||
|
.enforce_equal(&mut cs.ns(|| "b + a == a + b?"), &gadget_ab)
|
||||
|
.unwrap();
|
||||
|
assert_eq!(gadget_ab.x.get_value().unwrap(), ab_affine.x);
|
||||
|
assert_eq!(gadget_ab.y.get_value().unwrap(), ab_affine.y);
|
||||
|
|
||||
|
let aa = a.double();
|
||||
|
let aa_affine = aa.into_affine();
|
||||
|
gadget_a.double_in_place(&mut cs.ns(|| "2a")).unwrap();
|
||||
|
|
||||
|
assert_eq!(gadget_a.x.get_value().unwrap(), aa_affine.x);
|
||||
|
assert_eq!(gadget_a.y.get_value().unwrap(), aa_affine.y);
|
||||
|
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
|
}
|
||||
|
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,168 @@ |
|||||
|
use algebra::{
|
||||
|
curves::bls12::{Bls12Parameters, G1Prepared, TwistType},
|
||||
|
fields::Field,
|
||||
|
BitIterator, ProjectiveCurve,
|
||||
|
};
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
|
||||
|
use crate::prelude::*;
|
||||
|
use crate::fields::{fp::FpGadget, fp2::Fp2Gadget, FieldGadget};
|
||||
|
use crate::groups::curves::short_weierstrass::AffineGadget;
|
||||
|
|
||||
|
use std::fmt::Debug;
|
||||
|
|
||||
|
pub mod bls12_377;
|
||||
|
|
||||
|
pub type G1Gadget<P> = AffineGadget<<P as Bls12Parameters>::G1Parameters, <P as Bls12Parameters>::Fp, FpGadget<<P as Bls12Parameters>::Fp>>;
|
||||
|
pub type G2Gadget<P> = AffineGadget<<P as Bls12Parameters>::G2Parameters, <P as Bls12Parameters>::Fp, Fp2G<P>>;
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(
|
||||
|
Clone(bound = "G1Gadget<P>: Clone"),
|
||||
|
Debug(bound = "G1Gadget<P>: Debug")
|
||||
|
)]
|
||||
|
pub struct G1PreparedGadget<P: Bls12Parameters>(pub G1Gadget<P>);
|
||||
|
|
||||
|
impl<P: Bls12Parameters> G1PreparedGadget<P> {
|
||||
|
pub fn get_value(&self) -> Option<G1Prepared<P>> {
|
||||
|
Some(G1Prepared::from_affine(
|
||||
|
self.0.get_value().unwrap().into_affine(),
|
||||
|
))
|
||||
|
}
|
||||
|
|
||||
|
pub fn from_affine<CS: ConstraintSystem<P::Fp>>(
|
||||
|
_cs: CS,
|
||||
|
q: &G1Gadget<P>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
Ok(G1PreparedGadget(q.clone()))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Bls12Parameters> ToBytesGadget<P::Fp> for G1PreparedGadget<P> {
|
||||
|
#[inline]
|
||||
|
fn to_bytes<CS: ConstraintSystem<P::Fp>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
self.0.to_bytes(&mut cs.ns(|| "g_alpha to bytes"))
|
||||
|
}
|
||||
|
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<P::Fp>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
self.to_bytes(cs)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
type Fp2G<P> = Fp2Gadget<<P as Bls12Parameters>::Fp2Params, <P as Bls12Parameters>::Fp>;
|
||||
|
type LCoeff<P> = (Fp2G<P>, Fp2G<P>);
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(
|
||||
|
Clone(bound = "Fp2Gadget<P::Fp2Params, P::Fp>: Clone"),
|
||||
|
Debug(bound = "Fp2Gadget<P::Fp2Params, P::Fp>: Debug")
|
||||
|
)]
|
||||
|
pub struct G2PreparedGadget<P: Bls12Parameters> {
|
||||
|
pub ell_coeffs: Vec<LCoeff<P>>,
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Bls12Parameters> ToBytesGadget<P::Fp> for G2PreparedGadget<P> {
|
||||
|
#[inline]
|
||||
|
fn to_bytes<CS: ConstraintSystem<P::Fp>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let mut bytes = Vec::new();
|
||||
|
for (i, coeffs) in self.ell_coeffs.iter().enumerate() {
|
||||
|
let mut cs = cs.ns(|| format!("Iteration {}", i));
|
||||
|
bytes.extend_from_slice(&coeffs.0.to_bytes(&mut cs.ns(|| "c0"))?);
|
||||
|
bytes.extend_from_slice(&coeffs.1.to_bytes(&mut cs.ns(|| "c1"))?);
|
||||
|
}
|
||||
|
Ok(bytes)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<P::Fp>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
self.to_bytes(cs)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Bls12Parameters> G2PreparedGadget<P> {
|
||||
|
pub fn from_affine<CS: ConstraintSystem<P::Fp>>(
|
||||
|
mut cs: CS,
|
||||
|
q: &G2Gadget<P>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let two_inv = P::Fp::one().double().inverse().unwrap();
|
||||
|
let zero = G2Gadget::<P>::zero(cs.ns(|| "zero"))?;
|
||||
|
q.enforce_not_equal(cs.ns(|| "enforce not zero"), &zero)?;
|
||||
|
let mut ell_coeffs = vec![];
|
||||
|
let mut r = q.clone();
|
||||
|
|
||||
|
for (j, i) in BitIterator::new(P::X).skip(1).enumerate() {
|
||||
|
let mut cs = cs.ns(|| format!("Iteration {}", j));
|
||||
|
ell_coeffs.push(Self::double(cs.ns(|| "double"), &mut r, &two_inv)?);
|
||||
|
|
||||
|
if i {
|
||||
|
ell_coeffs.push(Self::add(cs.ns(|| "add"), &mut r, &q)?);
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
Ok(Self { ell_coeffs })
|
||||
|
}
|
||||
|
|
||||
|
fn double<CS: ConstraintSystem<P::Fp>>(
|
||||
|
mut cs: CS,
|
||||
|
r: &mut G2Gadget<P>,
|
||||
|
two_inv: &P::Fp,
|
||||
|
) -> Result<LCoeff<P>, SynthesisError> {
|
||||
|
let a = r.y.inverse(cs.ns(|| "Inverse"))?;
|
||||
|
let mut b = r.x.square(cs.ns(|| "square x"))?;
|
||||
|
let b_tmp = b.clone();
|
||||
|
b.mul_by_fp_constant_in_place(cs.ns(|| "mul by two_inv"), two_inv)?;
|
||||
|
b.add_in_place(cs.ns(|| "compute b"), &b_tmp)?;
|
||||
|
|
||||
|
let c = a.mul(cs.ns(|| "compute c"), &b)?;
|
||||
|
let d = r.x.double(cs.ns(|| "compute d"))?;
|
||||
|
let x3 = c.square(cs.ns(|| "c^2"))?.sub(cs.ns(|| "sub d"), &d)?;
|
||||
|
let e = c
|
||||
|
.mul(cs.ns(|| "c*r.x"), &r.x)?
|
||||
|
.sub(cs.ns(|| "sub r.y"), &r.y)?;
|
||||
|
let c_x3 = c.mul(cs.ns(|| "c*x_3"), &x3)?;
|
||||
|
let y3 = e.sub(cs.ns(|| "e = c * x3"), &c_x3)?;
|
||||
|
let mut f = c;
|
||||
|
f.negate_in_place(cs.ns(|| "c = -c"))?;
|
||||
|
r.x = x3;
|
||||
|
r.y = y3;
|
||||
|
match P::TWIST_TYPE {
|
||||
|
TwistType::M => Ok((e, f)),
|
||||
|
TwistType::D => Ok((f, e)),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn add<CS: ConstraintSystem<P::Fp>>(
|
||||
|
mut cs: CS,
|
||||
|
r: &mut G2Gadget<P>,
|
||||
|
q: &G2Gadget<P>,
|
||||
|
) -> Result<LCoeff<P>, SynthesisError> {
|
||||
|
let a =
|
||||
|
q.x.sub(cs.ns(|| "q.x - r.x"), &r.x)?
|
||||
|
.inverse(cs.ns(|| "calc a"))?;
|
||||
|
let b = q.y.sub(cs.ns(|| "q.y - r.y"), &r.y)?;
|
||||
|
let c = a.mul(cs.ns(|| "compute c"), &b)?;
|
||||
|
let d = r.x.add(cs.ns(|| "r.x + q.x"), &q.x)?;
|
||||
|
let x3 = c.square(cs.ns(|| "c^2"))?.sub(cs.ns(|| "sub d"), &d)?;
|
||||
|
|
||||
|
let e =
|
||||
|
r.x.sub(cs.ns(|| "r.x - x3"), &x3)?
|
||||
|
.mul(cs.ns(|| "c * (r.x - x3)"), &c)?;
|
||||
|
let y3 = e.sub(cs.ns(|| "calc y3"), &r.y)?;
|
||||
|
let g = c
|
||||
|
.mul(cs.ns(|| "c*r.x"), &r.x)?
|
||||
|
.sub(cs.ns(|| "calc g"), &r.y)?;
|
||||
|
let mut f = c;
|
||||
|
f.negate_in_place(cs.ns(|| "c = -c"))?;
|
||||
|
r.x = x3;
|
||||
|
r.y = y3;
|
||||
|
match P::TWIST_TYPE {
|
||||
|
TwistType::M => Ok((g, f)),
|
||||
|
TwistType::D => Ok((f, g)),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,625 @@ |
|||||
|
use algebra::{AffineCurve, Field, ProjectiveCurve};
|
||||
|
use algebra::{
|
||||
|
curves::{
|
||||
|
short_weierstrass_jacobian::{GroupAffine as SWAffine, GroupProjective as SWProjective},
|
||||
|
SWModelParameters,
|
||||
|
},
|
||||
|
BitIterator, PrimeField,
|
||||
|
};
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use std::{borrow::Borrow, marker::PhantomData, ops::Neg};
|
||||
|
|
||||
|
use crate::Assignment;
|
||||
|
use crate::prelude::*;
|
||||
|
|
||||
|
pub mod bls12;
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Debug, Clone)]
|
||||
|
#[must_use]
|
||||
|
pub struct AffineGadget<P: SWModelParameters, ConstraintF: Field, F: FieldGadget<P::BaseField, ConstraintF>> {
|
||||
|
pub x: F,
|
||||
|
pub y: F,
|
||||
|
_params: PhantomData<P>,
|
||||
|
_engine: PhantomData<ConstraintF>,
|
||||
|
}
|
||||
|
|
||||
|
impl<P: SWModelParameters, 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<SWProjective<P>, 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<P, ConstraintF, F> PartialEq for AffineGadget<P, ConstraintF, F>
|
||||
|
where
|
||||
|
P: SWModelParameters,
|
||||
|
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: SWModelParameters,
|
||||
|
ConstraintF: Field,
|
||||
|
F: FieldGadget<P::BaseField, ConstraintF>,
|
||||
|
{
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF, F> GroupGadget<SWProjective<P>, ConstraintF> for AffineGadget<P, ConstraintF, F>
|
||||
|
where
|
||||
|
P: SWModelParameters,
|
||||
|
ConstraintF: Field,
|
||||
|
F: FieldGadget<P::BaseField, ConstraintF>,
|
||||
|
{
|
||||
|
type Value = SWProjective<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)) => {
|
||||
|
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<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
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<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
// 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<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &SWProjective<P>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
// 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<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&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<CS: ConstraintSystem<ConstraintF>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
|
||||
|
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<P, ConstraintF, F> CondSelectGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
|
||||
|
where
|
||||
|
P: SWModelParameters,
|
||||
|
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: SWModelParameters,
|
||||
|
ConstraintF: Field,
|
||||
|
F: FieldGadget<P::BaseField, ConstraintF>,
|
||||
|
{
|
||||
|
}
|
||||
|
|
||||
|
impl<P, ConstraintF, F> ConditionalEqGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
|
||||
|
where
|
||||
|
P: SWModelParameters,
|
||||
|
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: SWModelParameters,
|
||||
|
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> AllocGadget<SWProjective<P>, ConstraintF> for AffineGadget<P, ConstraintF, F>
|
||||
|
where
|
||||
|
P: SWModelParameters,
|
||||
|
ConstraintF: Field,
|
||||
|
F: FieldGadget<P::BaseField, ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn alloc<FN, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: FN,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<SWProjective<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),
|
||||
|
),
|
||||
|
};
|
||||
|
|
||||
|
// 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<FN, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: FN,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<SWProjective<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)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
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<SWProjective<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 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<P, ConstraintF, F> ToBitsGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
|
||||
|
where
|
||||
|
P: SWModelParameters,
|
||||
|
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(&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<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<Boolean>, 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<P, ConstraintF, F> ToBytesGadget<ConstraintF> for AffineGadget<P, ConstraintF, F>
|
||||
|
where
|
||||
|
P: SWModelParameters,
|
||||
|
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(&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<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, 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)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,32 @@ |
|||||
|
use crate::groups::curves::twisted_edwards::AffineGadget;
|
||||
|
use algebra::curves::edwards_bls12::EdwardsParameters;
|
||||
|
use algebra::fields::edwards_bls12::fq::Fq;
|
||||
|
|
||||
|
use crate::fields::edwards_bls12::FqGadget;
|
||||
|
|
||||
|
pub type EdwardsBlsGadget = AffineGadget<EdwardsParameters, Fq, FqGadget>;
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use super::EdwardsBlsGadget as EdwardsG;
|
||||
|
use crate::{
|
||||
|
groups::curves::twisted_edwards::test::{edwards_constraint_costs, edwards_test},
|
||||
|
test_constraint_system::TestConstraintSystem,
|
||||
|
};
|
||||
|
use algebra::fields::edwards_bls12::fq::Fq;
|
||||
|
use algebra::curves::edwards_bls12::EdwardsParameters;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn edwards_constraint_costs_test() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
edwards_constraint_costs::<_, EdwardsParameters, EdwardsG, _>(&mut cs);
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn edwards_bls12_gadget_test() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
edwards_test::<_, EdwardsParameters, EdwardsG, _>(&mut cs);
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,32 @@ |
|||||
|
use crate::groups::curves::twisted_edwards::AffineGadget;
|
||||
|
use algebra::curves::edwards_sw6::EdwardsParameters;
|
||||
|
use algebra::fields::edwards_sw6::fq::Fq;
|
||||
|
|
||||
|
use crate::fields::edwards_sw6::FqGadget;
|
||||
|
|
||||
|
pub type EdwardsSWGadget = AffineGadget<EdwardsParameters, Fq, FqGadget>;
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use super::EdwardsSWGadget as EdwardsG;
|
||||
|
use crate::{
|
||||
|
groups::curves::twisted_edwards::test::{edwards_constraint_costs, edwards_test},
|
||||
|
test_constraint_system::TestConstraintSystem,
|
||||
|
};
|
||||
|
use algebra::curves::edwards_sw6::EdwardsParameters;
|
||||
|
use algebra::fields::edwards_sw6::fq::Fq;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn edwards_constraint_costs_test() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
edwards_constraint_costs::<_, EdwardsParameters, EdwardsG, _>(&mut cs);
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn edwards_sw6_gadget_test() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
edwards_test::<_, EdwardsParameters, EdwardsG, _>(&mut cs);
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,32 @@ |
|||||
|
use crate::groups::curves::twisted_edwards::AffineGadget;
|
||||
|
use algebra::curves::jubjub::JubJubParameters;
|
||||
|
use algebra::fields::jubjub::fq::Fq;
|
||||
|
|
||||
|
use crate::fields::jubjub::FqGadget;
|
||||
|
|
||||
|
pub type JubJubGadget = AffineGadget<JubJubParameters, Fq, FqGadget>;
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use super::JubJubGadget as EdwardsG;
|
||||
|
use crate::{
|
||||
|
groups::curves::twisted_edwards::test::{edwards_constraint_costs, edwards_test},
|
||||
|
test_constraint_system::TestConstraintSystem,
|
||||
|
};
|
||||
|
use algebra::fields::jubjub::fq::Fq;
|
||||
|
use algebra::curves::jubjub::JubJubParameters as EdwardsParameters;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn edwards_constraint_costs_test() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
edwards_constraint_costs::<_, EdwardsParameters, EdwardsG, _>(&mut cs);
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn jubjub_gadget_test() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
edwards_test::<_, EdwardsParameters, EdwardsG, _>(&mut cs);
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,77 @@ |
|||||
|
use rand;
|
||||
|
|
||||
|
use crate::{
|
||||
|
boolean::Boolean,
|
||||
|
groups::{test::group_test, GroupGadget},
|
||||
|
utils::{AllocGadget, CondSelectGadget},
|
||||
|
};
|
||||
|
|
||||
|
use algebra::{
|
||||
|
curves::{models::TEModelParameters, twisted_edwards_extended::GroupAffine as TEAffine},
|
||||
|
BitIterator, Group, PrimeField, Field,
|
||||
|
};
|
||||
|
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
pub(crate) fn edwards_test<ConstraintF, P, GG, CS>(cs: &mut CS)
|
||||
|
where
|
||||
|
ConstraintF: Field,
|
||||
|
P: TEModelParameters,
|
||||
|
GG: GroupGadget<TEAffine<P>, ConstraintF, Value = TEAffine<P>>,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
{
|
||||
|
let a: TEAffine<P> = rand::random();
|
||||
|
let b: TEAffine<P> = rand::random();
|
||||
|
let gadget_a = GG::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap();
|
||||
|
let gadget_b = GG::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap();
|
||||
|
assert_eq!(gadget_a.get_value().unwrap(), a);
|
||||
|
assert_eq!(gadget_b.get_value().unwrap(), b);
|
||||
|
group_test::<ConstraintF, TEAffine<P>, GG, _>(
|
||||
|
&mut cs.ns(|| "GroupTest(a, b)"),
|
||||
|
gadget_a.clone(),
|
||||
|
gadget_b,
|
||||
|
);
|
||||
|
|
||||
|
// Check mul_bits
|
||||
|
let scalar: <TEAffine<P> as Group>::ScalarField = rand::random();
|
||||
|
let native_result = a.mul(&scalar);
|
||||
|
|
||||
|
let mut scalar: Vec<bool> = BitIterator::new(scalar.into_repr()).collect();
|
||||
|
// Get the scalar bits into little-endian form.
|
||||
|
scalar.reverse();
|
||||
|
let input = Vec::<Boolean>::alloc(cs.ns(|| "Input"), || Ok(scalar)).unwrap();
|
||||
|
let zero = GG::zero(cs.ns(|| "zero")).unwrap();
|
||||
|
let result = gadget_a
|
||||
|
.mul_bits(cs.ns(|| "mul_bits"), &zero, input.iter())
|
||||
|
.unwrap();
|
||||
|
let gadget_value = result.get_value().expect("Gadget_result failed");
|
||||
|
assert_eq!(native_result, gadget_value);
|
||||
|
}
|
||||
|
|
||||
|
pub(crate) fn edwards_constraint_costs<ConstraintF, P, GG, CS>(cs: &mut CS)
|
||||
|
where
|
||||
|
ConstraintF: Field,
|
||||
|
P: TEModelParameters,
|
||||
|
GG: GroupGadget<TEAffine<P>, ConstraintF, Value = TEAffine<P>>,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
{
|
||||
|
use crate::boolean::AllocatedBit;
|
||||
|
|
||||
|
let bit = AllocatedBit::alloc(&mut cs.ns(|| "bool"), || Ok(true))
|
||||
|
.unwrap()
|
||||
|
.into();
|
||||
|
|
||||
|
let a: TEAffine<P> = rand::random();
|
||||
|
let b: TEAffine<P> = rand::random();
|
||||
|
let gadget_a = GG::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap();
|
||||
|
let gadget_b = GG::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap();
|
||||
|
let alloc_cost = cs.num_constraints();
|
||||
|
let _ =
|
||||
|
GG::conditionally_select(&mut cs.ns(|| "cond_select"), &bit, &gadget_a, &gadget_b).unwrap();
|
||||
|
let cond_select_cost = cs.num_constraints() - alloc_cost;
|
||||
|
|
||||
|
let _ = gadget_a.add(&mut cs.ns(|| "ab"), &gadget_b).unwrap();
|
||||
|
let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost;
|
||||
|
assert_eq!(cond_select_cost, <GG as CondSelectGadget<_>>::cost());
|
||||
|
assert_eq!(add_cost, GG::cost_of_add());
|
||||
|
}
|
@ -0,0 +1,218 @@ |
|||||
|
use crate::prelude::*;
|
||||
|
use algebra::{Group, Field};
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use std::{borrow::Borrow, fmt::Debug};
|
||||
|
|
||||
|
pub mod curves;
|
||||
|
|
||||
|
pub use self::curves::{
|
||||
|
short_weierstrass::bls12,
|
||||
|
twisted_edwards::{edwards_sw6, jubjub},
|
||||
|
};
|
||||
|
|
||||
|
pub trait GroupGadget<G: Group, ConstraintF: Field>: |
||||
|
Sized
|
||||
|
+ ToBytesGadget<ConstraintF>
|
||||
|
+ NEqGadget<ConstraintF>
|
||||
|
+ EqGadget<ConstraintF>
|
||||
|
+ ToBitsGadget<ConstraintF>
|
||||
|
+ CondSelectGadget<ConstraintF>
|
||||
|
+ AllocGadget<G, ConstraintF>
|
||||
|
+ Clone
|
||||
|
+ Debug
|
||||
|
{
|
||||
|
type Value: Debug;
|
||||
|
type Variable;
|
||||
|
|
||||
|
fn get_value(&self) -> Option<Self::Value>;
|
||||
|
|
||||
|
fn get_variable(&self) -> Self::Variable;
|
||||
|
|
||||
|
fn zero<CS: ConstraintSystem<ConstraintF>>(cs: CS) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn add<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS, other: &Self) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn sub<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let neg_other = other.negate(cs.ns(|| "Negate other"))?;
|
||||
|
self.add(cs.ns(|| "Self - other"), &neg_other)
|
||||
|
}
|
||||
|
|
||||
|
fn add_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
other: &G,
|
||||
|
) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn sub_constant<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &G,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let neg_other = -(*other);
|
||||
|
self.add_constant(cs.ns(|| "Self - other"), &neg_other)
|
||||
|
}
|
||||
|
|
||||
|
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(&mut self, cs: CS) -> Result<(), SynthesisError>;
|
||||
|
|
||||
|
fn negate<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
/// Inputs must be specified in *little-endian* form.
|
||||
|
/// If the addition law is incomplete for the identity element,
|
||||
|
/// `result` must not be the identity element.
|
||||
|
fn mul_bits<'a, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
result: &Self,
|
||||
|
bits: impl Iterator<Item = &'a Boolean>,
|
||||
|
) -> Result<Self, SynthesisError> {
|
||||
|
let mut power = self.clone();
|
||||
|
let mut result = result.clone();
|
||||
|
for (i, bit) in bits.enumerate() {
|
||||
|
let new_encoded = result.add(&mut cs.ns(|| format!("Add {}-th power", i)), &power)?;
|
||||
|
result = Self::conditionally_select(
|
||||
|
&mut cs.ns(|| format!("Select {}", i)),
|
||||
|
bit.borrow(),
|
||||
|
&new_encoded,
|
||||
|
&result,
|
||||
|
)?;
|
||||
|
power.double_in_place(&mut cs.ns(|| format!("{}-th Doubling", i)))?;
|
||||
|
}
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
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 G)>,
|
||||
|
B: Borrow<Boolean>,
|
||||
|
G: 'a,
|
||||
|
{
|
||||
|
for (i, (bit, base_power)) in scalar_bits_with_base_powers.enumerate() {
|
||||
|
let new_encoded = self.add_constant(
|
||||
|
&mut cs.ns(|| format!("Add {}-th base power", i)),
|
||||
|
&base_power,
|
||||
|
)?;
|
||||
|
*self = Self::conditionally_select(
|
||||
|
&mut cs.ns(|| format!("Conditional Select {}", i)),
|
||||
|
bit.borrow(),
|
||||
|
&new_encoded,
|
||||
|
&self,
|
||||
|
)?;
|
||||
|
}
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn precomputed_base_multiscalar_mul<'a, CS, T, I, B>(
|
||||
|
mut cs: CS,
|
||||
|
bases: &[B],
|
||||
|
scalars: I,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
T: 'a + ToBitsGadget<ConstraintF> + ?Sized,
|
||||
|
I: Iterator<Item = &'a T>,
|
||||
|
B: Borrow<[G]>,
|
||||
|
{
|
||||
|
let mut result = Self::zero(&mut cs.ns(|| "Declare Result"))?;
|
||||
|
// Compute ∏(h_i^{m_i}) for all i.
|
||||
|
for (i, (bits, base_powers)) in scalars.zip(bases).enumerate() {
|
||||
|
let base_powers = base_powers.borrow();
|
||||
|
let bits = bits.to_bits(&mut cs.ns(|| format!("Convert Scalar {} to bits", i)))?;
|
||||
|
result.precomputed_base_scalar_mul(
|
||||
|
cs.ns(|| format!("Chunk {}", i)),
|
||||
|
bits.iter().zip(base_powers),
|
||||
|
)?;
|
||||
|
}
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
fn cost_of_add() -> usize;
|
||||
|
|
||||
|
fn cost_of_double() -> usize;
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use algebra::Field;
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
use crate::{
|
||||
|
groups::GroupGadget, test_constraint_system::TestConstraintSystem, utils::AllocGadget,
|
||||
|
};
|
||||
|
use algebra::groups::Group;
|
||||
|
use rand;
|
||||
|
|
||||
|
pub(crate) fn group_test<
|
||||
|
ConstraintF: Field,
|
||||
|
G: Group,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
>(
|
||||
|
cs: &mut CS,
|
||||
|
a: GG,
|
||||
|
b: GG,
|
||||
|
) {
|
||||
|
let zero = GG::zero(cs.ns(|| "Zero")).unwrap();
|
||||
|
assert_eq!(zero, zero);
|
||||
|
|
||||
|
// a == a
|
||||
|
assert_eq!(a, a);
|
||||
|
// a + 0 = a
|
||||
|
assert_eq!(a.add(cs.ns(|| "a_plus_zero"), &zero).unwrap(), a);
|
||||
|
// a - 0 = a
|
||||
|
assert_eq!(a.sub(cs.ns(|| "a_minus_zero"), &zero).unwrap(), a);
|
||||
|
// a - a = 0
|
||||
|
assert_eq!(a.sub(cs.ns(|| "a_minus_a"), &a).unwrap(), zero);
|
||||
|
// a + b = b + a
|
||||
|
let a_b = a.add(cs.ns(|| "a_plus_b"), &b).unwrap();
|
||||
|
let b_a = b.add(cs.ns(|| "b_plus_a"), &a).unwrap();
|
||||
|
assert_eq!(a_b, b_a);
|
||||
|
// (a + b) + a = a + (b + a)
|
||||
|
let ab_a = a_b.add(&mut cs.ns(|| "a_b_plus_a"), &a).unwrap();
|
||||
|
let a_ba = a.add(&mut cs.ns(|| "a_plus_b_a"), &b_a).unwrap();
|
||||
|
assert_eq!(ab_a, a_ba);
|
||||
|
// a.double() = a + a
|
||||
|
let a_a = a.add(cs.ns(|| "a + a"), &a).unwrap();
|
||||
|
let mut a2 = a.clone();
|
||||
|
a2.double_in_place(cs.ns(|| "2a")).unwrap();
|
||||
|
assert_eq!(a2, a_a);
|
||||
|
// b.double() = b + b
|
||||
|
let mut b2 = b.clone();
|
||||
|
b2.double_in_place(cs.ns(|| "2b")).unwrap();
|
||||
|
let b_b = b.add(cs.ns(|| "b + b"), &b).unwrap();
|
||||
|
assert_eq!(b2, b_b);
|
||||
|
|
||||
|
let _ = a.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap();
|
||||
|
let _ = a.to_bytes_strict(&mut cs.ns(|| "ToBytes Strict")).unwrap();
|
||||
|
|
||||
|
let _ = b.to_bytes(&mut cs.ns(|| "b ToBytes")).unwrap();
|
||||
|
let _ = b
|
||||
|
.to_bytes_strict(&mut cs.ns(|| "b ToBytes Strict"))
|
||||
|
.unwrap();
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn jubjub_group_gadgets_test() {
|
||||
|
use crate::groups::jubjub::JubJubGadget;
|
||||
|
use algebra::fields::jubjub::fq::Fq;
|
||||
|
use algebra::curves::jubjub::JubJubProjective;
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
let a: JubJubProjective = rand::random();
|
||||
|
let b: JubJubProjective = rand::random();
|
||||
|
|
||||
|
let a = JubJubGadget::alloc(&mut cs.ns(|| "generate_a"), || Ok(a)).unwrap();
|
||||
|
let b = JubJubGadget::alloc(&mut cs.ns(|| "generate_b"), || Ok(b)).unwrap();
|
||||
|
group_test::<_, JubJubProjective, _, _>(&mut cs.ns(|| "GroupTest(a, b)"), a, b);
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,358 @@ |
|||||
|
#![deny(
|
||||
|
unused_import_braces,
|
||||
|
unused_qualifications,
|
||||
|
trivial_casts,
|
||||
|
trivial_numeric_casts
|
||||
|
)]
|
||||
|
#![deny(
|
||||
|
unused_qualifications,
|
||||
|
variant_size_differences,
|
||||
|
stable_features,
|
||||
|
unreachable_pub
|
||||
|
)]
|
||||
|
#![deny(
|
||||
|
non_shorthand_field_patterns,
|
||||
|
unused_attributes,
|
||||
|
unused_imports,
|
||||
|
unused_extern_crates
|
||||
|
)]
|
||||
|
#![deny(
|
||||
|
renamed_and_removed_lints,
|
||||
|
stable_features,
|
||||
|
unused_allocation,
|
||||
|
unused_comparisons,
|
||||
|
bare_trait_objects
|
||||
|
)]
|
||||
|
#![deny(
|
||||
|
const_err,
|
||||
|
unused_must_use,
|
||||
|
unused_mut,
|
||||
|
unused_unsafe,
|
||||
|
private_in_public,
|
||||
|
unsafe_code
|
||||
|
)]
|
||||
|
#![forbid(unsafe_code)]
|
||||
|
|
||||
|
#[macro_use]
|
||||
|
extern crate algebra;
|
||||
|
#[macro_use]
|
||||
|
extern crate derivative;
|
||||
|
|
||||
|
use crate::ConstraintVar::*;
|
||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{LinearCombination, SynthesisError, Variable};
|
||||
|
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub};
|
||||
|
|
||||
|
pub mod test_constraint_system;
|
||||
|
|
||||
|
pub mod bits;
|
||||
|
pub use self::bits::*;
|
||||
|
|
||||
|
pub mod fields;
|
||||
|
|
||||
|
pub mod groups;
|
||||
|
|
||||
|
pub mod pairing;
|
||||
|
|
||||
|
pub mod eq;
|
||||
|
pub mod select;
|
||||
|
pub mod alloc;
|
||||
|
|
||||
|
pub mod prelude {
|
||||
|
pub use crate::eq::*;
|
||||
|
pub use crate::select::*;
|
||||
|
pub use crate::alloc::*;
|
||||
|
pub use crate::fields::FieldGadget;
|
||||
|
pub use crate::groups::GroupGadget;
|
||||
|
pub use crate::bits::{ToBitsGadget, ToBytesGadget, boolean::Boolean, uint8::UInt8, uint32::UInt32};
|
||||
|
}
|
||||
|
|
||||
|
pub trait Assignment<T> {
|
||||
|
fn get(self) -> Result<T, SynthesisError>;
|
||||
|
}
|
||||
|
|
||||
|
impl<T> Assignment<T> for Option<T> {
|
||||
|
fn get(self) -> Result<T, SynthesisError> {
|
||||
|
match self {
|
||||
|
Some(v) => Ok(v),
|
||||
|
None => Err(SynthesisError::AssignmentMissing),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Clone, Debug)]
|
||||
|
pub enum ConstraintVar<F: Field> {
|
||||
|
LC(LinearCombination<F>),
|
||||
|
Var(Variable),
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> From<Variable> for ConstraintVar<F> {
|
||||
|
#[inline]
|
||||
|
fn from(var: Variable) -> Self {
|
||||
|
Var(var)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> From<(F, Variable)> for ConstraintVar<F> {
|
||||
|
#[inline]
|
||||
|
fn from(coeff_var: (F, Variable)) -> Self {
|
||||
|
LC(coeff_var.into())
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> From<(F, LinearCombination<F>)> for ConstraintVar<F> {
|
||||
|
#[inline]
|
||||
|
fn from((coeff, mut lc): (F, LinearCombination<F>)) -> Self {
|
||||
|
lc *= coeff;
|
||||
|
LC(lc)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> From<(F, ConstraintVar<F>)> for ConstraintVar<F> {
|
||||
|
#[inline]
|
||||
|
fn from((coeff, var): (F, ConstraintVar<F>)) -> Self {
|
||||
|
match var {
|
||||
|
LC(lc) => (coeff, lc).into(),
|
||||
|
Var(var) => (coeff, var).into(),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> ConstraintVar<F> {
|
||||
|
#[inline]
|
||||
|
pub fn zero() -> Self {
|
||||
|
LC(LinearCombination::zero())
|
||||
|
}
|
||||
|
|
||||
|
pub fn negate_in_place(&mut self) {
|
||||
|
match self {
|
||||
|
LC(ref mut lc) => lc.negate_in_place(),
|
||||
|
Var(var) => *self = (-F::one(), *var).into(),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn double_in_place(&mut self) {
|
||||
|
match self {
|
||||
|
LC(lc) => lc.double_in_place(),
|
||||
|
Var(var) => *self = (F::one().double(), *var).into(),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Add<LinearCombination<F>> for ConstraintVar<F> {
|
||||
|
type Output = LinearCombination<F>;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add(self, other_lc: LinearCombination<F>) -> LinearCombination<F> {
|
||||
|
match self {
|
||||
|
LC(lc) => other_lc + lc,
|
||||
|
Var(var) => other_lc + var,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Sub<LinearCombination<F>> for ConstraintVar<F> {
|
||||
|
type Output = LinearCombination<F>;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub(self, other_lc: LinearCombination<F>) -> LinearCombination<F> {
|
||||
|
let result = match self {
|
||||
|
LC(lc) => other_lc - lc,
|
||||
|
Var(var) => other_lc - var,
|
||||
|
};
|
||||
|
-result
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Add<LinearCombination<F>> for &ConstraintVar<F> {
|
||||
|
type Output = LinearCombination<F>;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add(self, other_lc: LinearCombination<F>) -> LinearCombination<F> {
|
||||
|
match self {
|
||||
|
LC(lc) => other_lc + lc,
|
||||
|
Var(var) => other_lc + *var,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Sub<LinearCombination<F>> for &ConstraintVar<F> {
|
||||
|
type Output = LinearCombination<F>;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub(self, other_lc: LinearCombination<F>) -> LinearCombination<F> {
|
||||
|
let result = match self {
|
||||
|
LC(lc) => other_lc - lc,
|
||||
|
Var(var) => other_lc - *var,
|
||||
|
};
|
||||
|
-result
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Add<(F, Variable)> for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add(self, var: (F, Variable)) -> Self {
|
||||
|
let lc = match self {
|
||||
|
LC(lc) => lc + var,
|
||||
|
Var(var2) => LinearCombination::from(var2) + var,
|
||||
|
};
|
||||
|
LC(lc)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> AddAssign<(F, Variable)> for ConstraintVar<F> {
|
||||
|
#[inline]
|
||||
|
fn add_assign(&mut self, var: (F, Variable)) {
|
||||
|
match self {
|
||||
|
LC(ref mut lc) => *lc += var,
|
||||
|
Var(var2) => *self = LC(LinearCombination::from(*var2) + var),
|
||||
|
};
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Neg for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn neg(mut self) -> Self {
|
||||
|
self.negate_in_place();
|
||||
|
self
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Mul<F> for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn mul(self, scalar: F) -> Self {
|
||||
|
match self {
|
||||
|
LC(lc) => LC(lc * scalar),
|
||||
|
Var(var) => (scalar, var).into(),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> MulAssign<F> for ConstraintVar<F> {
|
||||
|
#[inline]
|
||||
|
fn mul_assign(&mut self, scalar: F) {
|
||||
|
match self {
|
||||
|
LC(lc) => *lc *= scalar,
|
||||
|
Var(var) => *self = (scalar, *var).into(),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Sub<(F, Variable)> for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub(self, (coeff, var): (F, Variable)) -> Self {
|
||||
|
self + (-coeff, var)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Add<Variable> for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
fn add(self, other: Variable) -> Self {
|
||||
|
self + (F::one(), other)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Sub<Variable> for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub(self, other: Variable) -> Self {
|
||||
|
self - (F::one(), other)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<'a, F: Field> Add<&'a Self> for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add(self, other: &'a Self) -> Self {
|
||||
|
let lc = match self {
|
||||
|
LC(lc2) => lc2,
|
||||
|
Var(var) => var.into(),
|
||||
|
};
|
||||
|
let lc2 = match other {
|
||||
|
LC(lc2) => lc + lc2,
|
||||
|
Var(var) => lc + *var,
|
||||
|
};
|
||||
|
LC(lc2)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<'a, F: Field> Sub<&'a Self> for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub(self, other: &'a Self) -> Self {
|
||||
|
let lc = match self {
|
||||
|
LC(lc2) => lc2,
|
||||
|
Var(var) => var.into(),
|
||||
|
};
|
||||
|
let lc2 = match other {
|
||||
|
LC(lc2) => lc - lc2,
|
||||
|
Var(var) => lc - *var,
|
||||
|
};
|
||||
|
LC(lc2)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Add<&ConstraintVar<F>> for &ConstraintVar<F> {
|
||||
|
type Output = ConstraintVar<F>;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add(self, other: &ConstraintVar<F>) -> Self::Output {
|
||||
|
(ConstraintVar::zero() + self) + other
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: Field> Sub<&ConstraintVar<F>> for &ConstraintVar<F> {
|
||||
|
type Output = ConstraintVar<F>;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub(self, other: &ConstraintVar<F>) -> Self::Output {
|
||||
|
(ConstraintVar::zero() + self) - other
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<'a, F: Field> Add<(F, &'a Self)> for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn add(self, (coeff, other): (F, &'a Self)) -> Self {
|
||||
|
let mut lc = match self {
|
||||
|
LC(lc2) => lc2,
|
||||
|
Var(var) => LinearCombination::zero() + var,
|
||||
|
};
|
||||
|
|
||||
|
lc = match other {
|
||||
|
LC(lc2) => lc + (coeff, lc2),
|
||||
|
Var(var) => lc + (coeff, *var),
|
||||
|
};
|
||||
|
LC(lc)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<'a, F: Field> Sub<(F, &'a Self)> for ConstraintVar<F> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn sub(self, (coeff, other): (F, &'a Self)) -> Self {
|
||||
|
let mut lc = match self {
|
||||
|
LC(lc2) => lc2,
|
||||
|
Var(var) => LinearCombination::zero() + var,
|
||||
|
};
|
||||
|
lc = match other {
|
||||
|
LC(lc2) => lc - (coeff, lc2),
|
||||
|
Var(var) => lc - (coeff, *var),
|
||||
|
};
|
||||
|
LC(lc)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,4 @@ |
|||||
|
use crate::pairing::bls12::PairingGadget as Bls12PG;
|
||||
|
use algebra::curves::bls12_377::Bls12_377Parameters;
|
||||
|
|
||||
|
pub type PairingGadget = Bls12PG<Bls12_377Parameters>;
|
@ -0,0 +1,206 @@ |
|||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use super::PairingGadget as PG;
|
||||
|
|
||||
|
use crate::{
|
||||
|
fields::{fp::FpGadget, fp12::Fp12Gadget, fp2::Fp2Gadget, FieldGadget},
|
||||
|
groups::bls12::{G1Gadget, G1PreparedGadget, G2Gadget, G2PreparedGadget},
|
||||
|
};
|
||||
|
use algebra::{
|
||||
|
curves::{
|
||||
|
bls12::{
|
||||
|
Bls12, Bls12Parameters, G1Affine, G1Prepared, G1Projective, G2Affine, G2Prepared,
|
||||
|
G2Projective, TwistType,
|
||||
|
},
|
||||
|
models::ModelParameters,
|
||||
|
PairingCurve,
|
||||
|
},
|
||||
|
fields::{fp12_2over3over2::Fp12, BitIterator},
|
||||
|
};
|
||||
|
use std::marker::PhantomData;
|
||||
|
|
||||
|
pub mod bls12_377;
|
||||
|
|
||||
|
pub struct PairingGadget<P: Bls12Parameters>(PhantomData<P>);
|
||||
|
|
||||
|
type Fp2G<P> = Fp2Gadget<<P as Bls12Parameters>::Fp2Params, <P as Bls12Parameters>::Fp>;
|
||||
|
|
||||
|
impl<P: Bls12Parameters> PairingGadget<P> {
|
||||
|
// Evaluate the line function at point p.
|
||||
|
fn ell<CS: ConstraintSystem<P::Fp>>(
|
||||
|
mut cs: CS,
|
||||
|
f: &mut Fp12Gadget<P::Fp12Params, P::Fp>,
|
||||
|
coeffs: &(Fp2G<P>, Fp2G<P>),
|
||||
|
p: &G1Gadget<P>,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
let zero = FpGadget::<P::Fp>::zero(cs.ns(|| "fpg zero"))?;
|
||||
|
|
||||
|
match P::TWIST_TYPE {
|
||||
|
TwistType::M => {
|
||||
|
let c0 = coeffs.0.clone();
|
||||
|
let mut c1 = coeffs.1.clone();
|
||||
|
let c2 = Fp2G::<P>::new(p.y.clone(), zero.clone());
|
||||
|
|
||||
|
c1.c0 = c1.c0.mul(cs.ns(|| "mul c1.c0"), &p.x)?;
|
||||
|
c1.c1 = c1.c1.mul(cs.ns(|| "mul c1.c1"), &p.x)?;
|
||||
|
*f = f.mul_by_014(cs.ns(|| "sparse mul f"), &c0, &c1, &c2)?;
|
||||
|
Ok(())
|
||||
|
},
|
||||
|
TwistType::D => {
|
||||
|
let c0 = Fp2G::<P>::new(p.y.clone(), zero.clone());
|
||||
|
let mut c1 = coeffs.0.clone();
|
||||
|
let c2 = coeffs.1.clone();
|
||||
|
|
||||
|
c1.c0 = c1.c0.mul(cs.ns(|| "mul c1.c0"), &p.x)?;
|
||||
|
c1.c1 = c1.c1.mul(cs.ns(|| "mul c1.c1"), &p.x)?;
|
||||
|
*f = f.mul_by_034(cs.ns(|| "sparse mul f"), &c0, &c1, &c2)?;
|
||||
|
Ok(())
|
||||
|
},
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn exp_by_x<CS: ConstraintSystem<P::Fp>>(
|
||||
|
mut cs: CS,
|
||||
|
f: &Fp12Gadget<P::Fp12Params, P::Fp>,
|
||||
|
) -> Result<Fp12Gadget<P::Fp12Params, P::Fp>, SynthesisError> {
|
||||
|
let mut result = f.cyclotomic_exp(cs.ns(|| "exp_by_x"), P::X)?;
|
||||
|
if P::X_IS_NEGATIVE {
|
||||
|
result.conjugate_in_place(cs.ns(|| "conjugate"))?;
|
||||
|
}
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: Bls12Parameters> PG<Bls12<P>, P::Fp> for PairingGadget<P>
|
||||
|
where
|
||||
|
G1Affine<P>: PairingCurve<
|
||||
|
BaseField = <P::G1Parameters as ModelParameters>::BaseField,
|
||||
|
ScalarField = <P::G1Parameters as ModelParameters>::ScalarField,
|
||||
|
Projective = G1Projective<P>,
|
||||
|
PairWith = G2Affine<P>,
|
||||
|
Prepared = G1Prepared<P>,
|
||||
|
PairingResult = Fp12<P::Fp12Params>,
|
||||
|
>,
|
||||
|
G2Affine<P>: PairingCurve<
|
||||
|
BaseField = <P::G2Parameters as ModelParameters>::BaseField,
|
||||
|
ScalarField = <P::G1Parameters as ModelParameters>::ScalarField,
|
||||
|
Projective = G2Projective<P>,
|
||||
|
PairWith = G1Affine<P>,
|
||||
|
Prepared = G2Prepared<P>,
|
||||
|
PairingResult = Fp12<P::Fp12Params>,
|
||||
|
>,
|
||||
|
{
|
||||
|
type G1Gadget = G1Gadget<P>;
|
||||
|
type G2Gadget = G2Gadget<P>;
|
||||
|
type G1PreparedGadget = G1PreparedGadget<P>;
|
||||
|
type G2PreparedGadget = G2PreparedGadget<P>;
|
||||
|
type GTGadget = Fp12Gadget<P::Fp12Params, P::Fp>;
|
||||
|
|
||||
|
fn miller_loop<CS: ConstraintSystem<P::Fp>>(
|
||||
|
mut cs: CS,
|
||||
|
ps: &[Self::G1PreparedGadget],
|
||||
|
qs: &[Self::G2PreparedGadget],
|
||||
|
) -> Result<Self::GTGadget, SynthesisError> {
|
||||
|
let mut pairs = vec![];
|
||||
|
for (p, q) in ps.into_iter().zip(qs.into_iter()) {
|
||||
|
pairs.push((p, q.ell_coeffs.iter()));
|
||||
|
}
|
||||
|
let mut f = Self::GTGadget::one(cs.ns(|| "one"))?;
|
||||
|
|
||||
|
for (j, i) in BitIterator::new(P::X).skip(1).enumerate() {
|
||||
|
let mut cs = cs.ns(|| format!("Iteration {}", j));
|
||||
|
f.square_in_place(cs.ns(|| "square"))?;
|
||||
|
|
||||
|
for (k, &mut (p, ref mut coeffs)) in pairs.iter_mut().enumerate() {
|
||||
|
let cs = cs.ns(|| format!("Double input {}", k));
|
||||
|
Self::ell(cs, &mut f, coeffs.next().unwrap(), &p.0)?;
|
||||
|
}
|
||||
|
|
||||
|
if i {
|
||||
|
for (k, &mut (p, ref mut coeffs)) in pairs.iter_mut().enumerate() {
|
||||
|
let cs = cs.ns(|| format!("Addition input {}", k));
|
||||
|
Self::ell(cs, &mut f, &coeffs.next().unwrap(), &p.0)?;
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
if P::X_IS_NEGATIVE {
|
||||
|
f.conjugate_in_place(cs.ns(|| "f conjugate"))?;
|
||||
|
}
|
||||
|
|
||||
|
Ok(f)
|
||||
|
}
|
||||
|
|
||||
|
fn final_exponentiation<CS: ConstraintSystem<P::Fp>>(
|
||||
|
mut cs: CS,
|
||||
|
f: &Self::GTGadget,
|
||||
|
) -> Result<Self::GTGadget, SynthesisError> {
|
||||
|
// Computing the final exponentation following
|
||||
|
// https://eprint.iacr.org/2016/130.pdf.
|
||||
|
// We don't use their "faster" formula because it is difficult to make
|
||||
|
// it work for curves with odd `P::X`.
|
||||
|
// Hence we implement the slower algorithm from Table 1 below.
|
||||
|
|
||||
|
let f1 = f.frobenius_map(cs.ns(|| "frobmap 1"), 6)?;
|
||||
|
|
||||
|
f.inverse(cs.ns(|| "inverse 1")).and_then(|mut f2| {
|
||||
|
// f2 = f^(-1);
|
||||
|
// r = f^(p^6 - 1)
|
||||
|
let mut r = f1;
|
||||
|
r.mul_in_place(cs.ns(|| "r = f1 * f2"), &f2)?;
|
||||
|
|
||||
|
// f2 = f^(p^6 - 1)
|
||||
|
f2 = r.clone();
|
||||
|
// r = f^((p^6 - 1)(p^2))
|
||||
|
r.frobenius_map_in_place(cs.ns(|| "frobenius map 2"), 2)?;
|
||||
|
|
||||
|
// r = f^((p^6 - 1)(p^2) + (p^6 - 1))
|
||||
|
// r = f^((p^6 - 1)(p^2 + 1))
|
||||
|
r.mul_in_place(cs.ns(|| "mul 0"), &f2)?;
|
||||
|
|
||||
|
// Hard part of the final exponentation is below:
|
||||
|
// From https://eprint.iacr.org/2016/130.pdf, Table 1
|
||||
|
let mut y0 = r.cyclotomic_square(cs.ns(|| "cyclotomic_sq 1"))?;
|
||||
|
y0.conjugate_in_place(&mut cs.ns(|| "conjugate 2"))?;
|
||||
|
|
||||
|
let mut y5 = Self::exp_by_x(&mut cs.ns(|| "exp_by_x 1"), &r)?;
|
||||
|
|
||||
|
let mut y1 = y5.cyclotomic_square(&mut cs.ns(|| "square 1"))?;
|
||||
|
let mut y3 = y0.mul(&mut cs.ns(|| "mul 1"), &y5)?;
|
||||
|
y0 = Self::exp_by_x(cs.ns(|| "exp_by_x 2"), &y3)?;
|
||||
|
let y2 = Self::exp_by_x(cs.ns(|| "exp_by_x 3"), &y0)?;
|
||||
|
let mut y4 = Self::exp_by_x(cs.ns(|| "exp_by_x 4"), &y2)?;
|
||||
|
y4.mul_in_place(cs.ns(|| "mul 2"), &y1)?;
|
||||
|
y1 = Self::exp_by_x(cs.ns(|| "exp_by_x 5"), &y4)?;
|
||||
|
y3.conjugate_in_place(cs.ns(|| "conjugate 3"))?;
|
||||
|
y1.mul_in_place(cs.ns(|| "mul 3"), &y3)?;
|
||||
|
y1.mul_in_place(cs.ns(|| "mul 4"), &r)?;
|
||||
|
y3 = r.clone();
|
||||
|
y3.conjugate_in_place(cs.ns(|| "conjugate 4"))?;
|
||||
|
y0.mul_in_place(cs.ns(|| "mul 5"), &r)?;
|
||||
|
y0.frobenius_map_in_place(cs.ns(|| "frobmap 3"), 3)?;
|
||||
|
y4.mul_in_place(cs.ns(|| "mul 6"), &y3)?;
|
||||
|
y4.frobenius_map_in_place(cs.ns(|| "frobmap 4"), 1)?;
|
||||
|
y5.mul_in_place(cs.ns(|| "mul 7"), &y2)?;
|
||||
|
y5.frobenius_map_in_place(cs.ns(|| "frobmap 5"), 2)?;
|
||||
|
y5.mul_in_place(cs.ns(|| "mul 8"), &y0)?;
|
||||
|
y5.mul_in_place(cs.ns(|| "mul 9"), &y4)?;
|
||||
|
y5.mul_in_place(cs.ns(|| "mul 10"), &y1)?;
|
||||
|
Ok(y5)
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
fn prepare_g1<CS: ConstraintSystem<P::Fp>>(
|
||||
|
cs: CS,
|
||||
|
p: &Self::G1Gadget,
|
||||
|
) -> Result<Self::G1PreparedGadget, SynthesisError> {
|
||||
|
Self::G1PreparedGadget::from_affine(cs, p)
|
||||
|
}
|
||||
|
|
||||
|
fn prepare_g2<CS: ConstraintSystem<P::Fp>>(
|
||||
|
cs: CS,
|
||||
|
q: &Self::G2Gadget,
|
||||
|
) -> Result<Self::G2PreparedGadget, SynthesisError> {
|
||||
|
Self::G2PreparedGadget::from_affine(cs, q)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,178 @@ |
|||||
|
use crate::prelude::*;
|
||||
|
use algebra::{Field, PairingEngine};
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use std::fmt::Debug;
|
||||
|
|
||||
|
pub mod bls12;
|
||||
|
pub use self::bls12::bls12_377;
|
||||
|
|
||||
|
pub trait PairingGadget<PairingE: PairingEngine, ConstraintF: Field> {
|
||||
|
type G1Gadget: GroupGadget<PairingE::G1Projective, ConstraintF>;
|
||||
|
type G2Gadget: GroupGadget<PairingE::G2Projective, ConstraintF>;
|
||||
|
type G1PreparedGadget: ToBytesGadget<ConstraintF> + Clone + Debug;
|
||||
|
type G2PreparedGadget: ToBytesGadget<ConstraintF> + Clone + Debug;
|
||||
|
type GTGadget: FieldGadget<PairingE::Fqk, ConstraintF> + Clone;
|
||||
|
|
||||
|
fn miller_loop<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
p: &[Self::G1PreparedGadget],
|
||||
|
q: &[Self::G2PreparedGadget],
|
||||
|
) -> Result<Self::GTGadget, SynthesisError>;
|
||||
|
|
||||
|
fn final_exponentiation<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
p: &Self::GTGadget,
|
||||
|
) -> Result<Self::GTGadget, SynthesisError>;
|
||||
|
|
||||
|
fn pairing<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
p: Self::G1PreparedGadget,
|
||||
|
q: Self::G2PreparedGadget,
|
||||
|
) -> Result<Self::GTGadget, SynthesisError> {
|
||||
|
let tmp = Self::miller_loop(cs.ns(|| "miller loop"), &[p], &[q])?;
|
||||
|
Self::final_exponentiation(cs.ns(|| "final_exp"), &tmp)
|
||||
|
}
|
||||
|
|
||||
|
/// Computes a product of pairings.
|
||||
|
#[must_use]
|
||||
|
fn product_of_pairings<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
p: &[Self::G1PreparedGadget],
|
||||
|
q: &[Self::G2PreparedGadget],
|
||||
|
) -> Result<Self::GTGadget, SynthesisError> {
|
||||
|
let miller_result = Self::miller_loop(&mut cs.ns(|| "Miller loop"), p, q)?;
|
||||
|
Self::final_exponentiation(&mut cs.ns(|| "Final Exp"), &miller_result)
|
||||
|
}
|
||||
|
|
||||
|
fn prepare_g1<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
q: &Self::G1Gadget,
|
||||
|
) -> Result<Self::G1PreparedGadget, SynthesisError>;
|
||||
|
|
||||
|
fn prepare_g2<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
q: &Self::G2Gadget,
|
||||
|
) -> Result<Self::G2PreparedGadget, SynthesisError>;
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
// use rand;
|
||||
|
use crate::{boolean::Boolean, test_constraint_system::TestConstraintSystem};
|
||||
|
use algebra::{BitIterator, Field};
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn bls12_377_gadget_bilinearity_test() {
|
||||
|
use algebra::{
|
||||
|
fields::{bls12_377::{fr::Fr, fq::Fq}, PrimeField},
|
||||
|
PairingEngine, ProjectiveCurve,
|
||||
|
};
|
||||
|
|
||||
|
use super::bls12_377::PairingGadget;
|
||||
|
use crate::{
|
||||
|
fields::FieldGadget,
|
||||
|
groups::bls12::bls12_377::{G1Gadget, G1PreparedGadget, G2Gadget, G2PreparedGadget},
|
||||
|
pairing::PairingGadget as _,
|
||||
|
utils::{AllocGadget, EqGadget},
|
||||
|
};
|
||||
|
use algebra::curves::bls12_377::{Bls12_377, G1Projective, G2Projective};
|
||||
|
use std::ops::Mul;
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
// let a: G1Projective = rand::random();
|
||||
|
// let b: G2Projective = rand::random();
|
||||
|
// let s: Fr = rand::random();
|
||||
|
|
||||
|
let a: G1Projective = G1Projective::prime_subgroup_generator();
|
||||
|
let b: G2Projective = G2Projective::prime_subgroup_generator();
|
||||
|
let s: Fr = Fr::one() + &Fr::one();
|
||||
|
|
||||
|
let sa = a.mul(&s);
|
||||
|
let sb = b.mul(&s);
|
||||
|
|
||||
|
let a_g = G1Gadget::alloc(&mut cs.ns(|| "a"), || Ok(a)).unwrap();
|
||||
|
let b_g = G2Gadget::alloc(&mut cs.ns(|| "b"), || Ok(b)).unwrap();
|
||||
|
let sa_g = G1Gadget::alloc(&mut cs.ns(|| "sa"), || Ok(sa)).unwrap();
|
||||
|
let sb_g = G2Gadget::alloc(&mut cs.ns(|| "sb"), || Ok(sb)).unwrap();
|
||||
|
|
||||
|
let a_prep_g = G1PreparedGadget::from_affine(&mut cs.ns(|| "a_prep"), &a_g).unwrap();
|
||||
|
let b_prep_g = G2PreparedGadget::from_affine(&mut cs.ns(|| "b_prep"), &b_g).unwrap();
|
||||
|
|
||||
|
let sa_prep_g = G1PreparedGadget::from_affine(&mut cs.ns(|| "sa_prep"), &sa_g).unwrap();
|
||||
|
let sb_prep_g = G2PreparedGadget::from_affine(&mut cs.ns(|| "sb_prep"), &sb_g).unwrap();
|
||||
|
|
||||
|
let (ans1_g, ans1_n) = {
|
||||
|
let ans_g = PairingGadget::pairing(
|
||||
|
cs.ns(|| "pair(sa, b)"),
|
||||
|
sa_prep_g.clone(),
|
||||
|
b_prep_g.clone(),
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
let ans_n = Bls12_377::pairing(sa, b);
|
||||
|
(ans_g, ans_n)
|
||||
|
};
|
||||
|
|
||||
|
let (ans2_g, ans2_n) = {
|
||||
|
let ans_g = PairingGadget::pairing(
|
||||
|
cs.ns(|| "pair(a, sb)"),
|
||||
|
a_prep_g.clone(),
|
||||
|
sb_prep_g.clone(),
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
let ans_n = Bls12_377::pairing(a, sb);
|
||||
|
(ans_g, ans_n)
|
||||
|
};
|
||||
|
|
||||
|
let (ans3_g, ans3_n) = {
|
||||
|
let s_iter = BitIterator::new(s.into_repr())
|
||||
|
.map(|bit| Boolean::constant(bit))
|
||||
|
.collect::<Vec<_>>();
|
||||
|
|
||||
|
let mut ans_g =
|
||||
|
PairingGadget::pairing(cs.ns(|| "pair(a, b)"), a_prep_g.clone(), b_prep_g.clone())
|
||||
|
.unwrap();
|
||||
|
let mut ans_n = Bls12_377::pairing(a, b);
|
||||
|
ans_n = ans_n.pow(s.into_repr());
|
||||
|
ans_g = ans_g.pow(cs.ns(|| "pow"), &s_iter).unwrap();
|
||||
|
|
||||
|
(ans_g, ans_n)
|
||||
|
};
|
||||
|
|
||||
|
assert_eq!(ans1_n, ans2_n, "Failed ans1_native == ans2_native");
|
||||
|
assert_eq!(ans2_n, ans3_n, "Failed ans2_native == ans3_native");
|
||||
|
assert_eq!(
|
||||
|
ans1_g.get_value(),
|
||||
|
ans3_g.get_value(),
|
||||
|
"Failed ans1 == ans3"
|
||||
|
);
|
||||
|
assert_eq!(
|
||||
|
ans1_g.get_value(),
|
||||
|
ans2_g.get_value(),
|
||||
|
"Failed ans1 == ans2"
|
||||
|
);
|
||||
|
assert_eq!(
|
||||
|
ans2_g.get_value(),
|
||||
|
ans3_g.get_value(),
|
||||
|
"Failed ans2 == ans3"
|
||||
|
);
|
||||
|
|
||||
|
ans1_g
|
||||
|
.enforce_equal(&mut cs.ns(|| "ans1 == ans2?"), &ans2_g)
|
||||
|
.unwrap();
|
||||
|
ans2_g
|
||||
|
.enforce_equal(&mut cs.ns(|| "ans2 == ans3?"), &ans3_g)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
assert_eq!(ans1_g.get_value().unwrap(), ans1_n, "Failed native test 1");
|
||||
|
assert_eq!(ans2_g.get_value().unwrap(), ans2_n, "Failed native test 2");
|
||||
|
assert_eq!(ans3_g.get_value().unwrap(), ans3_n, "Failed native test 3");
|
||||
|
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("Unsatisfied: {:?}", cs.which_is_unsatisfied());
|
||||
|
}
|
||||
|
|
||||
|
assert!(cs.is_satisfied(), "cs is not satisfied");
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,36 @@ |
|||||
|
use crate::prelude::*;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use algebra::Field;
|
||||
|
|
||||
|
|
||||
|
/// If condition is `true`, return `first`; else, select `second`.
|
||||
|
pub trait CondSelectGadget<ConstraintF: Field>
|
||||
|
where
|
||||
|
Self: Sized,
|
||||
|
{
|
||||
|
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
cond: &Boolean,
|
||||
|
first: &Self,
|
||||
|
second: &Self,
|
||||
|
) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn cost() -> usize;
|
||||
|
}
|
||||
|
|
||||
|
/// Uses two bits to perform a lookup into a table
|
||||
|
pub trait TwoBitLookupGadget<ConstraintF: Field>
|
||||
|
where
|
||||
|
Self: Sized,
|
||||
|
{
|
||||
|
type TableConstant;
|
||||
|
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
bits: &[Boolean],
|
||||
|
constants: &[Self::TableConstant],
|
||||
|
) -> Result<Self, SynthesisError>;
|
||||
|
|
||||
|
fn cost() -> usize;
|
||||
|
}
|
||||
|
|
||||
|
|
@ -0,0 +1,230 @@ |
|||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
|
||||
|
use radix_trie::Trie;
|
||||
|
|
||||
|
#[derive(Debug)]
|
||||
|
enum NamedObject {
|
||||
|
Constraint(usize),
|
||||
|
Var(Variable),
|
||||
|
Namespace,
|
||||
|
}
|
||||
|
|
||||
|
/// Constraint system for testing purposes.
|
||||
|
pub struct TestConstraintSystem<ConstraintF: Field> {
|
||||
|
named_objects: Trie<String, NamedObject>,
|
||||
|
current_namespace: Vec<String>,
|
||||
|
pub constraints: Vec<(
|
||||
|
LinearCombination<ConstraintF>,
|
||||
|
LinearCombination<ConstraintF>,
|
||||
|
LinearCombination<ConstraintF>,
|
||||
|
String,
|
||||
|
)>,
|
||||
|
inputs: Vec<(ConstraintF, String)>,
|
||||
|
aux: Vec<(ConstraintF, String)>,
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field> TestConstraintSystem<ConstraintF> {
|
||||
|
fn eval_lc(
|
||||
|
terms: &[(Variable, ConstraintF)],
|
||||
|
inputs: &[(ConstraintF, String)],
|
||||
|
aux: &[(ConstraintF, String)],
|
||||
|
) -> ConstraintF {
|
||||
|
let mut acc = ConstraintF::zero();
|
||||
|
|
||||
|
for &(var, ref coeff) in terms {
|
||||
|
let mut tmp = match var.get_unchecked() {
|
||||
|
Index::Input(index) => inputs[index].0,
|
||||
|
Index::Aux(index) => aux[index].0,
|
||||
|
};
|
||||
|
|
||||
|
tmp.mul_assign(&coeff);
|
||||
|
acc.add_assign(&tmp);
|
||||
|
}
|
||||
|
|
||||
|
acc
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field> TestConstraintSystem<ConstraintF> {
|
||||
|
pub fn new() -> TestConstraintSystem<ConstraintF> {
|
||||
|
let mut map = Trie::new();
|
||||
|
map.insert(
|
||||
|
"ONE".into(),
|
||||
|
NamedObject::Var(TestConstraintSystem::<ConstraintF>::one()),
|
||||
|
);
|
||||
|
|
||||
|
TestConstraintSystem {
|
||||
|
named_objects: map,
|
||||
|
current_namespace: vec![],
|
||||
|
constraints: vec![],
|
||||
|
inputs: vec![(ConstraintF::one(), "ONE".into())],
|
||||
|
aux: vec![],
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn print_named_objects(&self) {
|
||||
|
for &(_, _, _, ref name) in &self.constraints {
|
||||
|
println!("{}", name);
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn which_is_unsatisfied(&self) -> Option<&str> {
|
||||
|
for &(ref a, ref b, ref c, ref path) in &self.constraints {
|
||||
|
let mut a = Self::eval_lc(a.as_ref(), &self.inputs, &self.aux);
|
||||
|
let b = Self::eval_lc(b.as_ref(), &self.inputs, &self.aux);
|
||||
|
let c = Self::eval_lc(c.as_ref(), &self.inputs, &self.aux);
|
||||
|
|
||||
|
a.mul_assign(&b);
|
||||
|
|
||||
|
if a != c {
|
||||
|
return Some(&*path);
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
None
|
||||
|
}
|
||||
|
|
||||
|
pub fn is_satisfied(&self) -> bool {
|
||||
|
self.which_is_unsatisfied().is_none()
|
||||
|
}
|
||||
|
|
||||
|
pub fn num_constraints(&self) -> usize {
|
||||
|
self.constraints.len()
|
||||
|
}
|
||||
|
|
||||
|
pub fn set(&mut self, path: &str, to: ConstraintF) {
|
||||
|
match self.named_objects.get(path) {
|
||||
|
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
|
||||
|
Index::Input(index) => self.inputs[index].0 = to,
|
||||
|
Index::Aux(index) => self.aux[index].0 = to,
|
||||
|
},
|
||||
|
Some(e) => panic!(
|
||||
|
"tried to set path `{}` to value, but `{:?}` already exists there.",
|
||||
|
path, e
|
||||
|
),
|
||||
|
_ => panic!("no variable exists at path: {}", path),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn get(&mut self, path: &str) -> ConstraintF {
|
||||
|
match self.named_objects.get(path) {
|
||||
|
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
|
||||
|
Index::Input(index) => self.inputs[index].0,
|
||||
|
Index::Aux(index) => self.aux[index].0,
|
||||
|
},
|
||||
|
Some(e) => panic!(
|
||||
|
"tried to get value of path `{}`, but `{:?}` exists there (not a variable)",
|
||||
|
path, e
|
||||
|
),
|
||||
|
_ => panic!("no variable exists at path: {}", path),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn set_named_obj(&mut self, path: String, to: NamedObject) {
|
||||
|
if self.named_objects.get(&path).is_some() {
|
||||
|
panic!("tried to create object at existing path: {}", path);
|
||||
|
}
|
||||
|
|
||||
|
self.named_objects.insert(path, to);
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn compute_path(ns: &[String], this: String) -> String {
|
||||
|
if this.chars().any(|a| a == '/') {
|
||||
|
panic!("'/' is not allowed in names");
|
||||
|
}
|
||||
|
|
||||
|
let mut name = String::new();
|
||||
|
|
||||
|
let mut needs_separation = false;
|
||||
|
for ns in ns.iter().chain(Some(&this).into_iter()) {
|
||||
|
if needs_separation {
|
||||
|
name += "/";
|
||||
|
}
|
||||
|
|
||||
|
name += ns;
|
||||
|
needs_separation = true;
|
||||
|
}
|
||||
|
|
||||
|
name
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field> ConstraintSystem<ConstraintF> for TestConstraintSystem<ConstraintF> {
|
||||
|
type Root = Self;
|
||||
|
|
||||
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<ConstraintF, SynthesisError>,
|
||||
|
A: FnOnce() -> AR,
|
||||
|
AR: Into<String>,
|
||||
|
{
|
||||
|
let index = self.aux.len();
|
||||
|
let path = compute_path(&self.current_namespace, annotation().into());
|
||||
|
self.aux.push((f()?, path.clone()));
|
||||
|
let var = Variable::new_unchecked(Index::Aux(index));
|
||||
|
self.set_named_obj(path, NamedObject::Var(var));
|
||||
|
|
||||
|
Ok(var)
|
||||
|
}
|
||||
|
|
||||
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<ConstraintF, SynthesisError>,
|
||||
|
A: FnOnce() -> AR,
|
||||
|
AR: Into<String>,
|
||||
|
{
|
||||
|
let index = self.inputs.len();
|
||||
|
let path = compute_path(&self.current_namespace, annotation().into());
|
||||
|
self.inputs.push((f()?, path.clone()));
|
||||
|
let var = Variable::new_unchecked(Index::Input(index));
|
||||
|
self.set_named_obj(path, NamedObject::Var(var));
|
||||
|
|
||||
|
Ok(var)
|
||||
|
}
|
||||
|
|
||||
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||
|
where
|
||||
|
A: FnOnce() -> AR,
|
||||
|
AR: Into<String>,
|
||||
|
LA: FnOnce(LinearCombination<ConstraintF>) -> LinearCombination<ConstraintF>,
|
||||
|
LB: FnOnce(LinearCombination<ConstraintF>) -> LinearCombination<ConstraintF>,
|
||||
|
LC: FnOnce(LinearCombination<ConstraintF>) -> LinearCombination<ConstraintF>,
|
||||
|
{
|
||||
|
let path = compute_path(&self.current_namespace, annotation().into());
|
||||
|
let index = self.constraints.len();
|
||||
|
self.set_named_obj(path.clone(), NamedObject::Constraint(index));
|
||||
|
|
||||
|
let mut a = a(LinearCombination::zero());
|
||||
|
let mut b = b(LinearCombination::zero());
|
||||
|
let mut c = c(LinearCombination::zero());
|
||||
|
a.0.shrink_to_fit();
|
||||
|
b.0.shrink_to_fit();
|
||||
|
c.0.shrink_to_fit();
|
||||
|
|
||||
|
self.constraints.push((a, b, c, path));
|
||||
|
}
|
||||
|
|
||||
|
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||
|
where
|
||||
|
NR: Into<String>,
|
||||
|
N: FnOnce() -> NR,
|
||||
|
{
|
||||
|
let name = name_fn().into();
|
||||
|
let path = compute_path(&self.current_namespace, name.clone());
|
||||
|
self.set_named_obj(path.clone(), NamedObject::Namespace);
|
||||
|
self.current_namespace.push(name);
|
||||
|
}
|
||||
|
|
||||
|
fn pop_namespace(&mut self) {
|
||||
|
assert!(self.current_namespace.pop().is_some());
|
||||
|
}
|
||||
|
|
||||
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||
|
self
|
||||
|
}
|
||||
|
|
||||
|
fn num_constraints(&self) -> usize {
|
||||
|
self.constraints.len()
|
||||
|
}
|
||||
|
}
|