diff --git a/crypto-primitives/src/crh/bowe_hopwood/constraints.rs b/crypto-primitives/src/crh/bowe_hopwood/constraints.rs new file mode 100644 index 0000000..37dd508 --- /dev/null +++ b/crypto-primitives/src/crh/bowe_hopwood/constraints.rs @@ -0,0 +1,206 @@ +use algebra::Field; +use std::hash::Hash; + +use crate::crh::{ + bowe_hopwood::{BoweHopwoodPedersenCRH, BoweHopwoodPedersenParameters, CHUNK_SIZE}, + pedersen::PedersenWindow, + FixedLengthCRHGadget, +}; +use algebra::groups::Group; +use r1cs_core::{ConstraintSystem, SynthesisError}; +use r1cs_std::{alloc::AllocGadget, groups::GroupGadget, uint8::UInt8}; + +use r1cs_std::bits::boolean::Boolean; +use std::{borrow::Borrow, marker::PhantomData}; + +#[derive(Derivative)] +#[derivative(Clone( + bound = "G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget" +))] +pub struct BoweHopwoodPedersenCRHGadgetParameters< + G: Group, + W: PedersenWindow, + ConstraintF: Field, + GG: GroupGadget, +> { + params: BoweHopwoodPedersenParameters, + _group_g: PhantomData, + _engine: PhantomData, + _window: PhantomData, +} + +pub struct BoweHopwoodPedersenCRHGadget< + G: Group, + ConstraintF: Field, + GG: GroupGadget, +> { + _group: PhantomData<*const G>, + _group_gadget: PhantomData<*const GG>, + _engine: PhantomData, +} + +impl FixedLengthCRHGadget, ConstraintF> + for BoweHopwoodPedersenCRHGadget +where + ConstraintF: Field, + G: Group + Hash, + GG: GroupGadget, + W: PedersenWindow, +{ + type OutputGadget = GG; + type ParametersGadget = BoweHopwoodPedersenCRHGadgetParameters; + + fn check_evaluation_gadget>( + cs: CS, + parameters: &Self::ParametersGadget, + input: &[UInt8], + ) -> Result { + // Pad the input if it is not the current length. + let mut input_in_bits: Vec<_> = input.iter().flat_map(|byte| byte.into_bits_le()).collect(); + if (input_in_bits.len()) % CHUNK_SIZE != 0 { + let current_length = input_in_bits.len(); + for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) { + input_in_bits.push(Boolean::constant(false)); + } + } + assert!(input_in_bits.len() % CHUNK_SIZE == 0); + assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS); + for generators in parameters.params.generators.iter() { + assert_eq!(generators.len(), W::WINDOW_SIZE); + } + + // Allocate new variable for the result. + let input_in_bits = input_in_bits + .chunks(W::WINDOW_SIZE * CHUNK_SIZE) + .map(|x| x.chunks(CHUNK_SIZE).into_iter().collect::>()) + .collect::>(); + let result = GG::precomputed_base_3_bit_signed_digit_scalar_mul( + cs, + ¶meters.params.generators, + &input_in_bits, + )?; + + Ok(result) + } +} + +impl> + AllocGadget, ConstraintF> + for BoweHopwoodPedersenCRHGadgetParameters +{ + fn alloc>( + _cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow>, + { + let params = value_gen()?.borrow().clone(); + Ok(BoweHopwoodPedersenCRHGadgetParameters { + params, + _group_g: PhantomData, + _engine: PhantomData, + _window: PhantomData, + }) + } + + fn alloc_input>( + _cs: CS, + value_gen: F, + ) -> Result + where + F: FnOnce() -> Result, + T: Borrow>, + { + let params = value_gen()?.borrow().clone(); + Ok(BoweHopwoodPedersenCRHGadgetParameters { + params, + _group_g: PhantomData, + _engine: PhantomData, + _window: PhantomData, + }) + } +} + +#[cfg(test)] +mod test { + use algebra::fields::sw6::fr::Fr; + use rand::{thread_rng, Rng}; + + use crate::crh::{ + bowe_hopwood::{constraints::BoweHopwoodPedersenCRHGadget, BoweHopwoodPedersenCRH}, + pedersen::PedersenWindow, + FixedLengthCRH, FixedLengthCRHGadget, + }; + use algebra::{curves::edwards_sw6::EdwardsProjective as Edwards, ProjectiveCurve}; + use r1cs_core::ConstraintSystem; + use r1cs_std::{ + alloc::AllocGadget, groups::curves::twisted_edwards::edwards_sw6::EdwardsSWGadget, + test_constraint_system::TestConstraintSystem, uint8::UInt8, + }; + + type TestCRH = BoweHopwoodPedersenCRH; + type TestCRHGadget = BoweHopwoodPedersenCRHGadget; + + #[derive(Clone, PartialEq, Eq, Hash)] + pub(super) struct Window; + + impl PedersenWindow for Window { + const WINDOW_SIZE: usize = 90; + const NUM_WINDOWS: usize = 8; + } + + fn generate_input, R: Rng>( + mut cs: CS, + rng: &mut R, + ) -> ([u8; 270], Vec) { + let mut input = [1u8; 270]; + rng.fill_bytes(&mut input); + + let mut input_bytes = vec![]; + for (byte_i, input_byte) in input.into_iter().enumerate() { + let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i)); + input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap()); + } + (input, input_bytes) + } + + #[test] + fn crh_primitive_gadget_test() { + let rng = &mut thread_rng(); + let mut cs = TestConstraintSystem::::new(); + + let (input, input_bytes) = generate_input(&mut cs, rng); + println!("number of constraints for input: {}", cs.num_constraints()); + + let parameters = TestCRH::setup(rng).unwrap(); + let primitive_result = TestCRH::evaluate(¶meters, &input).unwrap(); + + let gadget_parameters = + >::ParametersGadget::alloc( + &mut cs.ns(|| "gadget_parameters"), + || Ok(¶meters), + ) + .unwrap(); + println!( + "number of constraints for input + params: {}", + cs.num_constraints() + ); + + let gadget_result = + >::check_evaluation_gadget( + &mut cs.ns(|| "gadget_evaluation"), + &gadget_parameters, + &input_bytes, + ) + .unwrap(); + + println!("number of constraints total: {}", cs.num_constraints()); + + let primitive_result = primitive_result.into_affine(); + assert_eq!(primitive_result.x, gadget_result.x.value.unwrap()); + assert_eq!(primitive_result.y, gadget_result.y.value.unwrap()); + assert!(cs.is_satisfied()); + } +} diff --git a/crypto-primitives/src/crh/bowe_hopwood/mod.rs b/crypto-primitives/src/crh/bowe_hopwood/mod.rs new file mode 100644 index 0000000..8820600 --- /dev/null +++ b/crypto-primitives/src/crh/bowe_hopwood/mod.rs @@ -0,0 +1,194 @@ +use crate::Error; +use rand::Rng; +use rayon::prelude::*; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + marker::PhantomData, +}; + +use super::pedersen::{bytes_to_bits, PedersenCRH, PedersenWindow}; +use crate::crh::FixedLengthCRH; +use algebra::{biginteger::BigInteger, fields::PrimeField, groups::Group}; + +#[cfg(feature = "r1cs")] +pub mod constraints; + +pub const CHUNK_SIZE: usize = 3; + +#[derive(Clone, Default)] +pub struct BoweHopwoodPedersenParameters { + pub generators: Vec>, +} + +pub struct BoweHopwoodPedersenCRH { + group: PhantomData, + window: PhantomData, +} + +impl BoweHopwoodPedersenCRH { + pub fn create_generators(rng: &mut R) -> Vec> { + let mut generators = Vec::new(); + for _ in 0..W::NUM_WINDOWS { + let mut generators_for_segment = Vec::new(); + let mut base = G::rand(rng); + for _ in 0..W::WINDOW_SIZE { + generators_for_segment.push(base); + for _ in 0..4 { + base.double_in_place(); + } + } + generators.push(generators_for_segment); + } + generators + } +} + +impl FixedLengthCRH for BoweHopwoodPedersenCRH { + const INPUT_SIZE_BITS: usize = PedersenCRH::::INPUT_SIZE_BITS; + type Output = G; + type Parameters = BoweHopwoodPedersenParameters; + + fn setup(rng: &mut R) -> Result { + fn calculate_num_chunks_in_segment() -> usize { + let upper_limit = F::modulus_minus_one_div_two(); + let mut c = 0; + let mut range = F::BigInt::from(2_u64); + while range < upper_limit { + range.muln(4); + c += 1; + } + + c + } + + let maximum_num_chunks_in_segment = calculate_num_chunks_in_segment::(); + if W::WINDOW_SIZE > maximum_num_chunks_in_segment { + return Err(format!( + "Bowe-Hopwood hash must have a window size resulting in scalars < (p-1)/2, \ + maximum segment size is {}", + maximum_num_chunks_in_segment + ) + .into()); + } + + let time = start_timer!(|| format!( + "BoweHopwoodPedersenCRH::Setup: {} segments of {} 3-bit chunks; {{0,1}}^{{{}}} -> G", + W::NUM_WINDOWS, + W::WINDOW_SIZE, + W::WINDOW_SIZE * W::NUM_WINDOWS * CHUNK_SIZE + )); + let generators = Self::create_generators(rng); + end_timer!(time); + Ok(Self::Parameters { generators }) + } + + fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result { + let eval_time = start_timer!(|| "BoweHopwoodPedersenCRH::Eval"); + + if (input.len() * 8) > W::WINDOW_SIZE * W::NUM_WINDOWS * CHUNK_SIZE { + panic!( + "incorrect input length {:?} for window params {:?}x{:?}x{}", + input.len(), + W::WINDOW_SIZE, + W::NUM_WINDOWS, + CHUNK_SIZE, + ); + } + + let mut padded_input = Vec::with_capacity(input.len()); + let input = bytes_to_bits(input); + // Pad the input if it is not the current length. + padded_input.extend_from_slice(&input); + if input.len() % CHUNK_SIZE != 0 { + let current_length = input.len(); + for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) { + padded_input.push(false); + } + } + + assert_eq!(padded_input.len() % CHUNK_SIZE, 0); + + assert_eq!( + parameters.generators.len(), + W::NUM_WINDOWS, + "Incorrect pp of size {:?} for window params {:?}x{:?}x{}", + parameters.generators.len(), + W::WINDOW_SIZE, + W::NUM_WINDOWS, + CHUNK_SIZE, + ); + for generators in parameters.generators.iter() { + assert_eq!(generators.len(), W::WINDOW_SIZE); + } + assert_eq!(CHUNK_SIZE, 3); + + // Compute sum of h_i^{sum of (1-2*c_{i,j,2})*(1+c_{i,j,0}+2*c_{i,j,1})*2^{4*(j-1)} for all j in segment} for all i. + // Described in section 5.4.1.7 in the Zcash protocol specification. + let result = padded_input + .par_chunks(W::WINDOW_SIZE * CHUNK_SIZE) + .zip(¶meters.generators) + .map(|(segment_bits, segment_generators)| { + segment_bits + .par_chunks(CHUNK_SIZE) + .zip(segment_generators) + .map(|(chunk_bits, generator)| { + let mut encoded = generator.clone(); + if chunk_bits[0] { + encoded = encoded + &generator; + } + if chunk_bits[1] { + encoded = encoded + &generator.double(); + } + if chunk_bits[2] { + encoded = encoded.neg(); + } + encoded + }) + .reduce(|| G::zero(), |a, b| a + &b) + }) + .reduce(|| G::zero(), |a, b| a + &b); + end_timer!(eval_time); + + Ok(result) + } +} + +impl Debug for BoweHopwoodPedersenParameters { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "Bowe-Hopwood Pedersen Hash Parameters {{\n")?; + for (i, g) in self.generators.iter().enumerate() { + write!(f, "\t Generator {}: {:?}\n", i, g)?; + } + write!(f, "}}\n") + } +} + +#[cfg(test)] +mod test { + use crate::{ + crh::{bowe_hopwood::BoweHopwoodPedersenCRH, pedersen::PedersenWindow}, + FixedLengthCRH, + }; + use algebra::curves::edwards_sw6::EdwardsProjective; + use rand::thread_rng; + + #[test] + fn test_simple_bh() { + #[derive(Clone)] + struct TestWindow {} + impl PedersenWindow for TestWindow { + const WINDOW_SIZE: usize = 90; + const NUM_WINDOWS: usize = 8; + } + + let rng = &mut thread_rng(); + let params = + as FixedLengthCRH>::setup(rng) + .unwrap(); + as FixedLengthCRH>::evaluate( + ¶ms, + &[1, 2, 3], + ) + .unwrap(); + } +} diff --git a/crypto-primitives/src/crh/mod.rs b/crypto-primitives/src/crh/mod.rs index 4af4c6b..84200bd 100644 --- a/crypto-primitives/src/crh/mod.rs +++ b/crypto-primitives/src/crh/mod.rs @@ -2,6 +2,7 @@ use algebra::bytes::ToBytes; use rand::Rng; use std::hash::Hash; +pub mod bowe_hopwood; pub mod injective_map; pub mod pedersen; diff --git a/crypto-primitives/src/nizk/groth16/constraints.rs b/crypto-primitives/src/nizk/groth16/constraints.rs index be0f0da..60fd0d6 100644 --- a/crypto-primitives/src/nizk/groth16/constraints.rs +++ b/crypto-primitives/src/nizk/groth16/constraints.rs @@ -62,7 +62,7 @@ impl Result, T: Borrow; - fn alloc_checked>(cs: CS, f: F) -> Result + fn alloc_checked>( + cs: CS, + f: F, + ) -> Result where F: FnOnce() -> Result, T: Borrow, @@ -20,7 +23,10 @@ where Self::alloc(cs, f) } - fn alloc_input>(cs: CS, f: F) -> Result + fn alloc_input>( + cs: CS, + f: F, + ) -> Result where F: FnOnce() -> Result, T: Borrow; @@ -37,8 +43,13 @@ where } } -impl> AllocGadget<[I], ConstraintF> for Vec { - fn alloc>(mut cs: CS, f: F) -> Result +impl> AllocGadget<[I], ConstraintF> + for Vec +{ + fn alloc>( + mut cs: CS, + f: F, + ) -> Result where F: FnOnce() -> Result, T: Borrow<[I]>, @@ -52,7 +63,10 @@ impl> AllocGadget<[I], Con Ok(vec) } - fn alloc_input>(mut cs: CS, f: F) -> Result + fn alloc_input>( + mut cs: CS, + f: F, + ) -> Result where F: FnOnce() -> Result, T: Borrow<[I]>, diff --git a/r1cs-std/src/bits/boolean.rs b/r1cs-std/src/bits/boolean.rs index ed92527..6a32647 100644 --- a/r1cs-std/src/bits/boolean.rs +++ b/r1cs-std/src/bits/boolean.rs @@ -1,7 +1,6 @@ use algebra::{BitIterator, Field, FpParameters, PrimeField}; -use crate::prelude::*; -use crate::Assignment; +use crate::{prelude::*, Assignment}; use r1cs_core::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; use std::borrow::Borrow; @@ -346,7 +345,11 @@ impl Boolean { } } - pub fn lc(&self, one: Variable, coeff: ConstraintF) -> LinearCombination { + pub fn lc( + &self, + one: Variable, + coeff: ConstraintF, + ) -> LinearCombination { match *self { Boolean::Constant(c) => { if c { @@ -396,7 +399,11 @@ impl Boolean { } /// Perform XOR over two boolean operands - pub fn xor<'a, ConstraintF, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result + pub fn xor<'a, ConstraintF, CS>( + cs: CS, + a: &'a Self, + b: &'a Self, + ) -> Result where ConstraintF: Field, CS: ConstraintSystem, @@ -441,7 +448,11 @@ impl Boolean { } /// Perform AND over two boolean operands - pub fn and<'a, ConstraintF, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result + pub fn and<'a, ConstraintF, CS>( + cs: CS, + a: &'a Self, + b: &'a Self, + ) -> Result where ConstraintF: Field, CS: ConstraintSystem, @@ -629,7 +640,10 @@ impl From for Boolean { } impl AllocGadget for Boolean { - fn alloc>(cs: CS, value_gen: F) -> Result + fn alloc>( + cs: CS, + value_gen: F, + ) -> Result where F: FnOnce() -> Result, T: Borrow, @@ -713,7 +727,10 @@ impl ConditionalEqGadget for Boolean { } impl ToBytesGadget for Boolean { - fn to_bytes>(&self, _cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + _cs: CS, + ) -> Result, SynthesisError> { let mut bits = vec![Boolean::constant(false); 7]; bits.push(*self); bits.reverse(); @@ -734,15 +751,11 @@ impl ToBytesGadget for Boolean { #[cfg(test)] mod test { use super::{AllocatedBit, Boolean}; - use crate::{ - test_constraint_system::TestConstraintSystem, - prelude::* - }; - use algebra::{fields::bls12_381::Fr, BitIterator, Field, PrimeField}; - use algebra::UniformRand; -use rand::SeedableRng; -use rand_xorshift::XorShiftRng; + use crate::{prelude::*, test_constraint_system::TestConstraintSystem}; + use algebra::{fields::bls12_381::Fr, BitIterator, Field, PrimeField, UniformRand}; use r1cs_core::ConstraintSystem; + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; use std::str::FromStr; #[test] @@ -1775,8 +1788,8 @@ use rand_xorshift::XorShiftRng; // let mut bits = vec![]; // for (i, b) in BitIterator::new(r).skip(1).enumerate() { // bits.push(Boolean::from( - // AllocatedBit::alloc(cs.ns(|| format!("bit_gadget {}", i)), - // Some(b)) .unwrap(), + // AllocatedBit::alloc(cs.ns(|| format!("bit_gadget {}", + // i)), Some(b)) .unwrap(), // )); // } diff --git a/r1cs-std/src/bits/mod.rs b/r1cs-std/src/bits/mod.rs index 97b4940..e859d18 100644 --- a/r1cs-std/src/bits/mod.rs +++ b/r1cs-std/src/bits/mod.rs @@ -7,7 +7,10 @@ pub mod uint32; pub mod uint8; pub trait ToBitsGadget { - fn to_bits>(&self, cs: CS) -> Result, SynthesisError>; + fn to_bits>( + &self, + cs: CS, + ) -> Result, SynthesisError>; /// Additionally checks if the produced list of booleans is 'valid'. fn to_bits_strict>( @@ -17,7 +20,10 @@ pub trait ToBitsGadget { } impl ToBitsGadget for Boolean { - fn to_bits>(&self, _: CS) -> Result, SynthesisError> { + fn to_bits>( + &self, + _: CS, + ) -> Result, SynthesisError> { Ok(vec![self.clone()]) } @@ -30,7 +36,10 @@ impl ToBitsGadget for Boolean { } impl ToBitsGadget for [Boolean] { - fn to_bits>(&self, _cs: CS) -> Result, SynthesisError> { + fn to_bits>( + &self, + _cs: CS, + ) -> Result, SynthesisError> { Ok(self.to_vec()) } @@ -42,7 +51,10 @@ impl ToBitsGadget for [Boolean] { } } impl ToBitsGadget for Vec { - fn to_bits>(&self, _cs: CS) -> Result, SynthesisError> { + fn to_bits>( + &self, + _cs: CS, + ) -> Result, SynthesisError> { Ok(self.clone()) } @@ -55,7 +67,10 @@ impl ToBitsGadget for Vec { } impl ToBitsGadget for [UInt8] { - fn to_bits>(&self, _cs: CS) -> Result, SynthesisError> { + fn to_bits>( + &self, + _cs: CS, + ) -> Result, SynthesisError> { let mut result = Vec::with_capacity(&self.len() * 8); for byte in self { result.extend_from_slice(&byte.into_bits_le()); @@ -72,7 +87,10 @@ impl ToBitsGadget for [UInt8] { } pub trait ToBytesGadget { - fn to_bytes>(&self, cs: CS) -> Result, SynthesisError>; + fn to_bytes>( + &self, + cs: CS, + ) -> Result, SynthesisError>; /// Additionally checks if the produced list of booleans is 'valid'. fn to_bytes_strict>( @@ -82,7 +100,10 @@ pub trait ToBytesGadget { } impl ToBytesGadget for [UInt8] { - fn to_bytes>(&self, _cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + _cs: CS, + ) -> Result, SynthesisError> { Ok(self.to_vec()) } @@ -94,8 +115,13 @@ impl ToBytesGadget for [UInt8] { } } -impl<'a, ConstraintF: Field, T: 'a + ToBytesGadget> ToBytesGadget for &'a T { - fn to_bytes>(&self, cs: CS) -> Result, SynthesisError> { +impl<'a, ConstraintF: Field, T: 'a + ToBytesGadget> ToBytesGadget + for &'a T +{ + fn to_bytes>( + &self, + cs: CS, + ) -> Result, SynthesisError> { (*self).to_bytes(cs) } @@ -108,7 +134,10 @@ impl<'a, ConstraintF: Field, T: 'a + ToBytesGadget> ToBytesGadget ToBytesGadget for &'a [UInt8] { - fn to_bytes>(&self, _cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + _cs: CS, + ) -> Result, SynthesisError> { Ok(self.to_vec()) } diff --git a/r1cs-std/src/bits/uint32.rs b/r1cs-std/src/bits/uint32.rs index 855a171..77e4eca 100644 --- a/r1cs-std/src/bits/uint32.rs +++ b/r1cs-std/src/bits/uint32.rs @@ -1,10 +1,12 @@ -use algebra::{FpParameters, PrimeField, Field}; +use algebra::{Field, FpParameters, PrimeField}; use r1cs_core::{ConstraintSystem, LinearCombination, SynthesisError}; -use crate::boolean::{AllocatedBit, Boolean}; -use crate::Assignment; -use crate::prelude::*; +use crate::{ + boolean::{AllocatedBit, Boolean}, + prelude::*, + Assignment, +}; /// Represents an interpretation of 32 `Boolean` objects as an /// unsigned integer. @@ -270,7 +272,10 @@ impl UInt32 { impl ToBytesGadget for UInt32 { #[inline] - fn to_bytes>(&self, _cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + _cs: CS, + ) -> Result, SynthesisError> { let value_chunks = match self.value.map(|val| { use algebra::bytes::ToBytes; let mut bytes = [0u8; 4]; @@ -340,9 +345,9 @@ mod test { use super::UInt32; use crate::{bits::boolean::Boolean, test_constraint_system::TestConstraintSystem}; use algebra::fields::{bls12_381::Fr, Field}; + use r1cs_core::ConstraintSystem; use rand::{Rng, SeedableRng}; use rand_xorshift::XorShiftRng; - use r1cs_core::ConstraintSystem; #[test] fn test_uint32_from_bits() { diff --git a/r1cs-std/src/bits/uint8.rs b/r1cs-std/src/bits/uint8.rs index 5a46dff..b7de9ed 100644 --- a/r1cs-std/src/bits/uint8.rs +++ b/r1cs-std/src/bits/uint8.rs @@ -1,11 +1,8 @@ -use algebra::{ToConstraintField, FpParameters, Field, PrimeField}; +use algebra::{Field, FpParameters, PrimeField, ToConstraintField}; use r1cs_core::{ConstraintSystem, SynthesisError}; -use crate::boolean::AllocatedBit; -use crate::fields::fp::FpGadget; -use crate::prelude::*; -use crate::Assignment; +use crate::{boolean::AllocatedBit, fields::fp::FpGadget, prelude::*, Assignment}; use std::borrow::Borrow; /// Represents an interpretation of 8 `Boolean` objects as an @@ -53,7 +50,10 @@ impl UInt8 { } } - pub fn alloc_vec(mut cs: CS, values: &[T]) -> Result, SynthesisError> + pub fn alloc_vec( + mut cs: CS, + values: &[T], + ) -> Result, SynthesisError> where ConstraintF: Field, CS: ConstraintSystem, @@ -69,15 +69,20 @@ impl UInt8 { } /// 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(mut cs: CS, values: &[u8]) -> Result, SynthesisError> + /// `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( + mut cs: CS, + values: &[u8], + ) -> Result, SynthesisError> where ConstraintF: PrimeField, CS: ConstraintSystem, { let values_len = values.len(); - let field_elements: Vec = ToConstraintField::::to_field_elements(values).unwrap(); + let field_elements: Vec = + ToConstraintField::::to_field_elements(values).unwrap(); let max_size = 8 * (ConstraintF::Params::CAPACITY / 8) as usize; let mut allocated_bits = Vec::new(); @@ -294,9 +299,9 @@ mod test { use super::UInt8; use crate::{prelude::*, test_constraint_system::TestConstraintSystem}; use algebra::fields::bls12_381::Fr; + use r1cs_core::ConstraintSystem; use rand::{Rng, SeedableRng}; use rand_xorshift::XorShiftRng; - use r1cs_core::ConstraintSystem; #[test] fn test_uint8_from_bits_to_bits() { diff --git a/r1cs-std/src/eq.rs b/r1cs-std/src/eq.rs index 301fbfc..d978815 100644 --- a/r1cs-std/src/eq.rs +++ b/r1cs-std/src/eq.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -use r1cs_core::{ConstraintSystem, SynthesisError}; use algebra::Field; +use r1cs_core::{ConstraintSystem, SynthesisError}; /// If `condition == 1`, then enforces that `self` and `other` are equal; /// otherwise, it doesn't enforce anything. @@ -14,7 +14,9 @@ pub trait ConditionalEqGadget: Eq { fn cost() -> usize; } -impl, ConstraintF: Field> ConditionalEqGadget for [T] { +impl, ConstraintF: Field> ConditionalEqGadget + for [T] +{ fn conditional_enforce_equal>( &self, mut cs: CS, @@ -77,7 +79,9 @@ where fn cost() -> usize; } -impl> OrEqualsGadget for T { +impl> + OrEqualsGadget for T +{ fn enforce_equal_or>( cs: CS, cond: &Boolean, @@ -109,8 +113,10 @@ where fn cost() -> usize; } -impl + CondSelectGadget> - ConditionalOrEqualsGadget for T +impl< + ConstraintF: Field, + T: Sized + ConditionalEqGadget + CondSelectGadget, + > ConditionalOrEqualsGadget for T { fn conditional_enforce_equal_or>( mut cs: CS, @@ -130,8 +136,7 @@ impl + CondSelec } fn cost() -> usize { - >::cost() + >::cost() + >::cost() + + >::cost() } } - - diff --git a/r1cs-std/src/fields/bls12_377.rs b/r1cs-std/src/fields/bls12_377.rs index 85fcf54..14140bf 100644 --- a/r1cs-std/src/fields/bls12_377.rs +++ b/r1cs-std/src/fields/bls12_377.rs @@ -1,6 +1,4 @@ -use algebra::{ - fields::bls12_377::{Fq, Fq12Parameters, Fq2Parameters, Fq6Parameters}, -}; +use algebra::fields::bls12_377::{Fq, Fq12Parameters, Fq2Parameters, Fq6Parameters}; use super::{fp::FpGadget, fp12::Fp12Gadget, fp2::Fp2Gadget, fp6_3over2::Fp6Gadget}; diff --git a/r1cs-std/src/fields/edwards_bls12.rs b/r1cs-std/src/fields/edwards_bls12.rs index c66832f..62e2cba 100644 --- a/r1cs-std/src/fields/edwards_bls12.rs +++ b/r1cs-std/src/fields/edwards_bls12.rs @@ -1,4 +1,4 @@ -use algebra::fields::edwards_bls12::fq::Fq; use crate::fields::fp::FpGadget; +use algebra::fields::edwards_bls12::fq::Fq; pub type FqGadget = FpGadget; diff --git a/r1cs-std/src/fields/edwards_sw6.rs b/r1cs-std/src/fields/edwards_sw6.rs index 9c4969c..12d74f3 100644 --- a/r1cs-std/src/fields/edwards_sw6.rs +++ b/r1cs-std/src/fields/edwards_sw6.rs @@ -1,4 +1,4 @@ -use algebra::fields::edwards_sw6::fq::Fq; use crate::fields::fp::FpGadget; +use algebra::fields::edwards_sw6::fq::Fq; pub type FqGadget = FpGadget; diff --git a/r1cs-std/src/fields/fp.rs b/r1cs-std/src/fields/fp.rs index fc6403d..3bbe6ec 100644 --- a/r1cs-std/src/fields/fp.rs +++ b/r1cs-std/src/fields/fp.rs @@ -1,12 +1,13 @@ use algebra::{bytes::ToBytes, FpParameters, PrimeField}; -use r1cs_core::{ConstraintSystem, LinearCombination, SynthesisError, ConstraintVar::{self, *}}; +use r1cs_core::{ + ConstraintSystem, + ConstraintVar::{self, *}, + LinearCombination, SynthesisError, +}; use std::borrow::Borrow; -use crate::boolean::AllocatedBit; -use crate::Assignment; -use crate::prelude::*; - +use crate::{boolean::AllocatedBit, prelude::*, Assignment}; #[derive(Debug)] pub struct FpGadget { @@ -52,6 +53,23 @@ impl FieldGadget for FpGadget { }) } + #[inline] + fn conditionally_add_constant>( + &self, + mut _cs: CS, + bit: &Boolean, + coeff: F, + ) -> Result { + let value = match (self.value, bit.get_value()) { + (Some(v), Some(b)) => Some(if b { v + &coeff } else { v }), + (..) => None, + }; + Ok(FpGadget { + value, + variable: LC(bit.lc(CS::one(), coeff)) + &self.variable, + }) + } + #[inline] fn add>( &self, @@ -503,6 +521,52 @@ impl TwoBitLookupGadget for FpGadget { } } +impl ThreeBitCondNegLookupGadget for FpGadget { + type TableConstant = F; + + fn three_bit_cond_neg_lookup>( + mut cs: CS, + b: &[Boolean], + b0b1: &Boolean, + c: &[Self::TableConstant], + ) -> Result { + debug_assert!(b.len() == 3); + debug_assert!(c.len() == 4); + + let result = Self::alloc(cs.ns(|| "Allocate lookup result"), || { + let y = match (b[0].get_value().get()?, b[1].get_value().get()?) { + (false, false) => c[0], + (false, true) => c[2], + (true, false) => c[1], + (true, true) => c[3], + }; + if b[2].get_value().get()? { + Ok(-y) + } else { + Ok(y) + } + })?; + + let one = CS::one(); + let y_lc = b0b1.lc(one, c[3] - &c[2] - &c[1] + &c[0]) + + b[0].lc(one, c[1] - &c[0]) + + b[1].lc(one, c[2] - &c[0]) + + (c[0], one); + cs.enforce( + || "Enforce lookup", + |_| y_lc.clone() + y_lc.clone(), + |lc| lc + b[2].lc(one, F::one()), + |_| -result.get_variable() + y_lc.clone(), + ); + + Ok(result) + } + + fn cost() -> usize { + 2 + } +} + impl Clone for FpGadget { fn clone(&self) -> Self { Self { diff --git a/r1cs-std/src/fields/fp12.rs b/r1cs-std/src/fields/fp12.rs index e05b8c6..944c0f8 100644 --- a/r1cs-std/src/fields/fp12.rs +++ b/r1cs-std/src/fields/fp12.rs @@ -1,24 +1,27 @@ use r1cs_core::{ConstraintSystem, SynthesisError}; use algebra::{ - PrimeField, Field, fields::{ fp12_2over3over2::{Fp12, Fp12Parameters}, fp6_3over2::{Fp6, Fp6Parameters}, Fp2Parameters, }, - BitIterator, + BitIterator, Field, PrimeField, }; use std::{borrow::Borrow, marker::PhantomData}; -use crate::prelude::*; -use crate::Assignment; +use crate::{prelude::*, Assignment}; -type Fp2Gadget = - super::fp2::Fp2Gadget<<

