Merge pull request #105 from Eirik0/cargo-clean-up

cargo fmt
This commit is contained in:
ebfull 2019-08-16 20:30:00 -06:00 committed by GitHub
commit 6f0080ba72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 4027 additions and 3827 deletions

View File

@ -4,5 +4,9 @@ rust:
cache: cargo cache: cargo
before_script:
- rustup component add rustfmt
script: script:
- cargo fmt --all -- --check
- cargo test --verbose --release --all - cargo test --verbose --release --all

View File

@ -13,9 +13,7 @@
use ff::{Field, PrimeField, ScalarEngine}; use ff::{Field, PrimeField, ScalarEngine};
use group::CurveProjective; use group::CurveProjective;
use super::{ use super::SynthesisError;
SynthesisError
};
use super::multicore::Worker; use super::multicore::Worker;
@ -25,7 +23,7 @@ pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
omega: E::Fr, omega: E::Fr,
omegainv: E::Fr, omegainv: E::Fr,
geninv: E::Fr, geninv: E::Fr,
minv: E::Fr minv: E::Fr,
} }
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> { impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
@ -41,8 +39,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
self.coeffs self.coeffs
} }
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError> pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError> {
{
// Compute the size of our evaluation domain // Compute the size of our evaluation domain
let mut m = 1; let mut m = 1;
let mut exp = 0; let mut exp = 0;
@ -53,7 +50,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
// The pairing-friendly curve may not be able to support // The pairing-friendly curve may not be able to support
// large enough (radix2) evaluation domains. // large enough (radix2) evaluation domains.
if exp >= E::Fr::S { if exp >= E::Fr::S {
return Err(SynthesisError::PolynomialDegreeTooLarge) return Err(SynthesisError::PolynomialDegreeTooLarge);
} }
} }
@ -72,17 +69,18 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
omega: omega, omega: omega,
omegainv: omega.inverse().unwrap(), omegainv: omega.inverse().unwrap(),
geninv: E::Fr::multiplicative_generator().inverse().unwrap(), geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap() minv: E::Fr::from_str(&format!("{}", m))
.unwrap()
.inverse()
.unwrap(),
}) })
} }
pub fn fft(&mut self, worker: &Worker) pub fn fft(&mut self, worker: &Worker) {
{
best_fft(&mut self.coeffs, worker, &self.omega, self.exp); best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
} }
pub fn ifft(&mut self, worker: &Worker) pub fn ifft(&mut self, worker: &Worker) {
{
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp); best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
@ -98,8 +96,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
}); });
} }
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) {
{
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
scope.spawn(move || { scope.spawn(move || {
@ -113,14 +110,12 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
}); });
} }
pub fn coset_fft(&mut self, worker: &Worker) pub fn coset_fft(&mut self, worker: &Worker) {
{
self.distribute_powers(worker, E::Fr::multiplicative_generator()); self.distribute_powers(worker, E::Fr::multiplicative_generator());
self.fft(worker); self.fft(worker);
} }
pub fn icoset_fft(&mut self, worker: &Worker) pub fn icoset_fft(&mut self, worker: &Worker) {
{
let geninv = self.geninv; let geninv = self.geninv;
self.ifft(worker); self.ifft(worker);
@ -139,9 +134,11 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
/// The target polynomial is the zero polynomial in our /// The target polynomial is the zero polynomial in our
/// evaluation domain, so we must perform division over /// evaluation domain, so we must perform division over
/// a coset. /// a coset.
pub fn divide_by_z_on_coset(&mut self, worker: &Worker) pub fn divide_by_z_on_coset(&mut self, worker: &Worker) {
{ let i = self
let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap(); .z(&E::Fr::multiplicative_generator())
.inverse()
.unwrap();
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for v in self.coeffs.chunks_mut(chunk) { for v in self.coeffs.chunks_mut(chunk) {
@ -159,7 +156,11 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
assert_eq!(self.coeffs.len(), other.coeffs.len()); assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { for (a, b) in self
.coeffs
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move || { scope.spawn(move || {
for (a, b) in a.iter_mut().zip(b.iter()) { for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_mul_assign(&b.0); a.group_mul_assign(&b.0);
@ -174,7 +175,11 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
assert_eq!(self.coeffs.len(), other.coeffs.len()); assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { for (a, b) in self
.coeffs
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move || { scope.spawn(move || {
for (a, b) in a.iter_mut().zip(b.iter()) { for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_sub_assign(&b); a.group_sub_assign(&b);
@ -200,7 +205,7 @@ impl<G: CurveProjective> PartialEq for Point<G> {
} }
} }
impl<G: CurveProjective> Copy for Point<G> { } impl<G: CurveProjective> Copy for Point<G> {}
impl<G: CurveProjective> Clone for Point<G> { impl<G: CurveProjective> Clone for Point<G> {
fn clone(&self) -> Point<G> { fn clone(&self) -> Point<G> {
@ -231,7 +236,7 @@ impl<E: ScalarEngine> PartialEq for Scalar<E> {
} }
} }
impl<E: ScalarEngine> Copy for Scalar<E> { } impl<E: ScalarEngine> Copy for Scalar<E> {}
impl<E: ScalarEngine> Clone for Scalar<E> { impl<E: ScalarEngine> Clone for Scalar<E> {
fn clone(&self) -> Scalar<E> { fn clone(&self) -> Scalar<E> {
@ -254,8 +259,7 @@ impl<E: ScalarEngine> Group<E> for Scalar<E> {
} }
} }
fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) {
{
let log_cpus = worker.log_num_cpus(); let log_cpus = worker.log_num_cpus();
if log_n <= log_cpus { if log_n <= log_cpus {
@ -265,8 +269,7 @@ fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &
} }
} }
fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32) fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32) {
{
fn bitreverse(mut n: u32, l: u32) -> u32 { fn bitreverse(mut n: u32, l: u32) -> u32 {
let mut r = 0; let mut r = 0;
for _ in 0..l { for _ in 0..l {
@ -288,22 +291,22 @@ fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u
let mut m = 1; let mut m = 1;
for _ in 0..log_n { for _ in 0..log_n {
let w_m = omega.pow(&[(n / (2*m)) as u64]); let w_m = omega.pow(&[(n / (2 * m)) as u64]);
let mut k = 0; let mut k = 0;
while k < n { while k < n {
let mut w = E::Fr::one(); let mut w = E::Fr::one();
for j in 0..m { for j in 0..m {
let mut t = a[(k+j+m) as usize]; let mut t = a[(k + j + m) as usize];
t.group_mul_assign(&w); t.group_mul_assign(&w);
let mut tmp = a[(k+j) as usize]; let mut tmp = a[(k + j) as usize];
tmp.group_sub_assign(&t); tmp.group_sub_assign(&t);
a[(k+j+m) as usize] = tmp; a[(k + j + m) as usize] = tmp;
a[(k+j) as usize].group_add_assign(&t); a[(k + j) as usize].group_add_assign(&t);
w.mul_assign(&w_m); w.mul_assign(&w_m);
} }
k += 2*m; k += 2 * m;
} }
m *= 2; m *= 2;
@ -315,9 +318,8 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
worker: &Worker, worker: &Worker,
omega: &E::Fr, omega: &E::Fr,
log_n: u32, log_n: u32,
log_cpus: u32 log_cpus: u32,
) ) {
{
assert!(log_n >= log_cpus); assert!(log_n >= log_cpus);
let num_cpus = 1 << log_cpus; let num_cpus = 1 << log_cpus;
@ -377,14 +379,17 @@ fn polynomial_arith() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand_core::RngCore;
fn test_mul<E: ScalarEngine, R: RngCore>(rng: &mut R) fn test_mul<E: ScalarEngine, R: RngCore>(rng: &mut R) {
{
let worker = Worker::new(); let worker = Worker::new();
for coeffs_a in 0..70 { for coeffs_a in 0..70 {
for coeffs_b in 0..70 { for coeffs_b in 0..70 {
let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::<E>(E::Fr::random(rng))).collect(); let mut a: Vec<_> = (0..coeffs_a)
let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::<E>(E::Fr::random(rng))).collect(); .map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect();
let mut b: Vec<_> = (0..coeffs_b)
.map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect();
// naive evaluation // naive evaluation
let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b]; let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b];
@ -425,8 +430,7 @@ fn fft_composition() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand_core::RngCore;
fn test_comp<E: ScalarEngine, R: RngCore>(rng: &mut R) fn test_comp<E: ScalarEngine, R: RngCore>(rng: &mut R) {
{
let worker = Worker::new(); let worker = Worker::new();
for coeffs in 0..10 { for coeffs in 0..10 {
@ -465,19 +469,20 @@ fn parallel_fft_consistency() {
use rand_core::RngCore; use rand_core::RngCore;
use std::cmp::min; use std::cmp::min;
fn test_consistency<E: ScalarEngine, R: RngCore>(rng: &mut R) fn test_consistency<E: ScalarEngine, R: RngCore>(rng: &mut R) {
{
let worker = Worker::new(); let worker = Worker::new();
for _ in 0..5 { for _ in 0..5 {
for log_d in 0..10 { for log_d in 0..10 {
let d = 1 << log_d; let d = 1 << log_d;
let v1 = (0..d).map(|_| Scalar::<E>(E::Fr::random(rng))).collect::<Vec<_>>(); let v1 = (0..d)
.map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect::<Vec<_>>();
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap(); let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap(); let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
for log_cpus in log_d..min(log_d+1, 3) { for log_cpus in log_d..min(log_d + 1, 3) {
parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus); parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus);
serial_fft(&mut v2.coeffs, &v2.omega, log_d); serial_fft(&mut v2.coeffs, &v2.omega, log_d);

View File

@ -1,17 +1,15 @@
pub mod test; pub mod test;
pub mod boolean;
pub mod multieq;
pub mod uint32;
pub mod blake2s; pub mod blake2s;
pub mod num; pub mod boolean;
pub mod lookup; pub mod lookup;
pub mod multieq;
pub mod multipack; pub mod multipack;
pub mod num;
pub mod sha256; pub mod sha256;
pub mod uint32;
use crate::{ use crate::SynthesisError;
SynthesisError
};
// TODO: This should probably be removed and we // TODO: This should probably be removed and we
// should use existing helper methods on `Option` // should use existing helper methods on `Option`
@ -27,7 +25,7 @@ impl<T> Assignment<T> for Option<T> {
fn get(&self) -> Result<&T, SynthesisError> { fn get(&self) -> Result<&T, SynthesisError> {
match *self { match *self {
Some(ref v) => Ok(v), Some(ref v) => Ok(v),
None => Err(SynthesisError::AssignmentMissing) None => Err(SynthesisError::AssignmentMissing),
} }
} }
} }

View File

@ -1,19 +1,10 @@
use pairing::{ use pairing::Engine;
Engine,
};
use crate::{ use crate::{ConstraintSystem, SynthesisError};
SynthesisError,
ConstraintSystem
};
use super::boolean::{ use super::boolean::Boolean;
Boolean
};
use super::uint32::{ use super::uint32::UInt32;
UInt32
};
use super::multieq::MultiEq; use super::multieq::MultiEq;
@ -65,7 +56,7 @@ const SIGMA: [[usize; 16]; 10] = [
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0] [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
]; ];
/* /*
@ -98,17 +89,30 @@ fn mixing_g<E: Engine, CS: ConstraintSystem<E>, M>(
c: usize, c: usize,
d: usize, d: usize,
x: &UInt32, x: &UInt32,
y: &UInt32 y: &UInt32,
) -> Result<(), SynthesisError> ) -> Result<(), SynthesisError>
where M: ConstraintSystem<E, Root=MultiEq<E, CS>> where
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
{ {
v[a] = UInt32::addmany(cs.namespace(|| "mixing step 1"), &[v[a].clone(), v[b].clone(), x.clone()])?; v[a] = UInt32::addmany(
cs.namespace(|| "mixing step 1"),
&[v[a].clone(), v[b].clone(), x.clone()],
)?;
v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1); v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1);
v[c] = UInt32::addmany(cs.namespace(|| "mixing step 3"), &[v[c].clone(), v[d].clone()])?; v[c] = UInt32::addmany(
cs.namespace(|| "mixing step 3"),
&[v[c].clone(), v[d].clone()],
)?;
v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2); v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2);
v[a] = UInt32::addmany(cs.namespace(|| "mixing step 5"), &[v[a].clone(), v[b].clone(), y.clone()])?; v[a] = UInt32::addmany(
cs.namespace(|| "mixing step 5"),
&[v[a].clone(), v[b].clone(), y.clone()],
)?;
v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3); v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3);
v[c] = UInt32::addmany(cs.namespace(|| "mixing step 7"), &[v[c].clone(), v[d].clone()])?; v[c] = UInt32::addmany(
cs.namespace(|| "mixing step 7"),
&[v[c].clone(), v[d].clone()],
)?;
v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4); v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4);
Ok(()) Ok(())
@ -162,15 +166,13 @@ fn mixing_g<E: Engine, CS: ConstraintSystem<E>, M>(
END FUNCTION. END FUNCTION.
*/ */
fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>( fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS, mut cs: CS,
h: &mut [UInt32], h: &mut [UInt32],
m: &[UInt32], m: &[UInt32],
t: u64, t: u64,
f: bool f: bool,
) -> Result<(), SynthesisError> ) -> Result<(), SynthesisError> {
{
assert_eq!(h.len(), 8); assert_eq!(h.len(), 8);
assert_eq!(m.len(), 16); assert_eq!(m.len(), 16);
@ -196,10 +198,16 @@ fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
assert_eq!(v.len(), 16); assert_eq!(v.len(), 16);
v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?; v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?;
v[13] = v[13].xor(cs.namespace(|| "second xor"), &UInt32::constant((t >> 32) as u32))?; v[13] = v[13].xor(
cs.namespace(|| "second xor"),
&UInt32::constant((t >> 32) as u32),
)?;
if f { if f {
v[14] = v[14].xor(cs.namespace(|| "third xor"), &UInt32::constant(u32::max_value()))?; v[14] = v[14].xor(
cs.namespace(|| "third xor"),
&UInt32::constant(u32::max_value()),
)?;
} }
{ {
@ -210,20 +218,92 @@ fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
let s = SIGMA[i % 10]; let s = SIGMA[i % 10];
mixing_g(cs.namespace(|| "mixing invocation 1"), &mut v, 0, 4, 8, 12, &m[s[ 0]], &m[s[ 1]])?; mixing_g(
mixing_g(cs.namespace(|| "mixing invocation 2"), &mut v, 1, 5, 9, 13, &m[s[ 2]], &m[s[ 3]])?; cs.namespace(|| "mixing invocation 1"),
mixing_g(cs.namespace(|| "mixing invocation 3"), &mut v, 2, 6, 10, 14, &m[s[ 4]], &m[s[ 5]])?; &mut v,
mixing_g(cs.namespace(|| "mixing invocation 4"), &mut v, 3, 7, 11, 15, &m[s[ 6]], &m[s[ 7]])?; 0,
4,
8,
12,
&m[s[0]],
&m[s[1]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 2"),
&mut v,
1,
5,
9,
13,
&m[s[2]],
&m[s[3]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 3"),
&mut v,
2,
6,
10,
14,
&m[s[4]],
&m[s[5]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 4"),
&mut v,
3,
7,
11,
15,
&m[s[6]],
&m[s[7]],
)?;
mixing_g(cs.namespace(|| "mixing invocation 5"), &mut v, 0, 5, 10, 15, &m[s[ 8]], &m[s[ 9]])?; mixing_g(
mixing_g(cs.namespace(|| "mixing invocation 6"), &mut v, 1, 6, 11, 12, &m[s[10]], &m[s[11]])?; cs.namespace(|| "mixing invocation 5"),
mixing_g(cs.namespace(|| "mixing invocation 7"), &mut v, 2, 7, 8, 13, &m[s[12]], &m[s[13]])?; &mut v,
mixing_g(cs.namespace(|| "mixing invocation 8"), &mut v, 3, 4, 9, 14, &m[s[14]], &m[s[15]])?; 0,
5,
10,
15,
&m[s[8]],
&m[s[9]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 6"),
&mut v,
1,
6,
11,
12,
&m[s[10]],
&m[s[11]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 7"),
&mut v,
2,
7,
8,
13,
&m[s[12]],
&m[s[13]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 8"),
&mut v,
3,
4,
9,
14,
&m[s[14]],
&m[s[15]],
)?;
} }
} }
for i in 0..8 { for i in 0..8 {
let mut cs = cs.namespace(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i=i)); let mut cs = cs.namespace(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i = i));
h[i] = h[i].xor(cs.namespace(|| "first xor"), &v[i])?; h[i] = h[i].xor(cs.namespace(|| "first xor"), &v[i])?;
h[i] = h[i].xor(cs.namespace(|| "second xor"), &v[i + 8])?; h[i] = h[i].xor(cs.namespace(|| "second xor"), &v[i + 8])?;
@ -262,9 +342,8 @@ fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>( pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS, mut cs: CS,
input: &[Boolean], input: &[Boolean],
personalization: &[u8] personalization: &[u8],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError> {
{
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
assert_eq!(personalization.len(), 8); assert_eq!(personalization.len(), 8);
@ -279,8 +358,12 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
h.push(UInt32::constant(0x9B05688C)); h.push(UInt32::constant(0x9B05688C));
// Personalization is stored here // Personalization is stored here
h.push(UInt32::constant(0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4]))); h.push(UInt32::constant(
h.push(UInt32::constant(0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8]))); 0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4]),
));
h.push(UInt32::constant(
0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8]),
));
let mut blocks: Vec<Vec<UInt32>> = vec![]; let mut blocks: Vec<Vec<UInt32>> = vec![];
@ -312,7 +395,13 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
{ {
let cs = cs.namespace(|| "final block"); let cs = cs.namespace(|| "final block");
blake2s_compression(cs, &mut h, &blocks[blocks.len() - 1], (input.len() / 8) as u64, true)?; blake2s_compression(
cs,
&mut h,
&blocks[blocks.len() - 1],
(input.len() / 8) as u64,
true,
)?;
} }
Ok(h.iter().flat_map(|b| b.into_bits()).collect()) Ok(h.iter().flat_map(|b| b.into_bits()).collect())
@ -321,14 +410,14 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use blake2s_simd::Params as Blake2sParams; use blake2s_simd::Params as Blake2sParams;
use pairing::bls12_381::{Bls12}; use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use crate::gadgets::boolean::{Boolean, AllocatedBit};
use crate::gadgets::test::TestConstraintSystem;
use super::blake2s; use super::blake2s;
use crate::{ConstraintSystem}; use crate::gadgets::boolean::{AllocatedBit, Boolean};
use crate::gadgets::test::TestConstraintSystem;
use crate::ConstraintSystem;
#[test] #[test]
fn test_blank_hash() { fn test_blank_hash() {
@ -356,7 +445,13 @@ mod test {
#[test] #[test]
fn test_blake2s_constraints() { fn test_blake2s_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits: Vec<_> = (0..512).map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into()).collect(); let input_bits: Vec<_> = (0..512)
.map(|i| {
AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true))
.unwrap()
.into()
})
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap(); blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518); assert_eq!(cs.num_constraints(), 21518);
@ -369,14 +464,17 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let input_bits: Vec<_> = (0..512) let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) .map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.chain((0..512) .chain((0..512).map(|i| {
.map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into())) AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true))
.collect(); .unwrap()
.into()
}))
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap(); blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518); assert_eq!(cs.num_constraints(), 21518);
@ -386,10 +484,12 @@ mod test {
fn test_blake2s_constant_constraints() { fn test_blake2s_constant_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.next_u32() % 2 != 0)).collect(); let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap(); blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert_eq!(cs.num_constraints(), 0); assert_eq!(cs.num_constraints(), 0);
} }
@ -397,13 +497,15 @@ mod test {
#[test] #[test]
fn test_blake2s() { fn test_blake2s() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
{ let mut h = Blake2sParams::new()
let mut h = Blake2sParams::new().hash_length(32).personal(b"12345678").to_state(); .hash_length(32)
.personal(b"12345678")
.to_state();
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect(); let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
@ -419,7 +521,11 @@ mod test {
for bit_i in 0..8 { for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into()); input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
} }
} }
@ -427,17 +533,19 @@ mod test {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut s = hash_result.as_ref().iter() let mut s = hash_result
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)); .as_ref()
.iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r { for b in r {
match b { match b {
Boolean::Is(b) => { Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap()); assert!(s.next().unwrap() == b.get_value().unwrap());
}, }
Boolean::Not(b) => { Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap()); assert!(s.next().unwrap() != b.get_value().unwrap());
}, }
Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(input_len == 0); assert!(input_len == 0);
assert!(s.next().unwrap() == b); assert!(s.next().unwrap() == b);

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,15 @@
use ff::Field; use ff::Field;
use pairing::Engine; use pairing::Engine;
use super::*;
use super::num::{
AllocatedNum,
Num
};
use super::boolean::Boolean; use super::boolean::Boolean;
use crate::{ use super::num::{AllocatedNum, Num};
ConstraintSystem use super::*;
}; use crate::ConstraintSystem;
// Synthesize the constants for each base pattern. // Synthesize the constants for each base pattern.
fn synth<'a, E: Engine, I>( fn synth<'a, E: Engine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
window_size: usize, where
constants: I, I: IntoIterator<Item = &'a E::Fr>,
assignment: &mut [E::Fr]
)
where I: IntoIterator<Item=&'a E::Fr>
{ {
assert_eq!(assignment.len(), 1 << window_size); assert_eq!(assignment.len(), 1 << window_size);
@ -39,16 +31,20 @@ fn synth<'a, E: Engine, I>(
pub fn lookup3_xy<E: Engine, CS>( pub fn lookup3_xy<E: Engine, CS>(
mut cs: CS, mut cs: CS,
bits: &[Boolean], bits: &[Boolean],
coords: &[(E::Fr, E::Fr)] coords: &[(E::Fr, E::Fr)],
) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError> ) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError>
where CS: ConstraintSystem<E> where
CS: ConstraintSystem<E>,
{ {
assert_eq!(bits.len(), 3); assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 8); assert_eq!(coords.len(), 8);
// Calculate the index into `coords` // Calculate the index into `coords`
let i = let i = match (
match (bits[0].get_value(), bits[1].get_value(), bits[2].get_value()) { bits[0].get_value(),
bits[1].get_value(),
bits[2].get_value(),
) {
(Some(a_value), Some(b_value), Some(c_value)) => { (Some(a_value), Some(b_value), Some(c_value)) => {
let mut tmp = 0; let mut tmp = 0;
if a_value { if a_value {
@ -61,25 +57,15 @@ pub fn lookup3_xy<E: Engine, CS>(
tmp += 4; tmp += 4;
} }
Some(tmp) Some(tmp)
}, }
_ => None _ => None,
}; };
// Allocate the x-coordinate resulting from the lookup // Allocate the x-coordinate resulting from the lookup
let res_x = AllocatedNum::alloc( let res_x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(coords[*i.get()?].0))?;
cs.namespace(|| "x"),
|| {
Ok(coords[*i.get()?].0)
}
)?;
// Allocate the y-coordinate resulting from the lookup // Allocate the y-coordinate resulting from the lookup
let res_y = AllocatedNum::alloc( let res_y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords[*i.get()?].1))?;
cs.namespace(|| "y"),
|| {
Ok(coords[*i.get()?].1)
}
)?;
// Compute the coefficients for the lookup constraints // Compute the coefficients for the lookup constraints
let mut x_coeffs = [E::Fr::zero(); 8]; let mut x_coeffs = [E::Fr::zero(); 8];
@ -93,30 +79,38 @@ pub fn lookup3_xy<E: Engine, CS>(
cs.enforce( cs.enforce(
|| "x-coordinate lookup", || "x-coordinate lookup",
|lc| lc + (x_coeffs[0b001], one) |lc| {
lc + (x_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, x_coeffs[0b011]) + &bits[1].lc::<E>(one, x_coeffs[0b011])
+ &bits[2].lc::<E>(one, x_coeffs[0b101]) + &bits[2].lc::<E>(one, x_coeffs[0b101])
+ &precomp.lc::<E>(one, x_coeffs[0b111]), + &precomp.lc::<E>(one, x_coeffs[0b111])
},
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()), |lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|lc| lc + res_x.get_variable() |lc| {
lc + res_x.get_variable()
- (x_coeffs[0b000], one) - (x_coeffs[0b000], one)
- &bits[1].lc::<E>(one, x_coeffs[0b010]) - &bits[1].lc::<E>(one, x_coeffs[0b010])
- &bits[2].lc::<E>(one, x_coeffs[0b100]) - &bits[2].lc::<E>(one, x_coeffs[0b100])
- &precomp.lc::<E>(one, x_coeffs[0b110]), - &precomp.lc::<E>(one, x_coeffs[0b110])
},
); );
cs.enforce( cs.enforce(
|| "y-coordinate lookup", || "y-coordinate lookup",
|lc| lc + (y_coeffs[0b001], one) |lc| {
lc + (y_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, y_coeffs[0b011]) + &bits[1].lc::<E>(one, y_coeffs[0b011])
+ &bits[2].lc::<E>(one, y_coeffs[0b101]) + &bits[2].lc::<E>(one, y_coeffs[0b101])
+ &precomp.lc::<E>(one, y_coeffs[0b111]), + &precomp.lc::<E>(one, y_coeffs[0b111])
},
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()), |lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|lc| lc + res_y.get_variable() |lc| {
lc + res_y.get_variable()
- (y_coeffs[0b000], one) - (y_coeffs[0b000], one)
- &bits[1].lc::<E>(one, y_coeffs[0b010]) - &bits[1].lc::<E>(one, y_coeffs[0b010])
- &bits[2].lc::<E>(one, y_coeffs[0b100]) - &bits[2].lc::<E>(one, y_coeffs[0b100])
- &precomp.lc::<E>(one, y_coeffs[0b110]), - &precomp.lc::<E>(one, y_coeffs[0b110])
},
); );
Ok((res_x, res_y)) Ok((res_x, res_y))
@ -127,16 +121,16 @@ pub fn lookup3_xy<E: Engine, CS>(
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>( pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
mut cs: CS, mut cs: CS,
bits: &[Boolean], bits: &[Boolean],
coords: &[(E::Fr, E::Fr)] coords: &[(E::Fr, E::Fr)],
) -> Result<(Num<E>, Num<E>), SynthesisError> ) -> Result<(Num<E>, Num<E>), SynthesisError>
where CS: ConstraintSystem<E> where
CS: ConstraintSystem<E>,
{ {
assert_eq!(bits.len(), 3); assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 4); assert_eq!(coords.len(), 4);
// Calculate the index into `coords` // Calculate the index into `coords`
let i = let i = match (bits[0].get_value(), bits[1].get_value()) {
match (bits[0].get_value(), bits[1].get_value()) {
(Some(a_value), Some(b_value)) => { (Some(a_value), Some(b_value)) => {
let mut tmp = 0; let mut tmp = 0;
if a_value { if a_value {
@ -146,22 +140,19 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
tmp += 2; tmp += 2;
} }
Some(tmp) Some(tmp)
}, }
_ => None _ => None,
}; };
// Allocate the y-coordinate resulting from the lookup // Allocate the y-coordinate resulting from the lookup
// and conditional negation // and conditional negation
let y = AllocatedNum::alloc( let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
cs.namespace(|| "y"), let mut tmp = coords[*i.get()?].1;
|| { if *bits[2].get_value().get()? {
let mut tmp = coords[*i.get()?].1; tmp.negate();
if *bits[2].get_value().get()? {
tmp.negate();
}
Ok(tmp)
} }
)?; Ok(tmp)
})?;
let one = CS::one(); let one = CS::one();
@ -174,21 +165,21 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?; let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?;
let x = Num::zero() let x = Num::zero()
.add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00]) .add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00])
.add_bool_with_coeff(one, &bits[0], x_coeffs[0b01]) .add_bool_with_coeff(one, &bits[0], x_coeffs[0b01])
.add_bool_with_coeff(one, &bits[1], x_coeffs[0b10]) .add_bool_with_coeff(one, &bits[1], x_coeffs[0b10])
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]); .add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
let y_lc = precomp.lc::<E>(one, y_coeffs[0b11]) + let y_lc = precomp.lc::<E>(one, y_coeffs[0b11])
&bits[1].lc::<E>(one, y_coeffs[0b10]) + + &bits[1].lc::<E>(one, y_coeffs[0b10])
&bits[0].lc::<E>(one, y_coeffs[0b01]) + + &bits[0].lc::<E>(one, y_coeffs[0b01])
(y_coeffs[0b00], one); + (y_coeffs[0b00], one);
cs.enforce( cs.enforce(
|| "y-coordinate lookup", || "y-coordinate lookup",
|lc| lc + &y_lc + &y_lc, |lc| lc + &y_lc + &y_lc,
|lc| lc + &bits[2].lc::<E>(one, E::Fr::one()), |lc| lc + &bits[2].lc::<E>(one, E::Fr::one()),
|lc| lc + &y_lc - y.get_variable() |lc| lc + &y_lc - y.get_variable(),
); );
Ok((x, y.into())) Ok((x, y.into()))
@ -197,8 +188,8 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::gadgets::boolean::{AllocatedBit, Boolean};
use crate::gadgets::test::*; use crate::gadgets::test::*;
use crate::gadgets::boolean::{Boolean, AllocatedBit};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
@ -206,40 +197,42 @@ mod test {
#[test] #[test]
fn test_lookup3_xy() { fn test_lookup3_xy() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..100 { for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.next_u32() % 2 != 0; let a_val = rng.next_u32() % 2 != 0;
let a = Boolean::from( let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.next_u32() % 2 != 0; let b_val = rng.next_u32() % 2 != 0;
let b = Boolean::from( let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.next_u32() % 2 != 0; let c_val = rng.next_u32() % 2 != 0;
let c = Boolean::from( let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let bits = vec![a, b, c]; let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..8).map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))).collect(); let points: Vec<(Fr, Fr)> = (0..8)
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
.collect();
let res = lookup3_xy(&mut cs, &bits, &points).unwrap(); let res = lookup3_xy(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut index = 0; let mut index = 0;
if a_val { index += 1 } if a_val {
if b_val { index += 2 } index += 1
if c_val { index += 4 } }
if b_val {
index += 2
}
if c_val {
index += 4
}
assert_eq!(res.0.get_value().unwrap(), points[index].0); assert_eq!(res.0.get_value().unwrap(), points[index].0);
assert_eq!(res.1.get_value().unwrap(), points[index].1); assert_eq!(res.1.get_value().unwrap(), points[index].1);
@ -249,43 +242,45 @@ mod test {
#[test] #[test]
fn test_lookup3_xy_with_conditional_negation() { fn test_lookup3_xy_with_conditional_negation() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..100 { for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.next_u32() % 2 != 0; let a_val = rng.next_u32() % 2 != 0;
let a = Boolean::from( let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.next_u32() % 2 != 0; let b_val = rng.next_u32() % 2 != 0;
let b = Boolean::from( let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.next_u32() % 2 != 0; let c_val = rng.next_u32() % 2 != 0;
let c = Boolean::from( let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let bits = vec![a, b, c]; let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..4).map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))).collect(); let points: Vec<(Fr, Fr)> = (0..4)
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
.collect();
let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap(); let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut index = 0; let mut index = 0;
if a_val { index += 1 } if a_val {
if b_val { index += 2 } index += 1
}
if b_val {
index += 2
}
assert_eq!(res.0.get_value().unwrap(), points[index].0); assert_eq!(res.0.get_value().unwrap(), points[index].0);
let mut tmp = points[index].1; let mut tmp = points[index].1;
if c_val { tmp.negate() } if c_val {
tmp.negate()
}
assert_eq!(res.1.get_value().unwrap(), tmp); assert_eq!(res.1.get_value().unwrap(), tmp);
} }
} }
@ -293,14 +288,16 @@ mod test {
#[test] #[test]
fn test_synth() { fn test_synth() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let window_size = 4; let window_size = 4;
let mut assignment = vec![Fr::zero(); 1 << window_size]; let mut assignment = vec![Fr::zero(); 1 << window_size];
let constants: Vec<_> = (0..(1 << window_size)).map(|_| Fr::random(&mut rng)).collect(); let constants: Vec<_> = (0..(1 << window_size))
.map(|_| Fr::random(&mut rng))
.collect();
synth::<Bls12, _>(window_size, &constants, &mut assignment); synth::<Bls12, _>(window_size, &constants, &mut assignment);

View File

@ -1,14 +1,9 @@
use ff::{Field, PrimeField}; use ff::{Field, PrimeField};
use pairing::Engine; use pairing::Engine;
use crate::{ use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>>{ pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>> {
cs: CS, cs: CS,
ops: usize, ops: usize,
bits_used: usize, bits_used: usize,
@ -23,12 +18,11 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
ops: 0, ops: 0,
bits_used: 0, bits_used: 0,
lhs: LinearCombination::zero(), lhs: LinearCombination::zero(),
rhs: LinearCombination::zero() rhs: LinearCombination::zero(),
} }
} }
fn accumulate(&mut self) fn accumulate(&mut self) {
{
let ops = self.ops; let ops = self.ops;
let lhs = self.lhs.clone(); let lhs = self.lhs.clone();
let rhs = self.rhs.clone(); let rhs = self.rhs.clone();
@ -36,7 +30,7 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|| format!("multieq {}", ops), || format!("multieq {}", ops),
|_| lhs, |_| lhs,
|lc| lc + CS::one(), |lc| lc + CS::one(),
|_| rhs |_| rhs,
); );
self.lhs = LinearCombination::zero(); self.lhs = LinearCombination::zero();
self.rhs = LinearCombination::zero(); self.rhs = LinearCombination::zero();
@ -48,9 +42,8 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
&mut self, &mut self,
num_bits: usize, num_bits: usize,
lhs: &LinearCombination<E>, lhs: &LinearCombination<E>,
rhs: &LinearCombination<E> rhs: &LinearCombination<E>,
) ) {
{
// Check if we will exceed the capacity // Check if we will exceed the capacity
if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) { if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) {
self.accumulate(); self.accumulate();
@ -68,67 +61,60 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> { impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
fn drop(&mut self) { fn drop(&mut self) {
if self.bits_used > 0 { if self.bits_used > 0 {
self.accumulate(); self.accumulate();
} }
} }
} }
impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> {
{
type Root = Self; type Root = Self;
fn one() -> Variable { fn one() -> Variable {
CS::one() CS::one()
} }
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.cs.alloc(annotation, f) self.cs.alloc(annotation, f)
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.cs.alloc_input(annotation, f) self.cs.alloc_input(annotation, f)
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
self.cs.enforce(annotation, a, b, c) self.cs.enforce(annotation, a, b, c)
} }
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
self.cs.get_root().push_namespace(name_fn) self.cs.get_root().push_namespace(name_fn)
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
self.cs.get_root().pop_namespace() self.cs.get_root().pop_namespace()
} }
fn get_root(&mut self) -> &mut Self::Root fn get_root(&mut self) -> &mut Self::Root {
{
self self
} }
} }

View File

@ -1,20 +1,18 @@
use ff::{Field, PrimeField}; use super::boolean::Boolean;
use pairing::Engine;
use crate::{ConstraintSystem, SynthesisError};
use super::boolean::{Boolean};
use super::num::Num; use super::num::Num;
use super::Assignment; use super::Assignment;
use crate::{ConstraintSystem, SynthesisError};
use ff::{Field, PrimeField};
use pairing::Engine;
/// Takes a sequence of booleans and exposes them as compact /// Takes a sequence of booleans and exposes them as compact
/// public inputs /// public inputs
pub fn pack_into_inputs<E, CS>( pub fn pack_into_inputs<E, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError>
mut cs: CS, where
bits: &[Boolean] E: Engine,
) -> Result<(), SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>
{ {
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() {
{
let mut num = Num::<E>::zero(); let mut num = Num::<E>::zero();
let mut coeff = E::Fr::one(); let mut coeff = E::Fr::one();
for bit in bits { for bit in bits {
@ -23,44 +21,38 @@ pub fn pack_into_inputs<E, CS>(
coeff.double(); coeff.double();
} }
let input = cs.alloc_input(|| format!("input {}", i), || { let input = cs.alloc_input(|| format!("input {}", i), || Ok(*num.get_value().get()?))?;
Ok(*num.get_value().get()?)
})?;
// num * 1 = input // num * 1 = input
cs.enforce( cs.enforce(
|| format!("packing constraint {}", i), || format!("packing constraint {}", i),
|_| num.lc(E::Fr::one()), |_| num.lc(E::Fr::one()),
|lc| lc + CS::one(), |lc| lc + CS::one(),
|lc| lc + input |lc| lc + input,
); );
} }
Ok(()) Ok(())
} }
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
{ bytes
bytes.iter() .iter()
.flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1)) .flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1))
.collect() .collect()
} }
pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> {
{ bytes
bytes.iter() .iter()
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1)) .flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
.collect() .collect()
} }
pub fn compute_multipacking<E: Engine>( pub fn compute_multipacking<E: Engine>(bits: &[bool]) -> Vec<E::Fr> {
bits: &[bool]
) -> Vec<E::Fr>
{
let mut result = vec![]; let mut result = vec![];
for bits in bits.chunks(E::Fr::CAPACITY as usize) for bits in bits.chunks(E::Fr::CAPACITY as usize) {
{
let mut cur = E::Fr::zero(); let mut cur = E::Fr::zero();
let mut coeff = E::Fr::one(); let mut coeff = E::Fr::one();
@ -80,13 +72,13 @@ pub fn compute_multipacking<E: Engine>(
#[test] #[test]
fn test_multipacking() { fn test_multipacking() {
use crate::{ConstraintSystem}; use crate::ConstraintSystem;
use pairing::bls12_381::{Bls12}; use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use crate::gadgets::test::*;
use super::boolean::{AllocatedBit, Boolean}; use super::boolean::{AllocatedBit, Boolean};
use crate::gadgets::test::*;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
@ -98,16 +90,15 @@ fn test_multipacking() {
let bits: Vec<bool> = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect(); let bits: Vec<bool> = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect();
let circuit_bits = bits.iter().enumerate() let circuit_bits = bits
.map(|(i, &b)| { .iter()
Boolean::from( .enumerate()
AllocatedBit::alloc( .map(|(i, &b)| {
cs.namespace(|| format!("bit {}", i)), Boolean::from(
Some(b) AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(b)).unwrap(),
).unwrap() )
) })
}) .collect::<Vec<_>>();
.collect::<Vec<_>>();
let expected_inputs = compute_multipacking::<Bls12>(&bits); let expected_inputs = compute_multipacking::<Bls12>(&bits);

View File

@ -1,78 +1,61 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr};
use pairing::Engine; use pairing::Engine;
use crate::{ use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
use super::{ use super::Assignment;
Assignment
};
use super::boolean::{ use super::boolean::{self, AllocatedBit, Boolean};
self,
Boolean,
AllocatedBit
};
pub struct AllocatedNum<E: Engine> { pub struct AllocatedNum<E: Engine> {
value: Option<E::Fr>, value: Option<E::Fr>,
variable: Variable variable: Variable,
} }
impl<E: Engine> Clone for AllocatedNum<E> { impl<E: Engine> Clone for AllocatedNum<E> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
AllocatedNum { AllocatedNum {
value: self.value, value: self.value,
variable: self.variable variable: self.variable,
} }
} }
} }
impl<E: Engine> AllocatedNum<E> { impl<E: Engine> AllocatedNum<E> {
pub fn alloc<CS, F>( pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
mut cs: CS, where
value: F, CS: ConstraintSystem<E>,
) -> Result<Self, SynthesisError> F: FnOnce() -> Result<E::Fr, SynthesisError>,
where CS: ConstraintSystem<E>,
F: FnOnce() -> Result<E::Fr, SynthesisError>
{ {
let mut new_value = None; let mut new_value = None;
let var = cs.alloc(|| "num", || { let var = cs.alloc(
let tmp = value()?; || "num",
|| {
let tmp = value()?;
new_value = Some(tmp); new_value = Some(tmp);
Ok(tmp) Ok(tmp)
})?; },
)?;
Ok(AllocatedNum { Ok(AllocatedNum {
value: new_value, value: new_value,
variable: var variable: var,
}) })
} }
pub fn inputize<CS>( pub fn inputize<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{ {
let input = cs.alloc_input( let input = cs.alloc_input(|| "input variable", || Ok(*self.value.get()?))?;
|| "input variable",
|| {
Ok(*self.value.get()?)
}
)?;
cs.enforce( cs.enforce(
|| "enforce input is correct", || "enforce input is correct",
|lc| lc + input, |lc| lc + input,
|lc| lc + CS::one(), |lc| lc + CS::one(),
|lc| lc + self.variable |lc| lc + self.variable,
); );
Ok(()) Ok(())
@ -83,18 +66,17 @@ impl<E: Engine> AllocatedNum<E> {
/// order, requiring that the representation /// order, requiring that the representation
/// strictly exists "in the field" (i.e., a /// strictly exists "in the field" (i.e., a
/// congruency is not allowed.) /// congruency is not allowed.)
pub fn into_bits_le_strict<CS>( pub fn into_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
{ {
pub fn kary_and<E, CS>( pub fn kary_and<E, CS>(
mut cs: CS, mut cs: CS,
v: &[AllocatedBit] v: &[AllocatedBit],
) -> Result<AllocatedBit, SynthesisError> ) -> Result<AllocatedBit, SynthesisError>
where E: Engine, where
CS: ConstraintSystem<E> E: Engine,
CS: ConstraintSystem<E>,
{ {
assert!(v.len() > 0); assert!(v.len() > 0);
@ -109,7 +91,7 @@ impl<E: Engine> AllocatedNum<E> {
cur = Some(AllocatedBit::and( cur = Some(AllocatedBit::and(
cs.namespace(|| format!("and {}", i)), cs.namespace(|| format!("and {}", i)),
cur.as_ref().unwrap(), cur.as_ref().unwrap(),
v v,
)?); )?);
} }
} }
@ -145,10 +127,7 @@ impl<E: Engine> AllocatedNum<E> {
if b { if b {
// This is part of a run of ones. Let's just // This is part of a run of ones. Let's just
// allocate the boolean with the expected value. // allocate the boolean with the expected value.
let a_bit = AllocatedBit::alloc( let a_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), a_bit)?;
cs.namespace(|| format!("bit {}", i)),
a_bit
)?;
// ... and add it to the current run of ones. // ... and add it to the current run of ones.
current_run.push(a_bit.clone()); current_run.push(a_bit.clone());
result.push(a_bit); result.push(a_bit);
@ -162,7 +141,7 @@ impl<E: Engine> AllocatedNum<E> {
} }
last_run = Some(kary_and( last_run = Some(kary_and(
cs.namespace(|| format!("run ending at {}", i)), cs.namespace(|| format!("run ending at {}", i)),
&current_run &current_run,
)?); )?);
current_run.truncate(0); current_run.truncate(0);
} }
@ -175,7 +154,7 @@ impl<E: Engine> AllocatedNum<E> {
let a_bit = AllocatedBit::alloc_conditionally( let a_bit = AllocatedBit::alloc_conditionally(
cs.namespace(|| format!("bit {}", i)), cs.namespace(|| format!("bit {}", i)),
a_bit, a_bit,
&last_run.as_ref().expect("char always starts with a one") &last_run.as_ref().expect("char always starts with a one"),
)?; )?;
result.push(a_bit); result.push(a_bit);
} }
@ -201,12 +180,7 @@ impl<E: Engine> AllocatedNum<E> {
lc = lc - self.variable; lc = lc - self.variable;
cs.enforce( cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|| "unpacking constraint",
|lc| lc,
|lc| lc,
|_| lc
);
// Convert into booleans, and reverse for little-endian bit order // Convert into booleans, and reverse for little-endian bit order
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect()) Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect())
@ -215,16 +189,11 @@ impl<E: Engine> AllocatedNum<E> {
/// Convert the allocated number into its little-endian representation. /// Convert the allocated number into its little-endian representation.
/// Note that this does not strongly enforce that the commitment is /// Note that this does not strongly enforce that the commitment is
/// "in the field." /// "in the field."
pub fn into_bits_le<CS>( pub fn into_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let bits = boolean::field_into_allocated_bits_le( let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?;
&mut cs,
self.value
)?;
let mut lc = LinearCombination::zero(); let mut lc = LinearCombination::zero();
let mut coeff = E::Fr::one(); let mut coeff = E::Fr::one();
@ -237,94 +206,91 @@ impl<E: Engine> AllocatedNum<E> {
lc = lc - self.variable; lc = lc - self.variable;
cs.enforce( cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|| "unpacking constraint",
|lc| lc,
|lc| lc,
|_| lc
);
Ok(bits.into_iter().map(|b| Boolean::from(b)).collect()) Ok(bits.into_iter().map(|b| Boolean::from(b)).collect())
} }
pub fn mul<CS>( pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
&self, where
mut cs: CS, CS: ConstraintSystem<E>,
other: &Self
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let mut value = None; let mut value = None;
let var = cs.alloc(|| "product num", || { let var = cs.alloc(
let mut tmp = *self.value.get()?; || "product num",
tmp.mul_assign(other.value.get()?); || {
let mut tmp = *self.value.get()?;
tmp.mul_assign(other.value.get()?);
value = Some(tmp); value = Some(tmp);
Ok(tmp) Ok(tmp)
})?; },
)?;
// Constrain: a * b = ab // Constrain: a * b = ab
cs.enforce( cs.enforce(
|| "multiplication constraint", || "multiplication constraint",
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + other.variable, |lc| lc + other.variable,
|lc| lc + var |lc| lc + var,
); );
Ok(AllocatedNum { Ok(AllocatedNum {
value: value, value: value,
variable: var variable: var,
}) })
} }
pub fn square<CS>( pub fn square<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let mut value = None; let mut value = None;
let var = cs.alloc(|| "squared num", || { let var = cs.alloc(
let mut tmp = *self.value.get()?; || "squared num",
tmp.square(); || {
let mut tmp = *self.value.get()?;
tmp.square();
value = Some(tmp); value = Some(tmp);
Ok(tmp) Ok(tmp)
})?; },
)?;
// Constrain: a * a = aa // Constrain: a * a = aa
cs.enforce( cs.enforce(
|| "squaring constraint", || "squaring constraint",
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + var |lc| lc + var,
); );
Ok(AllocatedNum { Ok(AllocatedNum {
value: value, value: value,
variable: var variable: var,
}) })
} }
pub fn assert_nonzero<CS>( pub fn assert_nonzero<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{ {
let inv = cs.alloc(|| "ephemeral inverse", || { let inv = cs.alloc(
let tmp = *self.value.get()?; || "ephemeral inverse",
|| {
if tmp.is_zero() { let tmp = *self.value.get()?;
Err(SynthesisError::DivisionByZero)
} else { if tmp.is_zero() {
Ok(tmp.inverse().unwrap()) Err(SynthesisError::DivisionByZero)
} } else {
})?; Ok(tmp.inverse().unwrap())
}
},
)?;
// Constrain a * inv = 1, which is only valid // Constrain a * inv = 1, which is only valid
// iff a has a multiplicative inverse, untrue // iff a has a multiplicative inverse, untrue
@ -333,7 +299,7 @@ impl<E: Engine> AllocatedNum<E> {
|| "nonzero assertion constraint", || "nonzero assertion constraint",
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + inv, |lc| lc + inv,
|lc| lc + CS::one() |lc| lc + CS::one(),
); );
Ok(()) Ok(())
@ -346,44 +312,39 @@ impl<E: Engine> AllocatedNum<E> {
mut cs: CS, mut cs: CS,
a: &Self, a: &Self,
b: &Self, b: &Self,
condition: &Boolean condition: &Boolean,
) -> Result<(Self, Self), SynthesisError> ) -> Result<(Self, Self), SynthesisError>
where CS: ConstraintSystem<E> where
CS: ConstraintSystem<E>,
{ {
let c = Self::alloc( let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || {
cs.namespace(|| "conditional reversal result 1"), if *condition.get_value().get()? {
|| { Ok(*b.value.get()?)
if *condition.get_value().get()? { } else {
Ok(*b.value.get()?) Ok(*a.value.get()?)
} else {
Ok(*a.value.get()?)
}
} }
)?; })?;
cs.enforce( cs.enforce(
|| "first conditional reversal", || "first conditional reversal",
|lc| lc + a.variable - b.variable, |lc| lc + a.variable - b.variable,
|_| condition.lc(CS::one(), E::Fr::one()), |_| condition.lc(CS::one(), E::Fr::one()),
|lc| lc + a.variable - c.variable |lc| lc + a.variable - c.variable,
); );
let d = Self::alloc( let d = Self::alloc(cs.namespace(|| "conditional reversal result 2"), || {
cs.namespace(|| "conditional reversal result 2"), if *condition.get_value().get()? {
|| { Ok(*a.value.get()?)
if *condition.get_value().get()? { } else {
Ok(*a.value.get()?) Ok(*b.value.get()?)
} else {
Ok(*b.value.get()?)
}
} }
)?; })?;
cs.enforce( cs.enforce(
|| "second conditional reversal", || "second conditional reversal",
|lc| lc + b.variable - a.variable, |lc| lc + b.variable - a.variable,
|_| condition.lc(CS::one(), E::Fr::one()), |_| condition.lc(CS::one(), E::Fr::one()),
|lc| lc + b.variable - d.variable |lc| lc + b.variable - d.variable,
); );
Ok((c, d)) Ok((c, d))
@ -400,14 +361,14 @@ impl<E: Engine> AllocatedNum<E> {
pub struct Num<E: Engine> { pub struct Num<E: Engine> {
value: Option<E::Fr>, value: Option<E::Fr>,
lc: LinearCombination<E> lc: LinearCombination<E>,
} }
impl<E: Engine> From<AllocatedNum<E>> for Num<E> { impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
fn from(num: AllocatedNum<E>) -> Num<E> { fn from(num: AllocatedNum<E>) -> Num<E> {
Num { Num {
value: num.value, value: num.value,
lc: LinearCombination::<E>::zero() + num.variable lc: LinearCombination::<E>::zero() + num.variable,
} }
} }
} }
@ -416,7 +377,7 @@ impl<E: Engine> Num<E> {
pub fn zero() -> Self { pub fn zero() -> Self {
Num { Num {
value: Some(E::Fr::zero()), value: Some(E::Fr::zero()),
lc: LinearCombination::zero() lc: LinearCombination::zero(),
} }
} }
@ -428,13 +389,7 @@ impl<E: Engine> Num<E> {
LinearCombination::zero() + (coeff, &self.lc) LinearCombination::zero() + (coeff, &self.lc)
} }
pub fn add_bool_with_coeff( pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: E::Fr) -> Self {
self,
one: Variable,
bit: &Boolean,
coeff: E::Fr
) -> Self
{
let newval = match (self.value, bit.get_value()) { let newval = match (self.value, bit.get_value()) {
(Some(mut curval), Some(bval)) => { (Some(mut curval), Some(bval)) => {
if bval { if bval {
@ -442,27 +397,27 @@ impl<E: Engine> Num<E> {
} }
Some(curval) Some(curval)
}, }
_ => None _ => None,
}; };
Num { Num {
value: newval, value: newval,
lc: self.lc + &bit.lc(one, coeff) lc: self.lc + &bit.lc(one, coeff),
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ConstraintSystem}; use crate::ConstraintSystem;
use ff::{BitIterator, Field, PrimeField}; use ff::{BitIterator, Field, PrimeField};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::SeedableRng; use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use crate::gadgets::test::*;
use super::{AllocatedNum, Boolean}; use super::{AllocatedNum, Boolean};
use crate::gadgets::test::*;
#[test] #[test]
fn test_allocated_num() { fn test_allocated_num() {
@ -491,8 +446,10 @@ mod test {
fn test_num_multiplication() { fn test_num_multiplication() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap(); let n =
let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap(); AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap();
let n2 =
AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap();
let n3 = n.mul(&mut cs, &n2).unwrap(); let n3 = n.mul(&mut cs, &n2).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@ -505,8 +462,8 @@ mod test {
#[test] #[test]
fn test_num_conditional_reversal() { fn test_num_conditional_reversal() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
{ {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
@ -573,14 +530,17 @@ mod test {
cs.set("bit 254/boolean", Fr::one()); cs.set("bit 254/boolean", Fr::one());
// this makes the conditional boolean constraint fail // this makes the conditional boolean constraint fail
assert_eq!(cs.which_is_unsatisfied().unwrap(), "bit 254/boolean constraint"); assert_eq!(
cs.which_is_unsatisfied().unwrap(),
"bit 254/boolean constraint"
);
} }
#[test] #[test]
fn test_into_bits() { fn test_into_bits() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for i in 0..200 { for i in 0..200 {
@ -597,7 +557,10 @@ mod test {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
for (b, a) in BitIterator::new(r.into_repr()).skip(1).zip(bits.iter().rev()) { for (b, a) in BitIterator::new(r.into_repr())
.skip(1)
.zip(bits.iter().rev())
{
if let &Boolean::Is(ref a) = a { if let &Boolean::Is(ref a) = a {
assert_eq!(b, a.get_value().unwrap()); assert_eq!(b, a.get_value().unwrap());
} else { } else {

View File

@ -1,6 +1,6 @@
use super::uint32::UInt32;
use super::multieq::MultiEq;
use super::boolean::Boolean; use super::boolean::Boolean;
use super::multieq::MultiEq;
use super::uint32::UInt32;
use crate::{ConstraintSystem, SynthesisError}; use crate::{ConstraintSystem, SynthesisError};
use pairing::Engine; use pairing::Engine;
@ -12,37 +12,35 @@ const ROUND_CONSTANTS: [u32; 64] = [
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
]; ];
const IV: [u32; 8] = [ const IV: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]; ];
pub fn sha256_block_no_padding<E, CS>( pub fn sha256_block_no_padding<E, CS>(
mut cs: CS, mut cs: CS,
input: &[Boolean] input: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(input.len(), 512); assert_eq!(input.len(), 512);
Ok(sha256_compression_function( Ok(
&mut cs, sha256_compression_function(&mut cs, &input, &get_sha256_iv())?
&input, .into_iter()
&get_sha256_iv() .flat_map(|e| e.into_bits_be())
)? .collect(),
.into_iter() )
.flat_map(|e| e.into_bits_be())
.collect())
} }
pub fn sha256<E, CS>( pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
mut cs: CS, where
input: &[Boolean] E: Engine,
) -> Result<Vec<Boolean>, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>
{ {
assert!(input.len() % 8 == 0); assert!(input.len() % 8 == 0);
@ -62,16 +60,10 @@ pub fn sha256<E, CS>(
let mut cur = get_sha256_iv(); let mut cur = get_sha256_iv();
for (i, block) in padded.chunks(512).enumerate() { for (i, block) in padded.chunks(512).enumerate() {
cur = sha256_compression_function( cur = sha256_compression_function(cs.namespace(|| format!("block {}", i)), block, &cur)?;
cs.namespace(|| format!("block {}", i)),
block,
&cur
)?;
} }
Ok(cur.into_iter() Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect())
.flat_map(|e| e.into_bits_be())
.collect())
} }
fn get_sha256_iv() -> Vec<UInt32> { fn get_sha256_iv() -> Vec<UInt32> {
@ -81,16 +73,19 @@ fn get_sha256_iv() -> Vec<UInt32> {
fn sha256_compression_function<E, CS>( fn sha256_compression_function<E, CS>(
cs: CS, cs: CS,
input: &[Boolean], input: &[Boolean],
current_hash_value: &[UInt32] current_hash_value: &[UInt32],
) -> Result<Vec<UInt32>, SynthesisError> ) -> Result<Vec<UInt32>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(input.len(), 512); assert_eq!(input.len(), 512);
assert_eq!(current_hash_value.len(), 8); assert_eq!(current_hash_value.len(), 8);
let mut w = input.chunks(32) let mut w = input
.map(|e| UInt32::from_bits_be(e)) .chunks(32)
.collect::<Vec<_>>(); .map(|e| UInt32::from_bits_be(e))
.collect::<Vec<_>>();
// We can save some constraints by combining some of // We can save some constraints by combining some of
// the constraints in different u32 additions // the constraints in different u32 additions
@ -100,30 +95,18 @@ fn sha256_compression_function<E, CS>(
let cs = &mut cs.namespace(|| format!("w extension {}", i)); let cs = &mut cs.namespace(|| format!("w extension {}", i));
// s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3) // s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
let mut s0 = w[i-15].rotr(7); let mut s0 = w[i - 15].rotr(7);
s0 = s0.xor( s0 = s0.xor(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?;
cs.namespace(|| "first xor for s0"), s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?;
&w[i-15].rotr(18)
)?;
s0 = s0.xor(
cs.namespace(|| "second xor for s0"),
&w[i-15].shr(3)
)?;
// s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10) // s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
let mut s1 = w[i-2].rotr(17); let mut s1 = w[i - 2].rotr(17);
s1 = s1.xor( s1 = s1.xor(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?;
cs.namespace(|| "first xor for s1"), s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?;
&w[i-2].rotr(19)
)?;
s1 = s1.xor(
cs.namespace(|| "second xor for s1"),
&w[i-2].shr(10)
)?;
let tmp = UInt32::addmany( let tmp = UInt32::addmany(
cs.namespace(|| "computation of w[i]"), cs.namespace(|| "computation of w[i]"),
&[w[i-16].clone(), s0, w[i-7].clone(), s1] &[w[i - 16].clone(), s0, w[i - 7].clone(), s1],
)?; )?;
// w[i] := w[i-16] + s0 + w[i-7] + s1 // w[i] := w[i-16] + s0 + w[i-7] + s1
@ -134,29 +117,21 @@ fn sha256_compression_function<E, CS>(
enum Maybe { enum Maybe {
Deferred(Vec<UInt32>), Deferred(Vec<UInt32>),
Concrete(UInt32) Concrete(UInt32),
} }
impl Maybe { impl Maybe {
fn compute<E, CS, M>( fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
self, where
cs: M, E: Engine,
others: &[UInt32] CS: ConstraintSystem<E>,
) -> Result<UInt32, SynthesisError> M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
where E: Engine,
CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root=MultiEq<E, CS>>
{ {
Ok(match self { Ok(match self {
Maybe::Concrete(ref v) => { Maybe::Concrete(ref v) => return Ok(v.clone()),
return Ok(v.clone())
},
Maybe::Deferred(mut v) => { Maybe::Deferred(mut v) => {
v.extend(others.into_iter().cloned()); v.extend(others.into_iter().cloned());
UInt32::addmany( UInt32::addmany(cs, &v)?
cs,
&v
)?
} }
}) })
} }
@ -177,22 +152,11 @@ fn sha256_compression_function<E, CS>(
// S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25) // S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?; let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?;
let mut s1 = new_e.rotr(6); let mut s1 = new_e.rotr(6);
s1 = s1.xor( s1 = s1.xor(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?;
cs.namespace(|| "first xor for s1"), s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?;
&new_e.rotr(11)
)?;
s1 = s1.xor(
cs.namespace(|| "second xor for s1"),
&new_e.rotr(25)
)?;
// ch := (e and f) xor ((not e) and g) // ch := (e and f) xor ((not e) and g)
let ch = UInt32::sha256_ch( let ch = UInt32::sha256_ch(cs.namespace(|| "ch"), &new_e, &f, &g)?;
cs.namespace(|| "ch"),
&new_e,
&f,
&g
)?;
// temp1 := h + S1 + ch + k[i] + w[i] // temp1 := h + S1 + ch + k[i] + w[i]
let temp1 = vec![ let temp1 = vec![
@ -200,28 +164,17 @@ fn sha256_compression_function<E, CS>(
s1, s1,
ch, ch,
UInt32::constant(ROUND_CONSTANTS[i]), UInt32::constant(ROUND_CONSTANTS[i]),
w[i].clone() w[i].clone(),
]; ];
// S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22) // S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?; let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?;
let mut s0 = new_a.rotr(2); let mut s0 = new_a.rotr(2);
s0 = s0.xor( s0 = s0.xor(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?;
cs.namespace(|| "first xor for s0"), s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?;
&new_a.rotr(13)
)?;
s0 = s0.xor(
cs.namespace(|| "second xor for s0"),
&new_a.rotr(22)
)?;
// maj := (a and b) xor (a and c) xor (b and c) // maj := (a and b) xor (a and c) xor (b and c)
let maj = UInt32::sha256_maj( let maj = UInt32::sha256_maj(cs.namespace(|| "maj"), &new_a, &b, &c)?;
cs.namespace(|| "maj"),
&new_a,
&b,
&c
)?;
// temp2 := S0 + maj // temp2 := S0 + maj
let temp2 = vec![s0, maj]; let temp2 = vec![s0, maj];
@ -244,7 +197,13 @@ fn sha256_compression_function<E, CS>(
d = c; d = c;
c = b; c = b;
b = new_a; b = new_a;
a = Maybe::Deferred(temp1.iter().cloned().chain(temp2.iter().cloned()).collect::<Vec<_>>()); a = Maybe::Deferred(
temp1
.iter()
.cloned()
.chain(temp2.iter().cloned())
.collect::<Vec<_>>(),
);
} }
/* /*
@ -261,42 +220,42 @@ fn sha256_compression_function<E, CS>(
let h0 = a.compute( let h0 = a.compute(
cs.namespace(|| "deferred h0 computation"), cs.namespace(|| "deferred h0 computation"),
&[current_hash_value[0].clone()] &[current_hash_value[0].clone()],
)?; )?;
let h1 = UInt32::addmany( let h1 = UInt32::addmany(
cs.namespace(|| "new h1"), cs.namespace(|| "new h1"),
&[current_hash_value[1].clone(), b] &[current_hash_value[1].clone(), b],
)?; )?;
let h2 = UInt32::addmany( let h2 = UInt32::addmany(
cs.namespace(|| "new h2"), cs.namespace(|| "new h2"),
&[current_hash_value[2].clone(), c] &[current_hash_value[2].clone(), c],
)?; )?;
let h3 = UInt32::addmany( let h3 = UInt32::addmany(
cs.namespace(|| "new h3"), cs.namespace(|| "new h3"),
&[current_hash_value[3].clone(), d] &[current_hash_value[3].clone(), d],
)?; )?;
let h4 = e.compute( let h4 = e.compute(
cs.namespace(|| "deferred h4 computation"), cs.namespace(|| "deferred h4 computation"),
&[current_hash_value[4].clone()] &[current_hash_value[4].clone()],
)?; )?;
let h5 = UInt32::addmany( let h5 = UInt32::addmany(
cs.namespace(|| "new h5"), cs.namespace(|| "new h5"),
&[current_hash_value[5].clone(), f] &[current_hash_value[5].clone(), f],
)?; )?;
let h6 = UInt32::addmany( let h6 = UInt32::addmany(
cs.namespace(|| "new h6"), cs.namespace(|| "new h6"),
&[current_hash_value[6].clone(), g] &[current_hash_value[6].clone(), g],
)?; )?;
let h7 = UInt32::addmany( let h7 = UInt32::addmany(
cs.namespace(|| "new h7"), cs.namespace(|| "new h7"),
&[current_hash_value[7].clone(), h] &[current_hash_value[7].clone(), h],
)?; )?;
Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7]) Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7])
@ -306,8 +265,8 @@ fn sha256_compression_function<E, CS>(
mod test { mod test {
use super::*; use super::*;
use crate::gadgets::boolean::AllocatedBit; use crate::gadgets::boolean::AllocatedBit;
use pairing::bls12_381::Bls12;
use crate::gadgets::test::TestConstraintSystem; use crate::gadgets::test::TestConstraintSystem;
use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
@ -318,11 +277,7 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect();
input_bits[0] = Boolean::Constant(true); input_bits[0] = Boolean::Constant(true);
let out = sha256_compression_function( let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap();
&mut cs,
&input_bits,
&iv
).unwrap();
let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect(); let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@ -343,27 +298,26 @@ mod test {
#[test] #[test]
fn test_full_block() { fn test_full_block() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let iv = get_sha256_iv(); let iv = get_sha256_iv();
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits: Vec<_> = (0..512).map(|i| { let input_bits: Vec<_> = (0..512)
Boolean::from( .map(|i| {
AllocatedBit::alloc( Boolean::from(
cs.namespace(|| format!("input bit {}", i)), AllocatedBit::alloc(
Some(rng.next_u32() % 2 != 0) cs.namespace(|| format!("input bit {}", i)),
).unwrap() Some(rng.next_u32() % 2 != 0),
) )
}).collect(); .unwrap(),
)
})
.collect();
sha256_compression_function( sha256_compression_function(cs.namespace(|| "sha256"), &input_bits, &iv).unwrap();
cs.namespace(|| "sha256"),
&input_bits,
&iv
).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints() - 512, 25840); assert_eq!(cs.num_constraints() - 512, 25840);
@ -374,12 +328,11 @@ mod test {
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
{
let mut h = Sha256::new(); let mut h = Sha256::new();
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect(); let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
h.input(&data); h.input(&data);
@ -392,7 +345,11 @@ mod test {
for bit_i in (0..8).rev() { for bit_i in (0..8).rev() {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into()); input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
} }
} }
@ -400,17 +357,19 @@ mod test {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut s = hash_result.as_ref().iter() let mut s = hash_result
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8)); .as_ref()
.iter()
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r { for b in r {
match b { match b {
Boolean::Is(b) => { Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap()); assert!(s.next().unwrap() == b.get_value().unwrap());
}, }
Boolean::Not(b) => { Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap()); assert!(s.next().unwrap() != b.get_value().unwrap());
}, }
Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(input_len == 0); assert!(input_len == 0);
assert!(s.next().unwrap() == b); assert!(s.next().unwrap() == b);

View File

@ -1,13 +1,7 @@
use ff::{Field, PrimeField, PrimeFieldRepr}; use ff::{Field, PrimeField, PrimeFieldRepr};
use pairing::Engine; use pairing::Engine;
use crate::{ use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
LinearCombination,
SynthesisError,
ConstraintSystem,
Variable,
Index
};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
@ -22,7 +16,7 @@ use blake2s_simd::{Params as Blake2sParams, State as Blake2sState};
enum NamedObject { enum NamedObject {
Constraint(usize), Constraint(usize),
Var(Variable), Var(Variable),
Namespace Namespace,
} }
/// Constraint system for testing purposes. /// Constraint system for testing purposes.
@ -33,10 +27,10 @@ pub struct TestConstraintSystem<E: Engine> {
LinearCombination<E>, LinearCombination<E>,
LinearCombination<E>, LinearCombination<E>,
LinearCombination<E>, LinearCombination<E>,
String String,
)>, )>,
inputs: Vec<(E::Fr, String)>, inputs: Vec<(E::Fr, String)>,
aux: Vec<(E::Fr, String)> aux: Vec<(E::Fr, String)>,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -48,7 +42,7 @@ impl PartialEq for OrderedVariable {
match (self.0.get_unchecked(), other.0.get_unchecked()) { match (self.0.get_unchecked(), other.0.get_unchecked()) {
(Index::Input(ref a), Index::Input(ref b)) => a == b, (Index::Input(ref a), Index::Input(ref b)) => a == b,
(Index::Aux(ref a), Index::Aux(ref b)) => a == b, (Index::Aux(ref a), Index::Aux(ref b)) => a == b,
_ => false _ => false,
} }
} }
} }
@ -63,20 +57,17 @@ impl Ord for OrderedVariable {
(Index::Input(ref a), Index::Input(ref b)) => a.cmp(b), (Index::Input(ref a), Index::Input(ref b)) => a.cmp(b),
(Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b), (Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b),
(Index::Input(_), Index::Aux(_)) => Ordering::Less, (Index::Input(_), Index::Aux(_)) => Ordering::Less,
(Index::Aux(_), Index::Input(_)) => Ordering::Greater (Index::Aux(_), Index::Input(_)) => Ordering::Greater,
} }
} }
} }
fn proc_lc<E: Engine>( fn proc_lc<E: Engine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
terms: &[(Variable, E::Fr)],
) -> BTreeMap<OrderedVariable, E::Fr>
{
let mut map = BTreeMap::new(); let mut map = BTreeMap::new();
for &(var, coeff) in terms { for &(var, coeff) in terms {
map.entry(OrderedVariable(var)) map.entry(OrderedVariable(var))
.or_insert(E::Fr::zero()) .or_insert(E::Fr::zero())
.add_assign(&coeff); .add_assign(&coeff);
} }
// Remove terms that have a zero coefficient to normalize // Remove terms that have a zero coefficient to normalize
@ -94,11 +85,7 @@ fn proc_lc<E: Engine>(
map map
} }
fn hash_lc<E: Engine>( fn hash_lc<E: Engine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
terms: &[(Variable, E::Fr)],
h: &mut Blake2sState
)
{
let map = proc_lc::<E>(terms); let map = proc_lc::<E>(terms);
let mut buf = [0u8; 9 + 32]; let mut buf = [0u8; 9 + 32];
@ -110,13 +97,13 @@ fn hash_lc<E: Engine>(
Index::Input(i) => { Index::Input(i) => {
buf[0] = b'I'; buf[0] = b'I';
BigEndian::write_u64(&mut buf[1..9], i as u64); BigEndian::write_u64(&mut buf[1..9], i as u64);
}, }
Index::Aux(i) => { Index::Aux(i) => {
buf[0] = b'A'; buf[0] = b'A';
BigEndian::write_u64(&mut buf[1..9], i as u64); BigEndian::write_u64(&mut buf[1..9], i as u64);
} }
} }
coeff.into_repr().write_be(&mut buf[9..]).unwrap(); coeff.into_repr().write_be(&mut buf[9..]).unwrap();
h.update(&buf); h.update(&buf);
@ -126,15 +113,14 @@ fn hash_lc<E: Engine>(
fn eval_lc<E: Engine>( fn eval_lc<E: Engine>(
terms: &[(Variable, E::Fr)], terms: &[(Variable, E::Fr)],
inputs: &[(E::Fr, String)], inputs: &[(E::Fr, String)],
aux: &[(E::Fr, String)] aux: &[(E::Fr, String)],
) -> E::Fr ) -> E::Fr {
{
let mut acc = E::Fr::zero(); let mut acc = E::Fr::zero();
for &(var, ref coeff) in terms { for &(var, ref coeff) in terms {
let mut tmp = match var.get_unchecked() { let mut tmp = match var.get_unchecked() {
Index::Input(index) => inputs[index].0, Index::Input(index) => inputs[index].0,
Index::Aux(index) => aux[index].0 Index::Aux(index) => aux[index].0,
}; };
tmp.mul_assign(&coeff); tmp.mul_assign(&coeff);
@ -147,14 +133,17 @@ fn eval_lc<E: Engine>(
impl<E: Engine> TestConstraintSystem<E> { impl<E: Engine> TestConstraintSystem<E> {
pub fn new() -> TestConstraintSystem<E> { pub fn new() -> TestConstraintSystem<E> {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert("ONE".into(), NamedObject::Var(TestConstraintSystem::<E>::one())); map.insert(
"ONE".into(),
NamedObject::Var(TestConstraintSystem::<E>::one()),
);
TestConstraintSystem { TestConstraintSystem {
named_objects: map, named_objects: map,
current_namespace: vec![], current_namespace: vec![],
constraints: vec![], constraints: vec![],
inputs: vec![(E::Fr::one(), "ONE".into())], inputs: vec![(E::Fr::one(), "ONE".into())],
aux: vec![] aux: vec![],
} }
} }
@ -167,9 +156,9 @@ impl<E: Engine> TestConstraintSystem<E> {
tmp tmp
}; };
let powers_of_two = (0..E::Fr::NUM_BITS).map(|i| { let powers_of_two = (0..E::Fr::NUM_BITS)
E::Fr::from_str("2").unwrap().pow(&[i as u64]) .map(|i| E::Fr::from_str("2").unwrap().pow(&[i as u64]))
}).collect::<Vec<_>>(); .collect::<Vec<_>>();
let pp = |s: &mut String, lc: &LinearCombination<E>| { let pp = |s: &mut String, lc: &LinearCombination<E>| {
write!(s, "(").unwrap(); write!(s, "(").unwrap();
@ -196,7 +185,7 @@ impl<E: Engine> TestConstraintSystem<E> {
match var.0.get_unchecked() { match var.0.get_unchecked() {
Index::Input(i) => { Index::Input(i) => {
write!(s, "`{}`", &self.inputs[i].1).unwrap(); write!(s, "`{}`", &self.inputs[i].1).unwrap();
}, }
Index::Aux(i) => { Index::Aux(i) => {
write!(s, "`{}`", &self.aux[i].1).unwrap(); write!(s, "`{}`", &self.aux[i].1).unwrap();
} }
@ -259,45 +248,41 @@ impl<E: Engine> TestConstraintSystem<E> {
a.mul_assign(&b); a.mul_assign(&b);
if a != c { if a != c {
return Some(&*path) return Some(&*path);
} }
} }
None None
} }
pub fn is_satisfied(&self) -> bool pub fn is_satisfied(&self) -> bool {
{
self.which_is_unsatisfied().is_none() self.which_is_unsatisfied().is_none()
} }
pub fn num_constraints(&self) -> usize pub fn num_constraints(&self) -> usize {
{
self.constraints.len() self.constraints.len()
} }
pub fn set(&mut self, path: &str, to: E::Fr) pub fn set(&mut self, path: &str, to: E::Fr) {
{
match self.named_objects.get(path) { match self.named_objects.get(path) {
Some(&NamedObject::Var(ref v)) => { Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
match v.get_unchecked() { Index::Input(index) => self.inputs[index].0 = to,
Index::Input(index) => self.inputs[index].0 = to, Index::Aux(index) => self.aux[index].0 = to,
Index::Aux(index) => self.aux[index].0 = to },
} Some(e) => panic!(
} "tried to set path `{}` to value, but `{:?}` already exists there.",
Some(e) => panic!("tried to set path `{}` to value, but `{:?}` already exists there.", path, e), path, e
_ => panic!("no variable exists at path: {}", path) ),
_ => panic!("no variable exists at path: {}", path),
} }
} }
pub fn verify(&self, expected: &[E::Fr]) -> bool pub fn verify(&self, expected: &[E::Fr]) -> bool {
{
assert_eq!(expected.len() + 1, self.inputs.len()); assert_eq!(expected.len() + 1, self.inputs.len());
for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) {
{
if &a.0 != b { if &a.0 != b {
return false return false;
} }
} }
@ -308,8 +293,7 @@ impl<E: Engine> TestConstraintSystem<E> {
self.inputs.len() self.inputs.len()
} }
pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr {
{
let (assignment, name) = self.inputs[index].clone(); let (assignment, name) = self.inputs[index].clone();
assert_eq!(path, name); assert_eq!(path, name);
@ -317,17 +301,17 @@ impl<E: Engine> TestConstraintSystem<E> {
assignment assignment
} }
pub fn get(&mut self, path: &str) -> E::Fr pub fn get(&mut self, path: &str) -> E::Fr {
{
match self.named_objects.get(path) { match self.named_objects.get(path) {
Some(&NamedObject::Var(ref v)) => { Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
match v.get_unchecked() { Index::Input(index) => self.inputs[index].0,
Index::Input(index) => self.inputs[index].0, Index::Aux(index) => self.aux[index].0,
Index::Aux(index) => self.aux[index].0 },
} Some(e) => panic!(
} "tried to get value of path `{}`, but `{:?}` exists there (not a variable)",
Some(e) => panic!("tried to get value of path `{}`, but `{:?}` exists there (not a variable)", path, e), path, e
_ => panic!("no variable exists at path: {}", path) ),
_ => panic!("no variable exists at path: {}", path),
} }
} }
@ -348,8 +332,7 @@ fn compute_path(ns: &[String], this: String) -> String {
let mut name = String::new(); let mut name = String::new();
let mut needs_separation = false; let mut needs_separation = false;
for ns in ns.iter().chain(Some(&this).into_iter()) for ns in ns.iter().chain(Some(&this).into_iter()) {
{
if needs_separation { if needs_separation {
name += "/"; name += "/";
} }
@ -364,12 +347,11 @@ fn compute_path(ns: &[String], this: String) -> String {
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> { impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
let index = self.aux.len(); let index = self.aux.len();
let path = compute_path(&self.current_namespace, annotation().into()); let path = compute_path(&self.current_namespace, annotation().into());
@ -380,12 +362,11 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var) Ok(var)
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
let index = self.inputs.len(); let index = self.inputs.len();
let path = compute_path(&self.current_namespace, annotation().into()); let path = compute_path(&self.current_namespace, annotation().into());
@ -396,17 +377,13 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var) Ok(var)
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
let path = compute_path(&self.current_namespace, annotation().into()); let path = compute_path(&self.current_namespace, annotation().into());
let index = self.constraints.len(); let index = self.constraints.len();
@ -420,7 +397,9 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
} }
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
let name = name_fn().into(); let name = name_fn().into();
let path = compute_path(&self.current_namespace, name.clone()); let path = compute_path(&self.current_namespace, name.clone());
@ -428,13 +407,11 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
self.current_namespace.push(name); self.current_namespace.push(name);
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
assert!(self.current_namespace.pop().is_some()); assert!(self.current_namespace.pop().is_some());
} }
fn get_root(&mut self) -> &mut Self::Root fn get_root(&mut self) -> &mut Self::Root {
{
self self
} }
} }
@ -447,28 +424,26 @@ fn test_cs() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 0); assert_eq!(cs.num_constraints(), 0);
let a = cs.namespace(|| "a").alloc(|| "var", || Ok(Fr::from_str("10").unwrap())).unwrap(); let a = cs
let b = cs.namespace(|| "b").alloc(|| "var", || Ok(Fr::from_str("4").unwrap())).unwrap(); .namespace(|| "a")
let c = cs.alloc(|| "product", || Ok(Fr::from_str("40").unwrap())).unwrap(); .alloc(|| "var", || Ok(Fr::from_str("10").unwrap()))
.unwrap();
let b = cs
.namespace(|| "b")
.alloc(|| "var", || Ok(Fr::from_str("4").unwrap()))
.unwrap();
let c = cs
.alloc(|| "product", || Ok(Fr::from_str("40").unwrap()))
.unwrap();
cs.enforce( cs.enforce(|| "mult", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|| "mult",
|lc| lc + a,
|lc| lc + b,
|lc| lc + c
);
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1); assert_eq!(cs.num_constraints(), 1);
cs.set("a/var", Fr::from_str("4").unwrap()); cs.set("a/var", Fr::from_str("4").unwrap());
let one = TestConstraintSystem::<Bls12>::one(); let one = TestConstraintSystem::<Bls12>::one();
cs.enforce( cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b);
|| "eq",
|lc| lc + a,
|lc| lc + one,
|lc| lc + b
);
assert!(!cs.is_satisfied()); assert!(!cs.is_satisfied());
assert!(cs.which_is_unsatisfied() == Some("mult")); assert!(cs.which_is_unsatisfied() == Some("mult"));

View File

@ -1,16 +1,9 @@
use ff::{Field, PrimeField}; use ff::{Field, PrimeField};
use pairing::Engine; use pairing::Engine;
use crate::{ use crate::{ConstraintSystem, LinearCombination, SynthesisError};
SynthesisError,
ConstraintSystem,
LinearCombination
};
use super::boolean::{ use super::boolean::{AllocatedBit, Boolean};
Boolean,
AllocatedBit
};
use super::multieq::MultiEq; use super::multieq::MultiEq;
@ -20,13 +13,12 @@ use super::multieq::MultiEq;
pub struct UInt32 { pub struct UInt32 {
// Least significant bit first // Least significant bit first
bits: Vec<Boolean>, bits: Vec<Boolean>,
value: Option<u32> value: Option<u32>,
} }
impl UInt32 { impl UInt32 {
/// Construct a constant `UInt32` from a `u32` /// Construct a constant `UInt32` from a `u32`
pub fn constant(value: u32) -> Self pub fn constant(value: u32) -> Self {
{
let mut bits = Vec::with_capacity(32); let mut bits = Vec::with_capacity(32);
let mut tmp = value; let mut tmp = value;
@ -42,17 +34,15 @@ impl UInt32 {
UInt32 { UInt32 {
bits: bits, bits: bits,
value: Some(value) value: Some(value),
} }
} }
/// Allocate a `UInt32` in the constraint system /// Allocate a `UInt32` in the constraint system
pub fn alloc<E, CS>( pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
mut cs: CS, where
value: Option<u32> E: Engine,
) -> Result<Self, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine,
CS: ConstraintSystem<E>
{ {
let values = match value { let values = match value {
Some(mut val) => { Some(mut val) => {
@ -64,23 +54,24 @@ impl UInt32 {
} }
v v
}, }
None => vec![None; 32] None => vec![None; 32],
}; };
let bits = values.into_iter() let bits = values
.enumerate() .into_iter()
.map(|(i, v)| { .enumerate()
Ok(Boolean::from(AllocatedBit::alloc( .map(|(i, v)| {
cs.namespace(|| format!("allocated bit {}", i)), Ok(Boolean::from(AllocatedBit::alloc(
v cs.namespace(|| format!("allocated bit {}", i)),
)?)) v,
}) )?))
.collect::<Result<Vec<_>, SynthesisError>>()?; })
.collect::<Result<Vec<_>, SynthesisError>>()?;
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits: bits,
value: value value: value,
}) })
} }
@ -96,19 +87,22 @@ impl UInt32 {
value.as_mut().map(|v| *v <<= 1); value.as_mut().map(|v| *v <<= 1);
match b.get_value() { match b.get_value() {
Some(true) => { value.as_mut().map(|v| *v |= 1); }, Some(true) => {
Some(false) => {}, value.as_mut().map(|v| *v |= 1);
None => { value = None; } }
Some(false) => {}
None => {
value = None;
}
} }
} }
UInt32 { UInt32 {
value: value, value: value,
bits: bits.iter().rev().cloned().collect() bits: bits.iter().rev().cloned().collect(),
} }
} }
/// Turns this `UInt32` into its little-endian byte order representation. /// Turns this `UInt32` into its little-endian byte order representation.
pub fn into_bits(&self) -> Vec<Boolean> { pub fn into_bits(&self) -> Vec<Boolean> {
self.bits.clone() self.bits.clone()
@ -116,8 +110,7 @@ impl UInt32 {
/// Converts a little-endian byte order representation of bits into a /// Converts a little-endian byte order representation of bits into a
/// `UInt32`. /// `UInt32`.
pub fn from_bits(bits: &[Boolean]) -> Self pub fn from_bits(bits: &[Boolean]) -> Self {
{
assert_eq!(bits.len(), 32); assert_eq!(bits.len(), 32);
let new_bits = bits.to_vec(); let new_bits = bits.to_vec();
@ -131,43 +124,45 @@ impl UInt32 {
if b { if b {
value.as_mut().map(|v| *v |= 1); 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 }
}
} }
&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,
},
} }
} }
UInt32 { UInt32 {
value: value, value: value,
bits: new_bits bits: new_bits,
} }
} }
pub fn rotr(&self, by: usize) -> Self { pub fn rotr(&self, by: usize) -> Self {
let by = by % 32; let by = by % 32;
let new_bits = self.bits.iter() let new_bits = self
.skip(by) .bits
.chain(self.bits.iter()) .iter()
.take(32) .skip(by)
.cloned() .chain(self.bits.iter())
.collect(); .take(32)
.cloned()
.collect();
UInt32 { UInt32 {
bits: new_bits, bits: new_bits,
value: self.value.map(|v| v.rotate_right(by as u32)) value: self.value.map(|v| v.rotate_right(by as u32)),
} }
} }
@ -176,17 +171,18 @@ impl UInt32 {
let fill = Boolean::constant(false); let fill = Boolean::constant(false);
let new_bits = self.bits let new_bits = self
.iter() // The bits are least significant first .bits
.skip(by) // Skip the bits that will be lost during the shift .iter() // The bits are least significant first
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros .skip(by) // Skip the bits that will be lost during the shift
.take(32) // Only 32 bits needed! .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
.cloned() .take(32) // Only 32 bits needed!
.collect(); .cloned()
.collect();
UInt32 { UInt32 {
bits: new_bits, bits: new_bits,
value: self.value.map(|v| v >> by as u32) value: self.value.map(|v| v >> by as u32),
} }
} }
@ -196,121 +192,99 @@ impl UInt32 {
b: &Self, b: &Self,
c: &Self, c: &Self,
tri_fn: F, tri_fn: F,
circuit_fn: U circuit_fn: U,
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where E: Engine, where
CS: ConstraintSystem<E>, E: Engine,
F: Fn(u32, u32, u32) -> u32, CS: ConstraintSystem<E>,
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError> F: Fn(u32, u32, u32) -> u32,
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
{ {
let new_value = match (a.value, b.value, c.value) { let new_value = match (a.value, b.value, c.value) {
(Some(a), Some(b), Some(c)) => { (Some(a), Some(b), Some(c)) => Some(tri_fn(a, b, c)),
Some(tri_fn(a, b, c)) _ => None,
},
_ => None
}; };
let bits = a.bits.iter() let bits = a
.zip(b.bits.iter()) .bits
.zip(c.bits.iter()) .iter()
.enumerate() .zip(b.bits.iter())
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c)) .zip(c.bits.iter())
.collect::<Result<_, _>>()?; .enumerate()
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
.collect::<Result<_, _>>()?;
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits: bits,
value: new_value value: new_value,
}) })
} }
/// Compute the `maj` value (a and b) xor (a and c) xor (b and c) /// Compute the `maj` value (a and b) xor (a and c) xor (b and c)
/// during SHA256. /// during SHA256.
pub fn sha256_maj<E, CS>( pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
cs: CS, where
a: &Self, E: Engine,
b: &Self, CS: ConstraintSystem<E>,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ (a & c) ^ (b & c), Self::triop(
|cs, i, a, b, c| { cs,
Boolean::sha256_maj( a,
cs.namespace(|| format!("maj {}", i)), b,
a, c,
b, |a, b, c| (a & b) ^ (a & c) ^ (b & c),
c |cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c),
)
}
) )
} }
/// Compute the `ch` value `(a and b) xor ((not a) and c)` /// Compute the `ch` value `(a and b) xor ((not a) and c)`
/// during SHA256. /// during SHA256.
pub fn sha256_ch<E, CS>( pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
cs: CS, where
a: &Self, E: Engine,
b: &Self, CS: ConstraintSystem<E>,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ ((!a) & c), Self::triop(
|cs, i, a, b, c| { cs,
Boolean::sha256_ch( a,
cs.namespace(|| format!("ch {}", i)), b,
a, c,
b, |a, b, c| (a & b) ^ ((!a) & c),
c |cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c),
)
}
) )
} }
/// XOR this `UInt32` with another `UInt32` /// XOR this `UInt32` with another `UInt32`
pub fn xor<E, CS>( pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
&self, where
mut cs: CS, E: Engine,
other: &Self CS: ConstraintSystem<E>,
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
let new_value = match (self.value, other.value) { let new_value = match (self.value, other.value) {
(Some(a), Some(b)) => { (Some(a), Some(b)) => Some(a ^ b),
Some(a ^ b) _ => None,
},
_ => None
}; };
let bits = self.bits.iter() let bits = self
.zip(other.bits.iter()) .bits
.enumerate() .iter()
.map(|(i, (a, b))| { .zip(other.bits.iter())
Boolean::xor( .enumerate()
cs.namespace(|| format!("xor of bit {}", i)), .map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b))
a, .collect::<Result<_, _>>()?;
b
)
})
.collect::<Result<_, _>>()?;
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits: bits,
value: new_value value: new_value,
}) })
} }
/// Perform modular addition of several `UInt32` objects. /// Perform modular addition of several `UInt32` objects.
pub fn addmany<E, CS, M>( pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
mut cs: M, where
operands: &[Self] E: Engine,
) -> Result<Self, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root=MultiEq<E, CS>>
{ {
// Make some arbitrary bounds for ourselves to avoid overflows // Make some arbitrary bounds for ourselves to avoid overflows
// in the scalar field // in the scalar field
@ -337,7 +311,7 @@ impl UInt32 {
match op.value { match op.value {
Some(val) => { Some(val) => {
result_value.as_mut().map(|v| *v += val as u64); result_value.as_mut().map(|v| *v += val as u64);
}, }
None => { None => {
// If any of our operands have unknown value, we won't // If any of our operands have unknown value, we won't
// know the value of the result // know the value of the result
@ -381,7 +355,7 @@ impl UInt32 {
// Allocate the bit // Allocate the bit
let b = AllocatedBit::alloc( let b = AllocatedBit::alloc(
cs.namespace(|| format!("result bit {}", i)), cs.namespace(|| format!("result bit {}", i)),
result_value.map(|v| (v >> i) & 1 == 1) result_value.map(|v| (v >> i) & 1 == 1),
)?; )?;
// Add this bit to the result combination // Add this bit to the result combination
@ -402,32 +376,34 @@ impl UInt32 {
Ok(UInt32 { Ok(UInt32 {
bits: result_bits, bits: result_bits,
value: modular_value value: modular_value,
}) })
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::gadgets::boolean::{Boolean}; use super::UInt32;
use super::{UInt32}; use crate::gadgets::boolean::Boolean;
use ff::Field;
use pairing::bls12_381::{Bls12};
use crate::gadgets::test::*;
use crate::{ConstraintSystem};
use crate::gadgets::multieq::MultiEq; use crate::gadgets::multieq::MultiEq;
use crate::gadgets::test::*;
use crate::ConstraintSystem;
use ff::Field;
use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_uint32_from_bits_be() { fn test_uint32_from_bits_be() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..1000 { for _ in 0..1000 {
let mut v = (0..32).map(|_| Boolean::constant(rng.next_u32() % 2 != 0)).collect::<Vec<_>>(); let mut v = (0..32)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits_be(&v); let b = UInt32::from_bits_be(&v);
@ -435,19 +411,18 @@ mod test {
match bit { match bit {
&Boolean::Constant(bit) => { &Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
}, }
_ => unreachable!() _ => unreachable!(),
} }
} }
let expected_to_be_same = b.into_bits_be(); let expected_to_be_same = b.into_bits_be();
for x in v.iter().zip(expected_to_be_same.iter()) for x in v.iter().zip(expected_to_be_same.iter()) {
{
match x { match x {
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}, (&Boolean::Constant(true), &Boolean::Constant(true)) => {}
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}, (&Boolean::Constant(false), &Boolean::Constant(false)) => {}
_ => unreachable!() _ => unreachable!(),
} }
} }
} }
@ -456,12 +431,14 @@ mod test {
#[test] #[test]
fn test_uint32_from_bits() { fn test_uint32_from_bits() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..1000 { for _ in 0..1000 {
let mut v = (0..32).map(|_| Boolean::constant(rng.next_u32() % 2 != 0)).collect::<Vec<_>>(); let mut v = (0..32)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits(&v); let b = UInt32::from_bits(&v);
@ -469,19 +446,18 @@ mod test {
match bit { match bit {
&Boolean::Constant(bit) => { &Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
}, }
_ => unreachable!() _ => unreachable!(),
} }
} }
let expected_to_be_same = b.into_bits(); let expected_to_be_same = b.into_bits();
for x in v.iter().zip(expected_to_be_same.iter()) for x in v.iter().zip(expected_to_be_same.iter()) {
{
match x { match x {
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}, (&Boolean::Constant(true), &Boolean::Constant(true)) => {}
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}, (&Boolean::Constant(false), &Boolean::Constant(false)) => {}
_ => unreachable!() _ => unreachable!(),
} }
} }
} }
@ -490,8 +466,8 @@ mod test {
#[test] #[test]
fn test_uint32_xor() { fn test_uint32_xor() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..1000 { for _ in 0..1000 {
@ -518,10 +494,10 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
@ -535,8 +511,8 @@ mod test {
#[test] #[test]
fn test_uint32_addmany_constants() { fn test_uint32_addmany_constants() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..1000 { for _ in 0..1000 {
@ -554,7 +530,8 @@ mod test {
let r = { let r = {
let mut cs = MultiEq::new(&mut cs); let mut cs = MultiEq::new(&mut cs);
let r = UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); let r =
UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
r r
}; };
@ -577,8 +554,8 @@ mod test {
#[test] #[test]
fn test_uint32_addmany() { fn test_uint32_addmany() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..1000 { for _ in 0..1000 {
@ -611,13 +588,11 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Constant(_) => {
unreachable!()
} }
&Boolean::Constant(_) => unreachable!(),
} }
expected >>= 1; expected >>= 1;
@ -637,8 +612,8 @@ mod test {
#[test] #[test]
fn test_uint32_rotr() { fn test_uint32_rotr() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let mut num = rng.next_u32(); let mut num = rng.next_u32();
@ -656,8 +631,8 @@ mod test {
match b { match b {
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert_eq!(b, tmp & 1 == 1); assert_eq!(b, tmp & 1 == 1);
}, }
_ => unreachable!() _ => unreachable!(),
} }
tmp >>= 1; tmp >>= 1;
@ -670,8 +645,8 @@ mod test {
#[test] #[test]
fn test_uint32_shr() { fn test_uint32_shr() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..50 { for _ in 0..50 {
@ -693,8 +668,8 @@ mod test {
#[test] #[test]
fn test_uint32_sha256_maj() { fn test_uint32_sha256_maj() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..1000 { for _ in 0..1000 {
@ -720,10 +695,10 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
@ -737,8 +712,8 @@ mod test {
#[test] #[test]
fn test_uint32_sha256_ch() { fn test_uint32_sha256_ch() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..1000 { for _ in 0..1000 {
@ -764,10 +739,10 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }

View File

@ -6,36 +6,24 @@ use ff::{Field, PrimeField};
use group::{CurveAffine, CurveProjective, Wnaf}; use group::{CurveAffine, CurveProjective, Wnaf};
use pairing::Engine; use pairing::Engine;
use super::{ use super::{Parameters, VerifyingKey};
Parameters,
VerifyingKey
};
use ::{ use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use ::domain::{ use domain::{EvaluationDomain, Scalar};
EvaluationDomain,
Scalar
};
use ::multicore::{ use multicore::Worker;
Worker
};
/// Generates a random common reference string for /// Generates a random common reference string for
/// a circuit. /// a circuit.
pub fn generate_random_parameters<E, C, R>( pub fn generate_random_parameters<E, C, R>(
circuit: C, circuit: C,
rng: &mut R rng: &mut R,
) -> Result<Parameters<E>, SynthesisError> ) -> Result<Parameters<E>, SynthesisError>
where E: Engine, C: Circuit<E>, R: RngCore where
E: Engine,
C: Circuit<E>,
R: RngCore,
{ {
let g1 = E::G1::random(rng); let g1 = E::G1::random(rng);
let g2 = E::G2::random(rng); let g2 = E::G2::random(rng);
@ -45,16 +33,7 @@ pub fn generate_random_parameters<E, C, R>(
let delta = E::Fr::random(rng); let delta = E::Fr::random(rng);
let tau = E::Fr::random(rng); let tau = E::Fr::random(rng);
generate_parameters::<E, C>( generate_parameters::<E, C>(circuit, g1, g2, alpha, beta, gamma, delta, tau)
circuit,
g1,
g2,
alpha,
beta,
gamma,
delta,
tau
)
} }
/// This is our assembly structure that we'll use to synthesize the /// This is our assembly structure that we'll use to synthesize the
@ -68,18 +47,17 @@ struct KeypairAssembly<E: Engine> {
ct_inputs: Vec<Vec<(E::Fr, usize)>>, ct_inputs: Vec<Vec<(E::Fr, usize)>>,
at_aux: Vec<Vec<(E::Fr, usize)>>, at_aux: Vec<Vec<(E::Fr, usize)>>,
bt_aux: Vec<Vec<(E::Fr, usize)>>, bt_aux: Vec<Vec<(E::Fr, usize)>>,
ct_aux: Vec<Vec<(E::Fr, usize)>> ct_aux: Vec<Vec<(E::Fr, usize)>>,
} }
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> { impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
&mut self, where
_: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
_: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
// There is no assignment, so we don't even invoke the // There is no assignment, so we don't even invoke the
// function for obtaining one. // function for obtaining one.
@ -94,12 +72,11 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
Ok(Variable(Index::Aux(index))) Ok(Variable(Index::Aux(index)))
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
&mut self, where
_: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
_: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
// There is no assignment, so we don't even invoke the // There is no assignment, so we don't even invoke the
// function for obtaining one. // function for obtaining one.
@ -114,48 +91,59 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
Ok(Variable(Index::Input(index))) Ok(Variable(Index::Input(index)))
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
&mut self, where
_: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
fn eval<E: Engine>( fn eval<E: Engine>(
l: LinearCombination<E>, l: LinearCombination<E>,
inputs: &mut [Vec<(E::Fr, usize)>], inputs: &mut [Vec<(E::Fr, usize)>],
aux: &mut [Vec<(E::Fr, usize)>], aux: &mut [Vec<(E::Fr, usize)>],
this_constraint: usize this_constraint: usize,
) ) {
{
for (index, coeff) in l.0 { for (index, coeff) in l.0 {
match index { match index {
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)), Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)) Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)),
} }
} }
} }
eval(a(LinearCombination::zero()), &mut self.at_inputs, &mut self.at_aux, self.num_constraints); eval(
eval(b(LinearCombination::zero()), &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints); a(LinearCombination::zero()),
eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints); &mut self.at_inputs,
&mut self.at_aux,
self.num_constraints,
);
eval(
b(LinearCombination::zero()),
&mut self.bt_inputs,
&mut self.bt_aux,
self.num_constraints,
);
eval(
c(LinearCombination::zero()),
&mut self.ct_inputs,
&mut self.ct_aux,
self.num_constraints,
);
self.num_constraints += 1; self.num_constraints += 1;
} }
fn push_namespace<NR, N>(&mut self, _: N) fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
@ -173,9 +161,11 @@ pub fn generate_parameters<E, C>(
beta: E::Fr, beta: E::Fr,
gamma: E::Fr, gamma: E::Fr,
delta: E::Fr, delta: E::Fr,
tau: E::Fr tau: E::Fr,
) -> Result<Parameters<E>, SynthesisError> ) -> Result<Parameters<E>, SynthesisError>
where E: Engine, C: Circuit<E> where
E: Engine,
C: Circuit<E>,
{ {
let mut assembly = KeypairAssembly { let mut assembly = KeypairAssembly {
num_inputs: 0, num_inputs: 0,
@ -186,7 +176,7 @@ pub fn generate_parameters<E, C>(
ct_inputs: vec![], ct_inputs: vec![],
at_aux: vec![], at_aux: vec![],
bt_aux: vec![], bt_aux: vec![],
ct_aux: vec![] ct_aux: vec![],
}; };
// Allocate the "one" input variable // Allocate the "one" input variable
@ -198,11 +188,7 @@ pub fn generate_parameters<E, C>(
// Input constraints to ensure full density of IC query // Input constraints to ensure full density of IC query
// x * 0 = 0 // x * 0 = 0
for i in 0..assembly.num_inputs { for i in 0..assembly.num_inputs {
assembly.enforce(|| "", assembly.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
} }
// Create bases for blind evaluation of polynomials at tau // Create bases for blind evaluation of polynomials at tau
@ -240,10 +226,9 @@ pub fn generate_parameters<E, C>(
{ {
let powers_of_tau = powers_of_tau.as_mut(); let powers_of_tau = powers_of_tau.as_mut();
worker.scope(powers_of_tau.len(), |scope, chunk| { worker.scope(powers_of_tau.len(), |scope, chunk| {
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
{
scope.spawn(move || { scope.spawn(move || {
let mut current_tau_power = tau.pow(&[(i*chunk) as u64]); let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
for p in powers_of_tau { for p in powers_of_tau {
p.0 = current_tau_power; p.0 = current_tau_power;
@ -260,14 +245,15 @@ pub fn generate_parameters<E, C>(
// Compute the H query with multiple threads // Compute the H query with multiple threads
worker.scope(h.len(), |scope, chunk| { worker.scope(h.len(), |scope, chunk| {
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk)) for (h, p) in h
.chunks_mut(chunk)
.zip(powers_of_tau.as_ref().chunks(chunk))
{ {
let mut g1_wnaf = g1_wnaf.shared(); let mut g1_wnaf = g1_wnaf.shared();
scope.spawn(move || { scope.spawn(move || {
// Set values of the H query to g1^{(tau^i * t(tau)) / delta} // Set values of the H query to g1^{(tau^i * t(tau)) / delta}
for (h, p) in h.iter_mut().zip(p.iter()) for (h, p) in h.iter_mut().zip(p.iter()) {
{
// Compute final exponent // Compute final exponent
let mut exp = p.0; let mut exp = p.0;
exp.mul_assign(&coeff); exp.mul_assign(&coeff);
@ -320,9 +306,8 @@ pub fn generate_parameters<E, C>(
beta: &E::Fr, beta: &E::Fr,
// Worker // Worker
worker: &Worker worker: &Worker,
) ) {
{
// Sanity check // Sanity check
assert_eq!(a.len(), at.len()); assert_eq!(a.len(), at.len());
assert_eq!(a.len(), bt.len()); assert_eq!(a.len(), bt.len());
@ -333,31 +318,32 @@ pub fn generate_parameters<E, C>(
// Evaluate polynomials in multiple threads // Evaluate polynomials in multiple threads
worker.scope(a.len(), |scope, chunk| { worker.scope(a.len(), |scope, chunk| {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk) for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
.zip(b_g1.chunks_mut(chunk)) .chunks_mut(chunk)
.zip(b_g2.chunks_mut(chunk)) .zip(b_g1.chunks_mut(chunk))
.zip(ext.chunks_mut(chunk)) .zip(b_g2.chunks_mut(chunk))
.zip(at.chunks(chunk)) .zip(ext.chunks_mut(chunk))
.zip(bt.chunks(chunk)) .zip(at.chunks(chunk))
.zip(ct.chunks(chunk)) .zip(bt.chunks(chunk))
.zip(ct.chunks(chunk))
{ {
let mut g1_wnaf = g1_wnaf.shared(); let mut g1_wnaf = g1_wnaf.shared();
let mut g2_wnaf = g2_wnaf.shared(); let mut g2_wnaf = g2_wnaf.shared();
scope.spawn(move || { scope.spawn(move || {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut() for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
.zip(b_g1.iter_mut()) .iter_mut()
.zip(b_g2.iter_mut()) .zip(b_g1.iter_mut())
.zip(ext.iter_mut()) .zip(b_g2.iter_mut())
.zip(at.iter()) .zip(ext.iter_mut())
.zip(bt.iter()) .zip(at.iter())
.zip(ct.iter()) .zip(bt.iter())
.zip(ct.iter())
{ {
fn eval_at_tau<E: Engine>( fn eval_at_tau<E: Engine>(
powers_of_tau: &[Scalar<E>], powers_of_tau: &[Scalar<E>],
p: &[(E::Fr, usize)] p: &[(E::Fr, usize)],
) -> E::Fr ) -> E::Fr {
{
let mut acc = E::Fr::zero(); let mut acc = E::Fr::zero();
for &(ref coeff, index) in p { for &(ref coeff, index) in p {
@ -422,7 +408,7 @@ pub fn generate_parameters<E, C>(
&gamma_inverse, &gamma_inverse,
&alpha, &alpha,
&beta, &beta,
&worker &worker,
); );
// Evaluate for auxiliary variables. // Evaluate for auxiliary variables.
@ -440,7 +426,7 @@ pub fn generate_parameters<E, C>(
&delta_inverse, &delta_inverse,
&alpha, &alpha,
&beta, &beta,
&worker &worker,
); );
// Don't allow any elements be unconstrained, so that // Don't allow any elements be unconstrained, so that
@ -461,7 +447,7 @@ pub fn generate_parameters<E, C>(
gamma_g2: g2.mul(gamma).into_affine(), gamma_g2: g2.mul(gamma).into_affine(),
delta_g1: g1.mul(delta).into_affine(), delta_g1: g1.mul(delta).into_affine(),
delta_g2: g2.mul(delta).into_affine(), delta_g2: g2.mul(delta).into_affine(),
ic: ic.into_iter().map(|e| e.into_affine()).collect() ic: ic.into_iter().map(|e| e.into_affine()).collect(),
}; };
Ok(Parameters { Ok(Parameters {
@ -470,8 +456,23 @@ pub fn generate_parameters<E, C>(
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
// Filter points at infinity away from A/B queries // Filter points at infinity away from A/B queries
a: Arc::new(a.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), a: Arc::new(
b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), a.into_iter()
b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()) .filter(|e| !e.is_zero())
.map(|e| e.into_affine())
.collect(),
),
b_g1: Arc::new(
b_g1.into_iter()
.filter(|e| !e.is_zero())
.map(|e| e.into_affine())
.collect(),
),
b_g2: Arc::new(
b_g2.into_iter()
.filter(|e| !e.is_zero())
.map(|e| e.into_affine())
.collect(),
),
}) })
} }

View File

@ -1,17 +1,12 @@
use group::{CurveAffine, EncodedPoint}; use group::{CurveAffine, EncodedPoint};
use pairing::{ use pairing::{Engine, PairingCurveAffine};
Engine,
PairingCurveAffine,
};
use ::{ use SynthesisError;
SynthesisError
};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use multiexp::SourceBuilder; use multiexp::SourceBuilder;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::sync::Arc; use std::sync::Arc;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -28,23 +23,17 @@ pub use self::verifier::*;
pub struct Proof<E: Engine> { pub struct Proof<E: Engine> {
pub a: E::G1Affine, pub a: E::G1Affine,
pub b: E::G2Affine, pub b: E::G2Affine,
pub c: E::G1Affine pub c: E::G1Affine,
} }
impl<E: Engine> PartialEq for Proof<E> { impl<E: Engine> PartialEq for Proof<E> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.a == other.a && self.a == other.a && self.b == other.b && self.c == other.c
self.b == other.b &&
self.c == other.c
} }
} }
impl<E: Engine> Proof<E> { impl<E: Engine> Proof<E> {
pub fn write<W: Write>( pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
&self,
mut writer: W
) -> io::Result<()>
{
writer.write_all(self.a.into_compressed().as_ref())?; writer.write_all(self.a.into_compressed().as_ref())?;
writer.write_all(self.b.into_compressed().as_ref())?; writer.write_all(self.b.into_compressed().as_ref())?;
writer.write_all(self.c.into_compressed().as_ref())?; writer.write_all(self.c.into_compressed().as_ref())?;
@ -52,48 +41,56 @@ impl<E: Engine> Proof<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>( pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty(); let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty(); let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty();
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let a = g1_repr let a = g1_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
})?; }
})?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let b = g2_repr let b = g2_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
})?; }
})?;
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let c = g1_repr let c = g1_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
})?; }
})?;
Ok(Proof { Ok(Proof { a: a, b: b, c: c })
a: a,
b: b,
c: c
})
} }
} }
@ -122,27 +119,23 @@ pub struct VerifyingKey<E: Engine> {
// for all public inputs. Because all public inputs have a dummy constraint, // for all public inputs. Because all public inputs have a dummy constraint,
// this is the same size as the number of inputs, and never contains points // this is the same size as the number of inputs, and never contains points
// at infinity. // at infinity.
pub ic: Vec<E::G1Affine> pub ic: Vec<E::G1Affine>,
} }
impl<E: Engine> PartialEq for VerifyingKey<E> { impl<E: Engine> PartialEq for VerifyingKey<E> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.alpha_g1 == other.alpha_g1 && self.alpha_g1 == other.alpha_g1
self.beta_g1 == other.beta_g1 && && self.beta_g1 == other.beta_g1
self.beta_g2 == other.beta_g2 && && self.beta_g2 == other.beta_g2
self.gamma_g2 == other.gamma_g2 && && self.gamma_g2 == other.gamma_g2
self.delta_g1 == other.delta_g1 && && self.delta_g1 == other.delta_g1
self.delta_g2 == other.delta_g2 && && self.delta_g2 == other.delta_g2
self.ic == other.ic && self.ic == other.ic
} }
} }
impl<E: Engine> VerifyingKey<E> { impl<E: Engine> VerifyingKey<E> {
pub fn write<W: Write>( pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
&self,
mut writer: W
) -> io::Result<()>
{
writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?; writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?;
writer.write_all(self.beta_g1.into_uncompressed().as_ref())?; writer.write_all(self.beta_g1.into_uncompressed().as_ref())?;
writer.write_all(self.beta_g2.into_uncompressed().as_ref())?; writer.write_all(self.beta_g2.into_uncompressed().as_ref())?;
@ -157,30 +150,39 @@ impl<E: Engine> VerifyingKey<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>( pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty(); let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty(); let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let alpha_g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let beta_g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let beta_g2 = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let gamma_g2 = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let delta_g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let delta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let delta_g2 = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let ic_len = reader.read_u32::<BigEndian>()? as usize; let ic_len = reader.read_u32::<BigEndian>()? as usize;
@ -189,13 +191,18 @@ impl<E: Engine> VerifyingKey<E> {
for _ in 0..ic_len { for _ in 0..ic_len {
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let g1 = g1_repr let g1 = g1_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
} else { Err(io::Error::new(
Ok(e) io::ErrorKind::InvalidData,
})?; "point at infinity",
))
} else {
Ok(e)
}
})?;
ic.push(g1); ic.push(g1);
} }
@ -207,7 +214,7 @@ impl<E: Engine> VerifyingKey<E> {
gamma_g2: gamma_g2, gamma_g2: gamma_g2,
delta_g1: delta_g1, delta_g1: delta_g1,
delta_g2: delta_g2, delta_g2: delta_g2,
ic: ic ic: ic,
}) })
} }
} }
@ -216,7 +223,7 @@ impl<E: Engine> VerifyingKey<E> {
pub struct Parameters<E: Engine> { pub struct Parameters<E: Engine> {
pub vk: VerifyingKey<E>, pub vk: VerifyingKey<E>,
// Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and // Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and
// m-2 inclusive. Never contains points at infinity. // m-2 inclusive. Never contains points at infinity.
pub h: Arc<Vec<E::G1Affine>>, pub h: Arc<Vec<E::G1Affine>>,
@ -234,26 +241,22 @@ pub struct Parameters<E: Engine> {
// G1 and G2 for C/B queries, respectively. Never contains points at // G1 and G2 for C/B queries, respectively. Never contains points at
// infinity for the same reason as the "A" polynomials. // infinity for the same reason as the "A" polynomials.
pub b_g1: Arc<Vec<E::G1Affine>>, pub b_g1: Arc<Vec<E::G1Affine>>,
pub b_g2: Arc<Vec<E::G2Affine>> pub b_g2: Arc<Vec<E::G2Affine>>,
} }
impl<E: Engine> PartialEq for Parameters<E> { impl<E: Engine> PartialEq for Parameters<E> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.vk == other.vk && self.vk == other.vk
self.h == other.h && && self.h == other.h
self.l == other.l && && self.l == other.l
self.a == other.a && && self.a == other.a
self.b_g1 == other.b_g1 && && self.b_g1 == other.b_g1
self.b_g2 == other.b_g2 && self.b_g2 == other.b_g2
} }
} }
impl<E: Engine> Parameters<E> { impl<E: Engine> Parameters<E> {
pub fn write<W: Write>( pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
&self,
mut writer: W
) -> io::Result<()>
{
self.vk.write(&mut writer)?; self.vk.write(&mut writer)?;
writer.write_u32::<BigEndian>(self.h.len() as u32)?; writer.write_u32::<BigEndian>(self.h.len() as u32)?;
@ -284,27 +287,26 @@ impl<E: Engine> Parameters<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>( pub fn read<R: Read>(mut reader: R, checked: bool) -> io::Result<Self> {
mut reader: R,
checked: bool
) -> io::Result<Self>
{
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> { let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty(); let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(repr.as_mut())?; reader.read_exact(repr.as_mut())?;
if checked { if checked {
repr repr.into_affine()
.into_affine()
} else { } else {
repr repr.into_affine_unchecked()
.into_affine_unchecked()
} }
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
} else { Err(io::Error::new(
Ok(e) io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
}
}) })
}; };
@ -313,17 +315,20 @@ impl<E: Engine> Parameters<E> {
reader.read_exact(repr.as_mut())?; reader.read_exact(repr.as_mut())?;
if checked { if checked {
repr repr.into_affine()
.into_affine()
} else { } else {
repr repr.into_affine_unchecked()
.into_affine_unchecked()
} }
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
} else { Err(io::Error::new(
Ok(e) io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
}
}) })
}; };
@ -376,7 +381,7 @@ impl<E: Engine> Parameters<E> {
l: Arc::new(l), l: Arc::new(l),
a: Arc::new(a), a: Arc::new(a),
b_g1: Arc::new(b_g1), b_g1: Arc::new(b_g1),
b_g2: Arc::new(b_g2) b_g2: Arc::new(b_g2),
}) })
} }
} }
@ -389,39 +394,30 @@ pub struct PreparedVerifyingKey<E: Engine> {
/// -delta in G2 /// -delta in G2
neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared, neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
/// Copy of IC from `VerifiyingKey`. /// Copy of IC from `VerifiyingKey`.
ic: Vec<E::G1Affine> ic: Vec<E::G1Affine>,
} }
pub trait ParameterSource<E: Engine> { pub trait ParameterSource<E: Engine> {
type G1Builder: SourceBuilder<E::G1Affine>; type G1Builder: SourceBuilder<E::G1Affine>;
type G2Builder: SourceBuilder<E::G2Affine>; type G2Builder: SourceBuilder<E::G2Affine>;
fn get_vk( fn get_vk(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, SynthesisError>;
&mut self, fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, SynthesisError>;
num_ic: usize fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, SynthesisError>;
) -> Result<VerifyingKey<E>, SynthesisError>;
fn get_h(
&mut self,
num_h: usize
) -> Result<Self::G1Builder, SynthesisError>;
fn get_l(
&mut self,
num_l: usize
) -> Result<Self::G1Builder, SynthesisError>;
fn get_a( fn get_a(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
num_aux: usize num_aux: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g1( fn get_b_g1(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
num_aux: usize num_aux: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g2( fn get_b_g2(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
num_aux: usize num_aux: usize,
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>; ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
} }
@ -429,54 +425,39 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
type G1Builder = (Arc<Vec<E::G1Affine>>, usize); type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
type G2Builder = (Arc<Vec<E::G2Affine>>, usize); type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
fn get_vk( fn get_vk(&mut self, _: usize) -> Result<VerifyingKey<E>, SynthesisError> {
&mut self,
_: usize
) -> Result<VerifyingKey<E>, SynthesisError>
{
Ok(self.vk.clone()) Ok(self.vk.clone())
} }
fn get_h( fn get_h(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
&mut self,
_: usize
) -> Result<Self::G1Builder, SynthesisError>
{
Ok((self.h.clone(), 0)) Ok((self.h.clone(), 0))
} }
fn get_l( fn get_l(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
&mut self,
_: usize
) -> Result<Self::G1Builder, SynthesisError>
{
Ok((self.l.clone(), 0)) Ok((self.l.clone(), 0))
} }
fn get_a( fn get_a(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
_: usize _: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
{
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs))) Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
} }
fn get_b_g1( fn get_b_g1(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
_: usize _: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
{
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs))) Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
} }
fn get_b_g2( fn get_b_g2(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
_: usize _: usize,
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> {
{
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs))) Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
} }
} }
@ -484,41 +465,38 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
#[cfg(test)] #[cfg(test)]
mod test_with_bls12_381 { mod test_with_bls12_381 {
use super::*; use super::*;
use {Circuit, SynthesisError, ConstraintSystem}; use {Circuit, ConstraintSystem, SynthesisError};
use ff::Field; use ff::Field;
use rand::{thread_rng};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand::thread_rng;
#[test] #[test]
fn serialization() { fn serialization() {
struct MySillyCircuit<E: Engine> { struct MySillyCircuit<E: Engine> {
a: Option<E::Fr>, a: Option<E::Fr>,
b: Option<E::Fr> b: Option<E::Fr>,
} }
impl<E: Engine> Circuit<E> for MySillyCircuit<E> { impl<E: Engine> Circuit<E> for MySillyCircuit<E> {
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(
self, self,
cs: &mut CS cs: &mut CS,
) -> Result<(), SynthesisError> ) -> Result<(), SynthesisError> {
{
let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
let c = cs.alloc_input(|| "c", || { let c = cs.alloc_input(
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; || "c",
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; || {
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
a.mul_assign(&b); a.mul_assign(&b);
Ok(a) Ok(a)
})?; },
)?;
cs.enforce( cs.enforce(|| "a*b=c", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|| "a*b=c",
|lc| lc + a,
|lc| lc + b,
|lc| lc + c
);
Ok(()) Ok(())
} }
@ -526,10 +504,9 @@ mod test_with_bls12_381 {
let rng = &mut thread_rng(); let rng = &mut thread_rng();
let params = generate_random_parameters::<Bls12, _, _>( let params =
MySillyCircuit { a: None, b: None }, generate_random_parameters::<Bls12, _, _>(MySillyCircuit { a: None, b: None }, rng)
rng .unwrap();
).unwrap();
{ {
let mut v = vec![]; let mut v = vec![];
@ -555,11 +532,12 @@ mod test_with_bls12_381 {
let proof = create_random_proof( let proof = create_random_proof(
MySillyCircuit { MySillyCircuit {
a: Some(a), a: Some(a),
b: Some(b) b: Some(b),
}, },
&params, &params,
rng rng,
).unwrap(); )
.unwrap();
let mut v = vec![]; let mut v = vec![];
proof.write(&mut v).unwrap(); proof.write(&mut v).unwrap();

View File

@ -8,43 +8,23 @@ use ff::{Field, PrimeField};
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use pairing::Engine; use pairing::Engine;
use super::{ use super::{ParameterSource, Proof};
ParameterSource,
Proof
};
use ::{ use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use ::domain::{ use domain::{EvaluationDomain, Scalar};
EvaluationDomain,
Scalar
};
use ::multiexp::{ use multiexp::{multiexp, DensityTracker, FullDensity};
DensityTracker,
FullDensity,
multiexp
};
use ::multicore::{ use multicore::Worker;
Worker
};
fn eval<E: Engine>( fn eval<E: Engine>(
lc: &LinearCombination<E>, lc: &LinearCombination<E>,
mut input_density: Option<&mut DensityTracker>, mut input_density: Option<&mut DensityTracker>,
mut aux_density: Option<&mut DensityTracker>, mut aux_density: Option<&mut DensityTracker>,
input_assignment: &[E::Fr], input_assignment: &[E::Fr],
aux_assignment: &[E::Fr] aux_assignment: &[E::Fr],
) -> E::Fr ) -> E::Fr {
{
let mut acc = E::Fr::zero(); let mut acc = E::Fr::zero();
for &(index, coeff) in lc.0.iter() { for &(index, coeff) in lc.0.iter() {
@ -56,7 +36,7 @@ fn eval<E: Engine>(
if let Some(ref mut v) = input_density { if let Some(ref mut v) = input_density {
v.inc(i); v.inc(i);
} }
}, }
Variable(Index::Aux(i)) => { Variable(Index::Aux(i)) => {
tmp = aux_assignment[i]; tmp = aux_assignment[i];
if let Some(ref mut v) = aux_density { if let Some(ref mut v) = aux_density {
@ -66,10 +46,10 @@ fn eval<E: Engine>(
} }
if coeff == E::Fr::one() { if coeff == E::Fr::one() {
acc.add_assign(&tmp); acc.add_assign(&tmp);
} else { } else {
tmp.mul_assign(&coeff); tmp.mul_assign(&coeff);
acc.add_assign(&tmp); acc.add_assign(&tmp);
} }
} }
@ -89,18 +69,17 @@ struct ProvingAssignment<E: Engine> {
// Assignments of variables // Assignments of variables
input_assignment: Vec<E::Fr>, input_assignment: Vec<E::Fr>,
aux_assignment: Vec<E::Fr> aux_assignment: Vec<E::Fr>,
} }
impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> { impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
_: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.aux_assignment.push(f()?); self.aux_assignment.push(f()?);
self.a_aux_density.add_element(); self.a_aux_density.add_element();
@ -109,12 +88,11 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1))) Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
_: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.input_assignment.push(f()?); self.input_assignment.push(f()?);
self.b_input_density.add_element(); self.b_input_density.add_element();
@ -122,17 +100,13 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
Ok(Variable(Index::Input(self.input_assignment.len() - 1))) Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
&mut self, where
_: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
let a = a(LinearCombination::zero()); let a = a(LinearCombination::zero());
let b = b(LinearCombination::zero()); let b = b(LinearCombination::zero());
@ -146,14 +120,14 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
None, None,
Some(&mut self.a_aux_density), Some(&mut self.a_aux_density),
&self.input_assignment, &self.input_assignment,
&self.aux_assignment &self.aux_assignment,
))); )));
self.b.push(Scalar(eval( self.b.push(Scalar(eval(
&b, &b,
Some(&mut self.b_input_density), Some(&mut self.b_input_density),
Some(&mut self.b_aux_density), Some(&mut self.b_aux_density),
&self.input_assignment, &self.input_assignment,
&self.aux_assignment &self.aux_assignment,
))); )));
self.c.push(Scalar(eval( self.c.push(Scalar(eval(
&c, &c,
@ -164,18 +138,19 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
None, None,
None, None,
&self.input_assignment, &self.input_assignment,
&self.aux_assignment &self.aux_assignment,
))); )));
} }
fn push_namespace<NR, N>(&mut self, _: N) fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
@ -187,9 +162,12 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>( pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
circuit: C, circuit: C,
params: P, params: P,
rng: &mut R rng: &mut R,
) -> Result<Proof<E>, SynthesisError> ) -> Result<Proof<E>, SynthesisError>
where E: Engine, C: Circuit<E>, R: RngCore where
E: Engine,
C: Circuit<E>,
R: RngCore,
{ {
let r = E::Fr::random(rng); let r = E::Fr::random(rng);
let s = E::Fr::random(rng); let s = E::Fr::random(rng);
@ -201,9 +179,11 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
circuit: C, circuit: C,
mut params: P, mut params: P,
r: E::Fr, r: E::Fr,
s: E::Fr s: E::Fr,
) -> Result<Proof<E>, SynthesisError> ) -> Result<Proof<E>, SynthesisError>
where E: Engine, C: Circuit<E> where
E: Engine,
C: Circuit<E>,
{ {
let mut prover = ProvingAssignment { let mut prover = ProvingAssignment {
a_aux_density: DensityTracker::new(), a_aux_density: DensityTracker::new(),
@ -213,7 +193,7 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
b: vec![], b: vec![],
c: vec![], c: vec![],
input_assignment: vec![], input_assignment: vec![],
aux_assignment: vec![] aux_assignment: vec![],
}; };
prover.alloc_input(|| "", || Ok(E::Fr::one()))?; prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
@ -221,11 +201,7 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
circuit.synthesize(&mut prover)?; circuit.synthesize(&mut prover)?;
for i in 0..prover.input_assignment.len() { for i in 0..prover.input_assignment.len() {
prover.enforce(|| "", prover.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
} }
let worker = Worker::new(); let worker = Worker::new();
@ -259,31 +235,76 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
}; };
// TODO: parallelize if it's even helpful // TODO: parallelize if it's even helpful
let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>()); let input_assignment = Arc::new(
let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>()); prover
.input_assignment
.into_iter()
.map(|s| s.into_repr())
.collect::<Vec<_>>(),
);
let aux_assignment = Arc::new(
prover
.aux_assignment
.into_iter()
.map(|s| s.into_repr())
.collect::<Vec<_>>(),
);
let l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone()); let l = multiexp(
&worker,
params.get_l(aux_assignment.len())?,
FullDensity,
aux_assignment.clone(),
);
let a_aux_density_total = prover.a_aux_density.get_total_density(); let a_aux_density_total = prover.a_aux_density.get_total_density();
let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?; let (a_inputs_source, a_aux_source) =
params.get_a(input_assignment.len(), a_aux_density_total)?;
let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone()); let a_inputs = multiexp(
let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone()); &worker,
a_inputs_source,
FullDensity,
input_assignment.clone(),
);
let a_aux = multiexp(
&worker,
a_aux_source,
Arc::new(prover.a_aux_density),
aux_assignment.clone(),
);
let b_input_density = Arc::new(prover.b_input_density); let b_input_density = Arc::new(prover.b_input_density);
let b_input_density_total = b_input_density.get_total_density(); let b_input_density_total = b_input_density.get_total_density();
let b_aux_density = Arc::new(prover.b_aux_density); let b_aux_density = Arc::new(prover.b_aux_density);
let b_aux_density_total = b_aux_density.get_total_density(); let b_aux_density_total = b_aux_density.get_total_density();
let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?; let (b_g1_inputs_source, b_g1_aux_source) =
params.get_b_g1(b_input_density_total, b_aux_density_total)?;
let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone()); let b_g1_inputs = multiexp(
let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone()); &worker,
b_g1_inputs_source,
b_input_density.clone(),
input_assignment.clone(),
);
let b_g1_aux = multiexp(
&worker,
b_g1_aux_source,
b_aux_density.clone(),
aux_assignment.clone(),
);
let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?; let (b_g2_inputs_source, b_g2_aux_source) =
params.get_b_g2(b_input_density_total, b_aux_density_total)?;
let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment);
let b_g2_inputs = multiexp(
&worker,
b_g2_inputs_source,
b_input_density,
input_assignment,
);
let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment);
if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() { if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() {
@ -325,6 +346,6 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
Ok(Proof { Ok(Proof {
a: g_a.into_affine(), a: g_a.into_affine(),
b: g_b.into_affine(), b: g_b.into_affine(),
c: g_c.into_affine() c: g_c.into_affine(),
}) })
} }

View File

@ -1,12 +1,13 @@
use ff::{ use ff::{
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine,
PrimeFieldRepr, ScalarEngine, SqrtField}; SqrtField,
};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use pairing::{Engine, PairingCurveAffine}; use pairing::{Engine, PairingCurveAffine};
use rand_core::RngCore;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
use rand_core::RngCore;
use std::num::Wrapping; use std::num::Wrapping;
const MODULUS_R: Wrapping<u32> = Wrapping(64513); const MODULUS_R: Wrapping<u32> = Wrapping(64513);
@ -80,9 +81,13 @@ impl SqrtField for Fr {
fn legendre(&self) -> LegendreSymbol { fn legendre(&self) -> LegendreSymbol {
// s = self^((r - 1) // 2) // s = self^((r - 1) // 2)
let s = self.pow([32256]); let s = self.pow([32256]);
if s == <Fr as Field>::zero() { LegendreSymbol::Zero } if s == <Fr as Field>::zero() {
else if s == <Fr as Field>::one() { LegendreSymbol::QuadraticResidue } LegendreSymbol::Zero
else { LegendreSymbol::QuadraticNonResidue } } else if s == <Fr as Field>::one() {
LegendreSymbol::QuadraticResidue
} else {
LegendreSymbol::QuadraticNonResidue
}
} }
fn sqrt(&self) -> Option<Self> { fn sqrt(&self) -> Option<Self> {
@ -100,7 +105,7 @@ impl SqrtField for Fr {
let mut m = Fr::S; let mut m = Fr::S;
while t != <Fr as Field>::one() { while t != <Fr as Field>::one() {
let mut i = 1; let mut i = 1;
{ {
let mut t2i = t; let mut t2i = t;
t2i.square(); t2i.square();
@ -258,15 +263,18 @@ impl Engine for DummyEngine {
type G2Affine = Fr; type G2Affine = Fr;
type Fq = Fr; type Fq = Fr;
type Fqe = Fr; type Fqe = Fr;
// TODO: This should be F_645131 or something. Doesn't matter for now. // TODO: This should be F_645131 or something. Doesn't matter for now.
type Fqk = Fr; type Fqk = Fr;
fn miller_loop<'a, I>(i: I) -> Self::Fqk fn miller_loop<'a, I>(i: I) -> Self::Fqk
where I: IntoIterator<Item=&'a ( where
&'a <Self::G1Affine as PairingCurveAffine>::Prepared, I: IntoIterator<
&'a <Self::G2Affine as PairingCurveAffine>::Prepared Item = &'a (
)> &'a <Self::G1Affine as PairingCurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared,
),
>,
{ {
let mut acc = <Fr as Field>::zero(); let mut acc = <Fr as Field>::zero();
@ -280,8 +288,7 @@ impl Engine for DummyEngine {
} }
/// Perform final exponentiation of the result of a miller loop. /// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk> fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk> {
{
Some(*this) Some(*this)
} }
} }
@ -308,9 +315,7 @@ impl CurveProjective for Fr {
<Fr as Field>::is_zero(self) <Fr as Field>::is_zero(self)
} }
fn batch_normalization(_: &mut [Self]) { fn batch_normalization(_: &mut [Self]) {}
}
fn is_normalized(&self) -> bool { fn is_normalized(&self) -> bool {
true true
@ -332,8 +337,7 @@ impl CurveProjective for Fr {
<Fr as Field>::negate(self); <Fr as Field>::negate(self);
} }
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) {
{
let tmp = Fr::from_repr(other.into()).unwrap(); let tmp = Fr::from_repr(other.into()).unwrap();
<Fr as Field>::mul_assign(self, &tmp); <Fr as Field>::mul_assign(self, &tmp);
@ -415,8 +419,7 @@ impl CurveAffine for Fr {
<Fr as Field>::negate(self); <Fr as Field>::negate(self);
} }
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective {
{
let mut res = *self; let mut res = *self;
let tmp = Fr::from_repr(other.into()).unwrap(); let tmp = Fr::from_repr(other.into()).unwrap();

View File

@ -6,86 +6,82 @@ use self::dummy_engine::*;
use std::marker::PhantomData; use std::marker::PhantomData;
use ::{ use {Circuit, ConstraintSystem, SynthesisError};
Circuit,
ConstraintSystem,
SynthesisError
};
use super::{ use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof};
generate_parameters,
prepare_verifying_key,
create_proof,
verify_proof
};
struct XORDemo<E: Engine> { struct XORDemo<E: Engine> {
a: Option<bool>, a: Option<bool>,
b: Option<bool>, b: Option<bool>,
_marker: PhantomData<E> _marker: PhantomData<E>,
} }
impl<E: Engine> Circuit<E> for XORDemo<E> { impl<E: Engine> Circuit<E> for XORDemo<E> {
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
self, let a_var = cs.alloc(
cs: &mut CS || "a",
) -> Result<(), SynthesisError> || {
{ if self.a.is_some() {
let a_var = cs.alloc(|| "a", || { if self.a.unwrap() {
if self.a.is_some() { Ok(E::Fr::one())
if self.a.unwrap() { } else {
Ok(E::Fr::one()) Ok(E::Fr::zero())
}
} else { } else {
Ok(E::Fr::zero()) Err(SynthesisError::AssignmentMissing)
} }
} else { },
Err(SynthesisError::AssignmentMissing) )?;
}
})?;
cs.enforce( cs.enforce(
|| "a_boolean_constraint", || "a_boolean_constraint",
|lc| lc + CS::one() - a_var, |lc| lc + CS::one() - a_var,
|lc| lc + a_var, |lc| lc + a_var,
|lc| lc |lc| lc,
); );
let b_var = cs.alloc(|| "b", || { let b_var = cs.alloc(
if self.b.is_some() { || "b",
if self.b.unwrap() { || {
Ok(E::Fr::one()) if self.b.is_some() {
if self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else { } else {
Ok(E::Fr::zero()) Err(SynthesisError::AssignmentMissing)
} }
} else { },
Err(SynthesisError::AssignmentMissing) )?;
}
})?;
cs.enforce( cs.enforce(
|| "b_boolean_constraint", || "b_boolean_constraint",
|lc| lc + CS::one() - b_var, |lc| lc + CS::one() - b_var,
|lc| lc + b_var, |lc| lc + b_var,
|lc| lc |lc| lc,
); );
let c_var = cs.alloc_input(|| "c", || { let c_var = cs.alloc_input(
if self.a.is_some() && self.b.is_some() { || "c",
if self.a.unwrap() ^ self.b.unwrap() { || {
Ok(E::Fr::one()) if self.a.is_some() && self.b.is_some() {
if self.a.unwrap() ^ self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else { } else {
Ok(E::Fr::zero()) Err(SynthesisError::AssignmentMissing)
} }
} else { },
Err(SynthesisError::AssignmentMissing) )?;
}
})?;
cs.enforce( cs.enforce(
|| "c_xor_constraint", || "c_xor_constraint",
|lc| lc + a_var + a_var, |lc| lc + a_var + a_var,
|lc| lc + b_var, |lc| lc + b_var,
|lc| lc + a_var + b_var - c_var |lc| lc + a_var + b_var - c_var,
); );
Ok(()) Ok(())
@ -106,19 +102,10 @@ fn test_xordemo() {
let c = XORDemo::<DummyEngine> { let c = XORDemo::<DummyEngine> {
a: None, a: None,
b: None, b: None,
_marker: PhantomData _marker: PhantomData,
}; };
generate_parameters( generate_parameters(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap()
c,
g1,
g2,
alpha,
beta,
gamma,
delta,
tau
).unwrap()
}; };
// This will synthesize the constraint system: // This will synthesize the constraint system:
@ -226,32 +213,35 @@ fn test_xordemo() {
59158 59158
*/ */
let u_i = [59158, 48317, 21767, 10402].iter().map(|e| { let u_i = [59158, 48317, 21767, 10402]
Fr::from_str(&format!("{}", e)).unwrap() .iter()
}).collect::<Vec<Fr>>(); .map(|e| Fr::from_str(&format!("{}", e)).unwrap())
let v_i = [0, 0, 60619, 30791].iter().map(|e| { .collect::<Vec<Fr>>();
Fr::from_str(&format!("{}", e)).unwrap() let v_i = [0, 0, 60619, 30791]
}).collect::<Vec<Fr>>(); .iter()
let w_i = [0, 23320, 41193, 41193].iter().map(|e| { .map(|e| Fr::from_str(&format!("{}", e)).unwrap())
Fr::from_str(&format!("{}", e)).unwrap() .collect::<Vec<Fr>>();
}).collect::<Vec<Fr>>(); let w_i = [0, 23320, 41193, 41193]
.iter()
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
.collect::<Vec<Fr>>();
for (u, a) in u_i.iter() for (u, a) in u_i.iter().zip(&params.a[..]) {
.zip(&params.a[..])
{
assert_eq!(u, a); assert_eq!(u, a);
} }
for (v, b) in v_i.iter() for (v, b) in v_i
.filter(|&&e| e != Fr::zero()) .iter()
.zip(&params.b_g1[..]) .filter(|&&e| e != Fr::zero())
.zip(&params.b_g1[..])
{ {
assert_eq!(v, b); assert_eq!(v, b);
} }
for (v, b) in v_i.iter() for (v, b) in v_i
.filter(|&&e| e != Fr::zero()) .iter()
.zip(&params.b_g2[..]) .filter(|&&e| e != Fr::zero())
.zip(&params.b_g2[..])
{ {
assert_eq!(v, b); assert_eq!(v, b);
} }
@ -296,15 +286,10 @@ fn test_xordemo() {
let c = XORDemo { let c = XORDemo {
a: Some(true), a: Some(true),
b: Some(false), b: Some(false),
_marker: PhantomData _marker: PhantomData,
}; };
create_proof( create_proof(c, &params, r, s).unwrap()
c,
&params,
r,
s
).unwrap()
}; };
// A(x) = // A(x) =
@ -320,7 +305,7 @@ fn test_xordemo() {
expected_a.add_assign(&u_i[0]); // a_0 = 1 expected_a.add_assign(&u_i[0]); // a_0 = 1
expected_a.add_assign(&u_i[1]); // a_1 = 1 expected_a.add_assign(&u_i[1]); // a_1 = 1
expected_a.add_assign(&u_i[2]); // a_2 = 1 expected_a.add_assign(&u_i[2]); // a_2 = 1
// a_3 = 0 // a_3 = 0
assert_eq!(proof.a, expected_a); assert_eq!(proof.a, expected_a);
} }
@ -337,7 +322,7 @@ fn test_xordemo() {
expected_b.add_assign(&v_i[0]); // a_0 = 1 expected_b.add_assign(&v_i[0]); // a_0 = 1
expected_b.add_assign(&v_i[1]); // a_1 = 1 expected_b.add_assign(&v_i[1]); // a_1 = 1
expected_b.add_assign(&v_i[2]); // a_2 = 1 expected_b.add_assign(&v_i[2]); // a_2 = 1
// a_3 = 0 // a_3 = 0
assert_eq!(proof.b, expected_b); assert_eq!(proof.b, expected_b);
} }
@ -378,7 +363,10 @@ fn test_xordemo() {
expected_c.add_assign(&params.l[0]); expected_c.add_assign(&params.l[0]);
// H query answer // H query answer
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() { for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739]
.iter()
.enumerate()
{
let coeff = Fr::from_str(&format!("{}", coeff)).unwrap(); let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
let mut tmp = params.h[i]; let mut tmp = params.h[i];
@ -389,9 +377,5 @@ fn test_xordemo() {
assert_eq!(expected_c, proof.c); assert_eq!(expected_c, proof.c);
} }
assert!(verify_proof( assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap());
&pvk,
&proof,
&[Fr::one()]
).unwrap());
} }

View File

@ -2,20 +2,11 @@ use ff::PrimeField;
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use pairing::{Engine, PairingCurveAffine}; use pairing::{Engine, PairingCurveAffine};
use super::{ use super::{PreparedVerifyingKey, Proof, VerifyingKey};
Proof,
VerifyingKey,
PreparedVerifyingKey
};
use ::{ use SynthesisError;
SynthesisError
};
pub fn prepare_verifying_key<E: Engine>( pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
vk: &VerifyingKey<E>
) -> PreparedVerifyingKey<E>
{
let mut gamma = vk.gamma_g2; let mut gamma = vk.gamma_g2;
gamma.negate(); gamma.negate();
let mut delta = vk.delta_g2; let mut delta = vk.delta_g2;
@ -25,16 +16,15 @@ pub fn prepare_verifying_key<E: Engine>(
alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2), alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2),
neg_gamma_g2: gamma.prepare(), neg_gamma_g2: gamma.prepare(),
neg_delta_g2: delta.prepare(), neg_delta_g2: delta.prepare(),
ic: vk.ic.clone() ic: vk.ic.clone(),
} }
} }
pub fn verify_proof<'a, E: Engine>( pub fn verify_proof<'a, E: Engine>(
pvk: &'a PreparedVerifyingKey<E>, pvk: &'a PreparedVerifyingKey<E>,
proof: &Proof<E>, proof: &Proof<E>,
public_inputs: &[E::Fr] public_inputs: &[E::Fr],
) -> Result<bool, SynthesisError> ) -> Result<bool, SynthesisError> {
{
if (public_inputs.len() + 1) != pvk.ic.len() { if (public_inputs.len() + 1) != pvk.ic.len() {
return Err(SynthesisError::MalformedVerifyingKey); return Err(SynthesisError::MalformedVerifyingKey);
} }
@ -53,11 +43,14 @@ pub fn verify_proof<'a, E: Engine>(
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
// which allows us to do a single final exponentiation. // which allows us to do a single final exponentiation.
Ok(E::final_exponentiation( Ok(E::final_exponentiation(&E::miller_loop(
&E::miller_loop([ [
(&proof.a.prepare(), &proof.b.prepare()), (&proof.a.prepare(), &proof.b.prepare()),
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2), (&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
(&proof.c.prepare(), &pvk.neg_delta_g2) (&proof.c.prepare(), &pvk.neg_delta_g2),
].into_iter()) ]
).unwrap() == pvk.alpha_g1_beta_g2) .into_iter(),
))
.unwrap()
== pvk.alpha_g1_beta_g2)
} }

View File

@ -4,10 +4,10 @@ extern crate group;
extern crate pairing; extern crate pairing;
extern crate rand_core; extern crate rand_core;
extern crate futures;
extern crate bit_vec; extern crate bit_vec;
extern crate blake2s_simd; extern crate blake2s_simd;
extern crate byteorder; extern crate byteorder;
extern crate futures;
#[cfg(feature = "multicore")] #[cfg(feature = "multicore")]
extern crate crossbeam; extern crate crossbeam;
@ -29,20 +29,20 @@ extern crate rand_xorshift;
#[cfg(test)] #[cfg(test)]
extern crate sha2; extern crate sha2;
pub mod gadgets;
pub mod multicore;
mod multiexp;
pub mod domain; pub mod domain;
pub mod gadgets;
#[cfg(feature = "groth16")] #[cfg(feature = "groth16")]
pub mod groth16; pub mod groth16;
pub mod multicore;
mod multiexp;
use ff::{Field, ScalarEngine}; use ff::{Field, ScalarEngine};
use std::ops::{Add, Sub};
use std::fmt;
use std::error::Error; use std::error::Error;
use std::fmt;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::{Add, Sub};
/// Computations are expressed in terms of arithmetic circuits, in particular /// Computations are expressed in terms of arithmetic circuits, in particular
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a /// rank-1 quadratic constraint systems. The `Circuit` trait represents a
@ -50,10 +50,7 @@ use std::marker::PhantomData;
/// CRS generation and during proving. /// CRS generation and during proving.
pub trait Circuit<E: ScalarEngine> { pub trait Circuit<E: ScalarEngine> {
/// Synthesize the circuit into a rank-1 quadratic constraint system /// Synthesize the circuit into a rank-1 quadratic constraint system
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError>;
self,
cs: &mut CS
) -> Result<(), SynthesisError>;
} }
/// Represents a variable in our constraint system. /// Represents a variable in our constraint system.
@ -79,7 +76,7 @@ impl Variable {
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Copy, Clone, PartialEq, Debug)]
pub enum Index { pub enum Index {
Input(usize), Input(usize),
Aux(usize) Aux(usize),
} }
/// This represents a linear combination of some variables, with coefficients /// This represents a linear combination of some variables, with coefficients
@ -206,7 +203,7 @@ pub enum SynthesisError {
/// During verification, our verifying key was malformed. /// During verification, our verifying key was malformed.
MalformedVerifyingKey, MalformedVerifyingKey,
/// During CRS generation, we observed an unconstrained auxiliary variable /// During CRS generation, we observed an unconstrained auxiliary variable
UnconstrainedVariable UnconstrainedVariable,
} }
impl From<io::Error> for SynthesisError { impl From<io::Error> for SynthesisError {
@ -218,14 +215,16 @@ impl From<io::Error> for SynthesisError {
impl Error for SynthesisError { impl Error for SynthesisError {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed", SynthesisError::AssignmentMissing => {
"an assignment for a variable could not be computed"
}
SynthesisError::DivisionByZero => "division by zero", SynthesisError::DivisionByZero => "division by zero",
SynthesisError::Unsatisfiable => "unsatisfiable constraint system", SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large", SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS", SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
SynthesisError::IoError(_) => "encountered an I/O error", SynthesisError::IoError(_) => "encountered an I/O error",
SynthesisError::MalformedVerifyingKey => "malformed verifying key", SynthesisError::MalformedVerifyingKey => "malformed verifying key",
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained" SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained",
} }
} }
} }
@ -257,40 +256,36 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// determine the assignment of the variable. The given `annotation` function is invoked /// determine the assignment of the variable. The given `annotation` function is invoked
/// in testing contexts in order to derive a unique name for this variable in the current /// in testing contexts in order to derive a unique name for this variable in the current
/// namespace. /// namespace.
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>;
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
/// Allocate a public variable in the constraint system. The provided function is used to /// Allocate a public variable in the constraint system. The provided function is used to
/// determine the assignment of the variable. /// determine the assignment of the variable.
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>;
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
/// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts /// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
/// in order to derive a unique name for the constraint in the current namespace. /// in order to derive a unique name for the constraint in the current namespace.
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
/// Create a new (sub)namespace and enter into it. Not intended /// Create a new (sub)namespace and enter into it. Not intended
/// for downstream use; use `namespace` instead. /// for downstream use; use `namespace` instead.
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR; where
NR: Into<String>,
N: FnOnce() -> NR;
/// Exit out of the existing namespace. Not intended for /// Exit out of the existing namespace. Not intended for
/// downstream use; use `namespace` instead. /// downstream use; use `namespace` instead.
@ -301,11 +296,10 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
fn get_root(&mut self) -> &mut Self::Root; fn get_root(&mut self) -> &mut Self::Root;
/// Begin a namespace for this constraint system. /// Begin a namespace for this constraint system.
fn namespace<'a, NR, N>( fn namespace<'a, NR, N>(&'a mut self, name_fn: N) -> Namespace<'a, E, Self::Root>
&'a mut self, where
name_fn: N NR: Into<String>,
) -> Namespace<'a, E, Self::Root> N: FnOnce() -> NR,
where NR: Into<String>, N: FnOnce() -> NR
{ {
self.get_root().push_namespace(name_fn); self.get_root().push_namespace(name_fn);
@ -324,37 +318,31 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
CS::one() CS::one()
} }
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.0.alloc(annotation, f) self.0.alloc(annotation, f)
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.0.alloc_input(annotation, f) self.0.alloc_input(annotation, f)
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
self.0.enforce(annotation, a, b, c) self.0.enforce(annotation, a, b, c)
} }
@ -364,18 +352,18 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
// never a root constraint system. // never a root constraint system.
fn push_namespace<NR, N>(&mut self, _: N) fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
panic!("only the root's push_namespace should be called"); panic!("only the root's push_namespace should be called");
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
panic!("only the root's pop_namespace should be called"); panic!("only the root's pop_namespace should be called");
} }
fn get_root(&mut self) -> &mut Self::Root fn get_root(&mut self) -> &mut Self::Root {
{
self.0.get_root() self.0.get_root()
} }
} }
@ -395,54 +383,48 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs
CS::one() CS::one()
} }
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
(**self).alloc(annotation, f) (**self).alloc(annotation, f)
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
(**self).alloc_input(annotation, f) (**self).alloc_input(annotation, f)
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
(**self).enforce(annotation, a, b, c) (**self).enforce(annotation, a, b, c)
} }
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
(**self).push_namespace(name_fn) (**self).push_namespace(name_fn)
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
(**self).pop_namespace() (**self).pop_namespace()
} }
fn get_root(&mut self) -> &mut Self::Root fn get_root(&mut self) -> &mut Self::Root {
{
(**self).get_root() (**self).get_root()
} }
} }

View File

@ -6,15 +6,15 @@
#[cfg(feature = "multicore")] #[cfg(feature = "multicore")]
mod implementation { mod implementation {
use num_cpus;
use futures::{Future, IntoFuture, Poll};
use futures_cpupool::{CpuPool, CpuFuture};
use crossbeam::{self, Scope}; use crossbeam::{self, Scope};
use futures::{Future, IntoFuture, Poll};
use futures_cpupool::{CpuFuture, CpuPool};
use num_cpus;
#[derive(Clone)] #[derive(Clone)]
pub struct Worker { pub struct Worker {
cpus: usize, cpus: usize,
pool: CpuPool pool: CpuPool,
} }
impl Worker { impl Worker {
@ -24,7 +24,7 @@ mod implementation {
pub(crate) fn new_with_cpus(cpus: usize) -> Worker { pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
Worker { Worker {
cpus: cpus, cpus: cpus,
pool: CpuPool::new(cpus) pool: CpuPool::new(cpus),
} }
} }
@ -36,26 +36,22 @@ mod implementation {
log2_floor(self.cpus) log2_floor(self.cpus)
} }
pub fn compute<F, R>( pub fn compute<F, R>(&self, f: F) -> WorkerFuture<R::Item, R::Error>
&self, f: F where
) -> WorkerFuture<R::Item, R::Error> F: FnOnce() -> R + Send + 'static,
where F: FnOnce() -> R + Send + 'static, R: IntoFuture + 'static,
R: IntoFuture + 'static, R::Future: Send + 'static,
R::Future: Send + 'static, R::Item: Send + 'static,
R::Item: Send + 'static, R::Error: Send + 'static,
R::Error: Send + 'static
{ {
WorkerFuture { WorkerFuture {
future: self.pool.spawn_fn(f) future: self.pool.spawn_fn(f),
} }
} }
pub fn scope<'a, F, R>( pub fn scope<'a, F, R>(&self, elements: usize, f: F) -> R
&self, where
elements: usize, F: FnOnce(&Scope<'a>, usize) -> R,
f: F
) -> R
where F: FnOnce(&Scope<'a>, usize) -> R
{ {
let chunk_size = if elements < self.cpus { let chunk_size = if elements < self.cpus {
1 1
@ -63,22 +59,19 @@ mod implementation {
elements / self.cpus elements / self.cpus
}; };
crossbeam::scope(|scope| { crossbeam::scope(|scope| f(scope, chunk_size))
f(scope, chunk_size)
})
} }
} }
pub struct WorkerFuture<T, E> { pub struct WorkerFuture<T, E> {
future: CpuFuture<T, E> future: CpuFuture<T, E>,
} }
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> { impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
type Item = T; type Item = T;
type Error = E; type Error = E;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
{
self.future.poll() self.future.poll()
} }
} }
@ -88,7 +81,7 @@ mod implementation {
let mut pow = 0; let mut pow = 0;
while (1 << (pow+1)) <= num { while (1 << (pow + 1)) <= num {
pow += 1; pow += 1;
} }

View File

@ -1,11 +1,11 @@
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use group::{CurveAffine, CurveProjective};
use std::sync::Arc;
use std::io;
use bit_vec::{self, BitVec};
use std::iter;
use futures::{Future};
use super::multicore::Worker; use super::multicore::Worker;
use bit_vec::{self, BitVec};
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use futures::Future;
use group::{CurveAffine, CurveProjective};
use std::io;
use std::iter;
use std::sync::Arc;
use super::SynthesisError; use super::SynthesisError;
@ -19,7 +19,10 @@ pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
/// A source of bases, like an iterator. /// A source of bases, like an iterator.
pub trait Source<G: CurveAffine> { pub trait Source<G: CurveAffine> {
/// Parses the element from the source. Fails if the point is at infinity. /// Parses the element from the source. Fails if the point is at infinity.
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError>; fn add_assign_mixed(
&mut self,
to: &mut <G as CurveAffine>::Projective,
) -> Result<(), SynthesisError>;
/// Skips `amt` elements from the source, avoiding deserialization. /// Skips `amt` elements from the source, avoiding deserialization.
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
@ -34,13 +37,20 @@ impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
} }
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) { impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError> { fn add_assign_mixed(
&mut self,
to: &mut <G as CurveAffine>::Projective,
) -> Result<(), SynthesisError> {
if self.0.len() <= self.1 { if self.0.len() <= self.1 {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into()); return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"expected more bases from source",
)
.into());
} }
if self.0[self.1].is_zero() { if self.0[self.1].is_zero() {
return Err(SynthesisError::UnexpectedIdentity) return Err(SynthesisError::UnexpectedIdentity);
} }
to.add_assign_mixed(&self.0[self.1]); to.add_assign_mixed(&self.0[self.1]);
@ -52,7 +62,11 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
if self.0.len() <= self.1 { if self.0.len() <= self.1 {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into()); return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"expected more bases from source",
)
.into());
} }
self.1 += amt; self.1 += amt;
@ -63,7 +77,7 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
pub trait QueryDensity { pub trait QueryDensity {
/// Returns whether the base exists. /// Returns whether the base exists.
type Iter: Iterator<Item=bool>; type Iter: Iterator<Item = bool>;
fn iter(self) -> Self::Iter; fn iter(self) -> Self::Iter;
fn get_query_size(self) -> Option<usize>; fn get_query_size(self) -> Option<usize>;
@ -92,7 +106,7 @@ impl<'a> QueryDensity for &'a FullDensity {
pub struct DensityTracker { pub struct DensityTracker {
bv: BitVec, bv: BitVec,
total_density: usize total_density: usize,
} }
impl<'a> QueryDensity for &'a DensityTracker { impl<'a> QueryDensity for &'a DensityTracker {
@ -111,7 +125,7 @@ impl DensityTracker {
pub fn new() -> DensityTracker { pub fn new() -> DensityTracker {
DensityTracker { DensityTracker {
bv: BitVec::new(), bv: BitVec::new(),
total_density: 0 total_density: 0,
} }
} }
@ -138,12 +152,13 @@ fn multiexp_inner<Q, D, G, S>(
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>, exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
mut skip: u32, mut skip: u32,
c: u32, c: u32,
handle_trivial: bool handle_trivial: bool,
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>> ) -> Box<Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
where for<'a> &'a Q: QueryDensity, where
D: Send + Sync + 'static + Clone + AsRef<Q>, for<'a> &'a Q: QueryDensity,
G: CurveAffine, D: Send + Sync + 'static + Clone + AsRef<Q>,
S: SourceBuilder<G> G: CurveAffine,
S: SourceBuilder<G>,
{ {
// Perform this region of the multiexp // Perform this region of the multiexp
let this = { let this = {
@ -212,16 +227,24 @@ fn multiexp_inner<Q, D, G, S>(
// There's another region more significant. Calculate and join it with // There's another region more significant. Calculate and join it with
// this region recursively. // this region recursively.
Box::new( Box::new(
this.join(multiexp_inner(pool, bases, density_map, exponents, skip, c, false)) this.join(multiexp_inner(
.map(move |(this, mut higher)| { pool,
for _ in 0..c { bases,
higher.double(); density_map,
} exponents,
skip,
c,
false,
))
.map(move |(this, mut higher)| {
for _ in 0..c {
higher.double();
}
higher.add_assign(&this); higher.add_assign(&this);
higher higher
}) }),
) )
} }
} }
@ -232,12 +255,13 @@ pub fn multiexp<Q, D, G, S>(
pool: &Worker, pool: &Worker,
bases: S, bases: S,
density_map: D, density_map: D,
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>> exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>> ) -> Box<Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
where for<'a> &'a Q: QueryDensity, where
D: Send + Sync + 'static + Clone + AsRef<Q>, for<'a> &'a Q: QueryDensity,
G: CurveAffine, D: Send + Sync + 'static + Clone + AsRef<Q>,
S: SourceBuilder<G> G: CurveAffine,
S: SourceBuilder<G>,
{ {
let c = if exponents.len() < 32 { let c = if exponents.len() < 32 {
3u32 3u32
@ -260,9 +284,8 @@ pub fn multiexp<Q, D, G, S>(
fn test_with_bls12() { fn test_with_bls12() {
fn naive_multiexp<G: CurveAffine>( fn naive_multiexp<G: CurveAffine>(
bases: Arc<Vec<G>>, bases: Arc<Vec<G>>,
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>> exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
) -> G::Projective ) -> G::Projective {
{
assert_eq!(bases.len(), exponents.len()); assert_eq!(bases.len(), exponents.len());
let mut acc = G::Projective::zero(); let mut acc = G::Projective::zero();
@ -274,25 +297,28 @@ fn test_with_bls12() {
acc acc
} }
use rand;
use pairing::{bls12_381::Bls12, Engine}; use pairing::{bls12_381::Bls12, Engine};
use rand;
const SAMPLES: usize = 1 << 14; const SAMPLES: usize = 1 << 14;
let rng = &mut rand::thread_rng(); let rng = &mut rand::thread_rng();
let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as ScalarEngine>::Fr::random(rng).into_repr()).collect::<Vec<_>>()); let v = Arc::new(
let g = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::G1::random(rng).into_affine()).collect::<Vec<_>>()); (0..SAMPLES)
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng).into_repr())
.collect::<Vec<_>>(),
);
let g = Arc::new(
(0..SAMPLES)
.map(|_| <Bls12 as Engine>::G1::random(rng).into_affine())
.collect::<Vec<_>>(),
);
let naive = naive_multiexp(g.clone(), v.clone()); let naive = naive_multiexp(g.clone(), v.clone());
let pool = Worker::new(); let pool = Worker::new();
let fast = multiexp( let fast = multiexp(&pool, (g, 0), FullDensity, v).wait().unwrap();
&pool,
(g, 0),
FullDensity,
v
).wait().unwrap();
assert_eq!(naive, fast); assert_eq!(naive, fast);
} }

View File

@ -14,31 +14,21 @@ use ff::{Field, ScalarEngine};
use pairing::Engine; use pairing::Engine;
// We're going to use the BLS12-381 pairing-friendly elliptic curve. // We're going to use the BLS12-381 pairing-friendly elliptic curve.
use pairing::bls12_381::{ use pairing::bls12_381::Bls12;
Bls12
};
// We'll use these interfaces to construct our circuit. // We'll use these interfaces to construct our circuit.
use bellman::{ use bellman::{Circuit, ConstraintSystem, SynthesisError};
Circuit,
ConstraintSystem,
SynthesisError
};
// We're going to use the Groth16 proving system. // We're going to use the Groth16 proving system.
use bellman::groth16::{ use bellman::groth16::{
Proof, create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof,
generate_random_parameters,
prepare_verifying_key,
create_random_proof,
verify_proof,
}; };
const MIMC_ROUNDS: usize = 322; const MIMC_ROUNDS: usize = 322;
/// This is an implementation of MiMC, specifically a /// This is an implementation of MiMC, specifically a
/// variant named `LongsightF322p3` for BLS12-381. /// variant named `LongsightF322p3` for BLS12-381.
/// See http://eprint.iacr.org/2016/492 for more /// See http://eprint.iacr.org/2016/492 for more
/// information about this construction. /// information about this construction.
/// ///
/// ``` /// ```
@ -49,12 +39,7 @@ const MIMC_ROUNDS: usize = 322;
/// return xL /// return xL
/// } /// }
/// ``` /// ```
fn mimc<E: Engine>( fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr {
mut xl: E::Fr,
mut xr: E::Fr,
constants: &[E::Fr]
) -> E::Fr
{
assert_eq!(constants.len(), MIMC_ROUNDS); assert_eq!(constants.len(), MIMC_ROUNDS);
for i in 0..MIMC_ROUNDS { for i in 0..MIMC_ROUNDS {
@ -76,31 +61,29 @@ fn mimc<E: Engine>(
struct MiMCDemo<'a, E: Engine> { struct MiMCDemo<'a, E: Engine> {
xl: Option<E::Fr>, xl: Option<E::Fr>,
xr: Option<E::Fr>, xr: Option<E::Fr>,
constants: &'a [E::Fr] constants: &'a [E::Fr],
} }
/// Our demo circuit implements this `Circuit` trait which /// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to /// is used during paramgen and proving in order to
/// synthesize the constraint system. /// synthesize the constraint system.
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> { impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
assert_eq!(self.constants.len(), MIMC_ROUNDS); assert_eq!(self.constants.len(), MIMC_ROUNDS);
// Allocate the first component of the preimage. // Allocate the first component of the preimage.
let mut xl_value = self.xl; let mut xl_value = self.xl;
let mut xl = cs.alloc(|| "preimage xl", || { let mut xl = cs.alloc(
xl_value.ok_or(SynthesisError::AssignmentMissing) || "preimage xl",
})?; || xl_value.ok_or(SynthesisError::AssignmentMissing),
)?;
// Allocate the second component of the preimage. // Allocate the second component of the preimage.
let mut xr_value = self.xr; let mut xr_value = self.xr;
let mut xr = cs.alloc(|| "preimage xr", || { let mut xr = cs.alloc(
xr_value.ok_or(SynthesisError::AssignmentMissing) || "preimage xr",
})?; || xr_value.ok_or(SynthesisError::AssignmentMissing),
)?;
for i in 0..MIMC_ROUNDS { for i in 0..MIMC_ROUNDS {
// xL, xR := xR + (xL + Ci)^3, xL // xL, xR := xR + (xL + Ci)^3, xL
@ -112,15 +95,16 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
e.square(); e.square();
e e
}); });
let mut tmp = cs.alloc(|| "tmp", || { let mut tmp = cs.alloc(
tmp_value.ok_or(SynthesisError::AssignmentMissing) || "tmp",
})?; || tmp_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce( cs.enforce(
|| "tmp = (xL + Ci)^2", || "tmp = (xL + Ci)^2",
|lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + tmp |lc| lc + tmp,
); );
// new_xL = xR + (xL + Ci)^3 // new_xL = xR + (xL + Ci)^3
@ -133,23 +117,25 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
e e
}); });
let mut new_xl = if i == (MIMC_ROUNDS-1) { let mut new_xl = if i == (MIMC_ROUNDS - 1) {
// This is the last round, xL is our image and so // This is the last round, xL is our image and so
// we allocate a public input. // we allocate a public input.
cs.alloc_input(|| "image", || { cs.alloc_input(
new_xl_value.ok_or(SynthesisError::AssignmentMissing) || "image",
})? || new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
} else { } else {
cs.alloc(|| "new_xl", || { cs.alloc(
new_xl_value.ok_or(SynthesisError::AssignmentMissing) || "new_xl",
})? || new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
}; };
cs.enforce( cs.enforce(
|| "new_xL = xR + (xL + Ci)^3", || "new_xL = xR + (xL + Ci)^3",
|lc| lc + tmp, |lc| lc + tmp,
|lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + new_xl - xr |lc| lc + new_xl - xr,
); );
// xR = xL // xR = xL
@ -172,7 +158,9 @@ fn test_mimc() {
let rng = &mut thread_rng(); let rng = &mut thread_rng();
// Generate the MiMC round constants // Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| <Bls12 as ScalarEngine>::Fr::random(rng)).collect::<Vec<_>>(); let constants = (0..MIMC_ROUNDS)
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng))
.collect::<Vec<_>>();
println!("Creating parameters..."); println!("Creating parameters...");
@ -181,7 +169,7 @@ fn test_mimc() {
let c = MiMCDemo::<Bls12> { let c = MiMCDemo::<Bls12> {
xl: None, xl: None,
xr: None, xr: None,
constants: &constants constants: &constants,
}; };
generate_random_parameters(c, rng).unwrap() generate_random_parameters(c, rng).unwrap()
@ -216,7 +204,7 @@ fn test_mimc() {
let c = MiMCDemo { let c = MiMCDemo {
xl: Some(xl), xl: Some(xl),
xr: Some(xr), xr: Some(xr),
constants: &constants constants: &constants,
}; };
// Create a groth16 proof with our parameters. // Create a groth16 proof with our parameters.
@ -230,20 +218,16 @@ fn test_mimc() {
let start = Instant::now(); let start = Instant::now();
let proof = Proof::read(&proof_vec[..]).unwrap(); let proof = Proof::read(&proof_vec[..]).unwrap();
// Check the proof // Check the proof
assert!(verify_proof( assert!(verify_proof(&pvk, &proof, &[image]).unwrap());
&pvk,
&proof,
&[image]
).unwrap());
total_verifying += start.elapsed(); total_verifying += start.elapsed();
} }
let proving_avg = total_proving / SAMPLES; let proving_avg = total_proving / SAMPLES;
let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 let proving_avg =
+ (proving_avg.as_secs() as f64); proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64);
let verifying_avg = total_verifying / SAMPLES; let verifying_avg = total_verifying / SAMPLES;
let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 let verifying_avg =
+ (verifying_avg.as_secs() as f64); verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64);
println!("Average proving time: {:?} seconds", proving_avg); println!("Average proving time: {:?} seconds", proving_avg);
println!("Average verifying time: {:?} seconds", verifying_avg); println!("Average verifying time: {:?} seconds", verifying_avg);

View File

@ -52,13 +52,8 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut gen = proc_macro2::TokenStream::new(); let mut gen = proc_macro2::TokenStream::new();
let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt( let (constants_impl, sqrt_impl) =
&ast.ident, prime_field_constants_and_sqrt(&ast.ident, &repr_ident, modulus, limbs, generator);
&repr_ident,
modulus,
limbs,
generator,
);
gen.extend(constants_impl); gen.extend(constants_impl);
gen.extend(prime_field_repr_impl(&repr_ident, limbs)); gen.extend(prime_field_repr_impl(&repr_ident, limbs));
@ -359,7 +354,8 @@ fn biguint_num_bits(mut v: BigUint) -> u32 {
fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
let mut ret = BigUint::one(); let mut ret = BigUint::one();
for i in exp.to_bytes_be() for i in exp
.to_bytes_be()
.into_iter() .into_iter()
.flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd())) .flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd()))
{ {
@ -380,11 +376,13 @@ fn test_exp() {
&BigUint::from_str("5489673498567349856734895").unwrap(), &BigUint::from_str("5489673498567349856734895").unwrap(),
&BigUint::from_str( &BigUint::from_str(
"52435875175126190479447740508185965837690552500527637822603658699938581184513" "52435875175126190479447740508185965837690552500527637822603658699938581184513"
).unwrap() )
.unwrap()
), ),
BigUint::from_str( BigUint::from_str(
"4371221214068404307866768905142520595925044802278091865033317963560480051536" "4371221214068404307866768905142520595925044802278091865033317963560480051536"
).unwrap() )
.unwrap()
); );
} }
@ -423,7 +421,7 @@ fn prime_field_constants_and_sqrt(
let mod_minus_1_over_2 = let mod_minus_1_over_2 =
biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs); biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs);
let legendre_impl = quote!{ let legendre_impl = quote! {
fn legendre(&self) -> ::ff::LegendreSymbol { fn legendre(&self) -> ::ff::LegendreSymbol {
// s = self^((modulus - 1) // 2) // s = self^((modulus - 1) // 2)
let s = self.pow(#mod_minus_1_over_2); let s = self.pow(#mod_minus_1_over_2);
@ -445,7 +443,7 @@ fn prime_field_constants_and_sqrt(
// Compute -R as (m - r) // Compute -R as (m - r)
let rneg = biguint_to_u64_vec(&modulus - &r, limbs); let rneg = biguint_to_u64_vec(&modulus - &r, limbs);
quote!{ quote! {
impl ::ff::SqrtField for #name { impl ::ff::SqrtField for #name {
#legendre_impl #legendre_impl
@ -472,7 +470,7 @@ fn prime_field_constants_and_sqrt(
let t_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs); let t_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs);
let t = biguint_to_u64_vec(t.clone(), limbs); let t = biguint_to_u64_vec(t.clone(), limbs);
quote!{ quote! {
impl ::ff::SqrtField for #name { impl ::ff::SqrtField for #name {
#legendre_impl #legendre_impl
@ -519,7 +517,7 @@ fn prime_field_constants_and_sqrt(
} }
} }
} else { } else {
quote!{} quote! {}
}; };
// Compute R^2 mod m // Compute R^2 mod m
@ -536,36 +534,39 @@ fn prime_field_constants_and_sqrt(
} }
inv = inv.wrapping_neg(); inv = inv.wrapping_neg();
(quote! { (
/// This is the modulus m of the prime field quote! {
const MODULUS: #repr = #repr([#(#modulus,)*]); /// This is the modulus m of the prime field
const MODULUS: #repr = #repr([#(#modulus,)*]);
/// The number of bits needed to represent the modulus. /// The number of bits needed to represent the modulus.
const MODULUS_BITS: u32 = #modulus_num_bits; const MODULUS_BITS: u32 = #modulus_num_bits;
/// The number of bits that must be shaved from the beginning of /// The number of bits that must be shaved from the beginning of
/// the representation when randomly sampling. /// the representation when randomly sampling.
const REPR_SHAVE_BITS: u32 = #repr_shave_bits; const REPR_SHAVE_BITS: u32 = #repr_shave_bits;
/// 2^{limbs*64} mod m /// 2^{limbs*64} mod m
const R: #repr = #repr(#r); const R: #repr = #repr(#r);
/// 2^{limbs*64*2} mod m /// 2^{limbs*64*2} mod m
const R2: #repr = #repr(#r2); const R2: #repr = #repr(#r2);
/// -(m^{-1} mod m) mod m /// -(m^{-1} mod m) mod m
const INV: u64 = #inv; const INV: u64 = #inv;
/// Multiplicative generator of `MODULUS` - 1 order, also quadratic /// Multiplicative generator of `MODULUS` - 1 order, also quadratic
/// nonresidue. /// nonresidue.
const GENERATOR: #repr = #repr(#generator); const GENERATOR: #repr = #repr(#generator);
/// 2^s * t = MODULUS - 1 with t odd /// 2^s * t = MODULUS - 1 with t odd
const S: u32 = #s; const S: u32 = #s;
/// 2^s root of unity computed by GENERATOR^t /// 2^s root of unity computed by GENERATOR^t
const ROOT_OF_UNITY: #repr = #repr(#root_of_unity); const ROOT_OF_UNITY: #repr = #repr(#root_of_unity);
}, sqrt_impl) },
sqrt_impl,
)
} }
/// Implement PrimeField for the derived type. /// Implement PrimeField for the derived type.
@ -585,9 +586,9 @@ fn prime_field_impl(
mont_paramlist.append_separated( mont_paramlist.append_separated(
(0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| { (0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| {
if i != 0 { if i != 0 {
quote!{mut #x: u64} quote! {mut #x: u64}
} else { } else {
quote!{#x: u64} quote! {#x: u64}
} }
}), }),
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
@ -600,7 +601,7 @@ fn prime_field_impl(
for i in 0..limbs { for i in 0..limbs {
{ {
let temp = get_temp(i); let temp = get_temp(i);
gen.extend(quote!{ gen.extend(quote! {
let k = #temp.wrapping_mul(INV); let k = #temp.wrapping_mul(INV);
let mut carry = 0; let mut carry = 0;
::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry); ::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry);
@ -609,7 +610,7 @@ fn prime_field_impl(
for j in 1..limbs { for j in 1..limbs {
let temp = get_temp(i + j); let temp = get_temp(i + j);
gen.extend(quote!{ gen.extend(quote! {
#temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry); #temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry);
}); });
} }
@ -617,17 +618,17 @@ fn prime_field_impl(
let temp = get_temp(i + limbs); let temp = get_temp(i + limbs);
if i == 0 { if i == 0 {
gen.extend(quote!{ gen.extend(quote! {
#temp = ::ff::adc(#temp, 0, &mut carry); #temp = ::ff::adc(#temp, 0, &mut carry);
}); });
} else { } else {
gen.extend(quote!{ gen.extend(quote! {
#temp = ::ff::adc(#temp, carry2, &mut carry); #temp = ::ff::adc(#temp, carry2, &mut carry);
}); });
} }
if i != (limbs - 1) { if i != (limbs - 1) {
gen.extend(quote!{ gen.extend(quote! {
let carry2 = carry; let carry2 = carry;
}); });
} }
@ -636,7 +637,7 @@ fn prime_field_impl(
for i in 0..limbs { for i in 0..limbs {
let temp = get_temp(limbs + i); let temp = get_temp(limbs + i);
gen.extend(quote!{ gen.extend(quote! {
(self.0).0[#i] = #temp; (self.0).0[#i] = #temp;
}); });
} }
@ -648,14 +649,14 @@ fn prime_field_impl(
let mut gen = proc_macro2::TokenStream::new(); let mut gen = proc_macro2::TokenStream::new();
for i in 0..(limbs - 1) { for i in 0..(limbs - 1) {
gen.extend(quote!{ gen.extend(quote! {
let mut carry = 0; let mut carry = 0;
}); });
for j in (i + 1)..limbs { for j in (i + 1)..limbs {
let temp = get_temp(i + j); let temp = get_temp(i + j);
if i == 0 { if i == 0 {
gen.extend(quote!{ gen.extend(quote! {
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry); let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry);
}); });
} else { } else {
@ -667,7 +668,7 @@ fn prime_field_impl(
let temp = get_temp(i + limbs); let temp = get_temp(i + limbs);
gen.extend(quote!{ gen.extend(quote! {
let #temp = carry; let #temp = carry;
}); });
} }
@ -677,21 +678,21 @@ fn prime_field_impl(
let temp1 = get_temp(limbs * 2 - i - 1); let temp1 = get_temp(limbs * 2 - i - 1);
if i == 1 { if i == 1 {
gen.extend(quote!{ gen.extend(quote! {
let #temp0 = #temp1 >> 63; let #temp0 = #temp1 >> 63;
}); });
} else if i == (limbs * 2 - 1) { } else if i == (limbs * 2 - 1) {
gen.extend(quote!{ gen.extend(quote! {
let #temp0 = #temp0 << 1; let #temp0 = #temp0 << 1;
}); });
} else { } else {
gen.extend(quote!{ gen.extend(quote! {
let #temp0 = (#temp0 << 1) | (#temp1 >> 63); let #temp0 = (#temp0 << 1) | (#temp1 >> 63);
}); });
} }
} }
gen.extend(quote!{ gen.extend(quote! {
let mut carry = 0; let mut carry = 0;
}); });
@ -699,7 +700,7 @@ fn prime_field_impl(
let temp0 = get_temp(i * 2); let temp0 = get_temp(i * 2);
let temp1 = get_temp(i * 2 + 1); let temp1 = get_temp(i * 2 + 1);
if i == 0 { if i == 0 {
gen.extend(quote!{ gen.extend(quote! {
let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry); let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry);
}); });
} else { } else {
@ -708,7 +709,7 @@ fn prime_field_impl(
}); });
} }
gen.extend(quote!{ gen.extend(quote! {
let #temp1 = ::ff::adc(#temp1, 0, &mut carry); let #temp1 = ::ff::adc(#temp1, 0, &mut carry);
}); });
} }
@ -719,7 +720,7 @@ fn prime_field_impl(
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
); );
gen.extend(quote!{ gen.extend(quote! {
self.mont_reduce(#mont_calling); self.mont_reduce(#mont_calling);
}); });
@ -734,7 +735,7 @@ fn prime_field_impl(
let mut gen = proc_macro2::TokenStream::new(); let mut gen = proc_macro2::TokenStream::new();
for i in 0..limbs { for i in 0..limbs {
gen.extend(quote!{ gen.extend(quote! {
let mut carry = 0; let mut carry = 0;
}); });
@ -742,7 +743,7 @@ fn prime_field_impl(
let temp = get_temp(i + j); let temp = get_temp(i + j);
if i == 0 { if i == 0 {
gen.extend(quote!{ gen.extend(quote! {
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry); let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry);
}); });
} else { } else {
@ -754,7 +755,7 @@ fn prime_field_impl(
let temp = get_temp(i + limbs); let temp = get_temp(i + limbs);
gen.extend(quote!{ gen.extend(quote! {
let #temp = carry; let #temp = carry;
}); });
} }
@ -765,29 +766,29 @@ fn prime_field_impl(
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
); );
gen.extend(quote!{ gen.extend(quote! {
self.mont_reduce(#mont_calling); self.mont_reduce(#mont_calling);
}); });
gen gen
} }
let squaring_impl = sqr_impl(quote!{self}, limbs); let squaring_impl = sqr_impl(quote! {self}, limbs);
let multiply_impl = mul_impl(quote!{self}, quote!{other}, limbs); let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs);
let montgomery_impl = mont_impl(limbs); let montgomery_impl = mont_impl(limbs);
// (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ... // (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ...
let mut into_repr_params = proc_macro2::TokenStream::new(); let mut into_repr_params = proc_macro2::TokenStream::new();
into_repr_params.append_separated( into_repr_params.append_separated(
(0..limbs) (0..limbs)
.map(|i| quote!{ (self.0).0[#i] }) .map(|i| quote! { (self.0).0[#i] })
.chain((0..limbs).map(|_| quote!{0})), .chain((0..limbs).map(|_| quote! {0})),
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
); );
let top_limb_index = limbs - 1; let top_limb_index = limbs - 1;
quote!{ quote! {
impl ::std::marker::Copy for #name { } impl ::std::marker::Copy for #name { }
impl ::std::clone::Clone for #name { impl ::std::clone::Clone for #name {

View File

@ -59,9 +59,7 @@ use std::os::windows::ffi::OsStringExt;
use zcash_primitives::{ use zcash_primitives::{
merkle_tree::CommitmentTreeWitness, merkle_tree::CommitmentTreeWitness,
note_encryption::sapling_ka_agree, note_encryption::sapling_ka_agree,
primitives::{ primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ViewingKey},
Diversifier, Note, PaymentAddress, ProofGenerationKey, ViewingKey,
},
redjubjub::{self, Signature}, redjubjub::{self, Signature},
sapling::{merkle_hash, spend_sig}, sapling::{merkle_hash, spend_sig},
transaction::components::Amount, transaction::components::Amount,

View File

@ -14,11 +14,10 @@ macro_rules! curve_impl {
pub struct $affine { pub struct $affine {
pub(crate) x: $basefield, pub(crate) x: $basefield,
pub(crate) y: $basefield, pub(crate) y: $basefield,
pub(crate) infinity: bool pub(crate) infinity: bool,
} }
impl ::std::fmt::Display for $affine impl ::std::fmt::Display for $affine {
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
if self.infinity { if self.infinity {
write!(f, "{}(Infinity)", $name) write!(f, "{}(Infinity)", $name)
@ -30,13 +29,12 @@ macro_rules! curve_impl {
#[derive(Copy, Clone, Debug, Eq)] #[derive(Copy, Clone, Debug, Eq)]
pub struct $projective { pub struct $projective {
pub(crate) x: $basefield, pub(crate) x: $basefield,
pub(crate) y: $basefield, pub(crate) y: $basefield,
pub(crate) z: $basefield pub(crate) z: $basefield,
} }
impl ::std::fmt::Display for $projective impl ::std::fmt::Display for $projective {
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.into_affine()) write!(f, "{}", self.into_affine())
} }
@ -89,7 +87,9 @@ macro_rules! curve_impl {
let mut res = $projective::zero(); let mut res = $projective::zero();
for i in bits { for i in bits {
res.double(); res.double();
if i { res.add_assign_mixed(self) } if i {
res.add_assign_mixed(self)
}
} }
res res
} }
@ -112,12 +112,8 @@ macro_rules! curve_impl {
$affine { $affine {
x: x, x: x,
y: if (y < negy) ^ greatest { y: if (y < negy) ^ greatest { y } else { negy },
y infinity: false,
} else {
negy
},
infinity: false
} }
}) })
} }
@ -156,7 +152,7 @@ macro_rules! curve_impl {
$affine { $affine {
x: $basefield::zero(), x: $basefield::zero(),
y: $basefield::one(), y: $basefield::one(),
infinity: true infinity: true,
} }
} }
@ -182,7 +178,6 @@ macro_rules! curve_impl {
fn into_projective(&self) -> $projective { fn into_projective(&self) -> $projective {
(*self).into() (*self).into()
} }
} }
impl PairingCurveAffine for $affine { impl PairingCurveAffine for $affine {
@ -197,7 +192,6 @@ macro_rules! curve_impl {
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
self.perform_pairing(other) self.perform_pairing(other)
} }
} }
impl CurveProjective for $projective { impl CurveProjective for $projective {
@ -227,7 +221,7 @@ macro_rules! curve_impl {
$projective { $projective {
x: $basefield::zero(), x: $basefield::zero(),
y: $basefield::one(), y: $basefield::one(),
z: $basefield::zero() z: $basefield::zero(),
} }
} }
@ -245,8 +239,7 @@ macro_rules! curve_impl {
self.is_zero() || self.z == $basefield::one() self.is_zero() || self.z == $basefield::one()
} }
fn batch_normalization(v: &mut [Self]) fn batch_normalization(v: &mut [Self]) {
{
// Montgomerys Trick and Fast Implementation of Masked AES // Montgomerys Trick and Fast Implementation of Masked AES
// Genelle, Prouff and Quisquater // Genelle, Prouff and Quisquater
// Section 3.2 // Section 3.2
@ -254,9 +247,10 @@ macro_rules! curve_impl {
// First pass: compute [a, ab, abc, ...] // First pass: compute [a, ab, abc, ...]
let mut prod = Vec::with_capacity(v.len()); let mut prod = Vec::with_capacity(v.len());
let mut tmp = $basefield::one(); let mut tmp = $basefield::one();
for g in v.iter_mut() for g in v
// Ignore normalized elements .iter_mut()
.filter(|g| !g.is_normalized()) // Ignore normalized elements
.filter(|g| !g.is_normalized())
{ {
tmp.mul_assign(&g.z); tmp.mul_assign(&g.z);
prod.push(tmp); prod.push(tmp);
@ -266,13 +260,19 @@ macro_rules! curve_impl {
tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero. tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero.
// Second pass: iterate backwards to compute inverses // Second pass: iterate backwards to compute inverses
for (g, s) in v.iter_mut() for (g, s) in v
// Backwards .iter_mut()
.rev() // Backwards
// Ignore normalized elements .rev()
.filter(|g| !g.is_normalized()) // Ignore normalized elements
// Backwards, skip last element, fill in one for last term. .filter(|g| !g.is_normalized())
.zip(prod.into_iter().rev().skip(1).chain(Some($basefield::one()))) // Backwards, skip last element, fill in one for last term.
.zip(
prod.into_iter()
.rev()
.skip(1)
.chain(Some($basefield::one())),
)
{ {
// tmp := tmp * g.z; g.z := tmp * s = 1/z // tmp := tmp * g.z; g.z := tmp * s = 1/z
let mut newtmp = tmp; let mut newtmp = tmp;
@ -283,9 +283,7 @@ macro_rules! curve_impl {
} }
// Perform affine transformations // Perform affine transformations
for g in v.iter_mut() for g in v.iter_mut().filter(|g| !g.is_normalized()) {
.filter(|g| !g.is_normalized())
{
let mut z = g.z; // 1/z let mut z = g.z; // 1/z
z.square(); // 1/z^2 z.square(); // 1/z^2
g.x.mul_assign(&z); // x/z^2 g.x.mul_assign(&z); // x/z^2
@ -538,8 +536,7 @@ macro_rules! curve_impl {
let mut found_one = false; let mut found_one = false;
for i in BitIterator::new(other.into()) for i in BitIterator::new(other.into()) {
{
if found_one { if found_one {
res.double(); res.double();
} else { } else {
@ -577,7 +574,7 @@ macro_rules! curve_impl {
$projective { $projective {
x: p.x, x: p.x,
y: p.y, y: p.y,
z: $basefield::one() z: $basefield::one(),
} }
} }
} }
@ -594,7 +591,7 @@ macro_rules! curve_impl {
$affine { $affine {
x: p.x, x: p.x,
y: p.y, y: p.y,
infinity: false infinity: false,
} }
} else { } else {
// Z is nonzero, so it must have an inverse in a field. // Z is nonzero, so it must have an inverse in a field.
@ -614,12 +611,12 @@ macro_rules! curve_impl {
$affine { $affine {
x: x, x: x,
y: y, y: y,
infinity: false infinity: false,
} }
} }
} }
} }
} };
} }
pub mod g1 { pub mod g1 {
@ -990,7 +987,8 @@ pub mod g1 {
0x9fe83b1b4a5d648d, 0x9fe83b1b4a5d648d,
0xf583cc5a508f6a40, 0xf583cc5a508f6a40,
0xc3ad2aefde0bb13, 0xc3ad2aefde0bb13,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x60aa6f9552f03aae, 0x60aa6f9552f03aae,
0xecd01d5181300d35, 0xecd01d5181300d35,
@ -998,7 +996,8 @@ pub mod g1 {
0xe760f57922998c9d, 0xe760f57922998c9d,
0x953703f5795a39e5, 0x953703f5795a39e5,
0xfe3ae0922df702c, 0xfe3ae0922df702c,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
assert!(!p.is_on_curve()); assert!(!p.is_on_curve());
@ -1015,7 +1014,8 @@ pub mod g1 {
0xea034ee2928b30a8, 0xea034ee2928b30a8,
0xbd8833dc7c79a7f7, 0xbd8833dc7c79a7f7,
0xe45c9f0c0438675, 0xe45c9f0c0438675,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x3b450eb1ab7b5dad, 0x3b450eb1ab7b5dad,
0xa65cb81e975e8675, 0xa65cb81e975e8675,
@ -1023,7 +1023,8 @@ pub mod g1 {
0x753ddf21a2601d20, 0x753ddf21a2601d20,
0x532d0b640bd3ff8b, 0x532d0b640bd3ff8b,
0x118d2c543f031102, 0x118d2c543f031102,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
assert!(!p.is_on_curve()); assert!(!p.is_on_curve());
@ -1041,7 +1042,8 @@ pub mod g1 {
0xf35de9ce0d6b4e84, 0xf35de9ce0d6b4e84,
0x265bddd23d1dec54, 0x265bddd23d1dec54,
0x12a8778088458308, 0x12a8778088458308,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x8a22defa0d526256, 0x8a22defa0d526256,
0xc57ca55456fcb9ae, 0xc57ca55456fcb9ae,
@ -1049,7 +1051,8 @@ pub mod g1 {
0x921beef89d4f29df, 0x921beef89d4f29df,
0x5b6fda44ad85fa78, 0x5b6fda44ad85fa78,
0xed74ab9f302cbe0, 0xed74ab9f302cbe0,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
assert!(p.is_on_curve()); assert!(p.is_on_curve());
@ -1067,7 +1070,8 @@ pub mod g1 {
0x485e77d50a5df10d, 0x485e77d50a5df10d,
0x4c6fcac4b55fd479, 0x4c6fcac4b55fd479,
0x86ed4d9906fb064, 0x86ed4d9906fb064,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xd25ee6461538c65, 0xd25ee6461538c65,
0x9f3bbb2ecd3719b9, 0x9f3bbb2ecd3719b9,
@ -1075,7 +1079,8 @@ pub mod g1 {
0xcefca68333c35288, 0xcefca68333c35288,
0x570c8005f8573fa6, 0x570c8005f8573fa6,
0x152ca696fe034442, 0x152ca696fe034442,
])).unwrap(), ]))
.unwrap(),
z: Fq::one(), z: Fq::one(),
}; };
@ -1087,7 +1092,8 @@ pub mod g1 {
0x5f44314ec5e3fb03, 0x5f44314ec5e3fb03,
0x24e8538737c6e675, 0x24e8538737c6e675,
0x8abd623a594fba8, 0x8abd623a594fba8,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x6b0528f088bb7044, 0x6b0528f088bb7044,
0x2fdeb5c82917ff9e, 0x2fdeb5c82917ff9e,
@ -1095,7 +1101,8 @@ pub mod g1 {
0xd65104c6f95a872a, 0xd65104c6f95a872a,
0x1f2998a5a9c61253, 0x1f2998a5a9c61253,
0xe74846154a9e44, 0xe74846154a9e44,
])).unwrap(), ]))
.unwrap(),
z: Fq::one(), z: Fq::one(),
}); });
@ -1111,7 +1118,8 @@ pub mod g1 {
0xc4f9a52a428e23bb, 0xc4f9a52a428e23bb,
0xd178b28dd4f407ef, 0xd178b28dd4f407ef,
0x17fb8905e9183c69 0x17fb8905e9183c69
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xd0de9d65292b7710, 0xd0de9d65292b7710,
0xf6a05f2bcf1d9ca7, 0xf6a05f2bcf1d9ca7,
@ -1119,7 +1127,8 @@ pub mod g1 {
0xeec8d1a5b7466c58, 0xeec8d1a5b7466c58,
0x4bc362649dce6376, 0x4bc362649dce6376,
0x430cbdc5455b00a 0x430cbdc5455b00a
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
} }
); );
@ -1135,7 +1144,8 @@ pub mod g1 {
0x485e77d50a5df10d, 0x485e77d50a5df10d,
0x4c6fcac4b55fd479, 0x4c6fcac4b55fd479,
0x86ed4d9906fb064, 0x86ed4d9906fb064,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xd25ee6461538c65, 0xd25ee6461538c65,
0x9f3bbb2ecd3719b9, 0x9f3bbb2ecd3719b9,
@ -1143,7 +1153,8 @@ pub mod g1 {
0xcefca68333c35288, 0xcefca68333c35288,
0x570c8005f8573fa6, 0x570c8005f8573fa6,
0x152ca696fe034442, 0x152ca696fe034442,
])).unwrap(), ]))
.unwrap(),
z: Fq::one(), z: Fq::one(),
}; };
@ -1161,7 +1172,8 @@ pub mod g1 {
0x4b914c16687dcde0, 0x4b914c16687dcde0,
0x66c8baf177d20533, 0x66c8baf177d20533,
0xaf960cff3d83833 0xaf960cff3d83833
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x3f0675695f5177a8, 0x3f0675695f5177a8,
0x2b6d82ae178a1ba0, 0x2b6d82ae178a1ba0,
@ -1169,7 +1181,8 @@ pub mod g1 {
0x1771a65b60572f4e, 0x1771a65b60572f4e,
0x8b547c1313b27555, 0x8b547c1313b27555,
0x135075589a687b1e 0x135075589a687b1e
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
} }
); );
@ -1192,7 +1205,8 @@ pub mod g1 {
0x71ffa8021531705, 0x71ffa8021531705,
0x7418d484386d267, 0x7418d484386d267,
0xd5108d8ff1fbd6, 0xd5108d8ff1fbd6,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xa776ccbfe9981766, 0xa776ccbfe9981766,
0x255632964ff40f4a, 0x255632964ff40f4a,
@ -1200,7 +1214,8 @@ pub mod g1 {
0x520f74773e74c8c3, 0x520f74773e74c8c3,
0x484c8fc982008f0, 0x484c8fc982008f0,
0xee2c3d922008cc6, 0xee2c3d922008cc6,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
@ -1212,7 +1227,8 @@ pub mod g1 {
0xc6e05201e5f83991, 0xc6e05201e5f83991,
0xf7c75910816f207c, 0xf7c75910816f207c,
0x18d4043e78103106, 0x18d4043e78103106,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xa776ccbfe9981766, 0xa776ccbfe9981766,
0x255632964ff40f4a, 0x255632964ff40f4a,
@ -1220,7 +1236,8 @@ pub mod g1 {
0x520f74773e74c8c3, 0x520f74773e74c8c3,
0x484c8fc982008f0, 0x484c8fc982008f0,
0xee2c3d922008cc6, 0xee2c3d922008cc6,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
@ -1235,7 +1252,8 @@ pub mod g1 {
0x9676ff02ec39c227, 0x9676ff02ec39c227,
0x4c12c15d7e55b9f3, 0x4c12c15d7e55b9f3,
0x57fd1e317db9bd, 0x57fd1e317db9bd,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x1288334016679345, 0x1288334016679345,
0xf955cd68615ff0b5, 0xf955cd68615ff0b5,
@ -1243,7 +1261,8 @@ pub mod g1 {
0x1267d70db51049fb, 0x1267d70db51049fb,
0x4696deb9ab2ba3e7, 0x4696deb9ab2ba3e7,
0xb1e4e11177f59d4, 0xb1e4e11177f59d4,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
@ -1673,7 +1692,8 @@ pub mod g2 {
0x7a17a004747e3dbe, 0x7a17a004747e3dbe,
0xcc65406a7c2e5a73, 0xcc65406a7c2e5a73,
0x10b8c03d64db4d0c, 0x10b8c03d64db4d0c,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xd30e70fe2f029778, 0xd30e70fe2f029778,
0xda30772df0f5212e, 0xda30772df0f5212e,
@ -1681,7 +1701,8 @@ pub mod g2 {
0xfb777e5b9b568608, 0xfb777e5b9b568608,
0x789bac1fec71a2b9, 0x789bac1fec71a2b9,
0x1342f02e2da54405, 0x1342f02e2da54405,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -1691,7 +1712,8 @@ pub mod g2 {
0x663015d9410eb608, 0x663015d9410eb608,
0x78e82a79d829a544, 0x78e82a79d829a544,
0x40a00545bb3c1e, 0x40a00545bb3c1e,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4709802348e79377, 0x4709802348e79377,
0xb5ac4dc9204bcfbd, 0xb5ac4dc9204bcfbd,
@ -1699,7 +1721,8 @@ pub mod g2 {
0x15008b1dc399e8df, 0x15008b1dc399e8df,
0x68128fd0548a3829, 0x68128fd0548a3829,
0x16a613db5c873aaa, 0x16a613db5c873aaa,
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@ -1718,7 +1741,8 @@ pub mod g2 {
0x41abba710d6c692c, 0x41abba710d6c692c,
0xffcc4b2b62ce8484, 0xffcc4b2b62ce8484,
0x6993ec01b8934ed, 0x6993ec01b8934ed,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xb94e92d5f874e26, 0xb94e92d5f874e26,
0x44516408bc115d95, 0x44516408bc115d95,
@ -1726,7 +1750,8 @@ pub mod g2 {
0xa5a0c2b7131f3555, 0xa5a0c2b7131f3555,
0x83800965822367e7, 0x83800965822367e7,
0x10cf1d3ad8d90bfa, 0x10cf1d3ad8d90bfa,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -1736,7 +1761,8 @@ pub mod g2 {
0x5a9171720e73eb51, 0x5a9171720e73eb51,
0x38eb4fd8d658adb7, 0x38eb4fd8d658adb7,
0xb649051bbc1164d, 0xb649051bbc1164d,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x9225814253d7df75, 0x9225814253d7df75,
0xc196c2513477f887, 0xc196c2513477f887,
@ -1744,7 +1770,8 @@ pub mod g2 {
0x55f2b8efad953e04, 0x55f2b8efad953e04,
0x7379345eda55265e, 0x7379345eda55265e,
0x377f2e6208fd4cb, 0x377f2e6208fd4cb,
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@ -1764,7 +1791,8 @@ pub mod g2 {
0x2199bc19c48c393d, 0x2199bc19c48c393d,
0x4a151b732a6075bf, 0x4a151b732a6075bf,
0x17762a3b9108c4a7, 0x17762a3b9108c4a7,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x26f461e944bbd3d1, 0x26f461e944bbd3d1,
0x298f3189a9cf6ed6, 0x298f3189a9cf6ed6,
@ -1772,7 +1800,8 @@ pub mod g2 {
0x7e147f3f9e6e241, 0x7e147f3f9e6e241,
0x72a9b63583963fff, 0x72a9b63583963fff,
0x158b0083c000462, 0x158b0083c000462,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -1782,7 +1811,8 @@ pub mod g2 {
0x68cad19430706b4d, 0x68cad19430706b4d,
0x3ccfb97b924dcea8, 0x3ccfb97b924dcea8,
0x1660f93434588f8d, 0x1660f93434588f8d,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xaaed3985b6dcb9c7, 0xaaed3985b6dcb9c7,
0xc1e985d6d898d9f4, 0xc1e985d6d898d9f4,
@ -1790,7 +1820,8 @@ pub mod g2 {
0x3940a2dbb914b529, 0x3940a2dbb914b529,
0xbeb88137cf34f3e7, 0xbeb88137cf34f3e7,
0x1699ee577c61b694, 0x1699ee577c61b694,
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@ -1810,7 +1841,8 @@ pub mod g2 {
0x72556c999f3707ac, 0x72556c999f3707ac,
0x4617f2e6774e9711, 0x4617f2e6774e9711,
0x100b2fe5bffe030b, 0x100b2fe5bffe030b,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x7a33555977ec608, 0x7a33555977ec608,
0xe23039d1fe9c0881, 0xe23039d1fe9c0881,
@ -1818,7 +1850,8 @@ pub mod g2 {
0x4637c4f417667e2e, 0x4637c4f417667e2e,
0x93ebe7c3e41f6acc, 0x93ebe7c3e41f6acc,
0xde884f89a9a371b, 0xde884f89a9a371b,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -1828,7 +1861,8 @@ pub mod g2 {
0x25fd427b4122f231, 0x25fd427b4122f231,
0xd83112aace35cae, 0xd83112aace35cae,
0x191b2432407cbb7f, 0x191b2432407cbb7f,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xf68ae82fe97662f5, 0xf68ae82fe97662f5,
0xe986057068b50b7d, 0xe986057068b50b7d,
@ -1836,7 +1870,8 @@ pub mod g2 {
0x9eaa6d19de569196, 0x9eaa6d19de569196,
0xf6a03d31e2ec2183, 0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b, 0x3bdafaf7ca9b39b,
])).unwrap(), ]))
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}; };
@ -1850,7 +1885,8 @@ pub mod g2 {
0x8e73a96b329ad190, 0x8e73a96b329ad190,
0x27c546f75ee1f3ab, 0x27c546f75ee1f3ab,
0xa33d27add5e7e82, 0xa33d27add5e7e82,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x93b1ebcd54870dfe, 0x93b1ebcd54870dfe,
0xf1578300e1342e11, 0xf1578300e1342e11,
@ -1858,7 +1894,8 @@ pub mod g2 {
0x2089faf462438296, 0x2089faf462438296,
0x828e5848cd48ea66, 0x828e5848cd48ea66,
0x141ecbac1deb038b, 0x141ecbac1deb038b,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -1868,7 +1905,8 @@ pub mod g2 {
0x2767032fc37cc31d, 0x2767032fc37cc31d,
0xd5ee2aba84fd10fe, 0xd5ee2aba84fd10fe,
0x16576ccd3dd0a4e8, 0x16576ccd3dd0a4e8,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4da9b6f6a96d1dd2, 0x4da9b6f6a96d1dd2,
0x9657f7da77f1650e, 0x9657f7da77f1650e,
@ -1876,7 +1914,8 @@ pub mod g2 {
0x31898db63f87363a, 0x31898db63f87363a,
0xabab040ddbd097cc, 0xabab040ddbd097cc,
0x11ad236b9ba02990, 0x11ad236b9ba02990,
])).unwrap(), ]))
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}); });
@ -1894,7 +1933,8 @@ pub mod g2 {
0xf1273e6406eef9cc, 0xf1273e6406eef9cc,
0xababd760ff05cb92, 0xababd760ff05cb92,
0xd7c20456617e89 0xd7c20456617e89
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xd1a50b8572cbd2b8, 0xd1a50b8572cbd2b8,
0x238f0ac6119d07df, 0x238f0ac6119d07df,
@ -1902,7 +1942,8 @@ pub mod g2 {
0x8b203284c51edf6b, 0x8b203284c51edf6b,
0xc8a0b730bbb21f5e, 0xc8a0b730bbb21f5e,
0x1a3b59d29a31274 0x1a3b59d29a31274
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -1912,7 +1953,8 @@ pub mod g2 {
0x64528ab3863633dc, 0x64528ab3863633dc,
0x159384333d7cba97, 0x159384333d7cba97,
0x4cb84741f3cafe8 0x4cb84741f3cafe8
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x242af0dc3640e1a4, 0x242af0dc3640e1a4,
0xe90a73ad65c66919, 0xe90a73ad65c66919,
@ -1920,7 +1962,8 @@ pub mod g2 {
0x38528f92b689644d, 0x38528f92b689644d,
0xb6884deec59fb21f, 0xb6884deec59fb21f,
0x3c075d3ec52ba90 0x3c075d3ec52ba90
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
} }
@ -1938,7 +1981,8 @@ pub mod g2 {
0x72556c999f3707ac, 0x72556c999f3707ac,
0x4617f2e6774e9711, 0x4617f2e6774e9711,
0x100b2fe5bffe030b, 0x100b2fe5bffe030b,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x7a33555977ec608, 0x7a33555977ec608,
0xe23039d1fe9c0881, 0xe23039d1fe9c0881,
@ -1946,7 +1990,8 @@ pub mod g2 {
0x4637c4f417667e2e, 0x4637c4f417667e2e,
0x93ebe7c3e41f6acc, 0x93ebe7c3e41f6acc,
0xde884f89a9a371b, 0xde884f89a9a371b,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -1956,7 +2001,8 @@ pub mod g2 {
0x25fd427b4122f231, 0x25fd427b4122f231,
0xd83112aace35cae, 0xd83112aace35cae,
0x191b2432407cbb7f, 0x191b2432407cbb7f,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xf68ae82fe97662f5, 0xf68ae82fe97662f5,
0xe986057068b50b7d, 0xe986057068b50b7d,
@ -1964,7 +2010,8 @@ pub mod g2 {
0x9eaa6d19de569196, 0x9eaa6d19de569196,
0xf6a03d31e2ec2183, 0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b, 0x3bdafaf7ca9b39b,
])).unwrap(), ]))
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}; };
@ -1984,7 +2031,8 @@ pub mod g2 {
0xbcedcfce1e52d986, 0xbcedcfce1e52d986,
0x9755d4a3926e9862, 0x9755d4a3926e9862,
0x18bab73760fd8024 0x18bab73760fd8024
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4e7c5e0a2ae5b99e, 0x4e7c5e0a2ae5b99e,
0x96e582a27f028961, 0x96e582a27f028961,
@ -1992,7 +2040,8 @@ pub mod g2 {
0xeb0cf5e610ef4fe7, 0xeb0cf5e610ef4fe7,
0x7b4c2bae8db6e70b, 0x7b4c2bae8db6e70b,
0xf136e43909fca0 0xf136e43909fca0
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -2002,7 +2051,8 @@ pub mod g2 {
0xa5a2a51f7fde787b, 0xa5a2a51f7fde787b,
0x8b92866bc6384188, 0x8b92866bc6384188,
0x81a53fe531d64ef 0x81a53fe531d64ef
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4c5d607666239b34, 0x4c5d607666239b34,
0xeddb5f48304d14b3, 0xeddb5f48304d14b3,
@ -2010,7 +2060,8 @@ pub mod g2 {
0xb271f52f12ead742, 0xb271f52f12ead742,
0x244e6c2015c83348, 0x244e6c2015c83348,
0x19e2deae6eb9b441 0x19e2deae6eb9b441
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
} }

View File

@ -1582,26 +1582,24 @@ fn test_fq_is_valid() {
a.0.sub_noborrow(&FqRepr::from(1)); a.0.sub_noborrow(&FqRepr::from(1));
assert!(a.is_valid()); assert!(a.is_valid());
assert!(Fq(FqRepr::from(0)).is_valid()); assert!(Fq(FqRepr::from(0)).is_valid());
assert!( assert!(Fq(FqRepr([
Fq(FqRepr([ 0xdf4671abd14dab3e,
0xdf4671abd14dab3e, 0xe2dc0c9f534fbd33,
0xe2dc0c9f534fbd33, 0x31ca6c880cc444a6,
0x31ca6c880cc444a6, 0x257a67e70ef33359,
0x257a67e70ef33359, 0xf9b29e493f899b36,
0xf9b29e493f899b36, 0x17c8be1800b9f059
0x17c8be1800b9f059 ]))
])).is_valid() .is_valid());
); assert!(!Fq(FqRepr([
assert!( 0xffffffffffffffff,
!Fq(FqRepr([ 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff
0xffffffffffffffff, ]))
0xffffffffffffffff .is_valid());
])).is_valid()
);
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
@ -1949,7 +1947,8 @@ fn test_fq_squaring() {
0xdc05c659b4e15b27, 0xdc05c659b4e15b27,
0x79361e5a802c6a23, 0x79361e5a802c6a23,
0x24bcbe5d51b9a6f 0x24bcbe5d51b9a6f
])).unwrap() ]))
.unwrap()
); );
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@ -2099,16 +2098,15 @@ fn test_fq_sqrt() {
#[test] #[test]
fn test_fq_from_into_repr() { fn test_fq_from_into_repr() {
// q + 1 should not be in the field // q + 1 should not be in the field
assert!( assert!(Fq::from_repr(FqRepr([
Fq::from_repr(FqRepr([ 0xb9feffffffffaaac,
0xb9feffffffffaaac, 0x1eabfffeb153ffff,
0x1eabfffeb153ffff, 0x6730d2a0f6b0f624,
0x6730d2a0f6b0f624, 0x64774b84f38512bf,
0x64774b84f38512bf, 0x4b1ba7b6434bacd7,
0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a
0x1a0111ea397fe69a ]))
])).is_err() .is_err());
);
// q should not be in the field // q should not be in the field
assert!(Fq::from_repr(Fq::char()).is_err()); assert!(Fq::from_repr(Fq::char()).is_err());

View File

@ -2,7 +2,7 @@ use super::fq::FROBENIUS_COEFF_FQ12_C1;
use super::fq2::Fq2; use super::fq2::Fq2;
use super::fq6::Fq6; use super::fq6::Fq6;
use ff::Field; use ff::Field;
use rand_core::{RngCore}; use rand_core::RngCore;
/// An element of Fq12, represented by c0 + c1 * w. /// An element of Fq12, represented by c0 + c1 * w.
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]

View File

@ -1,4 +1,4 @@
use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE}; use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE};
use ff::{Field, SqrtField}; use ff::{Field, SqrtField};
use rand_core::RngCore; use rand_core::RngCore;
@ -261,12 +261,11 @@ fn test_fq2_basics() {
); );
assert!(Fq2::zero().is_zero()); assert!(Fq2::zero().is_zero());
assert!(!Fq2::one().is_zero()); assert!(!Fq2::one().is_zero());
assert!( assert!(!Fq2 {
!Fq2 { c0: Fq::zero(),
c0: Fq::zero(), c1: Fq::one(),
c1: Fq::one(), }
}.is_zero() .is_zero());
);
} }
#[test] #[test]
@ -309,7 +308,8 @@ fn test_fq2_squaring() {
0xf7f295a94e58ae7c, 0xf7f295a94e58ae7c,
0x41b76dcc1c3fbe5e, 0x41b76dcc1c3fbe5e,
0x7080c5fa1d8e042, 0x7080c5fa1d8e042,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x38f473b3c870a4ab, 0x38f473b3c870a4ab,
0x6ad3291177c8c7e5, 0x6ad3291177c8c7e5,
@ -317,7 +317,8 @@ fn test_fq2_squaring() {
0xbfb99020604137a0, 0xbfb99020604137a0,
0xfc58a7b7be815407, 0xfc58a7b7be815407,
0x10d1615e75250a21, 0x10d1615e75250a21,
])).unwrap(), ]))
.unwrap(),
}; };
a.square(); a.square();
assert_eq!( assert_eq!(
@ -330,7 +331,8 @@ fn test_fq2_squaring() {
0xcb674157618da176, 0xcb674157618da176,
0x4cf17b5893c3d327, 0x4cf17b5893c3d327,
0x7eac81369c43361 0x7eac81369c43361
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xc1579cf58e980cf8, 0xc1579cf58e980cf8,
0xa23eb7e12dd54d98, 0xa23eb7e12dd54d98,
@ -338,7 +340,8 @@ fn test_fq2_squaring() {
0x38d0d7275a9689e1, 0x38d0d7275a9689e1,
0x739c983042779a65, 0x739c983042779a65,
0x1542a61c8a8db994 0x1542a61c8a8db994
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@ -356,7 +359,8 @@ fn test_fq2_mul() {
0x9ee53e7e84d7532e, 0x9ee53e7e84d7532e,
0x1c202d8ed97afb45, 0x1c202d8ed97afb45,
0x51d3f9253e2516f, 0x51d3f9253e2516f,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf, 0xa7348a8b511aedcf,
0x143c215d8176b319, 0x143c215d8176b319,
@ -364,7 +368,8 @@ fn test_fq2_mul() {
0x9533e4a9a5158be, 0x9533e4a9a5158be,
0x7a5e1ecb676d65f9, 0x7a5e1ecb676d65f9,
0x180c3ee46656b008, 0x180c3ee46656b008,
])).unwrap(), ]))
.unwrap(),
}; };
a.mul_assign(&Fq2 { a.mul_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -374,7 +379,8 @@ fn test_fq2_mul() {
0xcd460f9f0c23e430, 0xcd460f9f0c23e430,
0x6c9110292bfa409, 0x6c9110292bfa409,
0x2c93a72eb8af83e, 0x2c93a72eb8af83e,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4b1c3f936d8992d4, 0x4b1c3f936d8992d4,
0x1d2a72916dba4c8a, 0x1d2a72916dba4c8a,
@ -382,7 +388,8 @@ fn test_fq2_mul() {
0x57a06d3135a752ae, 0x57a06d3135a752ae,
0x634cd3c6c565096d, 0x634cd3c6c565096d,
0x19e17334d4e93558, 0x19e17334d4e93558,
])).unwrap(), ]))
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@ -394,7 +401,8 @@ fn test_fq2_mul() {
0x5511fe4d84ee5f78, 0x5511fe4d84ee5f78,
0x5310a202d92f9963, 0x5310a202d92f9963,
0x1751afbe166e5399 0x1751afbe166e5399
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x84af0e1bd630117a, 0x84af0e1bd630117a,
0x6c63cd4da2c2aa7, 0x6c63cd4da2c2aa7,
@ -402,7 +410,8 @@ fn test_fq2_mul() {
0xc975106579c275ee, 0xc975106579c275ee,
0x33a9ac82ce4c5083, 0x33a9ac82ce4c5083,
0x1ef1a36c201589d 0x1ef1a36c201589d
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@ -422,7 +431,8 @@ fn test_fq2_inverse() {
0x9ee53e7e84d7532e, 0x9ee53e7e84d7532e,
0x1c202d8ed97afb45, 0x1c202d8ed97afb45,
0x51d3f9253e2516f, 0x51d3f9253e2516f,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf, 0xa7348a8b511aedcf,
0x143c215d8176b319, 0x143c215d8176b319,
@ -430,7 +440,8 @@ fn test_fq2_inverse() {
0x9533e4a9a5158be, 0x9533e4a9a5158be,
0x7a5e1ecb676d65f9, 0x7a5e1ecb676d65f9,
0x180c3ee46656b008, 0x180c3ee46656b008,
])).unwrap(), ]))
.unwrap(),
}; };
let a = a.inverse().unwrap(); let a = a.inverse().unwrap();
assert_eq!( assert_eq!(
@ -443,7 +454,8 @@ fn test_fq2_inverse() {
0xdfba703293941c30, 0xdfba703293941c30,
0xa6c3d8f9586f2636, 0xa6c3d8f9586f2636,
0x1351ef01941b70c4 0x1351ef01941b70c4
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x8c39fd76a8312cb4, 0x8c39fd76a8312cb4,
0x15d7b6b95defbff0, 0x15d7b6b95defbff0,
@ -451,7 +463,8 @@ fn test_fq2_inverse() {
0xcbf651a0f367afb2, 0xcbf651a0f367afb2,
0xdf4e54f0d3ef15a6, 0xdf4e54f0d3ef15a6,
0x103bdf241afb0019 0x103bdf241afb0019
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@ -469,7 +482,8 @@ fn test_fq2_addition() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@ -477,7 +491,8 @@ fn test_fq2_addition() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.add_assign(&Fq2 { a.add_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -487,7 +502,8 @@ fn test_fq2_addition() {
0x3b88899a42a6318f, 0x3b88899a42a6318f,
0x986a4a62fa82a49d, 0x986a4a62fa82a49d,
0x13ce433fa26027f5, 0x13ce433fa26027f5,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9, 0x66323bf80b58b9b9,
0xa1379b6facf6e596, 0xa1379b6facf6e596,
@ -495,7 +511,8 @@ fn test_fq2_addition() {
0x2236f55246d0d44d, 0x2236f55246d0d44d,
0x4c8c1800eb104566, 0x4c8c1800eb104566,
0x11d6e20e986c2085, 0x11d6e20e986c2085,
])).unwrap(), ]))
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@ -507,7 +524,8 @@ fn test_fq2_addition() {
0xf4ef57d604b6bca2, 0xf4ef57d604b6bca2,
0x65309427b3d5d090, 0x65309427b3d5d090,
0x14c715d5553f01d2 0x14c715d5553f01d2
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xfdb032e7d9079a94, 0xfdb032e7d9079a94,
0x35a2809d15468d83, 0x35a2809d15468d83,
@ -515,7 +533,8 @@ fn test_fq2_addition() {
0xd62fa51334f560fa, 0xd62fa51334f560fa,
0x9ad265eb46e01984, 0x9ad265eb46e01984,
0x1303f3465112c8bc 0x1303f3465112c8bc
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@ -533,7 +552,8 @@ fn test_fq2_subtraction() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@ -541,7 +561,8 @@ fn test_fq2_subtraction() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.sub_assign(&Fq2 { a.sub_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -551,7 +572,8 @@ fn test_fq2_subtraction() {
0x3b88899a42a6318f, 0x3b88899a42a6318f,
0x986a4a62fa82a49d, 0x986a4a62fa82a49d,
0x13ce433fa26027f5, 0x13ce433fa26027f5,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9, 0x66323bf80b58b9b9,
0xa1379b6facf6e596, 0xa1379b6facf6e596,
@ -559,7 +581,8 @@ fn test_fq2_subtraction() {
0x2236f55246d0d44d, 0x2236f55246d0d44d,
0x4c8c1800eb104566, 0x4c8c1800eb104566,
0x11d6e20e986c2085, 0x11d6e20e986c2085,
])).unwrap(), ]))
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@ -571,7 +594,8 @@ fn test_fq2_subtraction() {
0xe255902672ef6c43, 0xe255902672ef6c43,
0x7f77a718021c342d, 0x7f77a718021c342d,
0x72ba14049fe9881 0x72ba14049fe9881
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xeb4abaf7c255d1cd, 0xeb4abaf7c255d1cd,
0x11df49bc6cacc256, 0x11df49bc6cacc256,
@ -579,7 +603,8 @@ fn test_fq2_subtraction() {
0xf63905f39ad8cb1f, 0xf63905f39ad8cb1f,
0x4cd5dd9fb40b3b8f, 0x4cd5dd9fb40b3b8f,
0x957411359ba6e4c 0x957411359ba6e4c
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@ -597,7 +622,8 @@ fn test_fq2_negation() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@ -605,7 +631,8 @@ fn test_fq2_negation() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.negate(); a.negate();
assert_eq!( assert_eq!(
@ -618,7 +645,8 @@ fn test_fq2_negation() {
0xab107d49317487ab, 0xab107d49317487ab,
0x7e555df189f880e3, 0x7e555df189f880e3,
0x19083f5486a10cbd 0x19083f5486a10cbd
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x228109103250c9d0, 0x228109103250c9d0,
0x8a411ad149045812, 0x8a411ad149045812,
@ -626,7 +654,8 @@ fn test_fq2_negation() {
0xb07e9bc405608611, 0xb07e9bc405608611,
0xfcd559cbe77bd8b8, 0xfcd559cbe77bd8b8,
0x18d400b280d93e62 0x18d400b280d93e62
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@ -644,7 +673,8 @@ fn test_fq2_doubling() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@ -652,7 +682,8 @@ fn test_fq2_doubling() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.double(); a.double();
assert_eq!( assert_eq!(
@ -665,7 +696,8 @@ fn test_fq2_doubling() {
0x72cd9c7784211627, 0x72cd9c7784211627,
0x998c938972a657e7, 0x998c938972a657e7,
0x1f1a52b65bdb3b9 0x1f1a52b65bdb3b9
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x2efbeddf9b5dc1b6, 0x2efbeddf9b5dc1b6,
0x28d5ca5ad09f4fdb, 0x28d5ca5ad09f4fdb,
@ -673,7 +705,8 @@ fn test_fq2_doubling() {
0x67f15f81dc49195b, 0x67f15f81dc49195b,
0x9c8c9bd4b79fa83d, 0x9c8c9bd4b79fa83d,
0x25a226f714d506e 0x25a226f714d506e
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@ -691,7 +724,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@ -699,7 +733,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.frobenius_map(0); a.frobenius_map(0);
assert_eq!( assert_eq!(
@ -712,7 +747,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@ -720,7 +756,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])).unwrap(), ]))
.unwrap(),
} }
); );
a.frobenius_map(1); a.frobenius_map(1);
@ -734,7 +771,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x228109103250c9d0, 0x228109103250c9d0,
0x8a411ad149045812, 0x8a411ad149045812,
@ -742,7 +780,8 @@ fn test_fq2_frobenius_map() {
0xb07e9bc405608611, 0xb07e9bc405608611,
0xfcd559cbe77bd8b8, 0xfcd559cbe77bd8b8,
0x18d400b280d93e62 0x18d400b280d93e62
])).unwrap(), ]))
.unwrap(),
} }
); );
a.frobenius_map(1); a.frobenius_map(1);
@ -756,7 +795,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@ -764,7 +804,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])).unwrap(), ]))
.unwrap(),
} }
); );
a.frobenius_map(2); a.frobenius_map(2);
@ -778,7 +819,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@ -786,7 +828,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@ -805,7 +848,8 @@ fn test_fq2_sqrt() {
0xdb4a116b5bf74aa1, 0xdb4a116b5bf74aa1,
0x1e58b2159dfe10e2, 0x1e58b2159dfe10e2,
0x7ca7da1f13606ac 0x7ca7da1f13606ac
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xfa8de88b7516d2c3, 0xfa8de88b7516d2c3,
0x371a75ed14f41629, 0x371a75ed14f41629,
@ -813,9 +857,11 @@ fn test_fq2_sqrt() {
0x212611bca4e99121, 0x212611bca4e99121,
0x8ee5394d77afb3d, 0x8ee5394d77afb3d,
0xec92336650e49d5 0xec92336650e49d5
])).unwrap(), ]))
}.sqrt()
.unwrap(), .unwrap(),
}
.sqrt()
.unwrap(),
Fq2 { Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
0x40b299b2704258c5, 0x40b299b2704258c5,
@ -824,7 +870,8 @@ fn test_fq2_sqrt() {
0x8d7f1f723d02c1d3, 0x8d7f1f723d02c1d3,
0x881b3e01b611c070, 0x881b3e01b611c070,
0x10f6963bbad2ebc5 0x10f6963bbad2ebc5
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xc099534fc209e752, 0xc099534fc209e752,
0x7670594665676447, 0x7670594665676447,
@ -832,7 +879,8 @@ fn test_fq2_sqrt() {
0x6b852aeaf2afcb1b, 0x6b852aeaf2afcb1b,
0xa4c93b08105d71a9, 0xa4c93b08105d71a9,
0x8d7cfff94216330 0x8d7cfff94216330
])).unwrap(), ]))
.unwrap(),
} }
); );
@ -845,10 +893,12 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf, 0x64774b84f38512bf,
0x4b1ba7b6434bacd7, 0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a 0x1a0111ea397fe69a
])).unwrap(), ]))
c1: Fq::zero(),
}.sqrt()
.unwrap(), .unwrap(),
c1: Fq::zero(),
}
.sqrt()
.unwrap(),
Fq2 { Fq2 {
c0: Fq::zero(), c0: Fq::zero(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
@ -858,7 +908,8 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf, 0x64774b84f38512bf,
0x4b1ba7b6434bacd7, 0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a 0x1a0111ea397fe69a
])).unwrap(), ]))
.unwrap(),
} }
); );
} }

View File

@ -17,7 +17,6 @@ impl ::std::fmt::Display for Fq6 {
} }
} }
impl Fq6 { impl Fq6 {
/// Multiply by quadratic nonresidue v. /// Multiply by quadratic nonresidue v.
pub fn mul_by_nonresidue(&mut self) { pub fn mul_by_nonresidue(&mut self) {

View File

@ -388,22 +388,20 @@ fn test_fr_is_valid() {
a.0.sub_noborrow(&FrRepr::from(1)); a.0.sub_noborrow(&FrRepr::from(1));
assert!(a.is_valid()); assert!(a.is_valid());
assert!(Fr(FrRepr::from(0)).is_valid()); assert!(Fr(FrRepr::from(0)).is_valid());
assert!( assert!(Fr(FrRepr([
Fr(FrRepr([ 0xffffffff00000000,
0xffffffff00000000, 0x53bda402fffe5bfe,
0x53bda402fffe5bfe, 0x3339d80809a1d805,
0x3339d80809a1d805, 0x73eda753299d7d48
0x73eda753299d7d48 ]))
])).is_valid() .is_valid());
); assert!(!Fr(FrRepr([
assert!( 0xffffffffffffffff,
!Fr(FrRepr([ 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff
0xffffffffffffffff, ]))
0xffffffffffffffff .is_valid());
])).is_valid()
);
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
@ -707,7 +705,8 @@ fn test_fr_squaring() {
0xb79a310579e76ec2, 0xb79a310579e76ec2,
0xac1da8d0a9af4e5f, 0xac1da8d0a9af4e5f,
0x13f629c49bf23e97 0x13f629c49bf23e97
])).unwrap() ]))
.unwrap()
); );
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@ -857,14 +856,13 @@ fn test_fr_sqrt() {
#[test] #[test]
fn test_fr_from_into_repr() { fn test_fr_from_into_repr() {
// r + 1 should not be in the field // r + 1 should not be in the field
assert!( assert!(Fr::from_repr(FrRepr([
Fr::from_repr(FrRepr([ 0xffffffff00000002,
0xffffffff00000002, 0x53bda402fffe5bfe,
0x53bda402fffe5bfe, 0x3339d80809a1d805,
0x3339d80809a1d805, 0x73eda753299d7d48
0x73eda753299d7d48 ]))
])).is_err() .is_err());
);
// r should not be in the field // r should not be in the field
assert!(Fr::from_repr(Fr::char()).is_err()); assert!(Fr::from_repr(Fr::char()).is_err());
@ -967,7 +965,8 @@ fn test_fr_display() {
0x185ec8eb3f5b5aee, 0x185ec8eb3f5b5aee,
0x684499ffe4b9dd99, 0x684499ffe4b9dd99,
0x7c9bba7afb68faa 0x7c9bba7afb68faa
])).unwrap() ]))
.unwrap()
), ),
"Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string() "Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string()
); );
@ -979,7 +978,8 @@ fn test_fr_display() {
0xb0ad10817df79b6a, 0xb0ad10817df79b6a,
0xd034a80a2b74132b, 0xd034a80a2b74132b,
0x41cf9a1336f50719 0x41cf9a1336f50719
])).unwrap() ]))
.unwrap()
), ),
"Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string() "Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string()
); );

View File

@ -9,8 +9,8 @@ mod fr;
mod tests; mod tests;
pub use self::ec::{ pub use self::ec::{
G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2, G2Affine, G2Compressed, G2Prepared, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared,
G2Uncompressed, G2Uncompressed, G1, G2,
}; };
pub use self::fq::{Fq, FqRepr}; pub use self::fq::{Fq, FqRepr};
pub use self::fq12::Fq12; pub use self::fq12::Fq12;

View File

@ -37,8 +37,7 @@ pub trait Engine: ScalarEngine {
Base = Self::Fq, Base = Self::Fq,
Scalar = Self::Fr, Scalar = Self::Fr,
Affine = Self::G1Affine, Affine = Self::G1Affine,
> > + From<Self::G1Affine>;
+ From<Self::G1Affine>;
/// The affine representation of an element in G1. /// The affine representation of an element in G1.
type G1Affine: PairingCurveAffine< type G1Affine: PairingCurveAffine<
@ -48,8 +47,7 @@ pub trait Engine: ScalarEngine {
Projective = Self::G1, Projective = Self::G1,
Pair = Self::G2Affine, Pair = Self::G2Affine,
PairingResult = Self::Fqk, PairingResult = Self::Fqk,
> > + From<Self::G1>;
+ From<Self::G1>;
/// The projective representation of an element in G2. /// The projective representation of an element in G2.
type G2: CurveProjective< type G2: CurveProjective<
@ -57,8 +55,7 @@ pub trait Engine: ScalarEngine {
Base = Self::Fqe, Base = Self::Fqe,
Scalar = Self::Fr, Scalar = Self::Fr,
Affine = Self::G2Affine, Affine = Self::G2Affine,
> > + From<Self::G2Affine>;
+ From<Self::G2Affine>;
/// The affine representation of an element in G2. /// The affine representation of an element in G2.
type G2Affine: PairingCurveAffine< type G2Affine: PairingCurveAffine<
@ -68,8 +65,7 @@ pub trait Engine: ScalarEngine {
Projective = Self::G2, Projective = Self::G2,
Pair = Self::G1Affine, Pair = Self::G1Affine,
PairingResult = Self::Fqk, PairingResult = Self::Fqk,
> > + From<Self::G2>;
+ From<Self::G2>;
/// The base field that hosts G1. /// The base field that hosts G1.
type Fq: PrimeField + SqrtField; type Fq: PrimeField + SqrtField;
@ -101,7 +97,8 @@ pub trait Engine: ScalarEngine {
{ {
Self::final_exponentiation(&Self::miller_loop( Self::final_exponentiation(&Self::miller_loop(
[(&(p.into().prepare()), &(q.into().prepare()))].iter(), [(&(p.into().prepare()), &(q.into().prepare()))].iter(),
)).unwrap() ))
.unwrap()
} }
} }

View File

@ -117,8 +117,8 @@ pub fn from_str_tests<F: PrimeField>() {
{ {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
for _ in 0..1000 { for _ in 0..1000 {

View File

@ -5,11 +5,11 @@
use bech32::{self, Error, FromBase32, ToBase32}; use bech32::{self, Error, FromBase32, ToBase32};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use std::io::{self, Write};
use zcash_primitives::{ use zcash_primitives::{
jubjub::edwards, jubjub::edwards,
primitives::{Diversifier, PaymentAddress}, primitives::{Diversifier, PaymentAddress},
}; };
use std::io::{self, Write};
use zcash_primitives::{ use zcash_primitives::{
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB, JUBJUB,
@ -187,11 +187,11 @@ mod tests {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::SeedableRng; use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use zcash_primitives::JUBJUB;
use zcash_primitives::{ use zcash_primitives::{
jubjub::edwards, jubjub::edwards,
primitives::{Diversifier, PaymentAddress}, primitives::{Diversifier, PaymentAddress},
}; };
use zcash_primitives::JUBJUB;
use super::{decode_payment_address, encode_payment_address}; use super::{decode_payment_address, encode_payment_address};
use crate::constants; use crate::constants;

View File

@ -1,14 +1,14 @@
#![feature(test)] #![feature(test)]
extern crate pairing;
extern crate rand_core; extern crate rand_core;
extern crate rand_os; extern crate rand_os;
extern crate test; extern crate test;
extern crate pairing;
extern crate zcash_primitives; extern crate zcash_primitives;
use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand_core::RngCore;
use rand_os::OsRng; use rand_os::OsRng;
use pairing::bls12_381::Bls12;
use zcash_primitives::jubjub::JubjubBls12; use zcash_primitives::jubjub::JubjubBls12;
use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization}; use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization};
@ -16,10 +16,10 @@ use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization};
fn bench_pedersen_hash(b: &mut test::Bencher) { fn bench_pedersen_hash(b: &mut test::Bencher) {
let params = JubjubBls12::new(); let params = JubjubBls12::new();
let rng = &mut OsRng; let rng = &mut OsRng;
let bits = (0..510).map(|_| (rng.next_u32() % 2) != 0).collect::<Vec<_>>(); let bits = (0..510)
.map(|_| (rng.next_u32() % 2) != 0)
.collect::<Vec<_>>();
let personalization = Personalization::MerkleTree(31); let personalization = Personalization::MerkleTree(31);
b.iter(|| { b.iter(|| pedersen_hash::<Bls12, _>(personalization, bits.clone(), &params));
pedersen_hash::<Bls12, _>(personalization, bits.clone(), &params)
});
} }

View File

@ -2,39 +2,31 @@
/// This is chosen to be some random string that we couldn't have anticipated when we designed /// This is chosen to be some random string that we couldn't have anticipated when we designed
/// the algorithm, for rigidity purposes. /// the algorithm, for rigidity purposes.
/// We deliberately use an ASCII hex string of 32 bytes here. /// We deliberately use an ASCII hex string of 32 bytes here.
pub const GH_FIRST_BLOCK: &'static [u8; 64] pub const GH_FIRST_BLOCK: &'static [u8; 64] =
= b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0"; b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0";
// BLAKE2s invocation personalizations // BLAKE2s invocation personalizations
/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk) /// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk)
pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] = b"Zcashivk";
= b"Zcashivk";
/// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho) /// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho)
pub const PRF_NF_PERSONALIZATION: &'static [u8; 8] pub const PRF_NF_PERSONALIZATION: &'static [u8; 8] = b"Zcash_nf";
= b"Zcash_nf";
// Group hash personalizations // Group hash personalizations
/// BLAKE2s Personalization for Pedersen hash generators. /// BLAKE2s Personalization for Pedersen hash generators.
pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] = b"Zcash_PH";
= b"Zcash_PH";
/// BLAKE2s Personalization for the group hash for key diversification /// BLAKE2s Personalization for the group hash for key diversification
pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] = b"Zcash_gd";
= b"Zcash_gd";
/// BLAKE2s Personalization for the spending key base point /// BLAKE2s Personalization for the spending key base point
pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_G_";
= b"Zcash_G_";
/// BLAKE2s Personalization for the proof generation key base point /// BLAKE2s Personalization for the proof generation key base point
pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_H_";
= b"Zcash_H_";
/// BLAKE2s Personalization for the value commitment generator for the value /// BLAKE2s Personalization for the value commitment generator for the value
pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8] pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_cv";
= b"Zcash_cv";
/// BLAKE2s Personalization for the nullifier position generator (for computing rho) /// BLAKE2s Personalization for the nullifier position generator (for computing rho)
pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_J_";
= b"Zcash_J_";

View File

@ -1,12 +1,6 @@
use jubjub::{ use jubjub::{edwards, JubjubEngine, PrimeOrder};
JubjubEngine,
PrimeOrder,
edwards
};
use ff::{ use ff::PrimeField;
PrimeField
};
use blake2s_simd::Params; use blake2s_simd::Params;
use constants; use constants;
@ -17,9 +11,8 @@ use constants;
pub fn group_hash<E: JubjubEngine>( pub fn group_hash<E: JubjubEngine>(
tag: &[u8], tag: &[u8],
personalization: &[u8], personalization: &[u8],
params: &E::Params params: &E::Params,
) -> Option<edwards::Point<E, PrimeOrder>> ) -> Option<edwards::Point<E, PrimeOrder>> {
{
assert_eq!(personalization.len(), 8); assert_eq!(personalization.len(), 8);
// Check to see that scalar field is 255 bits // Check to see that scalar field is 255 bits
@ -42,7 +35,7 @@ pub fn group_hash<E: JubjubEngine>(
} else { } else {
None None
} }
}, }
Err(_) => None Err(_) => None,
} }
} }

View File

@ -1,22 +1,12 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use super::{ use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
JubjubEngine,
JubjubParams,
Unknown,
PrimeOrder,
montgomery
};
use rand_core::RngCore; use rand_core::RngCore;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::io::{ use std::io::{self, Read, Write};
self,
Write,
Read
};
// Represents the affine point (X/Z, Y/Z) via the extended // Represents the affine point (X/Z, Y/Z) via the extended
// twisted Edwards coordinates. // twisted Edwards coordinates.
@ -29,46 +19,38 @@ pub struct Point<E: JubjubEngine, Subgroup> {
y: E::Fr, y: E::Fr,
t: E::Fr, t: E::Fr,
z: E::Fr, z: E::Fr,
_marker: PhantomData<Subgroup> _marker: PhantomData<Subgroup>,
} }
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2> fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2> {
{
Point { Point {
x: from.x, x: from.x,
y: from.y, y: from.y,
t: from.t, t: from.t,
z: from.z, z: from.z,
_marker: PhantomData _marker: PhantomData,
} }
} }
impl<E: JubjubEngine> From<&Point<E, Unknown>> for Point<E, Unknown> impl<E: JubjubEngine> From<&Point<E, Unknown>> for Point<E, Unknown> {
{ fn from(p: &Point<E, Unknown>) -> Point<E, Unknown> {
fn from(p: &Point<E, Unknown>) -> Point<E, Unknown>
{
p.clone() p.clone()
} }
} }
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown> impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown> {
{ fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown> {
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
{
convert_subgroup(&p) convert_subgroup(&p)
} }
} }
impl<E: JubjubEngine> From<&Point<E, PrimeOrder>> for Point<E, Unknown> impl<E: JubjubEngine> From<&Point<E, PrimeOrder>> for Point<E, Unknown> {
{ fn from(p: &Point<E, PrimeOrder>) -> Point<E, Unknown> {
fn from(p: &Point<E, PrimeOrder>) -> Point<E, Unknown>
{
convert_subgroup(p) convert_subgroup(p)
} }
} }
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup> impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup> {
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
convert_subgroup(self) convert_subgroup(self)
} }
@ -99,11 +81,7 @@ impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
} }
impl<E: JubjubEngine> Point<E, Unknown> { impl<E: JubjubEngine> Point<E, Unknown> {
pub fn read<R: Read>( pub fn read<R: Read>(reader: R, params: &E::Params) -> io::Result<Self> {
reader: R,
params: &E::Params
) -> io::Result<Self>
{
let mut y_repr = <E::Fr as PrimeField>::Repr::default(); let mut y_repr = <E::Fr as PrimeField>::Repr::default();
y_repr.read_le(reader)?; y_repr.read_le(reader)?;
@ -111,22 +89,18 @@ impl<E: JubjubEngine> Point<E, Unknown> {
y_repr.as_mut()[3] &= 0x7fffffffffffffff; y_repr.as_mut()[3] &= 0x7fffffffffffffff;
match E::Fr::from_repr(y_repr) { match E::Fr::from_repr(y_repr) {
Ok(y) => { Ok(y) => match Self::get_for_y(y, x_sign, params) {
match Self::get_for_y(y, x_sign, params) { Some(p) => Ok(p),
Some(p) => Ok(p), None => Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")),
None => {
Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve"))
}
}
}, },
Err(_) => { Err(_) => Err(io::Error::new(
Err(io::Error::new(io::ErrorKind::InvalidInput, "y is not in field")) io::ErrorKind::InvalidInput,
} "y is not in field",
)),
} }
} }
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self> pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self> {
{
// Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1) // Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1)
// This is defined for all valid y-coordinates, // This is defined for all valid y-coordinates,
// as dy^2 + 1 = 0 has no solution in Fr. // as dy^2 + 1 = 0 has no solution in Fr.
@ -162,29 +136,25 @@ impl<E: JubjubEngine> Point<E, Unknown> {
y: y, y: y,
t: t, t: t,
z: E::Fr::one(), z: E::Fr::one(),
_marker: PhantomData _marker: PhantomData,
}) })
}, }
None => None None => None,
} }
}, }
None => None None => None,
} }
} }
/// This guarantees the point is in the prime order subgroup /// This guarantees the point is in the prime order subgroup
#[must_use] #[must_use]
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder> pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder> {
{ let tmp = self.double(params).double(params).double(params);
let tmp = self.double(params)
.double(params)
.double(params);
convert_subgroup(&tmp) convert_subgroup(&tmp)
} }
pub fn rand<R: RngCore>(rng: &mut R, params: &E::Params) -> Self pub fn rand<R: RngCore>(rng: &mut R, params: &E::Params) -> Self {
{
loop { loop {
let y = E::Fr::random(rng); let y = E::Fr::random(rng);
let sign = rng.next_u32() % 2 != 0; let sign = rng.next_u32() % 2 != 0;
@ -197,11 +167,7 @@ impl<E: JubjubEngine> Point<E, Unknown> {
} }
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> { impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
pub fn write<W: Write>( pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
&self,
writer: W
) -> io::Result<()>
{
let (x, y) = self.into_xy(); let (x, y) = self.into_xy();
assert_eq!(E::Fr::NUM_BITS, 255); assert_eq!(E::Fr::NUM_BITS, 255);
@ -216,16 +182,12 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
} }
/// Convert from a Montgomery point /// Convert from a Montgomery point
pub fn from_montgomery( pub fn from_montgomery(m: &montgomery::Point<E, Subgroup>, params: &E::Params) -> Self {
m: &montgomery::Point<E, Subgroup>,
params: &E::Params
) -> Self
{
match m.into_xy() { match m.into_xy() {
None => { None => {
// Map the point at infinity to the neutral element. // Map the point at infinity to the neutral element.
Point::zero() Point::zero()
}, }
Some((x, y)) => { Some((x, y)) => {
// The map from a Montgomery curve is defined as: // The map from a Montgomery curve is defined as:
// (x, y) -> (u, v) where // (x, y) -> (u, v) where
@ -258,7 +220,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: neg1, y: neg1,
t: E::Fr::zero(), t: E::Fr::zero(),
z: E::Fr::one(), z: E::Fr::one(),
_marker: PhantomData _marker: PhantomData,
} }
} else { } else {
// Otherwise, as stated above, the mapping is still // Otherwise, as stated above, the mapping is still
@ -317,7 +279,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: v, y: v,
t: t, t: t,
z: z, z: z,
_marker: PhantomData _marker: PhantomData,
} }
} }
} }
@ -340,12 +302,11 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: E::Fr::one(), y: E::Fr::one(),
t: E::Fr::zero(), t: E::Fr::zero(),
z: E::Fr::one(), z: E::Fr::one(),
_marker: PhantomData _marker: PhantomData,
} }
} }
pub fn into_xy(&self) -> (E::Fr, E::Fr) pub fn into_xy(&self) -> (E::Fr, E::Fr) {
{
let zinv = self.z.inverse().unwrap(); let zinv = self.z.inverse().unwrap();
let mut x = self.x; let mut x = self.x;
@ -432,13 +393,12 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: y3, y: y3,
t: t3, t: t3,
z: z3, z: z3,
_marker: PhantomData _marker: PhantomData,
} }
} }
#[must_use] #[must_use]
pub fn add(&self, other: &Self, params: &E::Params) -> Self pub fn add(&self, other: &Self, params: &E::Params) -> Self {
{
// See "Twisted Edwards Curves Revisited" // See "Twisted Edwards Curves Revisited"
// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson
// 3.1 Unified Addition in E^e // 3.1 Unified Addition in E^e
@ -505,17 +465,12 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y: y3, y: y3,
t: t3, t: t3,
z: z3, z: z3,
_marker: PhantomData _marker: PhantomData,
} }
} }
#[must_use] #[must_use]
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>( pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(&self, scalar: S, params: &E::Params) -> Self {
&self,
scalar: S,
params: &E::Params
) -> Self
{
// Standard double-and-add scalar multiplication // Standard double-and-add scalar multiplication
let mut res = Self::zero(); let mut res = Self::zero();

View File

@ -9,7 +9,12 @@ use rand_core::RngCore;
use super::ToUniform; use super::ToUniform;
// s = 6554484396890773809930967563523245729705921265872317281365359162392183254199 // s = 6554484396890773809930967563523245729705921265872317281365359162392183254199
const MODULUS: FsRepr = FsRepr([0xd0970e5ed6f72cb7, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9]); const MODULUS: FsRepr = FsRepr([
0xd0970e5ed6f72cb7,
0xa6682093ccc81082,
0x6673b0101343b00,
0xe7db4ea6533afa9,
]);
// The number of bits needed to represent the modulus. // The number of bits needed to represent the modulus.
const MODULUS_BITS: u32 = 252; const MODULUS_BITS: u32 = 252;
@ -19,32 +24,56 @@ const MODULUS_BITS: u32 = 252;
const REPR_SHAVE_BITS: u32 = 4; const REPR_SHAVE_BITS: u32 = 4;
// R = 2**256 % s // R = 2**256 % s
const R: FsRepr = FsRepr([0x25f80bb3b99607d9, 0xf315d62f66b6e750, 0x932514eeeb8814f4, 0x9a6fc6f479155c6]); const R: FsRepr = FsRepr([
0x25f80bb3b99607d9,
0xf315d62f66b6e750,
0x932514eeeb8814f4,
0x9a6fc6f479155c6,
]);
// R2 = R^2 % s // R2 = R^2 % s
const R2: FsRepr = FsRepr([0x67719aa495e57731, 0x51b0cef09ce3fc26, 0x69dab7fac026e9a5, 0x4f6547b8d127688]); const R2: FsRepr = FsRepr([
0x67719aa495e57731,
0x51b0cef09ce3fc26,
0x69dab7fac026e9a5,
0x4f6547b8d127688,
]);
// INV = -(s^{-1} mod 2^64) mod s // INV = -(s^{-1} mod 2^64) mod s
const INV: u64 = 0x1ba3a358ef788ef9; const INV: u64 = 0x1ba3a358ef788ef9;
// GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) // GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue)
const GENERATOR: FsRepr = FsRepr([0x720b1b19d49ea8f1, 0xbf4aa36101f13a58, 0x5fa8cc968193ccbb, 0xe70cbdc7dccf3ac]); const GENERATOR: FsRepr = FsRepr([
0x720b1b19d49ea8f1,
0xbf4aa36101f13a58,
0x5fa8cc968193ccbb,
0xe70cbdc7dccf3ac,
]);
// 2^S * t = MODULUS - 1 with t odd // 2^S * t = MODULUS - 1 with t odd
const S: u32 = 1; const S: u32 = 1;
// 2^S root of unity computed by GENERATOR^t // 2^S root of unity computed by GENERATOR^t
const ROOT_OF_UNITY: FsRepr = FsRepr([0xaa9f02ab1d6124de, 0xb3524a6466112932, 0x7342261215ac260b, 0x4d6b87b1da259e2]); const ROOT_OF_UNITY: FsRepr = FsRepr([
0xaa9f02ab1d6124de,
0xb3524a6466112932,
0x7342261215ac260b,
0x4d6b87b1da259e2,
]);
// -((2**256) mod s) mod s // -((2**256) mod s) mod s
const NEGATIVE_ONE: Fs = Fs(FsRepr([0xaa9f02ab1d6124de, 0xb3524a6466112932, 0x7342261215ac260b, 0x4d6b87b1da259e2])); const NEGATIVE_ONE: Fs = Fs(FsRepr([
0xaa9f02ab1d6124de,
0xb3524a6466112932,
0x7342261215ac260b,
0x4d6b87b1da259e2,
]));
/// This is the underlying representation of an element of `Fs`. /// This is the underlying representation of an element of `Fs`.
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
pub struct FsRepr(pub [u64; 4]); pub struct FsRepr(pub [u64; 4]);
impl ::std::fmt::Display for FsRepr impl ::std::fmt::Display for FsRepr {
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
try!(write!(f, "0x")); try!(write!(f, "0x"));
for i in self.0.iter().rev() { for i in self.0.iter().rev() {
@ -83,9 +112,9 @@ impl Ord for FsRepr {
fn cmp(&self, other: &FsRepr) -> ::std::cmp::Ordering { fn cmp(&self, other: &FsRepr) -> ::std::cmp::Ordering {
for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) {
if a < b { if a < b {
return ::std::cmp::Ordering::Less return ::std::cmp::Ordering::Less;
} else if a > b { } else if a > b {
return ::std::cmp::Ordering::Greater return ::std::cmp::Ordering::Greater;
} }
} }
@ -227,8 +256,7 @@ impl PrimeFieldRepr for FsRepr {
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Fs(FsRepr); pub struct Fs(FsRepr);
impl ::std::fmt::Display for Fs impl ::std::fmt::Display for Fs {
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "Fs({})", self.into_repr()) write!(f, "Fs({})", self.into_repr())
} }
@ -256,9 +284,16 @@ impl PrimeField for Fs {
fn into_repr(&self) -> FsRepr { fn into_repr(&self) -> FsRepr {
let mut r = *self; let mut r = *self;
r.mont_reduce((self.0).0[0], (self.0).0[1], r.mont_reduce(
(self.0).0[2], (self.0).0[3], (self.0).0[0],
0, 0, 0, 0); (self.0).0[1],
(self.0).0[2],
(self.0).0[3],
0,
0,
0,
0,
);
r.0 r.0
} }
@ -296,7 +331,7 @@ impl Field for Fs {
tmp.0.as_mut()[3] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; tmp.0.as_mut()[3] &= 0xffffffffffffffff >> REPR_SHAVE_BITS;
if tmp.is_valid() { if tmp.is_valid() {
return tmp return tmp;
} }
} }
} }
@ -414,8 +449,7 @@ impl Field for Fs {
} }
#[inline] #[inline]
fn mul_assign(&mut self, other: &Fs) fn mul_assign(&mut self, other: &Fs) {
{
let mut carry = 0; let mut carry = 0;
let r0 = mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry); let r0 = mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry);
let r1 = mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry); let r1 = mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry);
@ -444,8 +478,7 @@ impl Field for Fs {
} }
#[inline] #[inline]
fn square(&mut self) fn square(&mut self) {
{
let mut carry = 0; let mut carry = 0;
let r1 = mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry); let r1 = mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry);
let r2 = mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry); let r2 = mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry);
@ -507,9 +540,8 @@ impl Fs {
mut r4: u64, mut r4: u64,
mut r5: u64, mut r5: u64,
mut r6: u64, mut r6: u64,
mut r7: u64 mut r7: u64,
) ) {
{
// The Montgomery reduction here is based on Algorithm 14.32 in // The Montgomery reduction here is based on Algorithm 14.32 in
// Handbook of Applied Cryptography // Handbook of Applied Cryptography
// <http://cacr.uwaterloo.ca/hac/about/chap14.pdf>. // <http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
@ -579,13 +611,21 @@ impl ToUniform for Fs {
} }
impl SqrtField for Fs { impl SqrtField for Fs {
fn legendre(&self) -> LegendreSymbol { fn legendre(&self) -> LegendreSymbol {
// s = self^((s - 1) // 2) // s = self^((s - 1) // 2)
let s = self.pow([0x684b872f6b7b965b, 0x53341049e6640841, 0x83339d80809a1d80, 0x73eda753299d7d4]); let s = self.pow([
if s == Self::zero() { Zero } 0x684b872f6b7b965b,
else if s == Self::one() { QuadraticResidue } 0x53341049e6640841,
else { QuadraticNonResidue } 0x83339d80809a1d80,
0x73eda753299d7d4,
]);
if s == Self::zero() {
Zero
} else if s == Self::one() {
QuadraticResidue
} else {
QuadraticNonResidue
}
} }
fn sqrt(&self) -> Option<Self> { fn sqrt(&self) -> Option<Self> {
@ -593,24 +633,25 @@ impl SqrtField for Fs {
// https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2)
// a1 = self^((s - 3) // 4) // a1 = self^((s - 3) // 4)
let mut a1 = self.pow([0xb425c397b5bdcb2d, 0x299a0824f3320420, 0x4199cec0404d0ec0, 0x39f6d3a994cebea]); let mut a1 = self.pow([
0xb425c397b5bdcb2d,
0x299a0824f3320420,
0x4199cec0404d0ec0,
0x39f6d3a994cebea,
]);
let mut a0 = a1; let mut a0 = a1;
a0.square(); a0.square();
a0.mul_assign(self); a0.mul_assign(self);
if a0 == NEGATIVE_ONE if a0 == NEGATIVE_ONE {
{
None None
} } else {
else
{
a1.mul_assign(self); a1.mul_assign(self);
Some(a1) Some(a1)
} }
} }
} }
#[test] #[test]
fn test_neg_one() { fn test_neg_one() {
let mut o = Fs::one(); let mut o = Fs::one();
@ -636,12 +677,30 @@ fn test_fs_repr_ordering() {
assert!(b > a); assert!(b > a);
} }
assert_equality(FsRepr([9999, 9999, 9999, 9999]), FsRepr([9999, 9999, 9999, 9999])); assert_equality(
assert_equality(FsRepr([9999, 9998, 9999, 9999]), FsRepr([9999, 9998, 9999, 9999])); FsRepr([9999, 9999, 9999, 9999]),
assert_equality(FsRepr([9999, 9999, 9999, 9997]), FsRepr([9999, 9999, 9999, 9997])); FsRepr([9999, 9999, 9999, 9999]),
assert_lt(FsRepr([9999, 9997, 9999, 9998]), FsRepr([9999, 9997, 9999, 9999])); );
assert_lt(FsRepr([9999, 9997, 9998, 9999]), FsRepr([9999, 9997, 9999, 9999])); assert_equality(
assert_lt(FsRepr([9, 9999, 9999, 9997]), FsRepr([9999, 9999, 9999, 9997])); FsRepr([9999, 9998, 9999, 9999]),
FsRepr([9999, 9998, 9999, 9999]),
);
assert_equality(
FsRepr([9999, 9999, 9999, 9997]),
FsRepr([9999, 9999, 9999, 9997]),
);
assert_lt(
FsRepr([9999, 9997, 9999, 9998]),
FsRepr([9999, 9997, 9999, 9999]),
);
assert_lt(
FsRepr([9999, 9997, 9998, 9999]),
FsRepr([9999, 9997, 9999, 9999]),
);
assert_lt(
FsRepr([9, 9999, 9999, 9997]),
FsRepr([9999, 9999, 9999, 9997]),
);
} }
#[test] #[test]
@ -670,13 +729,34 @@ fn test_fs_repr_is_zero() {
#[test] #[test]
fn test_fs_repr_div2() { fn test_fs_repr_div2() {
let mut a = FsRepr([0xbd2920b19c972321, 0x174ed0466a3be37e, 0xd468d5e3b551f0b5, 0xcb67c072733beefc]); let mut a = FsRepr([
0xbd2920b19c972321,
0x174ed0466a3be37e,
0xd468d5e3b551f0b5,
0xcb67c072733beefc,
]);
a.div2(); a.div2();
assert_eq!(a, FsRepr([0x5e949058ce4b9190, 0x8ba76823351df1bf, 0x6a346af1daa8f85a, 0x65b3e039399df77e])); assert_eq!(
a,
FsRepr([
0x5e949058ce4b9190,
0x8ba76823351df1bf,
0x6a346af1daa8f85a,
0x65b3e039399df77e
])
);
for _ in 0..10 { for _ in 0..10 {
a.div2(); a.div2();
} }
assert_eq!(a, FsRepr([0x6fd7a524163392e4, 0x16a2e9da08cd477c, 0xdf9a8d1abc76aa3e, 0x196cf80e4e677d])); assert_eq!(
a,
FsRepr([
0x6fd7a524163392e4,
0x16a2e9da08cd477c,
0xdf9a8d1abc76aa3e,
0x196cf80e4e677d
])
);
for _ in 0..200 { for _ in 0..200 {
a.div2(); a.div2();
} }
@ -695,32 +775,46 @@ fn test_fs_repr_div2() {
#[test] #[test]
fn test_fs_repr_shr() { fn test_fs_repr_shr() {
let mut a = FsRepr([0xb33fbaec482a283f, 0x997de0d3a88cb3df, 0x9af62d2a9a0e5525, 0x36003ab08de70da1]); let mut a = FsRepr([
0xb33fbaec482a283f,
0x997de0d3a88cb3df,
0x9af62d2a9a0e5525,
0x36003ab08de70da1,
]);
a.shr(0); a.shr(0);
assert_eq!( assert_eq!(
a, a,
FsRepr([0xb33fbaec482a283f, 0x997de0d3a88cb3df, 0x9af62d2a9a0e5525, 0x36003ab08de70da1]) FsRepr([
0xb33fbaec482a283f,
0x997de0d3a88cb3df,
0x9af62d2a9a0e5525,
0x36003ab08de70da1
])
); );
a.shr(1); a.shr(1);
assert_eq!( assert_eq!(
a, a,
FsRepr([0xd99fdd762415141f, 0xccbef069d44659ef, 0xcd7b16954d072a92, 0x1b001d5846f386d0]) FsRepr([
0xd99fdd762415141f,
0xccbef069d44659ef,
0xcd7b16954d072a92,
0x1b001d5846f386d0
])
); );
a.shr(50); a.shr(50);
assert_eq!( assert_eq!(
a, a,
FsRepr([0xbc1a7511967bf667, 0xc5a55341caa4b32f, 0x75611bce1b4335e, 0x6c0]) FsRepr([
0xbc1a7511967bf667,
0xc5a55341caa4b32f,
0x75611bce1b4335e,
0x6c0
])
); );
a.shr(130); a.shr(130);
assert_eq!( assert_eq!(a, FsRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0]));
a,
FsRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0])
);
a.shr(64); a.shr(64);
assert_eq!( assert_eq!(a, FsRepr([0x1b0, 0x0, 0x0, 0x0]));
a,
FsRepr([0x1b0, 0x0, 0x0, 0x0])
);
} }
#[test] #[test]
@ -765,9 +859,26 @@ fn test_fs_repr_sub_noborrow() {
0xe5, 0xe5,
]); ]);
let mut t = FsRepr([0x8e62a7e85264e2c3, 0xb23d34c1941d3ca, 0x5976930b7502dd15, 0x600f3fb517bf5495]); let mut t = FsRepr([
t.sub_noborrow(&FsRepr([0xd64f669809cbc6a4, 0xfa76cb9d90cf7637, 0xfefb0df9038d43b3, 0x298a30c744b31acf])); 0x8e62a7e85264e2c3,
assert!(t == FsRepr([0xb813415048991c1f, 0x10ad07ae88725d92, 0x5a7b851271759961, 0x36850eedd30c39c5])); 0xb23d34c1941d3ca,
0x5976930b7502dd15,
0x600f3fb517bf5495,
]);
t.sub_noborrow(&FsRepr([
0xd64f669809cbc6a4,
0xfa76cb9d90cf7637,
0xfefb0df9038d43b3,
0x298a30c744b31acf,
]));
assert!(
t == FsRepr([
0xb813415048991c1f,
0x10ad07ae88725d92,
0x5a7b851271759961,
0x36850eedd30c39c5
])
);
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fs::random(&mut rng).into_repr(); let mut a = Fs::random(&mut rng).into_repr();
@ -801,9 +912,19 @@ fn test_fs_legendre() {
assert_eq!(QuadraticResidue, Fs::one().legendre()); assert_eq!(QuadraticResidue, Fs::one().legendre());
assert_eq!(Zero, Fs::zero().legendre()); assert_eq!(Zero, Fs::zero().legendre());
let e = FsRepr([0x8385eec23df1f88e, 0x9a01fb412b2dba16, 0x4c928edcdd6c22f, 0x9f2df7ef69ecef9]); let e = FsRepr([
0x8385eec23df1f88e,
0x9a01fb412b2dba16,
0x4c928edcdd6c22f,
0x9f2df7ef69ecef9,
]);
assert_eq!(QuadraticResidue, Fs::from_repr(e).unwrap().legendre()); assert_eq!(QuadraticResidue, Fs::from_repr(e).unwrap().legendre());
let e = FsRepr([0xe8ed9f299da78568, 0x35efdebc88b2209, 0xc82125cb1f916dbe, 0x6813d2b38c39bd0]); let e = FsRepr([
0xe8ed9f299da78568,
0x35efdebc88b2209,
0xc82125cb1f916dbe,
0x6813d2b38c39bd0,
]);
assert_eq!(QuadraticNonResidue, Fs::from_repr(e).unwrap().legendre()); assert_eq!(QuadraticNonResidue, Fs::from_repr(e).unwrap().legendre());
} }
@ -814,9 +935,27 @@ fn test_fr_repr_add_nocarry() {
0xe5, 0xe5,
]); ]);
let mut t = FsRepr([0xd64f669809cbc6a4, 0xfa76cb9d90cf7637, 0xfefb0df9038d43b3, 0x298a30c744b31acf]); let mut t = FsRepr([
t.add_nocarry(&FsRepr([0x8e62a7e85264e2c3, 0xb23d34c1941d3ca, 0x5976930b7502dd15, 0x600f3fb517bf5495])); 0xd64f669809cbc6a4,
assert_eq!(t, FsRepr([0x64b20e805c30a967, 0x59a9ee9aa114a02, 0x5871a104789020c9, 0x8999707c5c726f65])); 0xfa76cb9d90cf7637,
0xfefb0df9038d43b3,
0x298a30c744b31acf,
]);
t.add_nocarry(&FsRepr([
0x8e62a7e85264e2c3,
0xb23d34c1941d3ca,
0x5976930b7502dd15,
0x600f3fb517bf5495,
]));
assert_eq!(
t,
FsRepr([
0x64b20e805c30a967,
0x59a9ee9aa114a02,
0x5871a104789020c9,
0x8999707c5c726f65
])
);
// Test for the associativity of addition. // Test for the associativity of addition.
for _ in 0..1000 { for _ in 0..1000 {
@ -868,8 +1007,20 @@ fn test_fs_is_valid() {
a.0.sub_noborrow(&FsRepr::from(1)); a.0.sub_noborrow(&FsRepr::from(1));
assert!(a.is_valid()); assert!(a.is_valid());
assert!(Fs(FsRepr::from(0)).is_valid()); assert!(Fs(FsRepr::from(0)).is_valid());
assert!(Fs(FsRepr([0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9])).is_valid()); assert!(Fs(FsRepr([
assert!(!Fs(FsRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])).is_valid()); 0xd0970e5ed6f72cb6,
0xa6682093ccc81082,
0x6673b0101343b00,
0xe7db4ea6533afa9
]))
.is_valid());
assert!(!Fs(FsRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff
]))
.is_valid());
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
@ -886,25 +1037,80 @@ fn test_fs_is_valid() {
fn test_fs_add_assign() { fn test_fs_add_assign() {
{ {
// Random number // Random number
let mut tmp = Fs::from_str("4577408157467272683998459759522778614363623736323078995109579213719612604198").unwrap(); let mut tmp = Fs::from_str(
"4577408157467272683998459759522778614363623736323078995109579213719612604198",
)
.unwrap();
assert!(tmp.is_valid()); assert!(tmp.is_valid());
// Test that adding zero has no effect. // Test that adding zero has no effect.
tmp.add_assign(&Fs(FsRepr::from(0))); tmp.add_assign(&Fs(FsRepr::from(0)));
assert_eq!(tmp, Fs(FsRepr([0x8e6bfff4722d6e67, 0x5643da5c892044f9, 0x9465f4b281921a69, 0x25f752d3edd7162]))); assert_eq!(
tmp,
Fs(FsRepr([
0x8e6bfff4722d6e67,
0x5643da5c892044f9,
0x9465f4b281921a69,
0x25f752d3edd7162
]))
);
// Add one and test for the result. // Add one and test for the result.
tmp.add_assign(&Fs(FsRepr::from(1))); tmp.add_assign(&Fs(FsRepr::from(1)));
assert_eq!(tmp, Fs(FsRepr([0x8e6bfff4722d6e68, 0x5643da5c892044f9, 0x9465f4b281921a69, 0x25f752d3edd7162]))); assert_eq!(
tmp,
Fs(FsRepr([
0x8e6bfff4722d6e68,
0x5643da5c892044f9,
0x9465f4b281921a69,
0x25f752d3edd7162
]))
);
// Add another random number that exercises the reduction. // Add another random number that exercises the reduction.
tmp.add_assign(&Fs(FsRepr([0xb634d07bc42d4a70, 0xf724f0c008411f5f, 0x456d4053d865af34, 0x24ce814e8c63027]))); tmp.add_assign(&Fs(FsRepr([
assert_eq!(tmp, Fs(FsRepr([0x44a0d070365ab8d8, 0x4d68cb1c91616459, 0xd9d3350659f7c99e, 0x4ac5d4227a3a189]))); 0xb634d07bc42d4a70,
0xf724f0c008411f5f,
0x456d4053d865af34,
0x24ce814e8c63027,
])));
assert_eq!(
tmp,
Fs(FsRepr([
0x44a0d070365ab8d8,
0x4d68cb1c91616459,
0xd9d3350659f7c99e,
0x4ac5d4227a3a189
]))
);
// Add one to (s - 1) and test for the result. // Add one to (s - 1) and test for the result.
tmp = Fs(FsRepr([0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9])); tmp = Fs(FsRepr([
0xd0970e5ed6f72cb6,
0xa6682093ccc81082,
0x6673b0101343b00,
0xe7db4ea6533afa9,
]));
tmp.add_assign(&Fs(FsRepr::from(1))); tmp.add_assign(&Fs(FsRepr::from(1)));
assert!(tmp.0.is_zero()); assert!(tmp.0.is_zero());
// Add a random number to another one such that the result is s - 1 // Add a random number to another one such that the result is s - 1
tmp = Fs(FsRepr([0xa11fda5950ce3636, 0x922e0dbccfe0ca0e, 0xacebb6e215b82d4a, 0x97ffb8cdc3aee93])); tmp = Fs(FsRepr([
tmp.add_assign(&Fs(FsRepr([0x2f7734058628f680, 0x143a12d6fce74674, 0x597b841eeb7c0db6, 0x4fdb95d88f8c115]))); 0xa11fda5950ce3636,
assert_eq!(tmp, Fs(FsRepr([0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9]))); 0x922e0dbccfe0ca0e,
0xacebb6e215b82d4a,
0x97ffb8cdc3aee93,
]));
tmp.add_assign(&Fs(FsRepr([
0x2f7734058628f680,
0x143a12d6fce74674,
0x597b841eeb7c0db6,
0x4fdb95d88f8c115,
])));
assert_eq!(
tmp,
Fs(FsRepr([
0xd0970e5ed6f72cb6,
0xa6682093ccc81082,
0x6673b0101343b00,
0xe7db4ea6533afa9
]))
);
// Add one to the result and test for it. // Add one to the result and test for it.
tmp.add_assign(&Fs(FsRepr::from(1))); tmp.add_assign(&Fs(FsRepr::from(1)));
assert!(tmp.0.is_zero()); assert!(tmp.0.is_zero());
@ -941,23 +1147,72 @@ fn test_fs_add_assign() {
fn test_fs_sub_assign() { fn test_fs_sub_assign() {
{ {
// Test arbitrary subtraction that tests reduction. // Test arbitrary subtraction that tests reduction.
let mut tmp = Fs(FsRepr([0xb384d9f6877afd99, 0x4442513958e1a1c1, 0x352c4b8a95eccc3f, 0x2db62dee4b0f2])); let mut tmp = Fs(FsRepr([
tmp.sub_assign(&Fs(FsRepr([0xec5bd2d13ed6b05a, 0x2adc0ab3a39b5fa, 0x82d3360a493e637e, 0x53ccff4a64d6679]))); 0xb384d9f6877afd99,
assert_eq!(tmp, Fs(FsRepr([0x97c015841f9b79f6, 0xe7fcb121eb6ffc49, 0xb8c050814de2a3c1, 0x943c0589dcafa21]))); 0x4442513958e1a1c1,
0x352c4b8a95eccc3f,
0x2db62dee4b0f2,
]));
tmp.sub_assign(&Fs(FsRepr([
0xec5bd2d13ed6b05a,
0x2adc0ab3a39b5fa,
0x82d3360a493e637e,
0x53ccff4a64d6679,
])));
assert_eq!(
tmp,
Fs(FsRepr([
0x97c015841f9b79f6,
0xe7fcb121eb6ffc49,
0xb8c050814de2a3c1,
0x943c0589dcafa21
]))
);
// Test the opposite subtraction which doesn't test reduction. // Test the opposite subtraction which doesn't test reduction.
tmp = Fs(FsRepr([0xec5bd2d13ed6b05a, 0x2adc0ab3a39b5fa, 0x82d3360a493e637e, 0x53ccff4a64d6679])); tmp = Fs(FsRepr([
tmp.sub_assign(&Fs(FsRepr([0xb384d9f6877afd99, 0x4442513958e1a1c1, 0x352c4b8a95eccc3f, 0x2db62dee4b0f2]))); 0xec5bd2d13ed6b05a,
assert_eq!(tmp, Fs(FsRepr([0x38d6f8dab75bb2c1, 0xbe6b6f71e1581439, 0x4da6ea7fb351973e, 0x539f491c768b587]))); 0x2adc0ab3a39b5fa,
0x82d3360a493e637e,
0x53ccff4a64d6679,
]));
tmp.sub_assign(&Fs(FsRepr([
0xb384d9f6877afd99,
0x4442513958e1a1c1,
0x352c4b8a95eccc3f,
0x2db62dee4b0f2,
])));
assert_eq!(
tmp,
Fs(FsRepr([
0x38d6f8dab75bb2c1,
0xbe6b6f71e1581439,
0x4da6ea7fb351973e,
0x539f491c768b587
]))
);
// Test for sensible results with zero // Test for sensible results with zero
tmp = Fs(FsRepr::from(0)); tmp = Fs(FsRepr::from(0));
tmp.sub_assign(&Fs(FsRepr::from(0))); tmp.sub_assign(&Fs(FsRepr::from(0)));
assert!(tmp.is_zero()); assert!(tmp.is_zero());
tmp = Fs(FsRepr([0x361e16aef5cce835, 0x55bbde2536e274c1, 0x4dc77a63fd15ee75, 0x1e14bb37c14f230])); tmp = Fs(FsRepr([
0x361e16aef5cce835,
0x55bbde2536e274c1,
0x4dc77a63fd15ee75,
0x1e14bb37c14f230,
]));
tmp.sub_assign(&Fs(FsRepr::from(0))); tmp.sub_assign(&Fs(FsRepr::from(0)));
assert_eq!(tmp, Fs(FsRepr([0x361e16aef5cce835, 0x55bbde2536e274c1, 0x4dc77a63fd15ee75, 0x1e14bb37c14f230]))); assert_eq!(
tmp,
Fs(FsRepr([
0x361e16aef5cce835,
0x55bbde2536e274c1,
0x4dc77a63fd15ee75,
0x1e14bb37c14f230
]))
);
} }
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@ -983,9 +1238,26 @@ fn test_fs_sub_assign() {
#[test] #[test]
fn test_fs_mul_assign() { fn test_fs_mul_assign() {
let mut tmp = Fs(FsRepr([0xb433b01287f71744, 0x4eafb86728c4d108, 0xfdd52c14b9dfbe65, 0x2ff1f3434821118])); let mut tmp = Fs(FsRepr([
tmp.mul_assign(&Fs(FsRepr([0xdae00fc63c9fa90f, 0x5a5ed89b96ce21ce, 0x913cd26101bd6f58, 0x3f0822831697fe9]))); 0xb433b01287f71744,
assert!(tmp == Fs(FsRepr([0xb68ecb61d54d2992, 0x5ff95874defce6a6, 0x3590eb053894657d, 0x53823a118515933]))); 0x4eafb86728c4d108,
0xfdd52c14b9dfbe65,
0x2ff1f3434821118,
]));
tmp.mul_assign(&Fs(FsRepr([
0xdae00fc63c9fa90f,
0x5a5ed89b96ce21ce,
0x913cd26101bd6f58,
0x3f0822831697fe9,
])));
assert!(
tmp == Fs(FsRepr([
0xb68ecb61d54d2992,
0x5ff95874defce6a6,
0x3590eb053894657d,
0x53823a118515933
]))
);
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
@ -1035,10 +1307,24 @@ fn test_fs_mul_assign() {
#[test] #[test]
fn test_fr_squaring() { fn test_fr_squaring() {
let mut a = Fs(FsRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xe7db4ea6533afa8])); let mut a = Fs(FsRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xe7db4ea6533afa8,
]));
assert!(a.is_valid()); assert!(a.is_valid());
a.square(); a.square();
assert_eq!(a, Fs::from_repr(FsRepr([0x12c7f55cbc52fbaa, 0xdedc98a0b5e6ce9e, 0xad2892726a5396a, 0x9fe82af8fee77b3])).unwrap()); assert_eq!(
a,
Fs::from_repr(FsRepr([
0x12c7f55cbc52fbaa,
0xdedc98a0b5e6ce9e,
0xad2892726a5396a,
0x9fe82af8fee77b3
]))
.unwrap()
);
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
@ -1185,17 +1471,38 @@ fn test_fs_sqrt() {
#[test] #[test]
fn test_fs_from_into_repr() { fn test_fs_from_into_repr() {
// r + 1 should not be in the field // r + 1 should not be in the field
assert!(Fs::from_repr(FsRepr([0xd0970e5ed6f72cb8, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9])).is_err()); assert!(Fs::from_repr(FsRepr([
0xd0970e5ed6f72cb8,
0xa6682093ccc81082,
0x6673b0101343b00,
0xe7db4ea6533afa9
]))
.is_err());
// r should not be in the field // r should not be in the field
assert!(Fs::from_repr(Fs::char()).is_err()); assert!(Fs::from_repr(Fs::char()).is_err());
// Multiply some arbitrary representations to see if the result is as expected. // Multiply some arbitrary representations to see if the result is as expected.
let a = FsRepr([0x5f2d0c05d0337b71, 0xa1df2b0f8a20479, 0xad73785e71bb863, 0x504a00480c9acec]); let a = FsRepr([
0x5f2d0c05d0337b71,
0xa1df2b0f8a20479,
0xad73785e71bb863,
0x504a00480c9acec,
]);
let mut a_fs = Fs::from_repr(a).unwrap(); let mut a_fs = Fs::from_repr(a).unwrap();
let b = FsRepr([0x66356ff51e477562, 0x60a92ab55cf7603, 0x8e4273c7364dd192, 0x36df8844a344dc5]); let b = FsRepr([
0x66356ff51e477562,
0x60a92ab55cf7603,
0x8e4273c7364dd192,
0x36df8844a344dc5,
]);
let b_fs = Fs::from_repr(b).unwrap(); let b_fs = Fs::from_repr(b).unwrap();
let c = FsRepr([0x7eef61708f4f2868, 0x747a7e6cf52946fb, 0x83dd75d7c9120017, 0x762f5177f0f3df7]); let c = FsRepr([
0x7eef61708f4f2868,
0x747a7e6cf52946fb,
0x83dd75d7c9120017,
0x762f5177f0f3df7,
]);
a_fs.mul_assign(&b_fs); a_fs.mul_assign(&b_fs);
assert_eq!(a_fs.into_repr(), c); assert_eq!(a_fs.into_repr(), c);
@ -1222,15 +1529,39 @@ fn test_fs_from_into_repr() {
#[test] #[test]
fn test_fs_repr_display() { fn test_fs_repr_display() {
assert_eq!( assert_eq!(
format!("{}", FsRepr([0xa296db59787359df, 0x8d3e33077430d318, 0xd1abf5c606102eb7, 0xcbc33ee28108f0])), format!(
"{}",
FsRepr([
0xa296db59787359df,
0x8d3e33077430d318,
0xd1abf5c606102eb7,
0xcbc33ee28108f0
])
),
"0x00cbc33ee28108f0d1abf5c606102eb78d3e33077430d318a296db59787359df".to_string() "0x00cbc33ee28108f0d1abf5c606102eb78d3e33077430d318a296db59787359df".to_string()
); );
assert_eq!( assert_eq!(
format!("{}", FsRepr([0x14cb03535054a620, 0x312aa2bf2d1dff52, 0x970fe98746ab9361, 0xc1e18acf82711e6])), format!(
"{}",
FsRepr([
0x14cb03535054a620,
0x312aa2bf2d1dff52,
0x970fe98746ab9361,
0xc1e18acf82711e6
])
),
"0x0c1e18acf82711e6970fe98746ab9361312aa2bf2d1dff5214cb03535054a620".to_string() "0x0c1e18acf82711e6970fe98746ab9361312aa2bf2d1dff5214cb03535054a620".to_string()
); );
assert_eq!( assert_eq!(
format!("{}", FsRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])), format!(
"{}",
FsRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff
])
),
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string()
); );
assert_eq!( assert_eq!(
@ -1242,11 +1573,29 @@ fn test_fs_repr_display() {
#[test] #[test]
fn test_fs_display() { fn test_fs_display() {
assert_eq!( assert_eq!(
format!("{}", Fs::from_repr(FsRepr([0x5528efb9998a01a3, 0x5bd2add5cb357089, 0xc061fa6adb491f98, 0x70db9d143db03d9])).unwrap()), format!(
"{}",
Fs::from_repr(FsRepr([
0x5528efb9998a01a3,
0x5bd2add5cb357089,
0xc061fa6adb491f98,
0x70db9d143db03d9
]))
.unwrap()
),
"Fs(0x070db9d143db03d9c061fa6adb491f985bd2add5cb3570895528efb9998a01a3)".to_string() "Fs(0x070db9d143db03d9c061fa6adb491f985bd2add5cb3570895528efb9998a01a3)".to_string()
); );
assert_eq!( assert_eq!(
format!("{}", Fs::from_repr(FsRepr([0xd674745e2717999e, 0xbeb1f52d3e96f338, 0x9c7ae147549482b9, 0x999706024530d22])).unwrap()), format!(
"{}",
Fs::from_repr(FsRepr([
0xd674745e2717999e,
0xbeb1f52d3e96f338,
0x9c7ae147549482b9,
0x999706024530d22
]))
.unwrap()
),
"Fs(0x0999706024530d229c7ae147549482b9beb1f52d3e96f338d674745e2717999e)".to_string() "Fs(0x0999706024530d229c7ae147549482b9beb1f52d3e96f338d674745e2717999e)".to_string()
); );
} }
@ -1260,14 +1609,19 @@ fn test_fs_num_bits() {
#[test] #[test]
fn test_fs_root_of_unity() { fn test_fs_root_of_unity() {
assert_eq!(Fs::S, 1); assert_eq!(Fs::S, 1);
assert_eq!(Fs::multiplicative_generator(), Fs::from_repr(FsRepr::from(6)).unwrap());
assert_eq!( assert_eq!(
Fs::multiplicative_generator().pow([0x684b872f6b7b965b, 0x53341049e6640841, 0x83339d80809a1d80, 0x73eda753299d7d4]), Fs::multiplicative_generator(),
Fs::from_repr(FsRepr::from(6)).unwrap()
);
assert_eq!(
Fs::multiplicative_generator().pow([
0x684b872f6b7b965b,
0x53341049e6640841,
0x83339d80809a1d80,
0x73eda753299d7d4
]),
Fs::root_of_unity() Fs::root_of_unity()
); );
assert_eq!( assert_eq!(Fs::root_of_unity().pow([1 << Fs::S]), Fs::one());
Fs::root_of_unity().pow([1 << Fs::S]),
Fs::one()
);
assert!(Fs::multiplicative_generator().sqrt().is_none()); assert!(Fs::multiplicative_generator().sqrt().is_none());
} }

View File

@ -24,10 +24,7 @@ use group_hash::group_hash;
use constants; use constants;
use pairing::bls12_381::{ use pairing::bls12_381::{Bls12, Fr};
Bls12,
Fr
};
/// This is an implementation of the twisted Edwards Jubjub curve. /// This is an implementation of the twisted Edwards Jubjub curve.
pub mod edwards; pub mod edwards;
@ -44,11 +41,11 @@ pub mod tests;
/// Point of unknown order. /// Point of unknown order.
#[derive(Debug)] #[derive(Debug)]
pub enum Unknown { } pub enum Unknown {}
/// Point of prime order. /// Point of prime order.
#[derive(Debug)] #[derive(Debug)]
pub enum PrimeOrder { } pub enum PrimeOrder {}
/// Fixed generators of the Jubjub curve of unknown /// Fixed generators of the Jubjub curve of unknown
/// exponent. /// exponent.
@ -80,7 +77,7 @@ pub enum FixedGenerators {
/// base at spend time. /// base at spend time.
SpendingKeyGenerator = 5, SpendingKeyGenerator = 5,
Max = 6 Max = 6,
} }
pub trait ToUniform { pub trait ToUniform {
@ -151,10 +148,18 @@ pub struct JubjubBls12 {
} }
impl JubjubParams<Bls12> for JubjubBls12 { impl JubjubParams<Bls12> for JubjubBls12 {
fn edwards_d(&self) -> &Fr { &self.edwards_d } fn edwards_d(&self) -> &Fr {
fn montgomery_a(&self) -> &Fr { &self.montgomery_a } &self.edwards_d
fn montgomery_2a(&self) -> &Fr { &self.montgomery_2a } }
fn scale(&self) -> &Fr { &self.scale } fn montgomery_a(&self) -> &Fr {
&self.montgomery_a
}
fn montgomery_2a(&self) -> &Fr {
&self.montgomery_2a
}
fn scale(&self) -> &Fr {
&self.scale
}
fn pedersen_hash_generators(&self) -> &[edwards::Point<Bls12, PrimeOrder>] { fn pedersen_hash_generators(&self) -> &[edwards::Point<Bls12, PrimeOrder>] {
&self.pedersen_hash_generators &self.pedersen_hash_generators
} }
@ -170,12 +175,10 @@ impl JubjubParams<Bls12> for JubjubBls12 {
fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(Fr, Fr)>>] { fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(Fr, Fr)>>] {
&self.pedersen_circuit_generators &self.pedersen_circuit_generators
} }
fn generator(&self, base: FixedGenerators) -> &edwards::Point<Bls12, PrimeOrder> fn generator(&self, base: FixedGenerators) -> &edwards::Point<Bls12, PrimeOrder> {
{
&self.fixed_base_generators[base as usize] &self.fixed_base_generators[base as usize]
} }
fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>] fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>] {
{
&self.fixed_base_circuit_generators[base as usize][..] &self.fixed_base_circuit_generators[base as usize][..]
} }
fn pedersen_hash_exp_window_size() -> u32 { fn pedersen_hash_exp_window_size() -> u32 {
@ -191,13 +194,19 @@ impl JubjubBls12 {
let mut tmp_params = JubjubBls12 { let mut tmp_params = JubjubBls12 {
// d = -(10240/10241) // d = -(10240/10241)
edwards_d: Fr::from_str("19257038036680949359750312669786877991949435402254120286184196891950884077233").unwrap(), edwards_d: Fr::from_str(
"19257038036680949359750312669786877991949435402254120286184196891950884077233",
)
.unwrap(),
// A = 40962 // A = 40962
montgomery_a: montgomery_a, montgomery_a: montgomery_a,
// 2A = 2.A // 2A = 2.A
montgomery_2a: montgomery_2a, montgomery_2a: montgomery_2a,
// scaling factor = sqrt(4 / (a - d)) // scaling factor = sqrt(4 / (a - d))
scale: Fr::from_str("17814886934372412843466061268024708274627479829237077604635722030778476050649").unwrap(), scale: Fr::from_str(
"17814886934372412843466061268024708274627479829237077604635722030778476050649",
)
.unwrap(),
// We'll initialize these below // We'll initialize these below
pedersen_hash_generators: vec![], pedersen_hash_generators: vec![],
@ -210,19 +219,14 @@ impl JubjubBls12 {
fn find_group_hash<E: JubjubEngine>( fn find_group_hash<E: JubjubEngine>(
m: &[u8], m: &[u8],
personalization: &[u8; 8], personalization: &[u8; 8],
params: &E::Params params: &E::Params,
) -> edwards::Point<E, PrimeOrder> ) -> edwards::Point<E, PrimeOrder> {
{
let mut tag = m.to_vec(); let mut tag = m.to_vec();
let i = tag.len(); let i = tag.len();
tag.push(0u8); tag.push(0u8);
loop { loop {
let gh = group_hash( let gh = group_hash(&tag, personalization, params);
&tag,
personalization,
params
);
// We don't want to overflow and start reusing generators // We don't want to overflow and start reusing generators
assert!(tag[i] != u8::max_value()); assert!(tag[i] != u8::max_value());
@ -239,18 +243,18 @@ impl JubjubBls12 {
let mut pedersen_hash_generators = vec![]; let mut pedersen_hash_generators = vec![];
for m in 0..5 { for m in 0..5 {
use byteorder::{WriteBytesExt, LittleEndian}; use byteorder::{LittleEndian, WriteBytesExt};
let mut segment_number = [0u8; 4]; let mut segment_number = [0u8; 4];
(&mut segment_number[0..4]).write_u32::<LittleEndian>(m).unwrap(); (&mut segment_number[0..4])
.write_u32::<LittleEndian>(m)
.unwrap();
pedersen_hash_generators.push( pedersen_hash_generators.push(find_group_hash(
find_group_hash( &segment_number,
&segment_number, constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params,
&tmp_params ));
)
);
} }
// Check for duplicates, far worse than spec inconsistencies! // Check for duplicates, far worse than spec inconsistencies!
@ -259,7 +263,7 @@ impl JubjubBls12 {
panic!("Neutral element!"); panic!("Neutral element!");
} }
for p2 in pedersen_hash_generators.iter().skip(i+1) { for p2 in pedersen_hash_generators.iter().skip(i + 1) {
if p1 == p2 { if p1 == p2 {
panic!("Duplicate generator!"); panic!("Duplicate generator!");
} }
@ -307,25 +311,46 @@ impl JubjubBls12 {
// Create the bases for other parts of the protocol // Create the bases for other parts of the protocol
{ {
let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize]; let mut fixed_base_generators =
vec![edwards::Point::zero(); FixedGenerators::Max as usize];
fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = find_group_hash(
find_group_hash(&[], constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params); &[],
constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] = fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] =
find_group_hash(b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params); find_group_hash(
b"r",
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NullifierPosition as usize] = fixed_base_generators[FixedGenerators::NullifierPosition as usize] = find_group_hash(
find_group_hash(&[], constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params); &[],
constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = find_group_hash(
find_group_hash(b"v", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params); b"v",
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] = fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] =
find_group_hash(b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params); find_group_hash(
b"r",
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = find_group_hash(
find_group_hash(&[], constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params); &[],
constants::SPENDING_KEY_GENERATOR_PERSONALIZATION,
&tmp_params,
);
// Check for duplicates, far worse than spec inconsistencies! // Check for duplicates, far worse than spec inconsistencies!
for (i, p1) in fixed_base_generators.iter().enumerate() { for (i, p1) in fixed_base_generators.iter().enumerate() {
@ -333,7 +358,7 @@ impl JubjubBls12 {
panic!("Neutral element!"); panic!("Neutral element!");
} }
for p2 in fixed_base_generators.iter().skip(i+1) { for p2 in fixed_base_generators.iter().skip(i + 1) {
if p1 == p2 { if p1 == p2 {
panic!("Duplicate generator!"); panic!("Duplicate generator!");
} }
@ -413,10 +438,14 @@ fn test_jubjub_bls12() {
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31"); let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31");
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap(); let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap();
let q = edwards::Point::<Bls12, _>::get_for_y( let q = edwards::Point::<Bls12, _>::get_for_y(
Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(), Fr::from_str(
"22440861827555040311190986994816762244378363690614952020532787748720529117853",
)
.unwrap(),
false, false,
&params &params,
).unwrap(); )
.unwrap();
assert!(p == q); assert!(p == q);
@ -424,10 +453,14 @@ fn test_jubjub_bls12() {
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1"); let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1");
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap(); let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap();
let q = edwards::Point::<Bls12, _>::get_for_y( let q = edwards::Point::<Bls12, _>::get_for_y(
Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(), Fr::from_str(
"22440861827555040311190986994816762244378363690614952020532787748720529117853",
)
.unwrap(),
true, true,
&params &params,
).unwrap(); )
.unwrap();
assert!(p == q); assert!(p == q);
} }

View File

@ -1,12 +1,6 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use super::{ use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
JubjubEngine,
JubjubParams,
Unknown,
PrimeOrder,
edwards
};
use rand_core::RngCore; use rand_core::RngCore;
@ -17,29 +11,25 @@ pub struct Point<E: JubjubEngine, Subgroup> {
x: E::Fr, x: E::Fr,
y: E::Fr, y: E::Fr,
infinity: bool, infinity: bool,
_marker: PhantomData<Subgroup> _marker: PhantomData<Subgroup>,
} }
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2> fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2> {
{
Point { Point {
x: from.x, x: from.x,
y: from.y, y: from.y,
infinity: from.infinity, infinity: from.infinity,
_marker: PhantomData _marker: PhantomData,
} }
} }
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown> impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown> {
{ fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown> {
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
{
convert_subgroup(&p) convert_subgroup(&p)
} }
} }
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup> impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup> {
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
convert_subgroup(self) convert_subgroup(self)
} }
@ -50,16 +40,13 @@ impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
match (self.infinity, other.infinity) { match (self.infinity, other.infinity) {
(true, true) => true, (true, true) => true,
(true, false) | (false, true) => false, (true, false) | (false, true) => false,
(false, false) => { (false, false) => self.x == other.x && self.y == other.y,
self.x == other.x && self.y == other.y
}
} }
} }
} }
impl<E: JubjubEngine> Point<E, Unknown> { impl<E: JubjubEngine> Point<E, Unknown> {
pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option<Self> pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option<Self> {
{
// Given an x on the curve, y = sqrt(x^3 + A*x^2 + x) // Given an x on the curve, y = sqrt(x^3 + A*x^2 + x)
let mut x2 = x; let mut x2 = x;
@ -81,34 +68,28 @@ impl<E: JubjubEngine> Point<E, Unknown> {
x: x, x: x,
y: y, y: y,
infinity: false, infinity: false,
_marker: PhantomData _marker: PhantomData,
}) });
}, }
None => None None => None,
} }
} }
/// This guarantees the point is in the prime order subgroup /// This guarantees the point is in the prime order subgroup
#[must_use] #[must_use]
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder> pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder> {
{ let tmp = self.double(params).double(params).double(params);
let tmp = self.double(params)
.double(params)
.double(params);
convert_subgroup(&tmp) convert_subgroup(&tmp)
} }
pub fn rand<R: RngCore>(rng: &mut R, params: &E::Params) -> Self pub fn rand<R: RngCore>(rng: &mut R, params: &E::Params) -> Self {
{
loop { loop {
let x = E::Fr::random(rng); let x = E::Fr::random(rng);
let sign = rng.next_u32() % 2 != 0; let sign = rng.next_u32() % 2 != 0;
match Self::get_for_x(x, sign, params) { match Self::get_for_x(x, sign, params) {
Some(p) => { Some(p) => return p,
return p
},
None => {} None => {}
} }
} }
@ -117,11 +98,7 @@ impl<E: JubjubEngine> Point<E, Unknown> {
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> { impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
/// Convert from an Edwards point /// Convert from an Edwards point
pub fn from_edwards( pub fn from_edwards(e: &edwards::Point<E, Subgroup>, params: &E::Params) -> Self {
e: &edwards::Point<E, Subgroup>,
params: &E::Params
) -> Self
{
let (x, y) = e.into_xy(); let (x, y) = e.into_xy();
if y == E::Fr::one() { if y == E::Fr::one() {
@ -149,7 +126,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: E::Fr::zero(), x: E::Fr::zero(),
y: E::Fr::zero(), y: E::Fr::zero(),
infinity: false, infinity: false,
_marker: PhantomData _marker: PhantomData,
} }
} else { } else {
// The mapping is defined as above. // The mapping is defined as above.
@ -176,7 +153,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: u, x: u,
y: v, y: v,
infinity: false, infinity: false,
_marker: PhantomData _marker: PhantomData,
} }
} }
} }
@ -197,12 +174,11 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: E::Fr::zero(), x: E::Fr::zero(),
y: E::Fr::zero(), y: E::Fr::zero(),
infinity: true, infinity: true,
_marker: PhantomData _marker: PhantomData,
} }
} }
pub fn into_xy(&self) -> Option<(E::Fr, E::Fr)> pub fn into_xy(&self) -> Option<(E::Fr, E::Fr)> {
{
if self.infinity { if self.infinity {
None None
} else { } else {
@ -272,13 +248,12 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: x3, x: x3,
y: y3, y: y3,
infinity: false, infinity: false,
_marker: PhantomData _marker: PhantomData,
} }
} }
#[must_use] #[must_use]
pub fn add(&self, other: &Self, params: &E::Params) -> Self pub fn add(&self, other: &Self, params: &E::Params) -> Self {
{
// This is a standard affine point addition formula // This is a standard affine point addition formula
// See 4.3.2 The group law for Weierstrass curves // See 4.3.2 The group law for Weierstrass curves
// Montgomery curves and the Montgomery Ladder // Montgomery curves and the Montgomery Ladder
@ -301,7 +276,10 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
{ {
let mut tmp = other.x; let mut tmp = other.x;
tmp.sub_assign(&self.x); tmp.sub_assign(&self.x);
delta.mul_assign(&tmp.inverse().expect("self.x != other.x, so this must be nonzero")); delta.mul_assign(
&tmp.inverse()
.expect("self.x != other.x, so this must be nonzero"),
);
} }
let mut x3 = delta; let mut x3 = delta;
@ -320,7 +298,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: x3, x: x3,
y: y3, y: y3,
infinity: false, infinity: false,
_marker: PhantomData _marker: PhantomData,
} }
} }
} }
@ -328,12 +306,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
} }
#[must_use] #[must_use]
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>( pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(&self, scalar: S, params: &E::Params) -> Self {
&self,
scalar: S,
params: &E::Params
) -> Self
{
// Standard double-and-add scalar multiplication // Standard double-and-add scalar multiplication
let mut res = Self::zero(); let mut res = Self::zero();

View File

@ -1,18 +1,6 @@
use super::{ use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder};
JubjubEngine,
JubjubParams,
PrimeOrder,
montgomery,
edwards
};
use ff::{ use ff::{Field, LegendreSymbol, PrimeField, PrimeFieldRepr, SqrtField};
Field,
PrimeField,
PrimeFieldRepr,
SqrtField,
LegendreSymbol
};
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
@ -30,12 +18,7 @@ pub fn test_suite<E: JubjubEngine>(params: &E::Params) {
test_read_write::<E>(params); test_read_write::<E>(params);
} }
fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>( fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(x: E::Fr, y: E::Fr, params: &P) -> bool {
x: E::Fr,
y: E::Fr,
params: &P
) -> bool
{
let mut lhs = y; let mut lhs = y;
lhs.square(); lhs.square();
@ -56,9 +39,8 @@ fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(
fn is_on_twisted_edwards_curve<E: JubjubEngine, P: JubjubParams<E>>( fn is_on_twisted_edwards_curve<E: JubjubEngine, P: JubjubParams<E>>(
x: E::Fr, x: E::Fr,
y: E::Fr, y: E::Fr,
params: &P params: &P,
) -> bool ) -> bool {
{
let mut x2 = x; let mut x2 = x;
x2.square(); x2.square();
@ -156,7 +138,9 @@ fn test_order<E: JubjubEngine>(params: &E::Params) {
]); ]);
// The neutral element is in the prime order subgroup. // The neutral element is in the prime order subgroup.
assert!(Point::<E, PrimeOrder>::zero().as_prime_order(params).is_some()); assert!(Point::<E, PrimeOrder>::zero()
.as_prime_order(params)
.is_some());
for _ in 0..50 { for _ in 0..50 {
// Pick a random point and multiply it by the cofactor // Pick a random point and multiply it by the cofactor
@ -256,11 +240,7 @@ fn test_get_for<E: JubjubEngine>(params: &E::Params) {
if let Some(mut p) = edwards::Point::<E, _>::get_for_y(y, sign, params) { if let Some(mut p) = edwards::Point::<E, _>::get_for_y(y, sign, params) {
assert!(p.into_xy().0.into_repr().is_odd() == sign); assert!(p.into_xy().0.into_repr().is_odd() == sign);
p = p.negate(); p = p.negate();
assert!( assert!(edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap() == p);
edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap()
==
p
);
} }
} }
} }
@ -321,13 +301,9 @@ fn test_back_and_forth<E: JubjubEngine>(params: &E::Params) {
let mont = mont_p1.add(&mont_p2, params).mul(s, params); let mont = mont_p1.add(&mont_p2, params).mul(s, params);
let edwards = edwards_p1.add(&edwards_p2, params).mul(s, params); let edwards = edwards_p1.add(&edwards_p2, params).mul(s, params);
assert!( assert!(montgomery::Point::from_edwards(&edwards, params) == mont);
montgomery::Point::from_edwards(&edwards, params) == mont
);
assert!( assert!(edwards::Point::from_montgomery(&mont, params) == edwards);
edwards::Point::from_montgomery(&mont, params) == edwards
);
} }
} }
@ -411,8 +387,7 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
let mut pacc = E::Fs::zero().into_repr(); let mut pacc = E::Fs::zero().into_repr();
let mut nacc = E::Fs::char(); let mut nacc = E::Fs::char();
for _ in 0..params.pedersen_hash_chunks_per_generator() for _ in 0..params.pedersen_hash_chunks_per_generator() {
{
// tmp = cur * 4 // tmp = cur * 4
let mut tmp = cur; let mut tmp = cur;
tmp.mul2(); tmp.mul2();

View File

@ -2,12 +2,12 @@
//! //!
//! Implements section 4.2.2 of the Zcash Protocol Specification. //! Implements section 4.2.2 of the Zcash Protocol Specification.
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use ff::{PrimeField, PrimeFieldRepr};
use crate::{ use crate::{
jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown}, jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown},
primitives::{ProofGenerationKey, ViewingKey}, primitives::{ProofGenerationKey, ViewingKey},
}; };
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use ff::{PrimeField, PrimeFieldRepr};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed"; pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed";
@ -187,8 +187,8 @@ impl<E: JubjubEngine> FullViewingKey<E> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pairing::bls12_381::Bls12;
use crate::jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder}; use crate::jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder};
use pairing::bls12_381::Bls12;
use std::error::Error; use std::error::Error;
use super::FullViewingKey; use super::FullViewingKey;

View File

@ -1,11 +1,5 @@
//! Implementation of in-band secret distribution for Zcash transactions. //! Implementation of in-band secret distribution for Zcash transactions.
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf};
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr};
use rand_core::{CryptoRng, RngCore};
use crate::{ use crate::{
jubjub::{ jubjub::{
edwards, edwards,
@ -14,6 +8,12 @@ use crate::{
}, },
primitives::{Diversifier, Note, PaymentAddress}, primitives::{Diversifier, Note, PaymentAddress},
}; };
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf};
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr};
use rand_core::{CryptoRng, RngCore};
use std::fmt; use std::fmt;
use std::str; use std::str;
@ -544,11 +544,6 @@ pub fn try_sapling_output_recovery(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crypto_api_chachapoly::ChachaPolyIetf;
use ff::{Field, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use rand_core::{CryptoRng, RngCore};
use rand_os::OsRng;
use crate::{ use crate::{
jubjub::{ jubjub::{
edwards, edwards,
@ -557,6 +552,11 @@ mod tests {
}, },
primitives::{Diversifier, PaymentAddress, ValueCommitment}, primitives::{Diversifier, PaymentAddress, ValueCommitment},
}; };
use crypto_api_chachapoly::ChachaPolyIetf;
use ff::{Field, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use rand_core::{CryptoRng, RngCore};
use rand_os::OsRng;
use super::{ use super::{
kdf_sapling, prf_ock, sapling_ka_agree, try_sapling_compact_note_decryption, kdf_sapling, prf_ock, sapling_ka_agree, try_sapling_compact_note_decryption,

View File

@ -4,14 +4,13 @@ use jubjub::*;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum Personalization { pub enum Personalization {
NoteCommitment, NoteCommitment,
MerkleTree(usize) MerkleTree(usize),
} }
impl Personalization { impl Personalization {
pub fn get_bits(&self) -> Vec<bool> { pub fn get_bits(&self) -> Vec<bool> {
match *self { match *self {
Personalization::NoteCommitment => Personalization::NoteCommitment => vec![true, true, true, true, true, true],
vec![true, true, true, true, true, true],
Personalization::MerkleTree(num) => { Personalization::MerkleTree(num) => {
assert!(num < 63); assert!(num < 63);
@ -24,12 +23,16 @@ impl Personalization {
pub fn pedersen_hash<E, I>( pub fn pedersen_hash<E, I>(
personalization: Personalization, personalization: Personalization,
bits: I, bits: I,
params: &E::Params params: &E::Params,
) -> edwards::Point<E, PrimeOrder> ) -> edwards::Point<E, PrimeOrder>
where I: IntoIterator<Item=bool>, where
E: JubjubEngine I: IntoIterator<Item = bool>,
E: JubjubEngine,
{ {
let mut bits = personalization.get_bits().into_iter().chain(bits.into_iter()); let mut bits = personalization
.get_bits()
.into_iter()
.chain(bits.into_iter());
let mut result = edwards::Point::zero(); let mut result = edwards::Point::zero();
let mut generators = params.pedersen_hash_exp_table().iter(); let mut generators = params.pedersen_hash_exp_table().iter();
@ -79,12 +82,13 @@ pub fn pedersen_hash<E, I>(
break; break;
} }
let mut table: &[Vec<edwards::Point<E, _>>] = &generators.next().expect("we don't have enough generators"); let mut table: &[Vec<edwards::Point<E, _>>] =
&generators.next().expect("we don't have enough generators");
let window = JubjubBls12::pedersen_hash_exp_window_size(); let window = JubjubBls12::pedersen_hash_exp_window_size();
let window_mask = (1 << window) - 1; let window_mask = (1 << window) - 1;
let mut acc = acc.into_repr(); let mut acc = acc.into_repr();
let mut tmp = edwards::Point::zero(); let mut tmp = edwards::Point::zero();
while !acc.is_zero() { while !acc.is_zero() {

View File

@ -4,60 +4,47 @@ use constants;
use group_hash::group_hash; use group_hash::group_hash;
use pedersen_hash::{ use pedersen_hash::{pedersen_hash, Personalization};
pedersen_hash,
Personalization
};
use byteorder::{ use byteorder::{LittleEndian, WriteBytesExt};
LittleEndian,
WriteBytesExt
};
use jubjub::{ use jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder};
JubjubEngine,
JubjubParams,
edwards,
PrimeOrder,
FixedGenerators
};
use blake2s_simd::Params as Blake2sParams; use blake2s_simd::Params as Blake2sParams;
#[derive(Clone)] #[derive(Clone)]
pub struct ValueCommitment<E: JubjubEngine> { pub struct ValueCommitment<E: JubjubEngine> {
pub value: u64, pub value: u64,
pub randomness: E::Fs pub randomness: E::Fs,
} }
impl<E: JubjubEngine> ValueCommitment<E> { impl<E: JubjubEngine> ValueCommitment<E> {
pub fn cm( pub fn cm(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
&self, params
params: &E::Params .generator(FixedGenerators::ValueCommitmentValue)
) -> edwards::Point<E, PrimeOrder> .mul(self.value, params)
{ .add(
params.generator(FixedGenerators::ValueCommitmentValue) &params
.mul(self.value, params) .generator(FixedGenerators::ValueCommitmentRandomness)
.add( .mul(self.randomness, params),
&params.generator(FixedGenerators::ValueCommitmentRandomness) params,
.mul(self.randomness, params), )
params
)
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct ProofGenerationKey<E: JubjubEngine> { pub struct ProofGenerationKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>, pub ak: edwards::Point<E, PrimeOrder>,
pub nsk: E::Fs pub nsk: E::Fs,
} }
impl<E: JubjubEngine> ProofGenerationKey<E> { impl<E: JubjubEngine> ProofGenerationKey<E> {
pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey<E> { pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey<E> {
ViewingKey { ViewingKey {
ak: self.ak.clone(), ak: self.ak.clone(),
nk: params.generator(FixedGenerators::ProofGenerationKey) nk: params
.mul(self.nsk, params) .generator(FixedGenerators::ProofGenerationKey)
.mul(self.nsk, params),
} }
} }
} }
@ -65,19 +52,16 @@ impl<E: JubjubEngine> ProofGenerationKey<E> {
#[derive(Debug)] #[derive(Debug)]
pub struct ViewingKey<E: JubjubEngine> { pub struct ViewingKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>, pub ak: edwards::Point<E, PrimeOrder>,
pub nk: edwards::Point<E, PrimeOrder> pub nk: edwards::Point<E, PrimeOrder>,
} }
impl<E: JubjubEngine> ViewingKey<E> { impl<E: JubjubEngine> ViewingKey<E> {
pub fn rk( pub fn rk(&self, ar: E::Fs, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
&self,
ar: E::Fs,
params: &E::Params
) -> edwards::Point<E, PrimeOrder> {
self.ak.add( self.ak.add(
&params.generator(FixedGenerators::SpendingKeyGenerator) &params
.mul(ar, params), .generator(FixedGenerators::SpendingKeyGenerator)
params .mul(ar, params),
params,
) )
} }
@ -88,11 +72,13 @@ impl<E: JubjubEngine> ViewingKey<E> {
self.nk.write(&mut preimage[32..64]).unwrap(); self.nk.write(&mut preimage[32..64]).unwrap();
let mut h = [0; 32]; let mut h = [0; 32];
h.copy_from_slice(Blake2sParams::new() h.copy_from_slice(
.hash_length(32) Blake2sParams::new()
.personal(constants::CRH_IVK_PERSONALIZATION) .hash_length(32)
.hash(&preimage) .personal(constants::CRH_IVK_PERSONALIZATION)
.as_bytes()); .hash(&preimage)
.as_bytes(),
);
// Drop the most significant five bits, so it can be interpreted as a scalar. // Drop the most significant five bits, so it can be interpreted as a scalar.
h[31] &= 0b0000_0111; h[31] &= 0b0000_0111;
@ -106,15 +92,14 @@ impl<E: JubjubEngine> ViewingKey<E> {
pub fn into_payment_address( pub fn into_payment_address(
&self, &self,
diversifier: Diversifier, diversifier: Diversifier,
params: &E::Params params: &E::Params,
) -> Option<PaymentAddress<E>> ) -> Option<PaymentAddress<E>> {
{
diversifier.g_d(params).map(|g_d| { diversifier.g_d(params).map(|g_d| {
let pk_d = g_d.mul(self.ivk(), params); let pk_d = g_d.mul(self.ivk(), params);
PaymentAddress { PaymentAddress {
pk_d: pk_d, pk_d: pk_d,
diversifier: diversifier diversifier: diversifier,
} }
}) })
} }
@ -126,17 +111,20 @@ pub struct Diversifier(pub [u8; 11]);
impl Diversifier { impl Diversifier {
pub fn g_d<E: JubjubEngine>( pub fn g_d<E: JubjubEngine>(
&self, &self,
params: &E::Params params: &E::Params,
) -> Option<edwards::Point<E, PrimeOrder>> ) -> Option<edwards::Point<E, PrimeOrder>> {
{ group_hash::<E>(
group_hash::<E>(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION, params) &self.0,
constants::KEY_DIVERSIFICATION_PERSONALIZATION,
params,
)
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PaymentAddress<E: JubjubEngine> { pub struct PaymentAddress<E: JubjubEngine> {
pub pk_d: edwards::Point<E, PrimeOrder>, pub pk_d: edwards::Point<E, PrimeOrder>,
pub diversifier: Diversifier pub diversifier: Diversifier,
} }
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> { impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
@ -146,11 +134,7 @@ impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
} }
impl<E: JubjubEngine> PaymentAddress<E> { impl<E: JubjubEngine> PaymentAddress<E> {
pub fn g_d( pub fn g_d(&self, params: &E::Params) -> Option<edwards::Point<E, PrimeOrder>> {
&self,
params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>>
{
self.diversifier.g_d(params) self.diversifier.g_d(params)
} }
@ -158,16 +142,13 @@ impl<E: JubjubEngine> PaymentAddress<E> {
&self, &self,
value: u64, value: u64,
randomness: E::Fs, randomness: E::Fs,
params: &E::Params params: &E::Params,
) -> Option<Note<E>> ) -> Option<Note<E>> {
{ self.g_d(params).map(|g_d| Note {
self.g_d(params).map(|g_d| { value: value,
Note { r: randomness,
value: value, g_d: g_d,
r: randomness, pk_d: self.pk_d.clone(),
g_d: g_d,
pk_d: self.pk_d.clone()
}
}) })
} }
} }
@ -181,7 +162,7 @@ pub struct Note<E: JubjubEngine> {
/// The public key of the address, g_d^ivk /// The public key of the address, g_d^ivk
pub pk_d: edwards::Point<E, PrimeOrder>, pub pk_d: edwards::Point<E, PrimeOrder>,
/// The commitment randomness /// The commitment randomness
pub r: E::Fs pub r: E::Fs,
} }
impl<E: JubjubEngine> PartialEq for Note<E> { impl<E: JubjubEngine> PartialEq for Note<E> {
@ -204,13 +185,14 @@ impl<E: JubjubEngine> Note<E> {
} }
/// Computes the note commitment, returning the full point. /// Computes the note commitment, returning the full point.
fn cm_full_point(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> fn cm_full_point(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
{
// Calculate the note contents, as bytes // Calculate the note contents, as bytes
let mut note_contents = vec![]; let mut note_contents = vec![];
// Writing the value in little endian // Writing the value in little endian
(&mut note_contents).write_u64::<LittleEndian>(self.value).unwrap(); (&mut note_contents)
.write_u64::<LittleEndian>(self.value)
.unwrap();
// Write g_d // Write g_d
self.g_d.write(&mut note_contents).unwrap(); self.g_d.write(&mut note_contents).unwrap();
@ -223,36 +205,29 @@ impl<E: JubjubEngine> Note<E> {
// Compute the Pedersen hash of the note contents // Compute the Pedersen hash of the note contents
let hash_of_contents = pedersen_hash( let hash_of_contents = pedersen_hash(
Personalization::NoteCommitment, Personalization::NoteCommitment,
note_contents.into_iter() note_contents
.flat_map(|byte| { .into_iter()
(0..8).map(move |i| ((byte >> i) & 1) == 1) .flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)),
}), params,
params
); );
// Compute final commitment // Compute final commitment
params.generator(FixedGenerators::NoteCommitmentRandomness) params
.mul(self.r, params) .generator(FixedGenerators::NoteCommitmentRandomness)
.add(&hash_of_contents, params) .mul(self.r, params)
.add(&hash_of_contents, params)
} }
/// Computes the nullifier given the viewing key and /// Computes the nullifier given the viewing key and
/// note position /// note position
pub fn nf( pub fn nf(&self, viewing_key: &ViewingKey<E>, position: u64, params: &E::Params) -> Vec<u8> {
&self,
viewing_key: &ViewingKey<E>,
position: u64,
params: &E::Params
) -> Vec<u8>
{
// Compute rho = cm + position.G // Compute rho = cm + position.G
let rho = self let rho = self.cm_full_point(params).add(
.cm_full_point(params) &params
.add( .generator(FixedGenerators::NullifierPosition)
&params.generator(FixedGenerators::NullifierPosition) .mul(position, params),
.mul(position, params), params,
params );
);
// Compute nf = BLAKE2s(nk | rho) // Compute nf = BLAKE2s(nk | rho)
let mut nf_preimage = [0u8; 64]; let mut nf_preimage = [0u8; 64];
@ -267,8 +242,7 @@ impl<E: JubjubEngine> Note<E> {
} }
/// Computes the note commitment /// Computes the note commitment
pub fn cm(&self, params: &E::Params) -> E::Fr pub fn cm(&self, params: &E::Params) -> E::Fr {
{
// The commitment is in the prime order subgroup, so mapping the // The commitment is in the prime order subgroup, so mapping the
// commitment to the x-coordinate is an injective encoding. // commitment to the x-coordinate is an injective encoding.
self.cm_full_point(params).into_xy().0 self.cm_full_point(params).into_xy().0

View File

@ -1,10 +1,10 @@
//! Abstractions over the proving system and parameters. //! Abstractions over the proving system and parameters.
use pairing::bls12_381::{Bls12, Fr};
use crate::{ use crate::{
jubjub::{edwards, fs::Fs, Unknown}, jubjub::{edwards, fs::Fs, Unknown},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, primitives::{Diversifier, PaymentAddress, ProofGenerationKey},
}; };
use pairing::bls12_381::{Bls12, Fr};
use crate::{ use crate::{
merkle_tree::CommitmentTreeWitness, merkle_tree::CommitmentTreeWitness,

View File

@ -1,11 +1,9 @@
//! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve. //! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve.
//! See section 5.4.6 of the Sapling protocol specification. //! See section 5.4.6 of the Sapling protocol specification.
use crate::jubjub::{edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown};
use ff::{Field, PrimeField, PrimeFieldRepr}; use ff::{Field, PrimeField, PrimeFieldRepr};
use rand_core::RngCore; use rand_core::RngCore;
use crate::jubjub::{
edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown,
};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use util::hash_to_scalar; use util::hash_to_scalar;
@ -150,10 +148,15 @@ impl<E: JubjubEngine> PublicKey<E> {
Err(_) => return false, Err(_) => return false,
}; };
// 0 = h_G(-S . P_G + R + c . vk) // 0 = h_G(-S . P_G + R + c . vk)
self.0.mul(c, params).add(&r, params).add( self.0
&params.generator(p_g).mul(s, params).negate().into(), .mul(c, params)
params .add(&r, params)
).mul_by_cofactor(params).eq(&Point::zero()) .add(
&params.generator(p_g).mul(s, params).negate().into(),
params,
)
.mul_by_cofactor(params)
.eq(&Point::zero())
} }
} }
@ -170,8 +173,7 @@ pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>(
batch: &[BatchEntry<'a, E>], batch: &[BatchEntry<'a, E>],
p_g: FixedGenerators, p_g: FixedGenerators,
params: &E::Params, params: &E::Params,
) -> bool ) -> bool {
{
let mut acc = Point::<E, Unknown>::zero(); let mut acc = Point::<E, Unknown>::zero();
for entry in batch { for entry in batch {
@ -218,8 +220,8 @@ mod tests {
#[test] #[test]
fn test_batch_verify() { fn test_batch_verify() {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
let p_g = FixedGenerators::SpendingKeyGenerator; let p_g = FixedGenerators::SpendingKeyGenerator;
@ -237,8 +239,16 @@ mod tests {
assert!(vk2.verify(msg2, &sig2, p_g, params)); assert!(vk2.verify(msg2, &sig2, p_g, params));
let mut batch = vec![ let mut batch = vec![
BatchEntry { vk: vk1, msg: msg1, sig: sig1 }, BatchEntry {
BatchEntry { vk: vk2, msg: msg2, sig: sig2 } vk: vk1,
msg: msg1,
sig: sig1,
},
BatchEntry {
vk: vk2,
msg: msg2,
sig: sig2,
},
]; ];
assert!(batch_verify(rng, &batch, p_g, params)); assert!(batch_verify(rng, &batch, p_g, params));
@ -251,8 +261,8 @@ mod tests {
#[test] #[test]
fn cofactor_check() { fn cofactor_check() {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
let zero = edwards::Point::zero(); let zero = edwards::Point::zero();
@ -286,8 +296,8 @@ mod tests {
#[test] #[test]
fn round_trip_serialization() { fn round_trip_serialization() {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let p_g = FixedGenerators::SpendingKeyGenerator; let p_g = FixedGenerators::SpendingKeyGenerator;
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
@ -322,8 +332,8 @@ mod tests {
#[test] #[test]
fn random_signatures() { fn random_signatures() {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let p_g = FixedGenerators::SpendingKeyGenerator; let p_g = FixedGenerators::SpendingKeyGenerator;
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();

View File

@ -1,13 +1,13 @@
//! Structs and constants specific to the Sapling shielded pool. //! Structs and constants specific to the Sapling shielded pool.
use ff::{BitIterator, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use rand_core::{CryptoRng, RngCore};
use crate::{ use crate::{
jubjub::{fs::Fs, FixedGenerators, JubjubBls12}, jubjub::{fs::Fs, FixedGenerators, JubjubBls12},
pedersen_hash::{pedersen_hash, Personalization}, pedersen_hash::{pedersen_hash, Personalization},
primitives::Note, primitives::Note,
}; };
use ff::{BitIterator, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use rand_core::{CryptoRng, RngCore};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use crate::merkle_tree::Hashable; use crate::merkle_tree::Hashable;

View File

@ -1,12 +1,12 @@
//! Structs for building transactions. //! Structs for building transactions.
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore};
use crate::{ use crate::{
jubjub::fs::Fs, jubjub::fs::Fs,
primitives::{Diversifier, Note, PaymentAddress}, primitives::{Diversifier, Note, PaymentAddress},
}; };
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore};
use zip32::ExtendedSpendingKey; use zip32::ExtendedSpendingKey;
use crate::{ use crate::{

View File

@ -1,7 +1,7 @@
use crate::jubjub::{edwards, Unknown};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use ff::{PrimeField, PrimeFieldRepr}; use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr, FrRepr}; use pairing::bls12_381::{Bls12, Fr, FrRepr};
use crate::jubjub::{edwards, Unknown};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use legacy::Script; use legacy::Script;

View File

@ -135,7 +135,10 @@ impl DiversifierKey {
/// Returns the first index starting from j that generates a valid /// Returns the first index starting from j that generates a valid
/// diversifier, along with the corresponding diversifier. Returns /// diversifier, along with the corresponding diversifier. Returns
/// an error if the diversifier space is exhausted. /// an error if the diversifier space is exhausted.
pub fn diversifier(&self, mut j: DiversifierIndex) -> Result<(DiversifierIndex, Diversifier), ()> { pub fn diversifier(
&self,
mut j: DiversifierIndex,
) -> Result<(DiversifierIndex, Diversifier), ()> {
let ff = FF1::<Aes256>::new(&self.0, 2).unwrap(); let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
loop { loop {
// Generate d_j // Generate d_j

View File

@ -1,30 +1,20 @@
extern crate ff;
extern crate bellman; extern crate bellman;
extern crate ff;
extern crate pairing; extern crate pairing;
extern crate rand_core; extern crate rand_core;
extern crate rand_xorshift; extern crate rand_xorshift;
extern crate zcash_primitives; extern crate zcash_primitives;
extern crate zcash_proofs; extern crate zcash_proofs;
use ff::Field;
use std::time::{Duration, Instant};
use zcash_primitives::jubjub::{
JubjubBls12,
edwards,
fs,
};
use zcash_proofs::circuit::sapling::{
Spend
};
use zcash_primitives::primitives::{
Diversifier,
ProofGenerationKey,
ValueCommitment
};
use bellman::groth16::*; use bellman::groth16::*;
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use pairing::bls12_381::{Bls12, Fr}; use std::time::{Duration, Instant};
use zcash_primitives::jubjub::{edwards, fs, JubjubBls12};
use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, ValueCommitment};
use zcash_proofs::circuit::sapling::Spend;
const TREE_DEPTH: usize = 32; const TREE_DEPTH: usize = 32;
@ -45,10 +35,11 @@ fn main() {
commitment_randomness: None, commitment_randomness: None,
ar: None, ar: None,
auth_path: vec![None; TREE_DEPTH], auth_path: vec![None; TREE_DEPTH],
anchor: None anchor: None,
}, },
rng rng,
).unwrap(); )
.unwrap();
const SAMPLES: u32 = 50; const SAMPLES: u32 = 50;
@ -56,7 +47,7 @@ fn main() {
for _ in 0..SAMPLES { for _ in 0..SAMPLES {
let value_commitment = ValueCommitment { let value_commitment = ValueCommitment {
value: 1, value: 1,
randomness: fs::Fs::random(rng) randomness: fs::Fs::random(rng),
}; };
let nsk = fs::Fs::random(rng); let nsk = fs::Fs::random(rng);
@ -64,7 +55,7 @@ fn main() {
let proof_generation_key = ProofGenerationKey { let proof_generation_key = ProofGenerationKey {
ak: ak.clone(), ak: ak.clone(),
nsk: nsk.clone() nsk: nsk.clone(),
}; };
let viewing_key = proof_generation_key.into_viewing_key(jubjub_params); let viewing_key = proof_generation_key.into_viewing_key(jubjub_params);
@ -78,11 +69,7 @@ fn main() {
Diversifier(d) Diversifier(d)
}; };
if let Some(p) = viewing_key.into_payment_address( if let Some(p) = viewing_key.into_payment_address(diversifier, jubjub_params) {
diversifier,
jubjub_params
)
{
payment_address = p; payment_address = p;
break; break;
} }
@ -94,21 +81,25 @@ fn main() {
let anchor = Fr::random(rng); let anchor = Fr::random(rng);
let start = Instant::now(); let start = Instant::now();
let _ = create_random_proof(Spend { let _ = create_random_proof(
params: jubjub_params, Spend {
value_commitment: Some(value_commitment), params: jubjub_params,
proof_generation_key: Some(proof_generation_key), value_commitment: Some(value_commitment),
payment_address: Some(payment_address), proof_generation_key: Some(proof_generation_key),
commitment_randomness: Some(commitment_randomness), payment_address: Some(payment_address),
ar: Some(ar), commitment_randomness: Some(commitment_randomness),
auth_path: auth_path, ar: Some(ar),
anchor: Some(anchor) auth_path: auth_path,
}, &groth_params, rng).unwrap(); anchor: Some(anchor),
},
&groth_params,
rng,
)
.unwrap();
total_time += start.elapsed(); total_time += start.elapsed();
} }
let avg = total_time / SAMPLES; let avg = total_time / SAMPLES;
let avg = avg.subsec_nanos() as f64 / 1_000_000_000f64 let avg = avg.subsec_nanos() as f64 / 1_000_000_000f64 + (avg.as_secs() as f64);
+ (avg.as_secs() as f64);
println!("Average proving time (in seconds): {}", avg); println!("Average proving time (in seconds): {}", avg);
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,13 @@
use super::ecc::{ use super::ecc::{EdwardsPoint, MontgomeryPoint};
MontgomeryPoint,
EdwardsPoint
};
use bellman::gadgets::boolean::Boolean; use bellman::gadgets::boolean::Boolean;
use zcash_primitives::jubjub::*;
use bellman::{
ConstraintSystem, SynthesisError
};
use bellman::gadgets::lookup::*; use bellman::gadgets::lookup::*;
use bellman::{ConstraintSystem, SynthesisError};
use zcash_primitives::jubjub::*;
pub use zcash_primitives::pedersen_hash::Personalization; pub use zcash_primitives::pedersen_hash::Personalization;
fn get_constant_bools(person: &Personalization) -> Vec<Boolean> { fn get_constant_bools(person: &Personalization) -> Vec<Boolean> {
person.get_bits() person
.get_bits()
.into_iter() .into_iter()
.map(|e| Boolean::constant(e)) .map(|e| Boolean::constant(e))
.collect() .collect()
@ -21,9 +17,10 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
mut cs: CS, mut cs: CS,
personalization: Personalization, personalization: Personalization,
bits: &[Boolean], bits: &[Boolean],
params: &E::Params params: &E::Params,
) -> Result<EdwardsPoint<E>, SynthesisError> ) -> Result<EdwardsPoint<E>, SynthesisError>
where CS: ConstraintSystem<E> where
CS: ConstraintSystem<E>,
{ {
let personalization = get_constant_bools(&personalization); let personalization = get_constant_bools(&personalization);
assert_eq!(personalization.len(), 6); assert_eq!(personalization.len(), 6);
@ -36,8 +33,7 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
let mut segment_i = 0; let mut segment_i = 0;
loop { loop {
let mut segment_result = None; let mut segment_result = None;
let mut segment_windows = &segment_generators.next() let mut segment_windows = &segment_generators.next().expect("enough segments")[..];
.expect("enough segments")[..];
let mut window_i = 0; let mut window_i = 0;
while let Some(a) = bits.next() { while let Some(a) = bits.next() {
@ -47,7 +43,7 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
let tmp = lookup3_xy_with_conditional_negation( let tmp = lookup3_xy_with_conditional_negation(
cs.namespace(|| format!("segment {}, window {}", segment_i, window_i)), cs.namespace(|| format!("segment {}, window {}", segment_i, window_i)),
&[a.clone(), b.clone(), c.clone()], &[a.clone(), b.clone(), c.clone()],
&segment_windows[0] &segment_windows[0],
)?; )?;
let tmp = MontgomeryPoint::interpret_unchecked(tmp.0, tmp.1); let tmp = MontgomeryPoint::interpret_unchecked(tmp.0, tmp.1);
@ -55,12 +51,14 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
match segment_result { match segment_result {
None => { None => {
segment_result = Some(tmp); segment_result = Some(tmp);
}, }
Some(ref mut segment_result) => { Some(ref mut segment_result) => {
*segment_result = tmp.add( *segment_result = tmp.add(
cs.namespace(|| format!("addition of segment {}, window {}", segment_i, window_i)), cs.namespace(|| {
format!("addition of segment {}, window {}", segment_i, window_i)
}),
segment_result, segment_result,
params params,
)?; )?;
} }
} }
@ -79,22 +77,24 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
// Convert this segment into twisted Edwards form. // Convert this segment into twisted Edwards form.
let segment_result = segment_result.into_edwards( let segment_result = segment_result.into_edwards(
cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)), cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)),
params params,
)?; )?;
match edwards_result { match edwards_result {
Some(ref mut edwards_result) => { Some(ref mut edwards_result) => {
*edwards_result = segment_result.add( *edwards_result = segment_result.add(
cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)), cs.namespace(|| {
format!("addition of segment {} to accumulator", segment_i)
}),
edwards_result, edwards_result,
params params,
)?; )?;
}, }
None => { None => {
edwards_result = Some(segment_result); edwards_result = Some(segment_result);
} }
} }
}, }
None => { None => {
// We didn't process any new bits. // We didn't process any new bits.
break; break;
@ -110,37 +110,44 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use bellman::gadgets::boolean::{AllocatedBit, Boolean};
use bellman::gadgets::test::*; use bellman::gadgets::test::*;
use bellman::gadgets::boolean::{Boolean, AllocatedBit};
use zcash_primitives::pedersen_hash;
use ff::PrimeField; use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use zcash_primitives::pedersen_hash;
#[test] #[test]
fn test_pedersen_hash_constraints() { fn test_pedersen_hash_constraints() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let input: Vec<bool> = (0..(Fr::NUM_BITS * 2)).map(|_| rng.next_u32() % 2 != 0).collect(); let input: Vec<bool> = (0..(Fr::NUM_BITS * 2))
.map(|_| rng.next_u32() % 2 != 0)
.collect();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| { let input_bools: Vec<Boolean> = input
Boolean::from( .iter()
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap() .enumerate()
) .map(|(i, b)| {
}).collect(); Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap(),
)
})
.collect();
pedersen_hash( pedersen_hash(
cs.namespace(|| "pedersen hash"), cs.namespace(|| "pedersen hash"),
Personalization::NoteCommitment, Personalization::NoteCommitment,
&input_bools, &input_bools,
params params,
).unwrap(); )
.unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1377); assert_eq!(cs.num_constraints(), 1377);
@ -149,8 +156,8 @@ mod test {
#[test] #[test]
fn test_pedersen_hash() { fn test_pedersen_hash() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
@ -160,26 +167,33 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| { let input_bools: Vec<Boolean> = input
Boolean::from( .iter()
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap() .enumerate()
) .map(|(i, b)| {
}).collect(); Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b))
.unwrap(),
)
})
.collect();
let res = pedersen_hash( let res = pedersen_hash(
cs.namespace(|| "pedersen hash"), cs.namespace(|| "pedersen hash"),
Personalization::MerkleTree(1), Personalization::MerkleTree(1),
&input_bools, &input_bools,
params params,
).unwrap(); )
.unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let expected = pedersen_hash::pedersen_hash::<Bls12, _>( let expected = pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(1), Personalization::MerkleTree(1),
input.clone().into_iter(), input.clone().into_iter(),
params params,
).into_xy(); )
.into_xy();
assert_eq!(res.get_x().get_value().unwrap(), expected.0); assert_eq!(res.get_x().get_value().unwrap(), expected.0);
assert_eq!(res.get_y().get_value().unwrap(), expected.1); assert_eq!(res.get_y().get_value().unwrap(), expected.1);
@ -188,8 +202,9 @@ mod test {
let unexpected = pedersen_hash::pedersen_hash::<Bls12, _>( let unexpected = pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(0), Personalization::MerkleTree(0),
input.into_iter(), input.into_iter(),
params params,
).into_xy(); )
.into_xy();
assert!(res.get_x().get_value().unwrap() != unexpected.0); assert!(res.get_x().get_value().unwrap() != unexpected.0);
assert!(res.get_y().get_value().unwrap() != unexpected.1); assert!(res.get_y().get_value().unwrap() != unexpected.1);

View File

@ -1,31 +1,20 @@
use ff::{Field, PrimeField, PrimeFieldRepr}; use ff::{Field, PrimeField, PrimeFieldRepr};
use bellman::{ use bellman::{Circuit, ConstraintSystem, SynthesisError};
SynthesisError,
ConstraintSystem,
Circuit
};
use zcash_primitives::jubjub::{ use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine};
JubjubEngine,
FixedGenerators
};
use zcash_primitives::constants; use zcash_primitives::constants;
use zcash_primitives::primitives::{ use zcash_primitives::primitives::{PaymentAddress, ProofGenerationKey, ValueCommitment};
ValueCommitment,
ProofGenerationKey,
PaymentAddress
};
use bellman::gadgets::Assignment;
use bellman::gadgets::boolean;
use super::ecc; use super::ecc;
use super::pedersen_hash; use super::pedersen_hash;
use bellman::gadgets::blake2s; use bellman::gadgets::blake2s;
use bellman::gadgets::num; use bellman::gadgets::boolean;
use bellman::gadgets::multipack; use bellman::gadgets::multipack;
use bellman::gadgets::num;
use bellman::gadgets::Assignment;
pub const TREE_DEPTH: usize = zcash_primitives::sapling::SAPLING_COMMITMENT_TREE_DEPTH; pub const TREE_DEPTH: usize = zcash_primitives::sapling::SAPLING_COMMITMENT_TREE_DEPTH;
@ -54,7 +43,7 @@ pub struct Spend<'a, E: JubjubEngine> {
/// The anchor; the root of the tree. If the note being /// The anchor; the root of the tree. If the note being
/// spent is zero-value, this can be anything. /// spent is zero-value, this can be anything.
pub anchor: Option<E::Fr> pub anchor: Option<E::Fr>,
} }
/// This is an output circuit instance. /// This is an output circuit instance.
@ -71,7 +60,7 @@ pub struct Output<'a, E: JubjubEngine> {
pub commitment_randomness: Option<E::Fs>, pub commitment_randomness: Option<E::Fs>,
/// The ephemeral secret key for DH with recipient /// The ephemeral secret key for DH with recipient
pub esk: Option<E::Fs> pub esk: Option<E::Fs>,
} }
/// Exposes a Pedersen commitment to the value as an /// Exposes a Pedersen commitment to the value as an
@ -79,15 +68,16 @@ pub struct Output<'a, E: JubjubEngine> {
fn expose_value_commitment<E, CS>( fn expose_value_commitment<E, CS>(
mut cs: CS, mut cs: CS,
value_commitment: Option<ValueCommitment<E>>, value_commitment: Option<ValueCommitment<E>>,
params: &E::Params params: &E::Params,
) -> Result<Vec<boolean::Boolean>, SynthesisError> ) -> Result<Vec<boolean::Boolean>, SynthesisError>
where E: JubjubEngine, where
CS: ConstraintSystem<E> E: JubjubEngine,
CS: ConstraintSystem<E>,
{ {
// Booleanize the value into little-endian bit order // Booleanize the value into little-endian bit order
let value_bits = boolean::u64_into_boolean_vec_le( let value_bits = boolean::u64_into_boolean_vec_le(
cs.namespace(|| "value"), cs.namespace(|| "value"),
value_commitment.as_ref().map(|c| c.value) value_commitment.as_ref().map(|c| c.value),
)?; )?;
// Compute the note value in the exponent // Compute the note value in the exponent
@ -95,7 +85,7 @@ fn expose_value_commitment<E, CS>(
cs.namespace(|| "compute the value in the exponent"), cs.namespace(|| "compute the value in the exponent"),
FixedGenerators::ValueCommitmentValue, FixedGenerators::ValueCommitmentValue,
&value_bits, &value_bits,
params params,
)?; )?;
// Booleanize the randomness. This does not ensure // Booleanize the randomness. This does not ensure
@ -103,7 +93,7 @@ fn expose_value_commitment<E, CS>(
// it doesn't matter for security. // it doesn't matter for security.
let rcv = boolean::field_into_boolean_vec_le( let rcv = boolean::field_into_boolean_vec_le(
cs.namespace(|| "rcv"), cs.namespace(|| "rcv"),
value_commitment.as_ref().map(|c| c.randomness) value_commitment.as_ref().map(|c| c.randomness),
)?; )?;
// Compute the randomness in the exponent // Compute the randomness in the exponent
@ -111,15 +101,11 @@ fn expose_value_commitment<E, CS>(
cs.namespace(|| "computation of rcv"), cs.namespace(|| "computation of rcv"),
FixedGenerators::ValueCommitmentRandomness, FixedGenerators::ValueCommitmentRandomness,
&rcv, &rcv,
params params,
)?; )?;
// Compute the Pedersen commitment to the value // Compute the Pedersen commitment to the value
let cv = value.add( let cv = value.add(cs.namespace(|| "computation of cv"), &rcv, params)?;
cs.namespace(|| "computation of cv"),
&rcv,
params
)?;
// Expose the commitment as an input to the circuit // Expose the commitment as an input to the circuit
cv.inputize(cs.namespace(|| "commitment point"))?; cv.inputize(cs.namespace(|| "commitment point"))?;
@ -128,43 +114,32 @@ fn expose_value_commitment<E, CS>(
} }
impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> { impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
{
// Prover witnesses ak (ensures that it's on the curve) // Prover witnesses ak (ensures that it's on the curve)
let ak = ecc::EdwardsPoint::witness( let ak = ecc::EdwardsPoint::witness(
cs.namespace(|| "ak"), cs.namespace(|| "ak"),
self.proof_generation_key.as_ref().map(|k| k.ak.clone()), self.proof_generation_key.as_ref().map(|k| k.ak.clone()),
self.params self.params,
)?; )?;
// There are no sensible attacks on small order points // There are no sensible attacks on small order points
// of ak (that we're aware of!) but it's a cheap check, // of ak (that we're aware of!) but it's a cheap check,
// so we do it. // so we do it.
ak.assert_not_small_order( ak.assert_not_small_order(cs.namespace(|| "ak not small order"), self.params)?;
cs.namespace(|| "ak not small order"),
self.params
)?;
// Rerandomize ak and expose it as an input to the circuit // Rerandomize ak and expose it as an input to the circuit
{ {
let ar = boolean::field_into_boolean_vec_le( let ar = boolean::field_into_boolean_vec_le(cs.namespace(|| "ar"), self.ar)?;
cs.namespace(|| "ar"),
self.ar
)?;
// Compute the randomness in the exponent // Compute the randomness in the exponent
let ar = ecc::fixed_base_multiplication( let ar = ecc::fixed_base_multiplication(
cs.namespace(|| "computation of randomization for the signing key"), cs.namespace(|| "computation of randomization for the signing key"),
FixedGenerators::SpendingKeyGenerator, FixedGenerators::SpendingKeyGenerator,
&ar, &ar,
self.params self.params,
)?; )?;
let rk = ak.add( let rk = ak.add(cs.namespace(|| "computation of rk"), &ar, self.params)?;
cs.namespace(|| "computation of rk"),
&ar,
self.params
)?;
rk.inputize(cs.namespace(|| "rk"))?; rk.inputize(cs.namespace(|| "rk"))?;
} }
@ -175,7 +150,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// Witness nsk as bits // Witness nsk as bits
let nsk = boolean::field_into_boolean_vec_le( let nsk = boolean::field_into_boolean_vec_le(
cs.namespace(|| "nsk"), cs.namespace(|| "nsk"),
self.proof_generation_key.as_ref().map(|k| k.nsk.clone()) self.proof_generation_key.as_ref().map(|k| k.nsk.clone()),
)?; )?;
// NB: We don't ensure that the bit representation of nsk // NB: We don't ensure that the bit representation of nsk
@ -188,7 +163,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "computation of nk"), cs.namespace(|| "computation of nk"),
FixedGenerators::ProofGenerationKey, FixedGenerators::ProofGenerationKey,
&nsk, &nsk,
self.params self.params,
)?; )?;
} }
@ -196,9 +171,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let mut ivk_preimage = vec![]; let mut ivk_preimage = vec![];
// Place ak in the preimage for CRH^ivk // Place ak in the preimage for CRH^ivk
ivk_preimage.extend( ivk_preimage.extend(ak.repr(cs.namespace(|| "representation of ak"))?);
ak.repr(cs.namespace(|| "representation of ak"))?
);
// This is the nullifier preimage for PRF^nf // This is the nullifier preimage for PRF^nf
let mut nf_preimage = vec![]; let mut nf_preimage = vec![];
@ -206,9 +179,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// Extend ivk and nf preimages with the representation of // Extend ivk and nf preimages with the representation of
// nk. // nk.
{ {
let repr_nk = nk.repr( let repr_nk = nk.repr(cs.namespace(|| "representation of nk"))?;
cs.namespace(|| "representation of nk")
)?;
ivk_preimage.extend(repr_nk.iter().cloned()); ivk_preimage.extend(repr_nk.iter().cloned());
nf_preimage.extend(repr_nk); nf_preimage.extend(repr_nk);
@ -221,7 +192,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let mut ivk = blake2s::blake2s( let mut ivk = blake2s::blake2s(
cs.namespace(|| "computation of ivk"), cs.namespace(|| "computation of ivk"),
&ivk_preimage, &ivk_preimage,
constants::CRH_IVK_PERSONALIZATION constants::CRH_IVK_PERSONALIZATION,
)?; )?;
// drop_5 to ensure it's in the field // drop_5 to ensure it's in the field
@ -239,7 +210,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
ecc::EdwardsPoint::witness( ecc::EdwardsPoint::witness(
cs.namespace(|| "witness g_d"), cs.namespace(|| "witness g_d"),
self.payment_address.as_ref().and_then(|a| a.g_d(params)), self.payment_address.as_ref().and_then(|a| a.g_d(params)),
self.params self.params,
)? )?
}; };
@ -247,17 +218,10 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// is already done in the Output circuit, and this proof ensures // is already done in the Output circuit, and this proof ensures
// g_d is bound to a product of that check, but for defense in // g_d is bound to a product of that check, but for defense in
// depth let's check it anyway. It's cheap. // depth let's check it anyway. It's cheap.
g_d.assert_not_small_order( g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?;
cs.namespace(|| "g_d not small order"),
self.params
)?;
// Compute pk_d = g_d^ivk // Compute pk_d = g_d^ivk
let pk_d = g_d.mul( let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk, self.params)?;
cs.namespace(|| "compute pk_d"),
&ivk,
self.params
)?;
// Compute note contents: // Compute note contents:
// value (in big endian) followed by g_d and pk_d // value (in big endian) followed by g_d and pk_d
@ -271,18 +235,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let value_bits = expose_value_commitment( let value_bits = expose_value_commitment(
cs.namespace(|| "value commitment"), cs.namespace(|| "value commitment"),
self.value_commitment, self.value_commitment,
self.params self.params,
)?; )?;
// Compute the note's value as a linear combination // Compute the note's value as a linear combination
// of the bits. // of the bits.
let mut coeff = E::Fr::one(); let mut coeff = E::Fr::one();
for bit in &value_bits { for bit in &value_bits {
value_num = value_num.add_bool_with_coeff( value_num = value_num.add_bool_with_coeff(CS::one(), bit, coeff);
CS::one(),
bit,
coeff
);
coeff.double(); coeff.double();
} }
@ -291,14 +251,10 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
} }
// Place g_d in the note // Place g_d in the note
note_contents.extend( note_contents.extend(g_d.repr(cs.namespace(|| "representation of g_d"))?);
g_d.repr(cs.namespace(|| "representation of g_d"))?
);
// Place pk_d in the note // Place pk_d in the note
note_contents.extend( note_contents.extend(pk_d.repr(cs.namespace(|| "representation of pk_d"))?);
pk_d.repr(cs.namespace(|| "representation of pk_d"))?
);
assert_eq!( assert_eq!(
note_contents.len(), note_contents.len(),
@ -312,14 +268,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "note content hash"), cs.namespace(|| "note content hash"),
pedersen_hash::Personalization::NoteCommitment, pedersen_hash::Personalization::NoteCommitment,
&note_contents, &note_contents,
self.params self.params,
)?; )?;
{ {
// Booleanize the randomness for the note commitment // Booleanize the randomness for the note commitment
let rcm = boolean::field_into_boolean_vec_le( let rcm = boolean::field_into_boolean_vec_le(
cs.namespace(|| "rcm"), cs.namespace(|| "rcm"),
self.commitment_randomness self.commitment_randomness,
)?; )?;
// Compute the note commitment randomness in the exponent // Compute the note commitment randomness in the exponent
@ -327,7 +283,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "computation of commitment randomness"), cs.namespace(|| "computation of commitment randomness"),
FixedGenerators::NoteCommitmentRandomness, FixedGenerators::NoteCommitmentRandomness,
&rcm, &rcm,
self.params self.params,
)?; )?;
// Randomize the note commitment. Pedersen hashes are not // Randomize the note commitment. Pedersen hashes are not
@ -335,7 +291,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cm = cm.add( cm = cm.add(
cs.namespace(|| "randomization of note commitment"), cs.namespace(|| "randomization of note commitment"),
&rcm, &rcm,
self.params self.params,
)?; )?;
} }
@ -356,7 +312,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// depth of the tree. // depth of the tree.
let cur_is_right = boolean::Boolean::from(boolean::AllocatedBit::alloc( let cur_is_right = boolean::Boolean::from(boolean::AllocatedBit::alloc(
cs.namespace(|| "position bit"), cs.namespace(|| "position bit"),
e.map(|e| e.1) e.map(|e| e.1),
)?); )?);
// Push this boolean for nullifier computation later // Push this boolean for nullifier computation later
@ -364,19 +320,15 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// Witness the authentication path element adjacent // Witness the authentication path element adjacent
// at this depth. // at this depth.
let path_element = num::AllocatedNum::alloc( let path_element =
cs.namespace(|| "path element"), num::AllocatedNum::alloc(cs.namespace(|| "path element"), || Ok(e.get()?.0))?;
|| {
Ok(e.get()?.0)
}
)?;
// Swap the two if the current subtree is on the right // Swap the two if the current subtree is on the right
let (xl, xr) = num::AllocatedNum::conditionally_reverse( let (xl, xr) = num::AllocatedNum::conditionally_reverse(
cs.namespace(|| "conditional reversal of preimage"), cs.namespace(|| "conditional reversal of preimage"),
&cur, &cur,
&path_element, &path_element,
&cur_is_right &cur_is_right,
)?; )?;
// We don't need to be strict, because the function is // We don't need to be strict, because the function is
@ -392,20 +344,19 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "computation of pedersen hash"), cs.namespace(|| "computation of pedersen hash"),
pedersen_hash::Personalization::MerkleTree(i), pedersen_hash::Personalization::MerkleTree(i),
&preimage, &preimage,
self.params self.params,
)?.get_x().clone(); // Injective encoding )?
.get_x()
.clone(); // Injective encoding
} }
{ {
let real_anchor_value = self.anchor; let real_anchor_value = self.anchor;
// Allocate the "real" anchor that will be exposed. // Allocate the "real" anchor that will be exposed.
let rt = num::AllocatedNum::alloc( let rt = num::AllocatedNum::alloc(cs.namespace(|| "conditional anchor"), || {
cs.namespace(|| "conditional anchor"), Ok(*real_anchor_value.get()?)
|| { })?;
Ok(*real_anchor_value.get()?)
}
)?;
// (cur - rt) * value = 0 // (cur - rt) * value = 0
// if value is zero, cur and rt can be different // if value is zero, cur and rt can be different
@ -414,7 +365,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
|| "conditionally enforce correct root", || "conditionally enforce correct root",
|lc| lc + cur.get_variable() - rt.get_variable(), |lc| lc + cur.get_variable() - rt.get_variable(),
|lc| lc + &value_num.lc(E::Fr::one()), |lc| lc + &value_num.lc(E::Fr::one()),
|lc| lc |lc| lc,
); );
// Expose the anchor // Expose the anchor
@ -430,29 +381,27 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "g^position"), cs.namespace(|| "g^position"),
FixedGenerators::NullifierPosition, FixedGenerators::NullifierPosition,
&position_bits, &position_bits,
self.params self.params,
)?; )?;
// Add the position to the commitment // Add the position to the commitment
rho = rho.add( rho = rho.add(
cs.namespace(|| "faerie gold prevention"), cs.namespace(|| "faerie gold prevention"),
&position, &position,
self.params self.params,
)?; )?;
} }
// Let's compute nf = BLAKE2s(nk || rho) // Let's compute nf = BLAKE2s(nk || rho)
nf_preimage.extend( nf_preimage.extend(rho.repr(cs.namespace(|| "representation of rho"))?);
rho.repr(cs.namespace(|| "representation of rho"))?
);
assert_eq!(nf_preimage.len(), 512); assert_eq!(nf_preimage.len(), 512);
// Compute nf // Compute nf
let nf = blake2s::blake2s( let nf = blake2s::blake2s(
cs.namespace(|| "nf computation"), cs.namespace(|| "nf computation"),
&nf_preimage, &nf_preimage,
constants::PRF_NF_PERSONALIZATION constants::PRF_NF_PERSONALIZATION,
)?; )?;
multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf) multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf)
@ -460,8 +409,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
} }
impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> { impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
{
// Let's start to construct our note, which contains // Let's start to construct our note, which contains
// value (big endian) // value (big endian)
let mut note_contents = vec![]; let mut note_contents = vec![];
@ -471,7 +419,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
note_contents.extend(expose_value_commitment( note_contents.extend(expose_value_commitment(
cs.namespace(|| "value commitment"), cs.namespace(|| "value commitment"),
self.value_commitment, self.value_commitment,
self.params self.params,
)?); )?);
// Let's deal with g_d // Let's deal with g_d
@ -483,7 +431,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
let g_d = ecc::EdwardsPoint::witness( let g_d = ecc::EdwardsPoint::witness(
cs.namespace(|| "witness g_d"), cs.namespace(|| "witness g_d"),
self.payment_address.as_ref().and_then(|a| a.g_d(params)), self.payment_address.as_ref().and_then(|a| a.g_d(params)),
self.params self.params,
)?; )?;
// g_d is ensured to be large order. The relationship // g_d is ensured to be large order. The relationship
@ -495,29 +443,17 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
// //
// Further, if it were small order, epk would be // Further, if it were small order, epk would be
// small order too! // small order too!
g_d.assert_not_small_order( g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?;
cs.namespace(|| "g_d not small order"),
self.params
)?;
// Extend our note contents with the representation of // Extend our note contents with the representation of
// g_d. // g_d.
note_contents.extend( note_contents.extend(g_d.repr(cs.namespace(|| "representation of g_d"))?);
g_d.repr(cs.namespace(|| "representation of g_d"))?
);
// Booleanize our ephemeral secret key // Booleanize our ephemeral secret key
let esk = boolean::field_into_boolean_vec_le( let esk = boolean::field_into_boolean_vec_le(cs.namespace(|| "esk"), self.esk)?;
cs.namespace(|| "esk"),
self.esk
)?;
// Create the ephemeral public key from g_d. // Create the ephemeral public key from g_d.
let epk = g_d.mul( let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk, self.params)?;
cs.namespace(|| "epk computation"),
&esk,
self.params
)?;
// Expose epk publicly. // Expose epk publicly.
epk.inputize(cs.namespace(|| "epk"))?; epk.inputize(cs.namespace(|| "epk"))?;
@ -534,13 +470,13 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
// endian bits (to match the representation) // endian bits (to match the representation)
let y_contents = boolean::field_into_boolean_vec_le( let y_contents = boolean::field_into_boolean_vec_le(
cs.namespace(|| "pk_d bits of y"), cs.namespace(|| "pk_d bits of y"),
pk_d.map(|e| e.1) pk_d.map(|e| e.1),
)?; )?;
// Witness the sign bit // Witness the sign bit
let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc(
cs.namespace(|| "pk_d bit of x"), cs.namespace(|| "pk_d bit of x"),
pk_d.map(|e| e.0.into_repr().is_odd()) pk_d.map(|e| e.0.into_repr().is_odd()),
)?); )?);
// Extend the note with pk_d representation // Extend the note with pk_d representation
@ -560,14 +496,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
cs.namespace(|| "note content hash"), cs.namespace(|| "note content hash"),
pedersen_hash::Personalization::NoteCommitment, pedersen_hash::Personalization::NoteCommitment,
&note_contents, &note_contents,
self.params self.params,
)?; )?;
{ {
// Booleanize the randomness // Booleanize the randomness
let rcm = boolean::field_into_boolean_vec_le( let rcm = boolean::field_into_boolean_vec_le(
cs.namespace(|| "rcm"), cs.namespace(|| "rcm"),
self.commitment_randomness self.commitment_randomness,
)?; )?;
// Compute the note commitment randomness in the exponent // Compute the note commitment randomness in the exponent
@ -575,14 +511,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
cs.namespace(|| "computation of commitment randomness"), cs.namespace(|| "computation of commitment randomness"),
FixedGenerators::NoteCommitmentRandomness, FixedGenerators::NoteCommitmentRandomness,
&rcm, &rcm,
self.params self.params,
)?; )?;
// Randomize our note commitment // Randomize our note commitment
cm = cm.add( cm = cm.add(
cs.namespace(|| "randomization of note commitment"), cs.namespace(|| "randomization of note commitment"),
&rcm, &rcm,
self.params self.params,
)?; )?;
} }
@ -604,7 +540,7 @@ fn test_input_circuit_with_bls12_381() {
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use zcash_primitives::{ use zcash_primitives::{
jubjub::{JubjubBls12, fs, edwards}, jubjub::{edwards, fs, JubjubBls12},
pedersen_hash, pedersen_hash,
primitives::{Diversifier, Note, ProofGenerationKey}, primitives::{Diversifier, Note, ProofGenerationKey},
}; };
@ -628,7 +564,7 @@ fn test_input_circuit_with_bls12_381() {
let proof_generation_key = ProofGenerationKey { let proof_generation_key = ProofGenerationKey {
ak: ak.clone(), ak: ak.clone(),
nsk: nsk.clone() nsk: nsk.clone(),
}; };
let viewing_key = proof_generation_key.into_viewing_key(params); let viewing_key = proof_generation_key.into_viewing_key(params);
@ -642,11 +578,7 @@ fn test_input_circuit_with_bls12_381() {
Diversifier(d) Diversifier(d)
}; };
if let Some(p) = viewing_key.into_payment_address( if let Some(p) = viewing_key.into_payment_address(diversifier, params) {
diversifier,
params
)
{
payment_address = p; payment_address = p;
break; break;
} }
@ -664,15 +596,14 @@ fn test_input_circuit_with_bls12_381() {
value: value_commitment.value, value: value_commitment.value,
g_d: g_d.clone(), g_d: g_d.clone(),
pk_d: payment_address.pk_d.clone(), pk_d: payment_address.pk_d.clone(),
r: commitment_randomness.clone() r: commitment_randomness.clone(),
}; };
let mut position = 0u64; let mut position = 0u64;
let cm: Fr = note.cm(params); let cm: Fr = note.cm(params);
let mut cur = cm.clone(); let mut cur = cm.clone();
for (i, val) in auth_path.clone().into_iter().enumerate() for (i, val) in auth_path.clone().into_iter().enumerate() {
{
let (uncle, b) = val.unwrap(); let (uncle, b) = val.unwrap();
let mut lhs = cur; let mut lhs = cur;
@ -691,10 +622,12 @@ fn test_input_circuit_with_bls12_381() {
cur = pedersen_hash::pedersen_hash::<Bls12, _>( cur = pedersen_hash::pedersen_hash::<Bls12, _>(
pedersen_hash::Personalization::MerkleTree(i), pedersen_hash::Personalization::MerkleTree(i),
lhs.into_iter() lhs.into_iter()
.take(Fr::NUM_BITS as usize) .take(Fr::NUM_BITS as usize)
.chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)),
params params,
).into_xy().0; )
.into_xy()
.0;
if b { if b {
position |= 1 << i; position |= 1 << i;
@ -716,14 +649,17 @@ fn test_input_circuit_with_bls12_381() {
commitment_randomness: Some(commitment_randomness), commitment_randomness: Some(commitment_randomness),
ar: Some(ar), ar: Some(ar),
auth_path: auth_path.clone(), auth_path: auth_path.clone(),
anchor: Some(cur) anchor: Some(cur),
}; };
instance.synthesize(&mut cs).unwrap(); instance.synthesize(&mut cs).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 98777); assert_eq!(cs.num_constraints(), 98777);
assert_eq!(cs.hash(), "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"); assert_eq!(
cs.hash(),
"d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"
);
assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); assert_eq!(cs.get("randomization of note commitment/x3/num"), cm);
@ -731,8 +667,14 @@ fn test_input_circuit_with_bls12_381() {
assert_eq!(cs.get_input(0, "ONE"), Fr::one()); assert_eq!(cs.get_input(0, "ONE"), Fr::one());
assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0);
assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1);
assert_eq!(cs.get_input(3, "value commitment/commitment point/x/input variable"), expected_value_cm.0); assert_eq!(
assert_eq!(cs.get_input(4, "value commitment/commitment point/y/input variable"), expected_value_cm.1); cs.get_input(3, "value commitment/commitment point/x/input variable"),
expected_value_cm.0
);
assert_eq!(
cs.get_input(4, "value commitment/commitment point/y/input variable"),
expected_value_cm.1
);
assert_eq!(cs.get_input(5, "anchor/input variable"), cur); assert_eq!(cs.get_input(5, "anchor/input variable"), cur);
assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]); assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
@ -748,7 +690,7 @@ fn test_output_circuit_with_bls12_381() {
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use zcash_primitives::{ use zcash_primitives::{
jubjub::{JubjubBls12, fs, edwards}, jubjub::{edwards, fs, JubjubBls12},
primitives::{Diversifier, ProofGenerationKey}, primitives::{Diversifier, ProofGenerationKey},
}; };
@ -769,7 +711,7 @@ fn test_output_circuit_with_bls12_381() {
let proof_generation_key = ProofGenerationKey { let proof_generation_key = ProofGenerationKey {
ak: ak.clone(), ak: ak.clone(),
nsk: nsk.clone() nsk: nsk.clone(),
}; };
let viewing_key = proof_generation_key.into_viewing_key(params); let viewing_key = proof_generation_key.into_viewing_key(params);
@ -783,11 +725,7 @@ fn test_output_circuit_with_bls12_381() {
Diversifier(d) Diversifier(d)
}; };
if let Some(p) = viewing_key.into_payment_address( if let Some(p) = viewing_key.into_payment_address(diversifier, params) {
diversifier,
params
)
{
payment_address = p; payment_address = p;
break; break;
} }
@ -804,30 +742,41 @@ fn test_output_circuit_with_bls12_381() {
value_commitment: Some(value_commitment.clone()), value_commitment: Some(value_commitment.clone()),
payment_address: Some(payment_address.clone()), payment_address: Some(payment_address.clone()),
commitment_randomness: Some(commitment_randomness), commitment_randomness: Some(commitment_randomness),
esk: Some(esk.clone()) esk: Some(esk.clone()),
}; };
instance.synthesize(&mut cs).unwrap(); instance.synthesize(&mut cs).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 7827); assert_eq!(cs.num_constraints(), 7827);
assert_eq!(cs.hash(), "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"); assert_eq!(
cs.hash(),
"c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"
);
let expected_cm = payment_address.create_note( let expected_cm = payment_address
value_commitment.value, .create_note(value_commitment.value, commitment_randomness, params)
commitment_randomness, .expect("should be valid")
params .cm(params);
).expect("should be valid").cm(params);
let expected_value_cm = value_commitment.cm(params).into_xy(); let expected_value_cm = value_commitment.cm(params).into_xy();
let expected_epk = payment_address.g_d(params).expect("should be valid").mul(esk, params); let expected_epk = payment_address
.g_d(params)
.expect("should be valid")
.mul(esk, params);
let expected_epk_xy = expected_epk.into_xy(); let expected_epk_xy = expected_epk.into_xy();
assert_eq!(cs.num_inputs(), 6); assert_eq!(cs.num_inputs(), 6);
assert_eq!(cs.get_input(0, "ONE"), Fr::one()); assert_eq!(cs.get_input(0, "ONE"), Fr::one());
assert_eq!(cs.get_input(1, "value commitment/commitment point/x/input variable"), expected_value_cm.0); assert_eq!(
assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm.1); cs.get_input(1, "value commitment/commitment point/x/input variable"),
expected_value_cm.0
);
assert_eq!(
cs.get_input(2, "value commitment/commitment point/y/input variable"),
expected_value_cm.1
);
assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0); assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0);
assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1); assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1);
assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm);

View File

@ -1,20 +1,18 @@
use pairing::{Engine}; use bellman::gadgets::boolean::Boolean;
use bellman::gadgets::sha256::sha256;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use bellman::gadgets::sha256::{ use pairing::Engine;
sha256
};
use bellman::gadgets::boolean::{
Boolean
};
pub fn note_comm<E, CS>( pub fn note_comm<E, CS>(
cs: CS, cs: CS,
a_pk: &[Boolean], a_pk: &[Boolean],
value: &[Boolean], value: &[Boolean],
rho: &[Boolean], rho: &[Boolean],
r: &[Boolean] r: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(a_pk.len(), 256); assert_eq!(a_pk.len(), 256);
assert_eq!(value.len(), 64); assert_eq!(value.len(), 64);
@ -35,8 +33,5 @@ pub fn note_comm<E, CS>(
image.extend(rho.iter().cloned()); image.extend(rho.iter().cloned());
image.extend(r.iter().cloned()); image.extend(r.iter().cloned());
sha256( sha256(cs, &image)
cs,
&image
)
} }

View File

@ -1,16 +1,11 @@
use pairing::{Engine}; use bellman::gadgets::boolean::{AllocatedBit, Boolean};
use bellman::gadgets::sha256::sha256_block_no_padding;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use bellman::gadgets::sha256::{ use pairing::Engine;
sha256_block_no_padding
};
use bellman::gadgets::boolean::{
AllocatedBit,
Boolean
};
use super::*;
use super::prfs::*;
use super::commitment::note_comm; use super::commitment::note_comm;
use super::prfs::*;
use super::*;
pub struct InputNote { pub struct InputNote {
pub nf: Vec<Boolean>, pub nf: Vec<Boolean>,
@ -27,49 +22,33 @@ impl InputNote {
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool, nonce: bool,
auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH], auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH],
rt: &[Boolean] rt: &[Boolean],
) -> Result<InputNote, SynthesisError> ) -> Result<InputNote, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
let a_sk = witness_u252( let a_sk = witness_u252(
cs.namespace(|| "a_sk"), cs.namespace(|| "a_sk"),
a_sk.as_ref().map(|a_sk| &a_sk.0[..]) a_sk.as_ref().map(|a_sk| &a_sk.0[..]),
)?; )?;
let rho = witness_u256( let rho = witness_u256(cs.namespace(|| "rho"), rho.as_ref().map(|rho| &rho.0[..]))?;
cs.namespace(|| "rho"),
rho.as_ref().map(|rho| &rho.0[..])
)?;
let r = witness_u256( let r = witness_u256(cs.namespace(|| "r"), r.as_ref().map(|r| &r.0[..]))?;
cs.namespace(|| "r"),
r.as_ref().map(|r| &r.0[..])
)?;
let a_pk = prf_a_pk( let a_pk = prf_a_pk(cs.namespace(|| "a_pk computation"), &a_sk)?;
cs.namespace(|| "a_pk computation"),
&a_sk
)?;
let nf = prf_nf( let nf = prf_nf(cs.namespace(|| "nf computation"), &a_sk, &rho)?;
cs.namespace(|| "nf computation"),
&a_sk,
&rho
)?;
let mac = prf_pk( let mac = prf_pk(cs.namespace(|| "mac computation"), &a_sk, h_sig, nonce)?;
cs.namespace(|| "mac computation"),
&a_sk,
h_sig,
nonce
)?;
let cm = note_comm( let cm = note_comm(
cs.namespace(|| "cm computation"), cs.namespace(|| "cm computation"),
&a_pk, &a_pk,
&value.bits_le(), &value.bits_le(),
&rho, &rho,
&r &r,
)?; )?;
// Witness into the merkle tree // Witness into the merkle tree
@ -80,13 +59,13 @@ impl InputNote {
let cur_is_right = AllocatedBit::alloc( let cur_is_right = AllocatedBit::alloc(
cs.namespace(|| "cur is right"), cs.namespace(|| "cur is right"),
layer.as_ref().map(|&(_, p)| p) layer.as_ref().map(|&(_, p)| p),
)?; )?;
let lhs = cur; let lhs = cur;
let rhs = witness_u256( let rhs = witness_u256(
cs.namespace(|| "sibling"), cs.namespace(|| "sibling"),
layer.as_ref().map(|&(ref sibling, _)| &sibling[..]) layer.as_ref().map(|&(ref sibling, _)| &sibling[..]),
)?; )?;
// Conditionally swap if cur is right // Conditionally swap if cur is right
@ -94,19 +73,16 @@ impl InputNote {
cs.namespace(|| "conditional swap"), cs.namespace(|| "conditional swap"),
&lhs[..], &lhs[..],
&rhs[..], &rhs[..],
&cur_is_right &cur_is_right,
)?; )?;
cur = sha256_block_no_padding( cur = sha256_block_no_padding(cs.namespace(|| "hash of this layer"), &preimage)?;
cs.namespace(|| "hash of this layer"),
&preimage
)?;
} }
// enforce must be true if the value is nonzero // enforce must be true if the value is nonzero
let enforce = AllocatedBit::alloc( let enforce = AllocatedBit::alloc(
cs.namespace(|| "enforce"), cs.namespace(|| "enforce"),
value.get_value().map(|n| n != 0) value.get_value().map(|n| n != 0),
)?; )?;
// value * (1 - enforce) = 0 // value * (1 - enforce) = 0
@ -116,7 +92,7 @@ impl InputNote {
|| "enforce validity", || "enforce validity",
|_| value.lc(), |_| value.lc(),
|lc| lc + CS::one() - enforce.get_variable(), |lc| lc + CS::one() - enforce.get_variable(),
|lc| lc |lc| lc,
); );
assert_eq!(cur.len(), rt.len()); assert_eq!(cur.len(), rt.len());
@ -132,14 +108,11 @@ impl InputNote {
|| format!("conditionally enforce correct root for bit {}", i), || format!("conditionally enforce correct root for bit {}", i),
|_| cur.lc(CS::one(), E::Fr::one()) - &rt.lc(CS::one(), E::Fr::one()), |_| cur.lc(CS::one(), E::Fr::one()) - &rt.lc(CS::one(), E::Fr::one()),
|lc| lc + enforce.get_variable(), |lc| lc + enforce.get_variable(),
|lc| lc |lc| lc,
); );
} }
Ok(InputNote { Ok(InputNote { mac: mac, nf: nf })
mac: mac,
nf: nf
})
} }
} }
@ -149,9 +122,11 @@ pub fn conditionally_swap_u256<E, CS>(
mut cs: CS, mut cs: CS,
lhs: &[Boolean], lhs: &[Boolean],
rhs: &[Boolean], rhs: &[Boolean],
condition: &AllocatedBit condition: &AllocatedBit,
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>, where
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(lhs.len(), 256); assert_eq!(lhs.len(), 256);
assert_eq!(rhs.len(), 256); assert_eq!(rhs.len(), 256);
@ -164,13 +139,9 @@ pub fn conditionally_swap_u256<E, CS>(
let x = Boolean::from(AllocatedBit::alloc( let x = Boolean::from(AllocatedBit::alloc(
cs.namespace(|| "x"), cs.namespace(|| "x"),
condition.get_value().and_then(|v| { condition
if v { .get_value()
rhs.get_value() .and_then(|v| if v { rhs.get_value() } else { lhs.get_value() }),
} else {
lhs.get_value()
}
})
)?); )?);
// x = (1-condition)lhs + (condition)rhs // x = (1-condition)lhs + (condition)rhs
@ -184,33 +155,25 @@ pub fn conditionally_swap_u256<E, CS>(
// x = rhs // x = rhs
cs.enforce( cs.enforce(
|| "conditional swap for x", || "conditional swap for x",
|lc| lc + &rhs.lc(CS::one(), E::Fr::one()) |lc| lc + &rhs.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()),
- &lhs.lc(CS::one(), E::Fr::one()),
|lc| lc + condition.get_variable(), |lc| lc + condition.get_variable(),
|lc| lc + &x.lc(CS::one(), E::Fr::one()) |lc| lc + &x.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()),
- &lhs.lc(CS::one(), E::Fr::one())
); );
let y = Boolean::from(AllocatedBit::alloc( let y = Boolean::from(AllocatedBit::alloc(
cs.namespace(|| "y"), cs.namespace(|| "y"),
condition.get_value().and_then(|v| { condition
if v { .get_value()
lhs.get_value() .and_then(|v| if v { lhs.get_value() } else { rhs.get_value() }),
} else {
rhs.get_value()
}
})
)?); )?);
// y = (1-condition)rhs + (condition)lhs // y = (1-condition)rhs + (condition)lhs
// y - rhs = condition (lhs - rhs) // y - rhs = condition (lhs - rhs)
cs.enforce( cs.enforce(
|| "conditional swap for y", || "conditional swap for y",
|lc| lc + &lhs.lc(CS::one(), E::Fr::one()) |lc| lc + &lhs.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()),
- &rhs.lc(CS::one(), E::Fr::one()),
|lc| lc + condition.get_variable(), |lc| lc + condition.get_variable(),
|lc| lc + &y.lc(CS::one(), E::Fr::one()) |lc| lc + &y.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()),
- &rhs.lc(CS::one(), E::Fr::one())
); );
new_lhs.push(x); new_lhs.push(x);

View File

@ -1,16 +1,13 @@
use bellman::gadgets::boolean::{AllocatedBit, Boolean};
use bellman::gadgets::multipack::pack_into_inputs;
use bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError};
use ff::Field; use ff::Field;
use pairing::Engine; use pairing::Engine;
use bellman::{ConstraintSystem, SynthesisError, Circuit, LinearCombination};
use bellman::gadgets::boolean::{
AllocatedBit,
Boolean
};
use bellman::gadgets::multipack::pack_into_inputs;
mod prfs;
mod commitment; mod commitment;
mod input; mod input;
mod output; mod output;
mod prfs;
use self::input::*; use self::input::*;
use self::output::*; use self::output::*;
@ -37,39 +34,29 @@ pub struct JSInput {
pub a_sk: Option<SpendingKey>, pub a_sk: Option<SpendingKey>,
pub rho: Option<UniqueRandomness>, pub rho: Option<UniqueRandomness>,
pub r: Option<CommitmentRandomness>, pub r: Option<CommitmentRandomness>,
pub auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH] pub auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH],
} }
pub struct JSOutput { pub struct JSOutput {
pub value: Option<u64>, pub value: Option<u64>,
pub a_pk: Option<PayingKey>, pub a_pk: Option<PayingKey>,
pub r: Option<CommitmentRandomness> pub r: Option<CommitmentRandomness>,
} }
impl<E: Engine> Circuit<E> for JoinSplit { impl<E: Engine> Circuit<E> for JoinSplit {
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
assert_eq!(self.inputs.len(), 2); assert_eq!(self.inputs.len(), 2);
assert_eq!(self.outputs.len(), 2); assert_eq!(self.outputs.len(), 2);
// vpub_old is the value entering the // vpub_old is the value entering the
// JoinSplit from the "outside" value // JoinSplit from the "outside" value
// pool // pool
let vpub_old = NoteValue::new( let vpub_old = NoteValue::new(cs.namespace(|| "vpub_old"), self.vpub_old)?;
cs.namespace(|| "vpub_old"),
self.vpub_old
)?;
// vpub_new is the value leaving the // vpub_new is the value leaving the
// JoinSplit into the "outside" value // JoinSplit into the "outside" value
// pool // pool
let vpub_new = NoteValue::new( let vpub_new = NoteValue::new(cs.namespace(|| "vpub_new"), self.vpub_new)?;
cs.namespace(|| "vpub_new"),
self.vpub_new
)?;
// The left hand side of the balance equation // The left hand side of the balance equation
// vpub_old + inputs[0].value + inputs[1].value // vpub_old + inputs[0].value + inputs[1].value
@ -80,22 +67,17 @@ impl<E: Engine> Circuit<E> for JoinSplit {
let mut rhs = vpub_new.lc(); let mut rhs = vpub_new.lc();
// Witness rt (merkle tree root) // Witness rt (merkle tree root)
let rt = witness_u256( let rt = witness_u256(cs.namespace(|| "rt"), self.rt.as_ref().map(|v| &v[..])).unwrap();
cs.namespace(|| "rt"),
self.rt.as_ref().map(|v| &v[..])
).unwrap();
// Witness h_sig // Witness h_sig
let h_sig = witness_u256( let h_sig = witness_u256(
cs.namespace(|| "h_sig"), cs.namespace(|| "h_sig"),
self.h_sig.as_ref().map(|v| &v[..]) self.h_sig.as_ref().map(|v| &v[..]),
).unwrap(); )
.unwrap();
// Witness phi // Witness phi
let phi = witness_u252( let phi = witness_u252(cs.namespace(|| "phi"), self.phi.as_ref().map(|v| &v[..])).unwrap();
cs.namespace(|| "phi"),
self.phi.as_ref().map(|v| &v[..])
).unwrap();
let mut input_notes = vec![]; let mut input_notes = vec![];
let mut lhs_total = self.vpub_old; let mut lhs_total = self.vpub_old;
@ -110,17 +92,14 @@ impl<E: Engine> Circuit<E> for JoinSplit {
} }
// Allocate the value of the note // Allocate the value of the note
let value = NoteValue::new( let value = NoteValue::new(cs.namespace(|| "value"), input.value)?;
cs.namespace(|| "value"),
input.value
)?;
// Compute the nonce (for PRF inputs) which is false // Compute the nonce (for PRF inputs) which is false
// for the first input, and true for the second input. // for the first input, and true for the second input.
let nonce = match i { let nonce = match i {
0 => false, 0 => false,
1 => true, 1 => true,
_ => unreachable!() _ => unreachable!(),
}; };
// Perform input note computations // Perform input note computations
@ -133,7 +112,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
&h_sig, &h_sig,
nonce, nonce,
input.auth_path, input.auth_path,
&rt &rt,
)?); )?);
// Add the note value to the left hand side of // Add the note value to the left hand side of
@ -148,10 +127,8 @@ impl<E: Engine> Circuit<E> for JoinSplit {
{ {
// Expected sum of the left hand side of the balance // Expected sum of the left hand side of the balance
// equation, expressed as a 64-bit unsigned integer // equation, expressed as a 64-bit unsigned integer
let lhs_total = NoteValue::new( let lhs_total =
cs.namespace(|| "total value of left hand side"), NoteValue::new(cs.namespace(|| "total value of left hand side"), lhs_total)?;
lhs_total
)?;
// Enforce that the left hand side can be expressed as a 64-bit // Enforce that the left hand side can be expressed as a 64-bit
// integer // integer
@ -159,7 +136,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
|| "left hand side can be expressed as a 64-bit unsigned integer", || "left hand side can be expressed as a 64-bit unsigned integer",
|_| lhs.clone(), |_| lhs.clone(),
|lc| lc + CS::one(), |lc| lc + CS::one(),
|_| lhs_total.lc() |_| lhs_total.lc(),
); );
} }
@ -169,17 +146,14 @@ impl<E: Engine> Circuit<E> for JoinSplit {
for (i, output) in self.outputs.into_iter().enumerate() { for (i, output) in self.outputs.into_iter().enumerate() {
let cs = &mut cs.namespace(|| format!("output {}", i)); let cs = &mut cs.namespace(|| format!("output {}", i));
let value = NoteValue::new( let value = NoteValue::new(cs.namespace(|| "value"), output.value)?;
cs.namespace(|| "value"),
output.value
)?;
// Compute the nonce (for PRF inputs) which is false // Compute the nonce (for PRF inputs) which is false
// for the first output, and true for the second output. // for the first output, and true for the second output.
let nonce = match i { let nonce = match i {
0 => false, 0 => false,
1 => true, 1 => true,
_ => unreachable!() _ => unreachable!(),
}; };
// Perform output note computations // Perform output note computations
@ -190,7 +164,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
output.r, output.r,
&phi, &phi,
&h_sig, &h_sig,
nonce nonce,
)?); )?);
// Add the note value to the right hand side of // Add the note value to the right hand side of
@ -203,7 +177,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
|| "balance equation", || "balance equation",
|_| lhs.clone(), |_| lhs.clone(),
|lc| lc + CS::one(), |lc| lc + CS::one(),
|_| rhs |_| rhs,
); );
let mut public_inputs = vec![]; let mut public_inputs = vec![];
@ -229,15 +203,14 @@ impl<E: Engine> Circuit<E> for JoinSplit {
pub struct NoteValue { pub struct NoteValue {
value: Option<u64>, value: Option<u64>,
// Least significant digit first // Least significant digit first
bits: Vec<AllocatedBit> bits: Vec<AllocatedBit>,
} }
impl NoteValue { impl NoteValue {
fn new<E, CS>( fn new<E, CS>(mut cs: CS, value: Option<u64>) -> Result<NoteValue, SynthesisError>
mut cs: CS, where
value: Option<u64> E: Engine,
) -> Result<NoteValue, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>,
{ {
let mut values; let mut values;
match value { match value {
@ -247,7 +220,7 @@ impl NoteValue {
values.push(Some(val & 1 == 1)); values.push(Some(val & 1 == 1));
val >>= 1; val >>= 1;
} }
}, }
None => { None => {
values = vec![None; 64]; values = vec![None; 64];
} }
@ -255,28 +228,27 @@ impl NoteValue {
let mut bits = vec![]; let mut bits = vec![];
for (i, value) in values.into_iter().enumerate() { for (i, value) in values.into_iter().enumerate() {
bits.push( bits.push(AllocatedBit::alloc(
AllocatedBit::alloc( cs.namespace(|| format!("bit {}", i)),
cs.namespace(|| format!("bit {}", i)), value,
value )?);
)?
);
} }
Ok(NoteValue { Ok(NoteValue {
value: value, value: value,
bits: bits bits: bits,
}) })
} }
/// Encodes the bits of the value into little-endian /// Encodes the bits of the value into little-endian
/// byte order. /// byte order.
fn bits_le(&self) -> Vec<Boolean> { fn bits_le(&self) -> Vec<Boolean> {
self.bits.chunks(8) self.bits
.flat_map(|v| v.iter().rev()) .chunks(8)
.cloned() .flat_map(|v| v.iter().rev())
.map(|e| Boolean::from(e)) .cloned()
.collect() .map(|e| Boolean::from(e))
.collect()
} }
/// Computes this value as a linear combination of /// Computes this value as a linear combination of
@ -304,15 +276,18 @@ fn witness_bits<E, CS>(
mut cs: CS, mut cs: CS,
value: Option<&[u8]>, value: Option<&[u8]>,
num_bits: usize, num_bits: usize,
skip_bits: usize skip_bits: usize,
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>, where
E: Engine,
CS: ConstraintSystem<E>,
{ {
let bit_values = if let Some(value) = value { let bit_values = if let Some(value) = value {
let mut tmp = vec![]; let mut tmp = vec![];
for b in value.iter() for b in value
.flat_map(|&m| (0..8).rev().map(move |i| m >> i & 1 == 1)) .iter()
.skip(skip_bits) .flat_map(|&m| (0..8).rev().map(move |i| m >> i & 1 == 1))
.skip(skip_bits)
{ {
tmp.push(Some(b)); tmp.push(Some(b));
} }
@ -327,37 +302,35 @@ fn witness_bits<E, CS>(
for (i, value) in bit_values.into_iter().enumerate() { for (i, value) in bit_values.into_iter().enumerate() {
bits.push(Boolean::from(AllocatedBit::alloc( bits.push(Boolean::from(AllocatedBit::alloc(
cs.namespace(|| format!("bit {}", i)), cs.namespace(|| format!("bit {}", i)),
value value,
)?)); )?));
} }
Ok(bits) Ok(bits)
} }
fn witness_u256<E, CS>( fn witness_u256<E, CS>(cs: CS, value: Option<&[u8]>) -> Result<Vec<Boolean>, SynthesisError>
cs: CS, where
value: Option<&[u8]>, E: Engine,
) -> Result<Vec<Boolean>, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>,
{ {
witness_bits(cs, value, 256, 0) witness_bits(cs, value, 256, 0)
} }
fn witness_u252<E, CS>( fn witness_u252<E, CS>(cs: CS, value: Option<&[u8]>) -> Result<Vec<Boolean>, SynthesisError>
cs: CS, where
value: Option<&[u8]>, E: Engine,
) -> Result<Vec<Boolean>, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>,
{ {
witness_bits(cs, value, 252, 4) witness_bits(cs, value, 252, 4)
} }
#[test] #[test]
fn test_sprout_constraints() { fn test_sprout_constraints() {
use pairing::bls12_381::{Bls12};
use bellman::gadgets::test::*; use bellman::gadgets::test::*;
use pairing::bls12_381::Bls12;
use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
let test_vector = include_bytes!("test_vectors.dat"); let test_vector = include_bytes!("test_vectors.dat");
let mut test_vector = &test_vector[..]; let mut test_vector = &test_vector[..];
@ -393,9 +366,7 @@ fn test_sprout_constraints() {
} }
let mut position = test_vector.read_u64::<LittleEndian>().unwrap(); let mut position = test_vector.read_u64::<LittleEndian>().unwrap();
for i in 0..TREE_DEPTH { for i in 0..TREE_DEPTH {
auth_path[i].as_mut().map(|p| { auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1);
p.1 = (position & 1) == 1
});
position >>= 1; position >>= 1;
} }
@ -407,15 +378,13 @@ fn test_sprout_constraints() {
let r = Some(CommitmentRandomness(get_u256(&mut test_vector))); let r = Some(CommitmentRandomness(get_u256(&mut test_vector)));
let a_sk = Some(SpendingKey(get_u256(&mut test_vector))); let a_sk = Some(SpendingKey(get_u256(&mut test_vector)));
inputs.push( inputs.push(JSInput {
JSInput { value: value,
value: value, a_sk: a_sk,
a_sk: a_sk, rho: rho,
rho: rho, r: r,
r: r, auth_path: auth_path,
auth_path: auth_path });
}
);
} }
let mut outputs = vec![]; let mut outputs = vec![];
@ -426,13 +395,11 @@ fn test_sprout_constraints() {
get_u256(&mut test_vector); get_u256(&mut test_vector);
let r = Some(CommitmentRandomness(get_u256(&mut test_vector))); let r = Some(CommitmentRandomness(get_u256(&mut test_vector)));
outputs.push( outputs.push(JSOutput {
JSOutput { value: value,
value: value, a_pk: a_pk,
a_pk: a_pk, r: r,
r: r });
}
);
} }
let vpub_old = Some(test_vector.read_u64::<LittleEndian>().unwrap()); let vpub_old = Some(test_vector.read_u64::<LittleEndian>().unwrap());
@ -454,7 +421,7 @@ fn test_sprout_constraints() {
phi: phi, phi: phi,
inputs: inputs, inputs: inputs,
outputs: outputs, outputs: outputs,
rt: rt rt: rt,
}; };
js.synthesize(&mut cs).unwrap(); js.synthesize(&mut cs).unwrap();
@ -465,7 +432,10 @@ fn test_sprout_constraints() {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1989085); assert_eq!(cs.num_constraints(), 1989085);
assert_eq!(cs.num_inputs(), 10); assert_eq!(cs.num_inputs(), 10);
assert_eq!(cs.hash(), "1a228d3c6377130d1778c7885811dc8b8864049cb5af8aff7e6cd46c5bc4b84c"); assert_eq!(
cs.hash(),
"1a228d3c6377130d1778c7885811dc8b8864049cb5af8aff7e6cd46c5bc4b84c"
);
let mut expected_inputs = vec![]; let mut expected_inputs = vec![];
expected_inputs.extend(rt.unwrap().to_vec()); expected_inputs.extend(rt.unwrap().to_vec());
@ -476,8 +446,12 @@ fn test_sprout_constraints() {
expected_inputs.extend(mac2.to_vec()); expected_inputs.extend(mac2.to_vec());
expected_inputs.extend(cm1.to_vec()); expected_inputs.extend(cm1.to_vec());
expected_inputs.extend(cm2.to_vec()); expected_inputs.extend(cm2.to_vec());
expected_inputs.write_u64::<LittleEndian>(vpub_old.unwrap()).unwrap(); expected_inputs
expected_inputs.write_u64::<LittleEndian>(vpub_new.unwrap()).unwrap(); .write_u64::<LittleEndian>(vpub_old.unwrap())
.unwrap();
expected_inputs
.write_u64::<LittleEndian>(vpub_new.unwrap())
.unwrap();
use bellman::gadgets::multipack; use bellman::gadgets::multipack;

View File

@ -1,13 +1,13 @@
use pairing::{Engine}; use bellman::gadgets::boolean::Boolean;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use bellman::gadgets::boolean::{Boolean}; use pairing::Engine;
use super::*;
use super::prfs::*;
use super::commitment::note_comm; use super::commitment::note_comm;
use super::prfs::*;
use super::*;
pub struct OutputNote { pub struct OutputNote {
pub cm: Vec<Boolean> pub cm: Vec<Boolean>,
} }
impl OutputNote { impl OutputNote {
@ -18,37 +18,29 @@ impl OutputNote {
r: Option<CommitmentRandomness>, r: Option<CommitmentRandomness>,
phi: &[Boolean], phi: &[Boolean],
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool nonce: bool,
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>, where
E: Engine,
CS: ConstraintSystem<E>,
{ {
let rho = prf_rho( let rho = prf_rho(cs.namespace(|| "rho"), phi, h_sig, nonce)?;
cs.namespace(|| "rho"),
phi,
h_sig,
nonce
)?;
let a_pk = witness_u256( let a_pk = witness_u256(
cs.namespace(|| "a_pk"), cs.namespace(|| "a_pk"),
a_pk.as_ref().map(|a_pk| &a_pk.0[..]) a_pk.as_ref().map(|a_pk| &a_pk.0[..]),
)?; )?;
let r = witness_u256( let r = witness_u256(cs.namespace(|| "r"), r.as_ref().map(|r| &r.0[..]))?;
cs.namespace(|| "r"),
r.as_ref().map(|r| &r.0[..])
)?;
let cm = note_comm( let cm = note_comm(
cs.namespace(|| "cm computation"), cs.namespace(|| "cm computation"),
&a_pk, &a_pk,
&value.bits_le(), &value.bits_le(),
&rho, &rho,
&r &r,
)?; )?;
Ok(OutputNote { Ok(OutputNote { cm: cm })
cm: cm
})
} }
} }

View File

@ -1,11 +1,7 @@
use pairing::{Engine}; use bellman::gadgets::boolean::Boolean;
use bellman::gadgets::sha256::sha256_block_no_padding;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use bellman::gadgets::sha256::{ use pairing::Engine;
sha256_block_no_padding
};
use bellman::gadgets::boolean::{
Boolean
};
fn prf<E, CS>( fn prf<E, CS>(
cs: CS, cs: CS,
@ -14,9 +10,11 @@ fn prf<E, CS>(
c: bool, c: bool,
d: bool, d: bool,
x: &[Boolean], x: &[Boolean],
y: &[Boolean] y: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(x.len(), 252); assert_eq!(x.len(), 252);
assert_eq!(y.len(), 256); assert_eq!(y.len(), 256);
@ -31,27 +29,35 @@ fn prf<E, CS>(
assert_eq!(image.len(), 512); assert_eq!(image.len(), 512);
sha256_block_no_padding( sha256_block_no_padding(cs, &image)
cs,
&image
)
} }
pub fn prf_a_pk<E, CS>( pub fn prf_a_pk<E, CS>(cs: CS, a_sk: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
cs: CS, where
a_sk: &[Boolean] E: Engine,
) -> Result<Vec<Boolean>, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>
{ {
prf(cs, true, true, false, false, a_sk, &(0..256).map(|_| Boolean::constant(false)).collect::<Vec<_>>()) prf(
cs,
true,
true,
false,
false,
a_sk,
&(0..256)
.map(|_| Boolean::constant(false))
.collect::<Vec<_>>(),
)
} }
pub fn prf_nf<E, CS>( pub fn prf_nf<E, CS>(
cs: CS, cs: CS,
a_sk: &[Boolean], a_sk: &[Boolean],
rho: &[Boolean] rho: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
prf(cs, true, true, true, false, a_sk, rho) prf(cs, true, true, true, false, a_sk, rho)
} }
@ -60,9 +66,11 @@ pub fn prf_pk<E, CS>(
cs: CS, cs: CS,
a_sk: &[Boolean], a_sk: &[Boolean],
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool nonce: bool,
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
prf(cs, false, nonce, false, false, a_sk, h_sig) prf(cs, false, nonce, false, false, a_sk, h_sig)
} }
@ -71,9 +79,11 @@ pub fn prf_rho<E, CS>(
cs: CS, cs: CS,
phi: &[Boolean], phi: &[Boolean],
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool nonce: bool,
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
prf(cs, false, nonce, true, false, phi, h_sig) prf(cs, false, nonce, true, false, phi, h_sig)
} }

View File

@ -3,11 +3,11 @@
use bellman::groth16::{Parameters, PreparedVerifyingKey}; use bellman::groth16::{Parameters, PreparedVerifyingKey};
use directories::BaseDirs; use directories::BaseDirs;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use std::path::Path;
use zcash_primitives::{ use zcash_primitives::{
jubjub::{edwards, fs::Fs, Unknown}, jubjub::{edwards, fs::Fs, Unknown},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, primitives::{Diversifier, PaymentAddress, ProofGenerationKey},
}; };
use std::path::Path;
use zcash_primitives::{ use zcash_primitives::{
merkle_tree::CommitmentTreeWitness, merkle_tree::CommitmentTreeWitness,
prover::TxProver, prover::TxProver,

View File

@ -1,8 +1,6 @@
use bellman::{ use bellman::{
gadgets::multipack, gadgets::multipack,
groth16::{ groth16::{create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof},
create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof,
},
}; };
use ff::Field; use ff::Field;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};