::Fp6Params as Fp6Parameters>::Fp2Params, ConstraintF>; -type Fp6Gadget = super::fp6_3over2::Fp6Gadget<

::Fp6Params, ConstraintF>; -type Fp6GadgetVariable = - as FieldGadget::Fp6Params>, ConstraintF>>::Variable; +type Fp2Gadget = super::fp2::Fp2Gadget< + <

::Fp6Params as Fp6Parameters>::Fp2Params, + ConstraintF, +>; +type Fp6Gadget = + super::fp6_3over2::Fp6Gadget<

::Fp6Params, ConstraintF>; +type Fp6GadgetVariable = as FieldGadget< + Fp6<

::Fp6Params>, + ConstraintF, +>>::Variable; #[derive(Derivative)] #[derivative(Debug(bound = "ConstraintF: PrimeField"))] @@ -272,7 +275,10 @@ where P: Fp12Parameters, ::Fp2Params: Fp2Parameters, { - type Variable = (Fp6GadgetVariable, Fp6GadgetVariable); + type Variable = ( + Fp6GadgetVariable, + Fp6GadgetVariable, + ); #[inline] fn get_value(&self) -> Option> { @@ -298,6 +304,22 @@ where Ok(Self::new(c0, c1)) } + #[inline] + fn conditionally_add_constant>( + &self, + mut cs: CS, + bit: &Boolean, + coeff: Fp12

, + ) -> Result { + let c0 = self + .c0 + .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; + let c1 = self + .c1 + .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; + Ok(Self::new(c0, c1)) + } + #[inline] fn add>( &self, @@ -343,7 +365,10 @@ where } #[inline] - fn negate>(&self, mut cs: CS) -> Result { + fn negate>( + &self, + mut cs: CS, + ) -> Result { let c0 = self.c0.negate(cs.ns(|| "c0"))?; let c1 = self.c1.negate(cs.ns(|| "c1"))?; Ok(Self::new(c0, c1)) @@ -396,7 +421,10 @@ where Ok(Self::new(c0, c1)) } - fn square>(&self, mut cs: CS) -> Result { + fn square>( + &self, + mut cs: CS, + ) -> Result { // From Libsnark/fp2_gadget.tcc // Complex multiplication for Fp2: // v0 = A.c0 * A.c1 @@ -518,7 +546,10 @@ where Ok(self) } - fn inverse>(&self, mut cs: CS) -> Result { + fn inverse>( + &self, + mut cs: CS, + ) -> Result { let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || { self.get_value().and_then(|val| val.inverse()).get() })?; @@ -690,7 +721,10 @@ where P: Fp12Parameters, ::Fp2Params: Fp2Parameters, { - fn to_bits>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bits>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { let mut c0 = self.c0.to_bits(&mut cs)?; let mut c1 = self.c1.to_bits(cs)?; c0.append(&mut c1); @@ -713,7 +747,10 @@ where P: Fp12Parameters, ::Fp2Params: Fp2Parameters, { - fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + mut cs: CS, + ) -> Result, 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); @@ -797,6 +834,42 @@ where } } +impl ThreeBitCondNegLookupGadget + for Fp12Gadget +where + P: Fp12Parameters, + ::Fp2Params: Fp2Parameters, +{ + type TableConstant = Fp12

; + + fn three_bit_cond_neg_lookup>( + mut cs: CS, + b: &[Boolean], + b0b1: &Boolean, + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + let c0 = Fp6Gadget::::three_bit_cond_neg_lookup( + cs.ns(|| "Lookup c0"), + b, + b0b1, + &c0s, + )?; + let c1 = Fp6Gadget::::three_bit_cond_neg_lookup( + cs.ns(|| "Lookup c1"), + b, + b0b1, + &c1s, + )?; + Ok(Self::new(c0, c1)) + } + + fn cost() -> usize { + 2 * as ThreeBitCondNegLookupGadget>::cost() + } +} + impl AllocGadget, ConstraintF> for Fp12Gadget where P: Fp12Parameters, diff --git a/r1cs-std/src/fields/fp2.rs b/r1cs-std/src/fields/fp2.rs index 26e8a08..1d7f670 100644 --- a/r1cs-std/src/fields/fp2.rs +++ b/r1cs-std/src/fields/fp2.rs @@ -2,12 +2,10 @@ use algebra::{ fields::{Fp2, Fp2Parameters}, Field, PrimeField, }; -use r1cs_core::{ConstraintSystem, SynthesisError, ConstraintVar}; +use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError}; use std::{borrow::Borrow, marker::PhantomData}; -use crate::fields::fp::FpGadget; -use crate::prelude::*; -use crate::Assignment; +use crate::{fields::fp::FpGadget, prelude::*, Assignment}; #[derive(Derivative)] #[derivative(Debug(bound = "P: Fp2Parameters, ConstraintF: PrimeField"))] @@ -62,7 +60,9 @@ impl, ConstraintF: PrimeField> Fp2Gadget, ConstraintF: PrimeField> FieldGadget, ConstraintF> for Fp2Gadget { +impl, ConstraintF: PrimeField> FieldGadget, ConstraintF> + for Fp2Gadget +{ type Variable = (ConstraintVar, ConstraintVar); #[inline] @@ -95,6 +95,22 @@ impl, ConstraintF: PrimeField> FieldGadget>( + &self, + mut cs: CS, + bit: &Boolean, + coeff: Fp2

, + ) -> Result { + let c0 = self + .c0 + .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; + let c1 = self + .c1 + .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; + Ok(Self::new(c0, c1)) + } + #[inline] fn add>( &self, @@ -191,7 +207,10 @@ impl, ConstraintF: PrimeField> FieldGadget>(&self, mut cs: CS) -> Result { + fn square>( + &self, + mut cs: CS, + ) -> Result { // From Libsnark/fp2_gadget.tcc // Complex multiplication for Fp2: // v0 = A.c0 * A.c1 @@ -274,7 +293,10 @@ impl, ConstraintF: PrimeField> FieldGadget>(&self, mut cs: CS) -> Result { + fn inverse>( + &self, + mut cs: CS, + ) -> Result { let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || { self.get_value().and_then(|val| val.inverse()).get() })?; @@ -439,7 +461,9 @@ impl, ConstraintF: PrimeField> FieldGadget, ConstraintF: PrimeField> PartialEq for Fp2Gadget { +impl, ConstraintF: PrimeField> PartialEq + for Fp2Gadget +{ fn eq(&self, other: &Self) -> bool { self.c0 == other.c0 && self.c1 == other.c1 } @@ -447,9 +471,14 @@ impl, ConstraintF: PrimeField> PartialEq for impl, ConstraintF: PrimeField> Eq for Fp2Gadget {} -impl, ConstraintF: PrimeField> EqGadget for Fp2Gadget {} +impl, ConstraintF: PrimeField> EqGadget + for Fp2Gadget +{ +} -impl, ConstraintF: PrimeField> ConditionalEqGadget for Fp2Gadget { +impl, ConstraintF: PrimeField> ConditionalEqGadget + for Fp2Gadget +{ #[inline] fn conditional_enforce_equal>( &self, @@ -469,7 +498,9 @@ impl, ConstraintF: PrimeField> ConditionalEqG } } -impl, ConstraintF: PrimeField> NEqGadget for Fp2Gadget { +impl, ConstraintF: PrimeField> NEqGadget + for Fp2Gadget +{ #[inline] fn enforce_not_equal>( &self, @@ -486,8 +517,13 @@ impl, ConstraintF: PrimeField> NEqGadget, ConstraintF: PrimeField> ToBitsGadget for Fp2Gadget { - fn to_bits>(&self, mut cs: CS) -> Result, SynthesisError> { +impl, ConstraintF: PrimeField> ToBitsGadget + for Fp2Gadget +{ + fn to_bits>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { let mut c0 = self.c0.to_bits(&mut cs)?; let mut c1 = self.c1.to_bits(cs)?; c0.append(&mut c1); @@ -505,8 +541,13 @@ impl, ConstraintF: PrimeField> ToBitsGadget, ConstraintF: PrimeField> ToBytesGadget for Fp2Gadget { - fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { +impl, ConstraintF: PrimeField> ToBytesGadget + for Fp2Gadget +{ + fn to_bytes>( + &self, + mut cs: CS, + ) -> Result, 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); @@ -524,7 +565,9 @@ impl, ConstraintF: PrimeField> ToBytesGadget< } } -impl, ConstraintF: PrimeField> Clone for Fp2Gadget { +impl, ConstraintF: PrimeField> Clone + for Fp2Gadget +{ fn clone(&self) -> Self { Self { c0: self.c0.clone(), @@ -534,7 +577,9 @@ impl, ConstraintF: PrimeField> Clone for Fp2G } } -impl, ConstraintF: PrimeField> CondSelectGadget for Fp2Gadget { +impl, ConstraintF: PrimeField> CondSelectGadget + for Fp2Gadget +{ #[inline] fn conditionally_select>( mut cs: CS, @@ -542,10 +587,18 @@ impl, ConstraintF: PrimeField> CondSelectGadg first: &Self, second: &Self, ) -> Result { - let c0 = - FpGadget::::conditionally_select(&mut cs.ns(|| "c0"), cond, &first.c0, &second.c0)?; - let c1 = - FpGadget::::conditionally_select(&mut cs.ns(|| "c1"), cond, &first.c1, &second.c1)?; + let c0 = FpGadget::::conditionally_select( + &mut cs.ns(|| "c0"), + cond, + &first.c0, + &second.c0, + )?; + let c1 = FpGadget::::conditionally_select( + &mut cs.ns(|| "c1"), + cond, + &first.c1, + &second.c1, + )?; Ok(Self::new(c0, c1)) } @@ -555,7 +608,9 @@ impl, ConstraintF: PrimeField> CondSelectGadg } } -impl, ConstraintF: PrimeField> TwoBitLookupGadget for Fp2Gadget { +impl, ConstraintF: PrimeField> TwoBitLookupGadget + for Fp2Gadget +{ type TableConstant = Fp2

; fn two_bit_lookup>( mut cs: CS, @@ -574,7 +629,32 @@ impl, ConstraintF: PrimeField> TwoBitLookupGa } } -impl, ConstraintF: PrimeField> AllocGadget, ConstraintF> for Fp2Gadget { +impl, ConstraintF: PrimeField> + ThreeBitCondNegLookupGadget for Fp2Gadget +{ + type TableConstant = Fp2

; + + fn three_bit_cond_neg_lookup>( + mut cs: CS, + b: &[Boolean], + b0b1: &Boolean, + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + let c0 = FpGadget::three_bit_cond_neg_lookup(cs.ns(|| "Lookup c0"), b, b0b1, &c0s)?; + let c1 = FpGadget::three_bit_cond_neg_lookup(cs.ns(|| "Lookup c1"), b, b0b1, &c1s)?; + Ok(Self::new(c0, c1)) + } + + fn cost() -> usize { + 2 * as ThreeBitCondNegLookupGadget>::cost() + } +} + +impl, ConstraintF: PrimeField> AllocGadget, ConstraintF> + for Fp2Gadget +{ #[inline] fn alloc>( mut cs: CS, diff --git a/r1cs-std/src/fields/fp6_3over2.rs b/r1cs-std/src/fields/fp6_3over2.rs index 01f3600..07a5120 100644 --- a/r1cs-std/src/fields/fp6_3over2.rs +++ b/r1cs-std/src/fields/fp6_3over2.rs @@ -5,13 +5,13 @@ use algebra::{ }, PrimeField, }; -use r1cs_core::{ConstraintSystem, SynthesisError, ConstraintVar}; +use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError}; use std::{borrow::Borrow, marker::PhantomData}; -use crate::prelude::*; -use crate::Assignment; +use crate::{prelude::*, Assignment}; -type Fp2Gadget = super::fp2::Fp2Gadget<

::Fp2Params, ConstraintF>; +type Fp2Gadget = + super::fp2::Fp2Gadget<

::Fp2Params, ConstraintF>; #[derive(Derivative)] #[derivative(Debug(bound = "ConstraintF: PrimeField"))] @@ -34,7 +34,11 @@ where P::Fp2Params: Fp2Parameters, { #[inline] - pub fn new(c0: Fp2Gadget, c1: Fp2Gadget, c2: Fp2Gadget) -> Self { + pub fn new( + c0: Fp2Gadget, + c1: Fp2Gadget, + c2: Fp2Gadget, + ) -> Self { Self { c0, c1, @@ -183,6 +187,25 @@ where Ok(Self::new(c0, c1, c2)) } + #[inline] + fn conditionally_add_constant>( + &self, + mut cs: CS, + bit: &Boolean, + coeff: Fp6

, + ) -> Result { + let c0 = self + .c0 + .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; + let c1 = self + .c1 + .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; + let c2 = self + .c2 + .conditionally_add_constant(cs.ns(|| "c2"), bit, coeff.c2)?; + Ok(Self::new(c0, c1, c2)) + } + #[inline] fn add>( &self, @@ -208,7 +231,10 @@ where } #[inline] - fn negate>(&self, mut cs: CS) -> Result { + fn negate>( + &self, + mut cs: CS, + ) -> Result { 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"))?; @@ -374,7 +400,10 @@ where /// Use the Toom-Cook-3x method to compute multiplication. #[inline] - fn square>(&self, mut cs: CS) -> Result { + fn square>( + &self, + mut cs: CS, + ) -> Result { // Uses Toom-Cool-3x multiplication from // // Reference: @@ -473,7 +502,10 @@ where // 18 constaints, we can probably do better but not sure it's worth it. #[inline] - fn inverse>(&self, mut cs: CS) -> Result { + fn inverse>( + &self, + mut cs: CS, + ) -> Result { let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || { self.get_value().and_then(|val| val.inverse()).get() })?; @@ -754,7 +786,10 @@ where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { - fn to_bits>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bits>( + &self, + mut cs: CS, + ) -> Result, 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)?; @@ -785,7 +820,10 @@ where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { - fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + mut cs: CS, + ) -> Result, 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"))?; @@ -878,6 +916,49 @@ where } } +impl ThreeBitCondNegLookupGadget + for Fp6Gadget +where + P: Fp6Parameters, + P::Fp2Params: Fp2Parameters, +{ + type TableConstant = Fp6

; + + fn three_bit_cond_neg_lookup>( + mut cs: CS, + b: &[Boolean], + b0b1: &Boolean, + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + let c2s = c.iter().map(|f| f.c2).collect::>(); + let c0 = Fp2Gadget::::three_bit_cond_neg_lookup( + cs.ns(|| "Lookup c0"), + b, + b0b1, + &c0s, + )?; + let c1 = Fp2Gadget::::three_bit_cond_neg_lookup( + cs.ns(|| "Lookup c1"), + b, + b0b1, + &c1s, + )?; + let c2 = Fp2Gadget::::three_bit_cond_neg_lookup( + cs.ns(|| "Lookup c2"), + b, + b0b1, + &c2s, + )?; + Ok(Self::new(c0, c1, c2)) + } + + fn cost() -> usize { + 3 * as ThreeBitCondNegLookupGadget>::cost() + } +} + impl AllocGadget, ConstraintF> for Fp6Gadget where P: Fp6Parameters, diff --git a/r1cs-std/src/fields/mod.rs b/r1cs-std/src/fields/mod.rs index f03bc9a..ec21ccf 100644 --- a/r1cs-std/src/fields/mod.rs +++ b/r1cs-std/src/fields/mod.rs @@ -26,6 +26,7 @@ pub trait FieldGadget: + ToBytesGadget + CondSelectGadget + TwoBitLookupGadget + + ThreeBitCondNegLookupGadget + Debug { type Variable: Clone + Debug; @@ -38,7 +39,18 @@ pub trait FieldGadget: fn one>(_: CS) -> Result; - fn add>(&self, _: CS, _: &Self) -> Result; + fn conditionally_add_constant>( + &self, + _: CS, + _: &Boolean, + _: F, + ) -> Result; + + fn add>( + &self, + _: CS, + _: &Self, + ) -> Result; fn add_in_place>( &mut self, @@ -61,7 +73,11 @@ pub trait FieldGadget: Ok(self) } - fn sub>(&self, _: CS, _: &Self) -> Result; + fn sub>( + &self, + _: CS, + _: &Self, + ) -> Result; fn sub_in_place>( &mut self, @@ -83,7 +99,11 @@ pub trait FieldGadget: Ok(self) } - fn mul>(&self, _: CS, _: &Self) -> Result; + fn mul>( + &self, + _: CS, + _: &Self, + ) -> Result; fn mul_in_place>( &mut self, @@ -125,7 +145,11 @@ pub trait FieldGadget: result.enforce_equal(&mut cs.ns(|| "test_equals"), &actual_result) } - fn add_constant>(&self, _: CS, _: &F) -> Result; + fn add_constant>( + &self, + _: CS, + _: &F, + ) -> Result; fn add_constant_in_place>( &mut self, @@ -217,10 +241,15 @@ mod test { use rand_xorshift::XorShiftRng; use crate::{prelude::*, test_constraint_system::TestConstraintSystem}; - use algebra::{UniformRand, Field, BitIterator}; + use algebra::{BitIterator, Field, UniformRand}; use r1cs_core::ConstraintSystem; - fn field_test, CS: ConstraintSystem>( + fn field_test< + FE: Field, + ConstraintF: Field, + F: FieldGadget, + CS: ConstraintSystem, + >( mut cs: CS, a: F, b: F, @@ -402,6 +431,23 @@ mod test { 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(); + + let ab_false = a + .conditionally_add_constant( + cs.ns(|| "Add bool with coeff false"), + &Boolean::constant(false), + b_native, + ) + .unwrap(); + assert_eq!(ab_false.get_value().unwrap(), a_native); + let ab_true = a + .conditionally_add_constant( + cs.ns(|| "Add bool with coeff true"), + &Boolean::constant(true), + b_native, + ) + .unwrap(); + assert_eq!(ab_true.get_value().unwrap(), a_native + &b_native); } fn random_frobenius_tests< diff --git a/r1cs-std/src/groups/curves/short_weierstrass/bls12/bls12_377.rs b/r1cs-std/src/groups/curves/short_weierstrass/bls12/bls12_377.rs index 3239102..4984362 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/bls12/bls12_377.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/bls12/bls12_377.rs @@ -15,11 +15,10 @@ mod test { use rand; use super::{G1Gadget, G2Gadget}; - use crate::{test_constraint_system::TestConstraintSystem, prelude::*}; + use crate::{prelude::*, test_constraint_system::TestConstraintSystem}; use algebra::{ curves::bls12_377::{G1Projective as G1, G2Projective as G2}, - fields::bls12_377::Fr, - fields::bls12_377::Fq, + fields::bls12_377::{Fq, Fr}, AffineCurve, BitIterator, PrimeField, ProjectiveCurve, }; use r1cs_core::ConstraintSystem; @@ -52,10 +51,7 @@ mod test { let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost; assert!(cs.is_satisfied()); - assert_eq!( - cond_select_cost, - >::cost() - ); + assert_eq!(cond_select_cost, >::cost()); assert_eq!(add_cost, G1Gadget::cost_of_add()); } @@ -87,10 +83,7 @@ mod test { let add_cost = cs.num_constraints() - cond_select_cost - alloc_cost; assert!(cs.is_satisfied()); - assert_eq!( - cond_select_cost, - >::cost() - ); + assert_eq!(cond_select_cost, >::cost()); assert_eq!(add_cost, G2Gadget::cost_of_add()); } diff --git a/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs index 7cc4bc8..88f333a 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs @@ -5,17 +5,23 @@ use algebra::{ }; use r1cs_core::{ConstraintSystem, SynthesisError}; - -use crate::prelude::*; -use crate::fields::{fp::FpGadget, fp2::Fp2Gadget, FieldGadget}; -use crate::groups::curves::short_weierstrass::AffineGadget; +use crate::{ + fields::{fp::FpGadget, fp2::Fp2Gadget, FieldGadget}, + groups::curves::short_weierstrass::AffineGadget, + prelude::*, +}; use std::fmt::Debug; pub mod bls12_377; -pub type G1Gadget

= AffineGadget<

::G1Parameters,

::Fp, FpGadget<

::Fp>>; -pub type G2Gadget

= AffineGadget<

::G2Parameters,

::Fp, Fp2G

>; +pub type G1Gadget

= AffineGadget< +

::G1Parameters, +

::Fp, + FpGadget<

::Fp>, +>; +pub type G2Gadget

= + AffineGadget<

::G2Parameters,

::Fp, Fp2G

>; #[derive(Derivative)] #[derivative( @@ -41,7 +47,10 @@ impl G1PreparedGadget

{ impl ToBytesGadget for G1PreparedGadget

{ #[inline] - fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { self.0.to_bytes(&mut cs.ns(|| "g_alpha to bytes")) } @@ -66,7 +75,10 @@ pub struct G2PreparedGadget { impl ToBytesGadget for G2PreparedGadget

{ #[inline] - fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { let mut bytes = Vec::new(); for (i, coeffs) in self.ell_coeffs.iter().enumerate() { let mut cs = cs.ns(|| format!("Iteration {}", i)); diff --git a/r1cs-std/src/groups/curves/short_weierstrass/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/mod.rs index 7a84cbc..f2a0ecf 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/mod.rs @@ -1,23 +1,25 @@ -use algebra::{AffineCurve, Field, ProjectiveCurve}; use algebra::{ curves::{ short_weierstrass_jacobian::{GroupAffine as SWAffine, GroupProjective as SWProjective}, SWModelParameters, }, - BitIterator, PrimeField, + AffineCurve, BitIterator, Field, PrimeField, ProjectiveCurve, }; use r1cs_core::{ConstraintSystem, SynthesisError}; use std::{borrow::Borrow, marker::PhantomData, ops::Neg}; -use crate::Assignment; -use crate::prelude::*; +use crate::{prelude::*, Assignment}; pub mod bls12; #[derive(Derivative)] #[derivative(Debug, Clone)] #[must_use] -pub struct AffineGadget> { +pub struct AffineGadget< + P: SWModelParameters, + ConstraintF: Field, + F: FieldGadget, +> { pub x: F, pub y: F, _params: PhantomData

, @@ -80,7 +82,8 @@ where { } -impl GroupGadget, ConstraintF> for AffineGadget +impl GroupGadget, ConstraintF> + for AffineGadget where P: SWModelParameters, ConstraintF: Field, @@ -297,7 +300,10 @@ where Ok(()) } - fn negate>(&self, mut cs: CS) -> Result { + fn negate>( + &self, + mut cs: CS, + ) -> Result { Ok(Self::new( self.x.clone(), self.y.negate(cs.ns(|| "negate y"))?, @@ -400,7 +406,8 @@ where } } -impl AllocGadget, ConstraintF> for AffineGadget +impl AllocGadget, ConstraintF> + for AffineGadget where P: SWModelParameters, ConstraintF: Field, @@ -572,7 +579,10 @@ where ConstraintF: Field, F: FieldGadget, { - fn to_bits>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bits>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { let mut x_bits = self.x.to_bits(&mut cs.ns(|| "X Coordinate To Bits"))?; let y_bits = self.y.to_bits(&mut cs.ns(|| "Y Coordinate To Bits"))?; x_bits.extend_from_slice(&y_bits); @@ -601,7 +611,10 @@ where ConstraintF: Field, F: FieldGadget, { - fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { let mut x_bytes = self.x.to_bytes(&mut cs.ns(|| "X Coordinate To Bytes"))?; let y_bytes = self.y.to_bytes(&mut cs.ns(|| "Y Coordinate To Bytes"))?; x_bytes.extend_from_slice(&y_bytes); diff --git a/r1cs-std/src/groups/curves/twisted_edwards/edwards_bls12.rs b/r1cs-std/src/groups/curves/twisted_edwards/edwards_bls12.rs index d59fa91..11a9f1e 100644 --- a/r1cs-std/src/groups/curves/twisted_edwards/edwards_bls12.rs +++ b/r1cs-std/src/groups/curves/twisted_edwards/edwards_bls12.rs @@ -1,6 +1,5 @@ use crate::groups::curves::twisted_edwards::AffineGadget; -use algebra::curves::edwards_bls12::EdwardsParameters; -use algebra::fields::edwards_bls12::fq::Fq; +use algebra::{curves::edwards_bls12::EdwardsParameters, fields::edwards_bls12::fq::Fq}; use crate::fields::edwards_bls12::FqGadget; @@ -13,8 +12,7 @@ mod test { 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; + use algebra::{curves::edwards_bls12::EdwardsParameters, fields::edwards_bls12::fq::Fq}; #[test] fn edwards_constraint_costs_test() { diff --git a/r1cs-std/src/groups/curves/twisted_edwards/edwards_sw6.rs b/r1cs-std/src/groups/curves/twisted_edwards/edwards_sw6.rs index e903eeb..6bd088a 100644 --- a/r1cs-std/src/groups/curves/twisted_edwards/edwards_sw6.rs +++ b/r1cs-std/src/groups/curves/twisted_edwards/edwards_sw6.rs @@ -1,6 +1,5 @@ use crate::groups::curves::twisted_edwards::AffineGadget; -use algebra::curves::edwards_sw6::EdwardsParameters; -use algebra::fields::edwards_sw6::fq::Fq; +use algebra::{curves::edwards_sw6::EdwardsParameters, fields::edwards_sw6::fq::Fq}; use crate::fields::edwards_sw6::FqGadget; @@ -13,8 +12,7 @@ mod test { 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; + use algebra::{curves::edwards_sw6::EdwardsParameters, fields::edwards_sw6::fq::Fq}; #[test] fn edwards_constraint_costs_test() { diff --git a/r1cs-std/src/groups/curves/twisted_edwards/jubjub.rs b/r1cs-std/src/groups/curves/twisted_edwards/jubjub.rs index 4cfbf50..24cfad5 100644 --- a/r1cs-std/src/groups/curves/twisted_edwards/jubjub.rs +++ b/r1cs-std/src/groups/curves/twisted_edwards/jubjub.rs @@ -1,6 +1,5 @@ use crate::groups::curves::twisted_edwards::AffineGadget; -use algebra::curves::jubjub::JubJubParameters; -use algebra::fields::jubjub::fq::Fq; +use algebra::{curves::jubjub::JubJubParameters, fields::jubjub::fq::Fq}; use crate::fields::jubjub::FqGadget; @@ -13,8 +12,7 @@ mod test { 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; + use algebra::{curves::jubjub::JubJubParameters as EdwardsParameters, fields::jubjub::fq::Fq}; #[test] fn edwards_constraint_costs_test() { diff --git a/r1cs-std/src/groups/curves/twisted_edwards/mod.rs b/r1cs-std/src/groups/curves/twisted_edwards/mod.rs index f0e77bd..d79dfdf 100644 --- a/r1cs-std/src/groups/curves/twisted_edwards/mod.rs +++ b/r1cs-std/src/groups/curves/twisted_edwards/mod.rs @@ -1,6 +1,9 @@ use algebra::{ - curves::{twisted_edwards_extended::GroupAffine as TEAffine, TEModelParameters}, - BitIterator, Field + curves::{ + twisted_edwards_extended::GroupAffine as TEAffine, MontgomeryModelParameters, + TEModelParameters, + }, + BitIterator, Field, }; use r1cs_core::{ConstraintSystem, SynthesisError}; @@ -19,9 +22,187 @@ mod test; #[derivative(Debug, Clone)] #[derivative(Debug(bound = "P: TEModelParameters, ConstraintF: Field"))] #[must_use] -pub struct AffineGadget> { - pub x: F, - pub y: F, +pub struct MontgomeryAffineGadget< + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +> { + pub x: F, + pub y: F, + #[derivative(Debug = "ignore")] + _params: PhantomData

, + #[derivative(Debug = "ignore")] + _engine: PhantomData, +} + +mod montgomery_affine_impl { + use super::*; + use crate::Assignment; + use algebra::{twisted_edwards_extended::GroupAffine, AffineCurve, Field}; + use std::ops::{AddAssign, MulAssign, SubAssign}; + + impl> + MontgomeryAffineGadget + { + pub fn new(x: F, y: F) -> Self { + Self { + x, + y, + _params: PhantomData, + _engine: PhantomData, + } + } + + pub fn from_edwards_to_coords( + p: &TEAffine

, + ) -> Result<(P::BaseField, P::BaseField), SynthesisError> { + let montgomery_point: GroupAffine

= if p.y == P::BaseField::one() { + GroupAffine::zero() + } else { + if p.x == P::BaseField::zero() { + GroupAffine::new(P::BaseField::zero(), P::BaseField::zero()) + } else { + let u = (P::BaseField::one() + &p.y) + * &(P::BaseField::one() - &p.y).inverse().unwrap(); + let v = u * &p.x.inverse().unwrap(); + GroupAffine::new(u, v) + } + }; + + Ok((montgomery_point.x, montgomery_point.y)) + } + + pub fn from_edwards>( + mut cs: CS, + p: &TEAffine

, + ) -> Result { + let montgomery_coords = Self::from_edwards_to_coords(p)?; + + let u = F::alloc(cs.ns(|| "u"), || Ok(montgomery_coords.0))?; + + let v = F::alloc(cs.ns(|| "v"), || Ok(montgomery_coords.1))?; + + Ok(Self::new(u, v)) + } + + pub fn into_edwards>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { + // Compute u = x / y + let u = F::alloc(cs.ns(|| "u"), || { + let mut t0 = self.x.get_value().get()?; + + match self.y.get_value().get()?.inverse() { + Some(invy) => { + t0.mul_assign(&invy); + + Ok(t0) + }, + None => Err(SynthesisError::DivisionByZero), + } + })?; + + u.mul_equals(cs.ns(|| "u equals"), &self.y, &self.x)?; + + let v = F::alloc(cs.ns(|| "v"), || { + let mut t0 = self.x.get_value().get()?; + let mut t1 = t0.clone(); + t0.sub_assign(&P::BaseField::one()); + t1.add_assign(&P::BaseField::one()); + + match t1.inverse() { + Some(t1) => { + t0.mul_assign(&t1); + + Ok(t0) + }, + None => Err(SynthesisError::DivisionByZero), + } + })?; + + let xplusone = self + .x + .add_constant(cs.ns(|| "x plus one"), &P::BaseField::one())?; + let xminusone = self + .x + .sub_constant(cs.ns(|| "x minus one"), &P::BaseField::one())?; + v.mul_equals(cs.ns(|| "v equals"), &xplusone, &xminusone)?; + + Ok(AffineGadget::new(u, v)) + } + + pub fn add>( + &self, + mut cs: CS, + other: &Self, + ) -> Result { + let lambda = F::alloc(cs.ns(|| "lambda"), || { + let mut n = other.y.get_value().get()?; + n.sub_assign(&self.y.get_value().get()?); + + let mut d = other.x.get_value().get()?; + d.sub_assign(&self.x.get_value().get()?); + + match d.inverse() { + Some(d) => { + n.mul_assign(&d); + Ok(n) + }, + None => Err(SynthesisError::DivisionByZero), + } + })?; + let lambda_n = other.y.sub(cs.ns(|| "other.y - self.y"), &self.y)?; + let lambda_d = other.x.sub(cs.ns(|| "other.x - self.x"), &self.x)?; + lambda_d.mul_equals(cs.ns(|| "lambda equals"), &lambda, &lambda_n)?; + + // Compute x'' = B*lambda^2 - A - x - x' + let xprime = F::alloc(cs.ns(|| "xprime"), || { + Ok( + lambda.get_value().get()?.square() * &P::MontgomeryModelParameters::COEFF_B + - &P::MontgomeryModelParameters::COEFF_A + - &self.x.get_value().get()? + - &other.x.get_value().get()?, + ) + })?; + + let xprime_lc = self + .x + .add(cs.ns(|| "self.x + other.x"), &other.x)? + .add(cs.ns(|| "+ xprime"), &xprime)? + .add_constant(cs.ns(|| "+ A"), &P::MontgomeryModelParameters::COEFF_A)?; + // (lambda) * (lambda) = (A + x + x' + x'') + let lambda_b = lambda.mul_by_constant( + cs.ns(|| "lambda * b"), + &P::MontgomeryModelParameters::COEFF_B, + )?; + lambda_b.mul_equals(cs.ns(|| "xprime equals"), &lambda, &xprime_lc)?; + + let yprime = F::alloc(cs.ns(|| "yprime"), || { + Ok(-(self.y.get_value().get()? + + &(lambda.get_value().get()? + * &(xprime.get_value().get()? - &self.x.get_value().get()?)))) + })?; + + let xres = self.x.sub(cs.ns(|| "xres"), &xprime)?; + let yres = self.y.add(cs.ns(|| "yres"), &yprime)?; + lambda.mul_equals(cs.ns(|| "yprime equals"), &xres, &yres)?; + Ok(MontgomeryAffineGadget::new(xprime, yprime)) + } + } +} + +#[derive(Derivative)] +#[derivative(Debug, Clone)] +#[derivative(Debug(bound = "P: TEModelParameters, ConstraintF: Field"))] +#[must_use] +pub struct AffineGadget< + P: TEModelParameters, + ConstraintF: Field, + F: FieldGadget, +> { + pub x: F, + pub y: F, #[derivative(Debug = "ignore")] _params: PhantomData

, #[derivative(Debug = "ignore")] @@ -293,7 +474,10 @@ mod affine_impl { Ok(()) } - fn negate>(&self, mut cs: CS) -> Result { + fn negate>( + &self, + mut cs: CS, + ) -> Result { Ok(Self::new( self.x.negate(cs.ns(|| "negate x"))?, self.y.clone(), @@ -487,7 +671,8 @@ mod projective_impl { }; use std::ops::Neg; - impl GroupGadget, ConstraintF> for AffineGadget + impl GroupGadget, ConstraintF> + for AffineGadget where P: TEModelParameters, ConstraintF: Field, @@ -694,7 +879,10 @@ mod projective_impl { Ok(()) } - fn negate>(&self, mut cs: CS) -> Result { + fn negate>( + &self, + mut cs: CS, + ) -> Result { Ok(Self::new( self.x.negate(cs.ns(|| "negate x"))?, self.y.clone(), @@ -757,6 +945,141 @@ mod projective_impl { Ok(()) } + fn precomputed_base_3_bit_signed_digit_scalar_mul<'a, CS, I, J, B>( + mut cs: CS, + bases: &[B], + scalars: &[J], + ) -> Result + where + CS: ConstraintSystem, + I: Borrow<[Boolean]>, + J: Borrow<[I]>, + B: Borrow<[TEProjective

]>, + { + const CHUNK_SIZE: usize = 3; + let mut edwards_result: Option> = None; + let mut result: Option> = None; + + let mut process_segment_result = + |mut cs: r1cs_core::Namespace<_, _>, + result: &MontgomeryAffineGadget| + -> Result<(), SynthesisError> { + let segment_result = result.into_edwards(cs.ns(|| "segment result"))?; + match edwards_result { + None => { + edwards_result = Some(segment_result); + }, + Some(ref mut edwards_result) => { + *edwards_result = GroupGadget::, ConstraintF>::add( + &segment_result, + cs.ns(|| "edwards addition"), + edwards_result, + )?; + }, + } + + Ok(()) + }; + + // Compute ∏(h_i^{m_i}) for all i. + for (segment_i, (segment_bits_chunks, segment_powers)) in + scalars.into_iter().zip(bases.iter()).enumerate() + { + for (i, (bits, base_power)) in segment_bits_chunks + .borrow() + .into_iter() + .zip(segment_powers.borrow().iter()) + .enumerate() + { + let base_power = base_power.borrow(); + let mut acc_power = *base_power; + let mut coords = vec![]; + for _ in 0..4 { + coords.push(acc_power); + acc_power = acc_power + base_power; + } + + let bits = bits.borrow().to_bits( + &mut cs.ns(|| format!("Convert Scalar {}, {} to bits", segment_i, i)), + )?; + if bits.len() != CHUNK_SIZE { + return Err(SynthesisError::Unsatisfiable); + } + + let coords = coords + .iter() + .map(|p| { + let p = p.into_affine(); + MontgomeryAffineGadget::::from_edwards_to_coords(&p) + .unwrap() + }) + .collect::>(); + + let x_coeffs = coords.iter().map(|p| p.0).collect::>(); + let y_coeffs = coords.iter().map(|p| p.1).collect::>(); + + let precomp = Boolean::and( + cs.ns(|| format!("precomp in window {}, {}", segment_i, i)), + &bits[0], + &bits[1], + )?; + + let x = F::zero(cs.ns(|| format!("x in window {}, {}", segment_i, i)))? + .conditionally_add_constant( + cs.ns(|| format!("add bool 00 in window {}, {}", segment_i, i)), + &Boolean::constant(true), + x_coeffs[0], + )? + .conditionally_add_constant( + cs.ns(|| format!("add bool 01 in window {}, {}", segment_i, i)), + &bits[0], + x_coeffs[1] - &x_coeffs[0], + )? + .conditionally_add_constant( + cs.ns(|| format!("add bool 10 in window {}, {}", segment_i, i)), + &bits[1], + x_coeffs[2] - &x_coeffs[0], + )? + .conditionally_add_constant( + cs.ns(|| format!("add bool 11 in window {}, {}", segment_i, i)), + &precomp, + x_coeffs[3] - &x_coeffs[2] - &x_coeffs[1] + &x_coeffs[0], + )?; + + let y = F::three_bit_cond_neg_lookup( + cs.ns(|| format!("y lookup in window {}, {}", segment_i, i)), + &bits, + &precomp, + &y_coeffs, + )?; + + let tmp = MontgomeryAffineGadget::new(x, y); + + match result { + None => { + result = Some(tmp); + }, + Some(ref mut result) => { + *result = tmp.add( + cs.ns(|| format!("addition of window {}, {}", segment_i, i)), + result, + )?; + }, + } + } + + process_segment_result( + cs.ns(|| format!("window {}", segment_i)), + &result.unwrap(), + )?; + result = None; + } + if result.is_some() { + process_segment_result(cs.ns(|| "leftover"), &result.unwrap())?; + } + Ok(edwards_result.unwrap()) + } + fn cost_of_add() -> usize { 4 + 2 * F::cost_of_mul() } @@ -766,7 +1089,8 @@ mod projective_impl { } } - impl AllocGadget, ConstraintF> for AffineGadget + impl AllocGadget, ConstraintF> + for AffineGadget where P: TEModelParameters, ConstraintF: Field, @@ -1033,7 +1357,10 @@ where ConstraintF: Field, F: FieldGadget, { - fn to_bits>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bits>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { let mut x_bits = self.x.to_bits(cs.ns(|| "X Coordinate To Bits"))?; let y_bits = self.y.to_bits(cs.ns(|| "Y Coordinate To Bits"))?; x_bits.extend_from_slice(&y_bits); @@ -1058,7 +1385,10 @@ where ConstraintF: Field, F: FieldGadget, { - fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { + fn to_bytes>( + &self, + mut cs: CS, + ) -> Result, SynthesisError> { let mut x_bytes = self.x.to_bytes(cs.ns(|| "x"))?; let y_bytes = self.y.to_bytes(cs.ns(|| "y"))?; x_bytes.extend_from_slice(&y_bytes); diff --git a/r1cs-std/src/groups/curves/twisted_edwards/test.rs b/r1cs-std/src/groups/curves/twisted_edwards/test.rs index 8b7c523..8720746 100644 --- a/r1cs-std/src/groups/curves/twisted_edwards/test.rs +++ b/r1cs-std/src/groups/curves/twisted_edwards/test.rs @@ -1,10 +1,10 @@ use rand::thread_rng; -use crate::{prelude::*, groups::test::group_test}; +use crate::{groups::test::group_test, prelude::*}; use algebra::{ curves::{models::TEModelParameters, twisted_edwards_extended::GroupAffine as TEAffine}, - BitIterator, Group, PrimeField, Field, UniformRand, + BitIterator, Field, Group, PrimeField, UniformRand, }; use r1cs_core::ConstraintSystem; diff --git a/r1cs-std/src/groups/mod.rs b/r1cs-std/src/groups/mod.rs index 4731d45..01f2902 100644 --- a/r1cs-std/src/groups/mod.rs +++ b/r1cs-std/src/groups/mod.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use algebra::{Group, Field}; +use algebra::{Field, Group}; use r1cs_core::{ConstraintSystem, SynthesisError}; use std::{borrow::Borrow, fmt::Debug}; @@ -31,7 +31,11 @@ pub trait GroupGadget: fn zero>(cs: CS) -> Result; - fn add>(&self, cs: CS, other: &Self) -> Result; + fn add>( + &self, + cs: CS, + other: &Self, + ) -> Result; fn sub>( &self, @@ -57,7 +61,10 @@ pub trait GroupGadget: self.add_constant(cs.ns(|| "Self - other"), &neg_other) } - fn double_in_place>(&mut self, cs: CS) -> Result<(), SynthesisError>; + fn double_in_place>( + &mut self, + cs: CS, + ) -> Result<(), SynthesisError>; fn negate>(&self, cs: CS) -> Result; @@ -111,6 +118,20 @@ pub trait GroupGadget: Ok(()) } + fn precomputed_base_3_bit_signed_digit_scalar_mul<'a, CS, I, J, B>( + _: CS, + _: &[B], + _: &[J], + ) -> Result + where + CS: ConstraintSystem, + I: Borrow<[Boolean]>, + J: Borrow<[I]>, + B: Borrow<[G]>, + { + Err(SynthesisError::AssignmentMissing) + } + fn precomputed_base_multiscalar_mul<'a, CS, T, I, B>( mut cs: CS, bases: &[B], @@ -145,9 +166,7 @@ mod test { use algebra::Field; use r1cs_core::ConstraintSystem; - use crate::{ - prelude::*, test_constraint_system::TestConstraintSystem, - }; + use crate::{prelude::*, test_constraint_system::TestConstraintSystem}; use algebra::groups::Group; use rand; @@ -203,8 +222,7 @@ mod test { #[test] fn jubjub_group_gadgets_test() { use crate::groups::jubjub::JubJubGadget; - use algebra::fields::jubjub::fq::Fq; - use algebra::curves::jubjub::JubJubProjective; + use algebra::{curves::jubjub::JubJubProjective, fields::jubjub::fq::Fq}; let mut cs = TestConstraintSystem::::new(); diff --git a/r1cs-std/src/lib.rs b/r1cs-std/src/lib.rs index fc35d68..1f500ca 100644 --- a/r1cs-std/src/lib.rs +++ b/r1cs-std/src/lib.rs @@ -49,18 +49,20 @@ pub mod groups; pub mod pairing; +pub mod alloc; 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::pairing::PairingGadget; - pub use crate::bits::{ToBitsGadget, ToBytesGadget, boolean::Boolean, uint8::UInt8, uint32::UInt32}; + pub use crate::{ + alloc::*, + bits::{boolean::Boolean, uint32::UInt32, uint8::UInt8, ToBitsGadget, ToBytesGadget}, + eq::*, + fields::FieldGadget, + groups::GroupGadget, + pairing::PairingGadget, + select::*, + }; } pub trait Assignment { diff --git a/r1cs-std/src/pairing/mod.rs b/r1cs-std/src/pairing/mod.rs index 5b3fcbc..42db4ff 100644 --- a/r1cs-std/src/pairing/mod.rs +++ b/r1cs-std/src/pairing/mod.rs @@ -65,14 +65,18 @@ mod test { #[test] fn bls12_377_gadget_bilinearity_test() { use algebra::{ - fields::{bls12_377::{fr::Fr, fq::Fq}, PrimeField}, + fields::{ + bls12_377::{fq::Fq, fr::Fr}, + PrimeField, + }, PairingEngine, ProjectiveCurve, }; use super::bls12_377::PairingGadget; use crate::{ groups::bls12::bls12_377::{G1Gadget, G1PreparedGadget, G2Gadget, G2PreparedGadget}, - prelude::*, pairing::PairingGadget as _, + pairing::PairingGadget as _, + prelude::*, }; use algebra::curves::bls12_377::{Bls12_377, G1Projective, G2Projective}; use std::ops::Mul; diff --git a/r1cs-std/src/select.rs b/r1cs-std/src/select.rs index e2828c4..3bd133f 100644 --- a/r1cs-std/src/select.rs +++ b/r1cs-std/src/select.rs @@ -1,7 +1,6 @@ use crate::prelude::*; -use r1cs_core::{ConstraintSystem, SynthesisError}; use algebra::Field; - +use r1cs_core::{ConstraintSystem, SynthesisError}; /// If condition is `true`, return `first`; else, select `second`. pub trait CondSelectGadget @@ -33,4 +32,19 @@ where fn cost() -> usize; } +/// Uses three bits to perform a lookup into a table, where the last bit +/// performs negation +pub trait ThreeBitCondNegLookupGadget +where + Self: Sized, +{ + type TableConstant; + fn three_bit_cond_neg_lookup>( + cs: CS, + bits: &[Boolean], + b0b1: &Boolean, + constants: &[Self::TableConstant], + ) -> Result; + fn cost() -> usize; +}