1 Commits

Author SHA1 Message Date
mdr0id
89a832d9dc trigger CI 2018-10-18 11:02:15 -07:00
183 changed files with 9644 additions and 29884 deletions

View File

@@ -1,120 +0,0 @@
name: CI checks
on: [push, pull_request]
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.37.0
override: true
# cargo fmt does not build the code, and running it in a fresh clone of
# the codebase will fail because the protobuf code has not been generated.
- name: cargo build
uses: actions-rs/cargo@v1
with:
command: build
args: --all
# Ensure all code has been formatted with rustfmt
- run: rustup component add rustfmt
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check --color always
test:
name: Test on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.37.0
override: true
- name: cargo fetch
uses: actions-rs/cargo@v1
with:
command: fetch
- name: Build tests
uses: actions-rs/cargo@v1
with:
command: build
args: --verbose --release --all --tests
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --release --all
- name: Run slow tests
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --release --all -- --ignored
codecov:
name: Code coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
# Use stable for this to ensure that cargo-tarpaulin can be built.
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Install cargo-tarpaulin
uses: actions-rs/cargo@v1
with:
command: install
args: cargo-tarpaulin
- name: Generate coverage report
uses: actions-rs/cargo@v1
with:
command: tarpaulin
args: --release --timeout 600 --out Xml --packages "librustzcash,zcash_client_backend,zcash_primitives,zcash_proofs"
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.0.3
with:
token: ${{secrets.CODECOV_TOKEN}}
doc-links:
name: Nightly lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: cargo fetch
uses: actions-rs/cargo@v1
with:
command: fetch
# Ensure intra-documentation links all resolve correctly
# Requires #![deny(intra_doc_link_resolution_failure)] in crates.
- name: Check intra-doc links
uses: actions-rs/cargo@v1
with:
command: doc
args: --all --document-private-items
# Build benchmarks to prevent bitrot
- name: Build benchmarks
uses: actions-rs/cargo@v1
with:
command: build
args: --verbose --all --benches

View File

@@ -9,6 +9,7 @@
# #
# Known bugs/missing features: # Known bugs/missing features:
# #
#
# ************************************************************************/ # ************************************************************************/
stages: stages:

View File

@@ -1,19 +0,0 @@
language: rust
rust:
- 1.37.0
cache: cargo
before_script:
- rustup component add rustfmt
script:
- cargo build --verbose --release --all
- cargo fmt --all -- --check
- cargo test --verbose --release --all
- cargo test --verbose --release --all -- --ignored
before_cache:
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
- cargo install cargo-update || echo "cargo-update already installed"
- cargo install-update -a # update outdated cached binaries

1219
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,13 @@
[workspace] [workspace]
members = [ members = [
"bellman", "bellman",
"ff",
"group",
"librustzcash", "librustzcash",
"pairing", "pairing",
"zcash_client_backend", "sapling-crypto",
"zcash_history",
"zcash_primitives", "zcash_primitives",
"zcash_proofs", "zcash_proofs",
"zcash_wallet",
"zip32",
] ]
[profile.release] [profile.release]

View File

@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2017-2019 Electric Coin Company Copyright (c) 2017 Zcash Company
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,42 +1,22 @@
[package] [package]
authors = ["Sean Bowe <ewillbefull@gmail.com>"] authors = ["Sean Bowe <ewillbefull@gmail.com>"]
description = "zk-SNARK library" description = "zk-SNARK library"
readme = "README.md" documentation = "https://github.com/ebfull/bellman"
homepage = "https://github.com/ebfull/bellman" homepage = "https://github.com/ebfull/bellman"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
name = "bellman" name = "bellman"
repository = "https://github.com/ebfull/bellman" repository = "https://github.com/ebfull/bellman"
version = "0.2.0" version = "0.1.0"
edition = "2018"
[dependencies] [dependencies]
rand = "0.4"
bit-vec = "0.4.4" bit-vec = "0.4.4"
blake2s_simd = "0.5"
ff = { version = "0.5.0", path = "../ff" }
futures = "0.1" futures = "0.1"
futures-cpupool = { version = "0.1", optional = true } futures-cpupool = "0.1"
group = { version = "0.2.0", path = "../group" } num_cpus = "1"
num_cpus = { version = "1", optional = true } crossbeam = "0.3"
crossbeam = { version = "0.7", optional = true } pairing = { path = "../pairing" }
pairing = { version = "0.15.0", path = "../pairing", optional = true }
rand_core = "0.5"
byteorder = "1" byteorder = "1"
[dev-dependencies]
hex-literal = "0.2"
rand = "0.7"
rand_xorshift = "0.2"
sha2 = "0.8"
[features] [features]
groth16 = ["pairing"] default = []
multicore = ["futures-cpupool", "crossbeam", "num_cpus"]
default = ["groth16", "multicore"]
[[test]]
name = "mimc"
path = "tests/mimc.rs"
required-features = ["groth16"]
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -1,23 +1,12 @@
# bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) # # bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) #
`bellman` is a crate for building zk-SNARK circuits. It provides circuit traits This is a research project being built for [Zcash](https://z.cash/).
and primitive structures, as well as basic gadget implementations such as
booleans and number abstractions.
## Roadmap
`bellman` is being refactored into a generic proving library. Currently it is
pairing-specific, and different types of proving systems need to be implemented
as sub-modules. After the refactor, `bellman` will be generic using the `ff` and
`group` crates, while specific proving systems will be separate crates that pull
in the dependencies they require.
## License ## License
Licensed under either of Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option. at your option.

View File

@@ -1,50 +1,52 @@
//! This module contains an [`EvaluationDomain`] abstraction for performing //! This module contains an `EvaluationDomain` abstraction for
//! various kinds of polynomial arithmetic on top of the scalar field. //! performing various kinds of polynomial arithmetic on top of
//! the scalar field.
//! //!
//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient //! In pairing-based SNARKs like Groth16, we need to calculate
//! polynomial over a target polynomial with roots at distinct points associated //! a quotient polynomial over a target polynomial with roots
//! with each constraint of the constraint system. In order to be efficient, we //! at distinct points associated with each constraint of the
//! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the //! constraint system. In order to be efficient, we choose these
//! field. This allows us to perform polynomial operations in O(n) by performing //! roots to be the powers of a 2^n root of unity in the field.
//! an O(n log n) FFT over such a domain. //! This allows us to perform polynomial operations in O(n)
//! //! by performing an O(n log n) FFT over such a domain.
//! [`EvaluationDomain`]: crate::domain::EvaluationDomain
//! [Groth16]: https://eprint.iacr.org/2016/260
use ff::{Field, PrimeField, ScalarEngine}; use pairing::{
use group::CurveProjective; Engine,
Field,
PrimeField,
CurveProjective
};
use super::SynthesisError; use super::{
SynthesisError
};
use super::multicore::Worker; use super::multicore::Worker;
pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> { pub struct EvaluationDomain<E: Engine, G: Group<E>> {
coeffs: Vec<G>, coeffs: Vec<G>,
exp: u32, exp: u32,
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>> AsRef<[G]> for EvaluationDomain<E, G> { impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
fn as_ref(&self) -> &[G] { pub fn as_ref(&self) -> &[G] {
&self.coeffs &self.coeffs
} }
}
impl<E: ScalarEngine, G: Group<E>> AsMut<[G]> for EvaluationDomain<E, G> { pub fn as_mut(&mut self) -> &mut [G] {
fn as_mut(&mut self) -> &mut [G] {
&mut self.coeffs &mut self.coeffs
} }
}
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
pub fn into_coeffs(self) -> Vec<G> { pub fn into_coeffs(self) -> Vec<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;
@@ -55,7 +57,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)
} }
} }
@@ -69,30 +71,29 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
coeffs.resize(m, G::group_zero()); coeffs.resize(m, G::group_zero());
Ok(EvaluationDomain { Ok(EvaluationDomain {
coeffs, coeffs: coeffs,
exp, exp: exp,
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)) minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap()
.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| {
let minv = self.minv; let minv = self.minv;
for v in self.coeffs.chunks_mut(chunk) { for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move |_scope| { scope.spawn(move || {
for v in v { for v in v {
v.group_mul_assign(&minv); v.group_mul_assign(&minv);
} }
@@ -101,10 +102,11 @@ 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| { scope.spawn(move || {
let mut u = g.pow(&[(i * chunk) as u64]); let mut u = g.pow(&[(i * chunk) as u64]);
for v in v.iter_mut() { for v in v.iter_mut() {
v.group_mul_assign(&u); v.group_mul_assign(&u);
@@ -115,12 +117,14 @@ 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,15 +143,13 @@ 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 {
.z(&E::Fr::multiplicative_generator()) let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap();
.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) {
scope.spawn(move |_scope| { scope.spawn(move || {
for v in v { for v in v {
v.group_mul_assign(&i); v.group_mul_assign(&i);
} }
@@ -161,12 +163,8 @@ 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 for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
.coeffs scope.spawn(move || {
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move |_scope| {
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);
} }
@@ -180,12 +178,8 @@ 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 for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
.coeffs scope.spawn(move || {
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move |_scope| {
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);
} }
@@ -195,7 +189,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
} }
} }
pub trait Group<E: ScalarEngine>: Sized + Copy + Clone + Send + Sync { pub trait Group<E: Engine>: Sized + Copy + Clone + Send + Sync {
fn group_zero() -> Self; fn group_zero() -> Self;
fn group_mul_assign(&mut self, by: &E::Fr); fn group_mul_assign(&mut self, by: &E::Fr);
fn group_add_assign(&mut self, other: &Self); fn group_add_assign(&mut self, other: &Self);
@@ -210,7 +204,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> {
@@ -233,23 +227,23 @@ impl<G: CurveProjective> Group<G::Engine> for Point<G> {
} }
} }
pub struct Scalar<E: ScalarEngine>(pub E::Fr); pub struct Scalar<E: Engine>(pub E::Fr);
impl<E: ScalarEngine> PartialEq for Scalar<E> { impl<E: Engine> PartialEq for Scalar<E> {
fn eq(&self, other: &Scalar<E>) -> bool { fn eq(&self, other: &Scalar<E>) -> bool {
self.0 == other.0 self.0 == other.0
} }
} }
impl<E: ScalarEngine> Copy for Scalar<E> {} impl<E: Engine> Copy for Scalar<E> { }
impl<E: ScalarEngine> Clone for Scalar<E> { impl<E: Engine> Clone for Scalar<E> {
fn clone(&self) -> Scalar<E> { fn clone(&self) -> Scalar<E> {
*self *self
} }
} }
impl<E: ScalarEngine> Group<E> for Scalar<E> { impl<E: Engine> Group<E> for Scalar<E> {
fn group_zero() -> Self { fn group_zero() -> Self {
Scalar(E::Fr::zero()) Scalar(E::Fr::zero())
} }
@@ -264,7 +258,8 @@ 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: Engine, 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 {
@@ -274,7 +269,8 @@ 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: Engine, 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 {
@@ -296,35 +292,36 @@ 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(&[u64::from(n / (2 * m))]); 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;
} }
} }
fn parallel_fft<E: ScalarEngine, T: Group<E>>( fn parallel_fft<E: Engine, T: Group<E>>(
a: &mut [T], a: &mut [T],
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;
@@ -336,18 +333,18 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
let a = &*a; let a = &*a;
for (j, tmp) in tmp.iter_mut().enumerate() { for (j, tmp) in tmp.iter_mut().enumerate() {
scope.spawn(move |_scope| { scope.spawn(move || {
// Shuffle into a sub-FFT // Shuffle into a sub-FFT
let omega_j = omega.pow(&[j as u64]); let omega_j = omega.pow(&[j as u64]);
let omega_step = omega.pow(&[(j as u64) << log_new_n]); let omega_step = omega.pow(&[(j as u64) << log_new_n]);
let mut elt = E::Fr::one(); let mut elt = E::Fr::one();
for (i, tmp) in tmp.iter_mut().enumerate() { for i in 0..(1 << log_new_n) {
for s in 0..num_cpus { for s in 0..num_cpus {
let idx = (i + (s << log_new_n)) % (1 << log_n); let idx = (i + (s << log_new_n)) % (1 << log_n);
let mut t = a[idx]; let mut t = a[idx];
t.group_mul_assign(&elt); t.group_mul_assign(&elt);
tmp.group_add_assign(&t); tmp[i].group_add_assign(&t);
elt.mul_assign(&omega_step); elt.mul_assign(&omega_step);
} }
elt.mul_assign(&omega_j); elt.mul_assign(&omega_j);
@@ -364,7 +361,7 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
let tmp = &tmp; let tmp = &tmp;
for (idx, a) in a.chunks_mut(chunk).enumerate() { for (idx, a) in a.chunks_mut(chunk).enumerate() {
scope.spawn(move |_scope| { scope.spawn(move || {
let mut idx = idx * chunk; let mut idx = idx * chunk;
let mask = (1 << log_cpus) - 1; let mask = (1 << log_cpus) - 1;
for a in a { for a in a {
@@ -378,23 +375,19 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
// Test multiplying various (low degree) polynomials together and // Test multiplying various (low degree) polynomials together and
// comparing with naive evaluations. // comparing with naive evaluations.
#[cfg(feature = "pairing")]
#[test] #[test]
fn polynomial_arith() { fn polynomial_arith() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand::{self, Rand};
fn test_mul<E: ScalarEngine, R: RngCore>(rng: &mut R) { fn test_mul<E: Engine, R: rand::Rng>(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) let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
.map(|_| Scalar::<E>(E::Fr::random(rng))) let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
.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];
@@ -429,13 +422,13 @@ fn polynomial_arith() {
test_mul::<Bls12, _>(rng); test_mul::<Bls12, _>(rng);
} }
#[cfg(feature = "pairing")]
#[test] #[test]
fn fft_composition() { fn fft_composition() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand;
fn test_comp<E: ScalarEngine, R: RngCore>(rng: &mut R) { fn test_comp<E: Engine, R: rand::Rng>(rng: &mut R)
{
let worker = Worker::new(); let worker = Worker::new();
for coeffs in 0..10 { for coeffs in 0..10 {
@@ -443,7 +436,7 @@ fn fft_composition() {
let mut v = vec![]; let mut v = vec![];
for _ in 0..coeffs { for _ in 0..coeffs {
v.push(Scalar::<E>(E::Fr::random(rng))); v.push(Scalar::<E>(rng.gen()));
} }
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap(); let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
@@ -467,27 +460,25 @@ fn fft_composition() {
test_comp::<Bls12, _>(rng); test_comp::<Bls12, _>(rng);
} }
#[cfg(feature = "pairing")]
#[test] #[test]
fn parallel_fft_consistency() { fn parallel_fft_consistency() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand::{self, Rand};
use std::cmp::min; use std::cmp::min;
fn test_consistency<E: ScalarEngine, R: RngCore>(rng: &mut R) { fn test_consistency<E: Engine, R: rand::Rng>(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) let v1 = (0..d).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect::<Vec<_>>();
.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,697 +0,0 @@
//! The [BLAKE2s] hash function with personalization support.
//!
//! [BLAKE2s]: https://tools.ietf.org/html/rfc7693
use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32};
use crate::{ConstraintSystem, SynthesisError};
use ff::ScalarEngine;
/*
2.1. Parameters
The following table summarizes various parameters and their ranges:
| BLAKE2b | BLAKE2s |
--------------+------------------+------------------+
Bits in word | w = 64 | w = 32 |
Rounds in F | r = 12 | r = 10 |
Block bytes | bb = 128 | bb = 64 |
Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 |
Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 |
Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 |
--------------+------------------+------------------+
G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
constants = | (32, 24, 16, 63) | (16, 12, 8, 7) |
--------------+------------------+------------------+
*/
const R1: usize = 16;
const R2: usize = 12;
const R3: usize = 8;
const R4: usize = 7;
/*
Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
----------+-------------------------------------------------+
SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 |
SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 |
SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 |
SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 |
SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 |
SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 |
SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 |
SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 |
SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 |
----------+-------------------------------------------------+
*/
const SIGMA: [[usize; 16]; 10] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[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],
[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],
];
/*
3.1. Mixing Function G
The G primitive function mixes two input words, "x" and "y", into
four words indexed by "a", "b", "c", and "d" in the working vector
v[0..15]. The full modified vector is returned. The rotation
constants (R1, R2, R3, R4) are given in Section 2.1.
FUNCTION G( v[0..15], a, b, c, d, x, y )
|
| v[a] := (v[a] + v[b] + x) mod 2**w
| v[d] := (v[d] ^ v[a]) >>> R1
| v[c] := (v[c] + v[d]) mod 2**w
| v[b] := (v[b] ^ v[c]) >>> R2
| v[a] := (v[a] + v[b] + y) mod 2**w
| v[d] := (v[d] ^ v[a]) >>> R3
| v[c] := (v[c] + v[d]) mod 2**w
| v[b] := (v[b] ^ v[c]) >>> R4
|
| RETURN v[0..15]
|
END FUNCTION.
*/
fn mixing_g<E: ScalarEngine, CS: ConstraintSystem<E>, M>(
mut cs: M,
v: &mut [UInt32],
a: usize,
b: usize,
c: usize,
d: usize,
x: &UInt32,
y: &UInt32,
) -> Result<(), SynthesisError>
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[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[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[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[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4);
Ok(())
}
/*
3.2. Compression Function F
Compression function F takes as an argument the state vector "h",
message block vector "m" (last block is padded with zeros to full
block size, if required), 2w-bit offset counter "t", and final block
indicator flag "f". Local vector v[0..15] is used in processing. F
returns a new state vector. The number of rounds, "r", is 12 for
BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
FUNCTION F( h[0..7], m[0..15], t, f )
|
| // Initialize local work vector v[0..15]
| v[0..7] := h[0..7] // First half from state.
| v[8..15] := IV[0..7] // Second half from IV.
|
| v[12] := v[12] ^ (t mod 2**w) // Low word of the offset.
| v[13] := v[13] ^ (t >> w) // High word.
|
| IF f = TRUE THEN // last block flag?
| | v[14] := v[14] ^ 0xFF..FF // Invert all bits.
| END IF.
|
| // Cryptographic mixing
| FOR i = 0 TO r - 1 DO // Ten or twelve rounds.
| |
| | // Message word selection permutation for this round.
| | s[0..15] := SIGMA[i mod 10][0..15]
| |
| | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] )
| | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] )
| | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
| | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
| |
| | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
| | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
| | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] )
| | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] )
| |
| END FOR
|
| FOR i = 0 TO 7 DO // XOR the two halves.
| | h[i] := h[i] ^ v[i] ^ v[i + 8]
| END FOR.
|
| RETURN h[0..7] // New state.
|
END FUNCTION.
*/
fn blake2s_compression<E: ScalarEngine, CS: ConstraintSystem<E>>(
mut cs: CS,
h: &mut [UInt32],
m: &[UInt32],
t: u64,
f: bool,
) -> Result<(), SynthesisError> {
assert_eq!(h.len(), 8);
assert_eq!(m.len(), 16);
/*
static const uint32_t blake2s_iv[8] =
{
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
*/
let mut v = Vec::with_capacity(16);
v.extend_from_slice(h);
v.push(UInt32::constant(0x6A09E667));
v.push(UInt32::constant(0xBB67AE85));
v.push(UInt32::constant(0x3C6EF372));
v.push(UInt32::constant(0xA54FF53A));
v.push(UInt32::constant(0x510E527F));
v.push(UInt32::constant(0x9B05688C));
v.push(UInt32::constant(0x1F83D9AB));
v.push(UInt32::constant(0x5BE0CD19));
assert_eq!(v.len(), 16);
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),
)?;
if f {
v[14] = v[14].xor(
cs.namespace(|| "third xor"),
&UInt32::constant(u32::max_value()),
)?;
}
{
let mut cs = MultiEq::new(&mut cs);
for i in 0..10 {
let mut cs = cs.namespace(|| format!("round {}", i));
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(
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(
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 {
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(|| "second xor"), &v[i + 8])?;
}
Ok(())
}
/*
FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
|
| h[0..7] := IV[0..7] // Initialization Vector.
|
| // Parameter block p[0]
| h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
|
| // Process padded key and data blocks
| IF dd > 1 THEN
| | FOR i = 0 TO dd - 2 DO
| | | h := F( h, d[i], (i + 1) * bb, FALSE )
| | END FOR.
| END IF.
|
| // Final block.
| IF kk = 0 THEN
| | h := F( h, d[dd - 1], ll, TRUE )
| ELSE
| | h := F( h, d[dd - 1], ll + bb, TRUE )
| END IF.
|
| RETURN first "nn" bytes from little-endian word array h[].
|
END FUNCTION.
*/
pub fn blake2s<E: ScalarEngine, CS: ConstraintSystem<E>>(
mut cs: CS,
input: &[Boolean],
personalization: &[u8],
) -> Result<Vec<Boolean>, SynthesisError> {
use byteorder::{ByteOrder, LittleEndian};
assert_eq!(personalization.len(), 8);
assert!(input.len() % 8 == 0);
let mut h = Vec::with_capacity(8);
h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32));
h.push(UInt32::constant(0xBB67AE85));
h.push(UInt32::constant(0x3C6EF372));
h.push(UInt32::constant(0xA54FF53A));
h.push(UInt32::constant(0x510E527F));
h.push(UInt32::constant(0x9B05688C));
// Personalization is stored here
h.push(UInt32::constant(
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![];
for block in input.chunks(512) {
let mut this_block = Vec::with_capacity(16);
for word in block.chunks(32) {
let mut tmp = word.to_vec();
while tmp.len() < 32 {
tmp.push(Boolean::constant(false));
}
this_block.push(UInt32::from_bits(&tmp));
}
while this_block.len() < 16 {
this_block.push(UInt32::constant(0));
}
blocks.push(this_block);
}
if blocks.is_empty() {
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
}
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
let cs = cs.namespace(|| format!("block {}", i));
blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
}
{
let cs = cs.namespace(|| "final block");
blake2s_compression(
cs,
&mut h,
&blocks[blocks.len() - 1],
(input.len() / 8) as u64,
true,
)?;
}
Ok(h.into_iter().flat_map(|b| b.into_bits()).collect())
}
#[cfg(test)]
mod test {
use blake2s_simd::Params as Blake2sParams;
use hex_literal::hex;
use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use super::blake2s;
use crate::gadgets::boolean::{AllocatedBit, Boolean};
use crate::gadgets::test::TestConstraintSystem;
use crate::ConstraintSystem;
#[test]
fn test_blank_hash() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits = vec![];
let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 0);
// >>> import blake2s from hashlib
// >>> h = blake2s(digest_size=32, person=b'12345678')
// >>> h.hexdigest()
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
let mut out = out.into_iter();
for b in expected.iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_constraints() {
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();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518);
}
#[test]
fn test_blake2s_precomp_constraints() {
// Test that 512 fixed leading bits (constants)
// doesn't result in more constraints.
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.chain((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();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518);
}
#[test]
fn test_blake2s_constant_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert_eq!(cs.num_constraints(), 0);
}
#[test]
fn test_blake2s() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
let mut h = Blake2sParams::new()
.hash_length(32)
.personal(b"12345678")
.to_state();
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
h.update(&data);
let hash_result = h.finalize();
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
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(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let mut s = hash_result
.as_ref()
.iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r {
match b {
Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
}
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
}
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
}
}
}
}
}
#[test]
fn test_blake2s_256_vars() {
let data: Vec<u8> = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec();
assert_eq!(data.len(), 256);
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
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(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let expected = hex!("0af5695115ced92c8a0341e43869209636e9aa6472e4576f0f2b996cf812b30e");
let mut out = r.into_iter();
for b in expected.iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_700_vars() {
let data: Vec<u8> = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec();
assert_eq!(data.len(), 700);
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
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(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let expected = hex!("2ab8f0683167ba220eef19dccf4f9b1a8193cc09b35e0235842323950530f18a");
let mut out = r.into_iter();
for b in expected.iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_test_vectors() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let expecteds = [
hex!("a1309e334376c8f36a736a4ab0e691ef931ee3ebdb9ea96187127136fea622a1"),
hex!("82fefff60f265cea255252f7c194a7f93965dffee0609ef74eb67f0d76cd41c6"),
];
for i in 0..2 {
let mut h = Blake2sParams::new()
.hash_length(32)
.personal(b"12345678")
.to_state();
let input_len = 1024;
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
h.update(&data);
let hash_result = h.finalize();
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
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(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let mut s = hash_result
.as_ref()
.iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r {
match b {
Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
}
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
}
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
}
}
}
assert_eq!(expecteds[i], hash_result.as_bytes());
}
}
}

View File

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

View File

@@ -1,39 +1,65 @@
use rand_core::RngCore; use rand::Rng;
use std::sync::Arc; use std::sync::Arc;
use ff::{Field, PrimeField}; use pairing::{
use group::{CurveAffine, CurveProjective, Wnaf}; Engine,
use pairing::Engine; PrimeField,
Field,
Wnaf,
CurveProjective,
CurveAffine
};
use super::{Parameters, VerifyingKey}; use super::{
Parameters,
VerifyingKey
};
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use ::{
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use crate::domain::{EvaluationDomain, Scalar}; use ::domain::{
EvaluationDomain,
Scalar
};
use crate::multicore::Worker; use ::multicore::{
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 where E: Engine, C: Circuit<E>, R: Rng
E: Engine,
C: Circuit<E>,
R: RngCore,
{ {
let g1 = E::G1::random(rng); let g1 = rng.gen();
let g2 = E::G2::random(rng); let g2 = rng.gen();
let alpha = E::Fr::random(rng); let alpha = rng.gen();
let beta = E::Fr::random(rng); let beta = rng.gen();
let gamma = E::Fr::random(rng); let gamma = rng.gen();
let delta = E::Fr::random(rng); let delta = rng.gen();
let tau = E::Fr::random(rng); let tau = rng.gen();
generate_parameters::<E, C>(circuit, g1, g2, alpha, beta, gamma, delta, tau) generate_parameters::<E, C>(
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
@@ -47,17 +73,18 @@ 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>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, _: A,
A: FnOnce() -> AR, _: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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.
@@ -72,11 +99,12 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
Ok(Variable(Index::Aux(index))) Ok(Variable(Index::Aux(index)))
} }
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, _: A,
A: FnOnce() -> AR, _: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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.
@@ -91,59 +119,48 @@ 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>(&mut self, _: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, _: A,
AR: Into<String>, a: LA,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, b: LB,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, c: LC
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( eval(a(LinearCombination::zero()), &mut self.at_inputs, &mut self.at_aux, self.num_constraints);
a(LinearCombination::zero()), eval(b(LinearCombination::zero()), &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints);
&mut self.at_inputs, eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints);
&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 where NR: Into<String>, N: FnOnce() -> NR
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.
} }
@@ -161,11 +178,9 @@ 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 where E: Engine, C: Circuit<E>
E: Engine,
C: Circuit<E>,
{ {
let mut assembly = KeypairAssembly { let mut assembly = KeypairAssembly {
num_inputs: 0, num_inputs: 0,
@@ -176,7 +191,7 @@ where
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
@@ -188,7 +203,11 @@ where
// 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(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc); assembly.enforce(|| "",
|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
@@ -226,9 +245,10 @@ where
{ {
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| { {
let mut current_tau_power = tau.pow(&[(i * chunk) as u64]); scope.spawn(move || {
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;
@@ -245,15 +265,14 @@ where
// 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 for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
.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| { 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);
@@ -306,8 +325,9 @@ where
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());
@@ -318,32 +338,31 @@ where
// 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 for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk)
.chunks_mut(chunk) .zip(b_g1.chunks_mut(chunk))
.zip(b_g1.chunks_mut(chunk)) .zip(b_g2.chunks_mut(chunk))
.zip(b_g2.chunks_mut(chunk)) .zip(ext.chunks_mut(chunk))
.zip(ext.chunks_mut(chunk)) .zip(at.chunks(chunk))
.zip(at.chunks(chunk)) .zip(bt.chunks(chunk))
.zip(bt.chunks(chunk)) .zip(ct.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| { scope.spawn(move || {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut()
.iter_mut() .zip(b_g1.iter_mut())
.zip(b_g1.iter_mut()) .zip(b_g2.iter_mut())
.zip(b_g2.iter_mut()) .zip(ext.iter_mut())
.zip(ext.iter_mut()) .zip(at.iter())
.zip(at.iter()) .zip(bt.iter())
.zip(bt.iter()) .zip(ct.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 {
@@ -408,10 +427,10 @@ where
&gamma_inverse, &gamma_inverse,
&alpha, &alpha,
&beta, &beta,
&worker, &worker
); );
// Evaluate for auxiliary variables. // Evaluate for auxillary variables.
eval( eval(
&g1_wnaf, &g1_wnaf,
&g2_wnaf, &g2_wnaf,
@@ -426,7 +445,7 @@ where
&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
@@ -447,32 +466,17 @@ where
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 {
vk, vk: vk,
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()), h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
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: Arc::new(a.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
a.into_iter() b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
.filter(|e| !e.is_zero()) b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect())
.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,16 +1,17 @@
//! The [Groth16] proving system. use pairing::{
//! Engine,
//! [Groth16]: https://eprint.iacr.org/2016/260 CurveAffine,
EncodedPoint
};
use group::{CurveAffine, EncodedPoint}; use ::{
use pairing::{Engine, PairingCurveAffine}; SynthesisError
};
use crate::SynthesisError; use multiexp::SourceBuilder;
use crate::multiexp::SourceBuilder;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
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;
@@ -27,17 +28,23 @@ 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.b == other.b && self.c == other.c self.a == other.a &&
self.b == other.b &&
self.c == other.c
} }
} }
impl<E: Engine> Proof<E> { impl<E: Engine> Proof<E> {
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> { pub fn write<W: Write>(
&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())?;
@@ -45,56 +52,48 @@ impl<E: Engine> Proof<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> { pub fn read<R: Read>(
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| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
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| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
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| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
} })?;
})?;
Ok(Proof { a, b, c }) Ok(Proof {
a: a,
b: b,
c: c
})
} }
} }
@@ -123,23 +122,27 @@ 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>(&self, mut writer: W) -> io::Result<()> { pub fn write<W: Write>(
&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())?;
@@ -154,39 +157,30 @@ impl<E: Engine> VerifyingKey<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> { pub fn read<R: Read>(
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 let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.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 let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.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 let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.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 let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.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 let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.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 let delta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.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;
@@ -195,30 +189,25 @@ 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| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new( } else {
io::ErrorKind::InvalidData, Ok(e)
"point at infinity", })?;
))
} else {
Ok(e)
}
})?;
ic.push(g1); ic.push(g1);
} }
Ok(VerifyingKey { Ok(VerifyingKey {
alpha_g1, alpha_g1: alpha_g1,
beta_g1, beta_g1: beta_g1,
beta_g2, beta_g2: beta_g2,
gamma_g2, gamma_g2: gamma_g2,
delta_g1, delta_g1: delta_g1,
delta_g2, delta_g2: delta_g2,
ic, ic: ic
}) })
} }
} }
@@ -232,7 +221,7 @@ pub struct Parameters<E: Engine> {
pub h: Arc<Vec<E::G1Affine>>, pub h: Arc<Vec<E::G1Affine>>,
// Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta // Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta
// for all auxiliary inputs. Variables can never be unconstrained, so this // for all auxillary inputs. Variables can never be unconstrained, so this
// never contains points at infinity. // never contains points at infinity.
pub l: Arc<Vec<E::G1Affine>>, pub l: Arc<Vec<E::G1Affine>>,
@@ -245,22 +234,26 @@ 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>(&self, mut writer: W) -> io::Result<()> { pub fn write<W: Write>(
&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)?;
@@ -291,26 +284,27 @@ impl<E: Engine> Parameters<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>(mut reader: R, checked: bool) -> io::Result<Self> { pub fn read<R: Read>(
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.into_affine() repr
.into_affine()
} else { } else {
repr.into_affine_unchecked() repr
.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| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new( } else {
io::ErrorKind::InvalidData, Ok(e)
"point at infinity",
))
} else {
Ok(e)
}
}) })
}; };
@@ -319,20 +313,17 @@ impl<E: Engine> Parameters<E> {
reader.read_exact(repr.as_mut())?; reader.read_exact(repr.as_mut())?;
if checked { if checked {
repr.into_affine() repr
.into_affine()
} else { } else {
repr.into_affine_unchecked() repr
.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| { .and_then(|e| if e.is_zero() {
if e.is_zero() { Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
Err(io::Error::new( } else {
io::ErrorKind::InvalidData, Ok(e)
"point at infinity",
))
} else {
Ok(e)
}
}) })
}; };
@@ -380,12 +371,12 @@ impl<E: Engine> Parameters<E> {
} }
Ok(Parameters { Ok(Parameters {
vk, vk: vk,
h: Arc::new(h), h: Arc::new(h),
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)
}) })
} }
} }
@@ -394,34 +385,43 @@ pub struct PreparedVerifyingKey<E: Engine> {
/// Pairing result of alpha*beta /// Pairing result of alpha*beta
alpha_g1_beta_g2: E::Fqk, alpha_g1_beta_g2: E::Fqk,
/// -gamma in G2 /// -gamma in G2
neg_gamma_g2: <E::G2Affine as PairingCurveAffine>::Prepared, neg_gamma_g2: <E::G2Affine as CurveAffine>::Prepared,
/// -delta in G2 /// -delta in G2
neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared, neg_delta_g2: <E::G2Affine as CurveAffine>::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(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, SynthesisError>; fn get_vk(
fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, SynthesisError>; &mut self,
fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, SynthesisError>; num_ic: usize
) -> 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,39 +429,54 @@ 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(&mut self, _: usize) -> Result<VerifyingKey<E>, SynthesisError> { fn get_vk(
&mut self,
_: usize
) -> Result<VerifyingKey<E>, SynthesisError>
{
Ok(self.vk.clone()) Ok(self.vk.clone())
} }
fn get_h(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> { fn get_h(
&mut self,
_: usize
) -> Result<Self::G1Builder, SynthesisError>
{
Ok((self.h.clone(), 0)) Ok((self.h.clone(), 0))
} }
fn get_l(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> { fn get_l(
&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)))
} }
} }
@@ -469,38 +484,41 @@ 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 crate::{Circuit, ConstraintSystem, SynthesisError}; use {Circuit, SynthesisError, ConstraintSystem};
use ff::Field; use rand::{Rand, thread_rng};
use pairing::{Field};
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( let c = cs.alloc_input(|| "c", || {
|| "c", let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
|| { 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(|| "a*b=c", |lc| lc + a, |lc| lc + b, |lc| lc + c); cs.enforce(
|| "a*b=c",
|lc| lc + a,
|lc| lc + b,
|lc| lc + c
);
Ok(()) Ok(())
} }
@@ -508,9 +526,10 @@ mod test_with_bls12_381 {
let rng = &mut thread_rng(); let rng = &mut thread_rng();
let params = let params = generate_random_parameters::<Bls12, _, _>(
generate_random_parameters::<Bls12, _, _>(MySillyCircuit { a: None, b: None }, rng) MySillyCircuit { a: None, b: None },
.unwrap(); rng
).unwrap();
{ {
let mut v = vec![]; let mut v = vec![];
@@ -528,20 +547,19 @@ mod test_with_bls12_381 {
let pvk = prepare_verifying_key::<Bls12>(&params.vk); let pvk = prepare_verifying_key::<Bls12>(&params.vk);
for _ in 0..100 { for _ in 0..100 {
let a = Fr::random(rng); let a = Fr::rand(rng);
let b = Fr::random(rng); let b = Fr::rand(rng);
let mut c = a; let mut c = a;
c.mul_assign(&b); c.mul_assign(&b);
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

@@ -1,30 +1,54 @@
use rand_core::RngCore; use rand::Rng;
use std::sync::Arc; use std::sync::Arc;
use futures::Future; use futures::Future;
use ff::{Field, PrimeField}; use pairing::{
use group::{CurveAffine, CurveProjective}; Engine,
use pairing::Engine; PrimeField,
Field,
CurveProjective,
CurveAffine
};
use super::{ParameterSource, Proof}; use super::{
ParameterSource,
Proof
};
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use ::{
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use crate::domain::{EvaluationDomain, Scalar}; use ::domain::{
EvaluationDomain,
Scalar
};
use crate::multiexp::{multiexp, DensityTracker, FullDensity}; use ::multiexp::{
DensityTracker,
FullDensity,
multiexp
};
use crate::multicore::Worker; use ::multicore::{
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() {
@@ -36,7 +60,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 {
@@ -46,10 +70,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);
} }
} }
@@ -69,17 +93,18 @@ 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>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, _: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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();
@@ -88,11 +113,12 @@ 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>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, _: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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();
@@ -100,13 +126,17 @@ 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>(&mut self, _: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, _: A,
AR: Into<String>, a: LA,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, b: LB,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, c: LC
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());
@@ -120,14 +150,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,
@@ -138,19 +168,18 @@ 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 where NR: Into<String>, N: FnOnce() -> NR
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.
} }
@@ -162,15 +191,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 where E: Engine, C: Circuit<E>, R: Rng
E: Engine,
C: Circuit<E>,
R: RngCore,
{ {
let r = E::Fr::random(rng); let r = rng.gen();
let s = E::Fr::random(rng); let s = rng.gen();
create_proof::<E, C, P>(circuit, params, r, s) create_proof::<E, C, P>(circuit, params, r, s)
} }
@@ -179,11 +205,9 @@ 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 where E: Engine, C: Circuit<E>
E: Engine,
C: Circuit<E>,
{ {
let mut prover = ProvingAssignment { let mut prover = ProvingAssignment {
a_aux_density: DensityTracker::new(), a_aux_density: DensityTracker::new(),
@@ -193,7 +217,7 @@ where
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()))?;
@@ -201,7 +225,11 @@ where
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(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc); prover.enforce(|| "",
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
} }
let worker = Worker::new(); let worker = Worker::new();
@@ -235,76 +263,31 @@ where
}; };
// TODO: parallelize if it's even helpful // TODO: parallelize if it's even helpful
let input_assignment = Arc::new( let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
prover let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
.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( let l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone());
&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) = let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?;
params.get_a(input_assignment.len(), a_aux_density_total)?;
let a_inputs = multiexp( let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone());
&worker, let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone());
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) = let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?;
params.get_b_g1(b_input_density_total, b_aux_density_total)?;
let b_g1_inputs = multiexp( let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone());
&worker, let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone());
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) = let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?;
params.get_b_g2(b_input_density_total, b_aux_density_total)?;
let b_g2_inputs = multiexp( let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment);
&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() {
@@ -346,6 +329,6 @@ where
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,13 +1,20 @@
use ff::{ use pairing::{
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, Engine,
PrimeField,
PrimeFieldRepr,
Field,
SqrtField, SqrtField,
LegendreSymbol,
CurveProjective,
CurveAffine,
PrimeFieldDecodingError,
GroupDecodingError,
EncodedPoint
}; };
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use pairing::{Engine, PairingCurveAffine};
use rand_core::RngCore;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
use rand::{Rand, Rng};
use std::num::Wrapping; use std::num::Wrapping;
const MODULUS_R: Wrapping<u32> = Wrapping(64513); const MODULUS_R: Wrapping<u32> = Wrapping(64513);
@@ -16,16 +23,18 @@ const MODULUS_R: Wrapping<u32> = Wrapping(64513);
pub struct Fr(Wrapping<u32>); pub struct Fr(Wrapping<u32>);
impl fmt::Display for Fr { impl fmt::Display for Fr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", (self.0).0) write!(f, "{}", (self.0).0)
} }
} }
impl Field for Fr { impl Rand for Fr {
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self { fn rand<R: Rng>(rng: &mut R) -> Self {
Fr(Wrapping(rng.next_u32()) % MODULUS_R) Fr(Wrapping(rng.gen()) % MODULUS_R)
} }
}
impl Field for Fr {
fn zero() -> Self { fn zero() -> Self {
Fr(Wrapping(0)) Fr(Wrapping(0))
} }
@@ -81,13 +90,9 @@ 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() { if s == <Fr as Field>::zero() { LegendreSymbol::Zero }
LegendreSymbol::Zero else if s == <Fr as Field>::one() { LegendreSymbol::QuadraticResidue }
} else if s == <Fr as Field>::one() { else { LegendreSymbol::QuadraticNonResidue }
LegendreSymbol::QuadraticResidue
} else {
LegendreSymbol::QuadraticNonResidue
}
} }
fn sqrt(&self) -> Option<Self> { fn sqrt(&self) -> Option<Self> {
@@ -105,7 +110,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();
@@ -148,8 +153,14 @@ impl PartialOrd for FrRepr {
} }
} }
impl Rand for FrRepr {
fn rand<R: Rng>(rng: &mut R) -> Self {
FrRepr([rng.gen()])
}
}
impl fmt::Display for FrRepr { impl fmt::Display for FrRepr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", (self.0)[0]) write!(f, "{}", (self.0)[0])
} }
} }
@@ -252,11 +263,8 @@ impl PrimeField for Fr {
#[derive(Clone)] #[derive(Clone)]
pub struct DummyEngine; pub struct DummyEngine;
impl ScalarEngine for DummyEngine {
type Fr = Fr;
}
impl Engine for DummyEngine { impl Engine for DummyEngine {
type Fr = Fr;
type G1 = Fr; type G1 = Fr;
type G1Affine = Fr; type G1Affine = Fr;
type G2 = Fr; type G2 = Fr;
@@ -268,13 +276,10 @@ impl Engine for DummyEngine {
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 where I: IntoIterator<Item=&'a (
I: IntoIterator< &'a <Self::G1Affine as CurveAffine>::Prepared,
Item = &'a ( &'a <Self::G2Affine as CurveAffine>::Prepared
&'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();
@@ -288,7 +293,8 @@ 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)
} }
} }
@@ -299,10 +305,6 @@ impl CurveProjective for Fr {
type Scalar = Fr; type Scalar = Fr;
type Engine = DummyEngine; type Engine = DummyEngine;
fn random<R: RngCore>(rng: &mut R) -> Self {
<Fr as Field>::random(rng)
}
fn zero() -> Self { fn zero() -> Self {
<Fr as Field>::zero() <Fr as Field>::zero()
} }
@@ -315,7 +317,9 @@ 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
@@ -337,7 +341,8 @@ 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);
@@ -396,8 +401,11 @@ impl EncodedPoint for FakePoint {
} }
impl CurveAffine for Fr { impl CurveAffine for Fr {
type Pair = Fr;
type PairingResult = Fr;
type Compressed = FakePoint; type Compressed = FakePoint;
type Uncompressed = FakePoint; type Uncompressed = FakePoint;
type Prepared = Fr;
type Projective = Fr; type Projective = Fr;
type Base = Fr; type Base = Fr;
type Scalar = Fr; type Scalar = Fr;
@@ -419,7 +427,8 @@ 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();
@@ -428,16 +437,6 @@ impl CurveAffine for Fr {
res res
} }
fn into_projective(&self) -> Self::Projective {
*self
}
}
impl PairingCurveAffine for Fr {
type Prepared = Fr;
type Pair = Fr;
type PairingResult = Fr;
fn prepare(&self) -> Self::Prepared { fn prepare(&self) -> Self::Prepared {
*self *self
} }
@@ -445,4 +444,8 @@ impl PairingCurveAffine for Fr {
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
self.mul(*other) self.mul(*other)
} }
fn into_projective(&self) -> Self::Projective {
*self
}
} }

View File

@@ -1,87 +1,94 @@
use ff::{Field, PrimeField}; use pairing::{
use pairing::Engine; Engine,
Field,
PrimeField
};
mod dummy_engine; mod dummy_engine;
use self::dummy_engine::*; use self::dummy_engine::*;
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::{Circuit, ConstraintSystem, SynthesisError}; use ::{
Circuit,
ConstraintSystem,
SynthesisError
};
use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof}; use super::{
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>>(self, cs: &mut CS) -> Result<(), SynthesisError> { fn synthesize<CS: ConstraintSystem<E>>(
let a_var = cs.alloc( self,
|| "a", cs: &mut CS
|| { ) -> Result<(), SynthesisError>
if self.a.is_some() { {
if self.a.unwrap() { let a_var = cs.alloc(|| "a", || {
Ok(E::Fr::one()) if self.a.is_some() {
} else { if self.a.unwrap() {
Ok(E::Fr::zero()) Ok(E::Fr::one())
}
} else { } else {
Err(SynthesisError::AssignmentMissing) Ok(E::Fr::zero())
} }
}, } 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( let b_var = cs.alloc(|| "b", || {
|| "b", if self.b.is_some() {
|| { if self.b.unwrap() {
if self.b.is_some() { Ok(E::Fr::one())
if self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else { } else {
Err(SynthesisError::AssignmentMissing) Ok(E::Fr::zero())
} }
}, } 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( let c_var = cs.alloc_input(|| "c", || {
|| "c", if self.a.is_some() && self.b.is_some() {
|| { if self.a.unwrap() ^ self.b.unwrap() {
if self.a.is_some() && self.b.is_some() { Ok(E::Fr::one())
if self.a.unwrap() ^ self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else { } else {
Err(SynthesisError::AssignmentMissing) Ok(E::Fr::zero())
} }
}, } 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(())
@@ -102,10 +109,19 @@ 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(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap() generate_parameters(
c,
g1,
g2,
alpha,
beta,
gamma,
delta,
tau
).unwrap()
}; };
// This will synthesize the constraint system: // This will synthesize the constraint system:
@@ -213,35 +229,32 @@ fn test_xordemo() {
59158 59158
*/ */
let u_i = [59158, 48317, 21767, 10402] let u_i = [59158, 48317, 21767, 10402].iter().map(|e| {
.iter() Fr::from_str(&format!("{}", e)).unwrap()
.map(|e| Fr::from_str(&format!("{}", e)).unwrap()) }).collect::<Vec<Fr>>();
.collect::<Vec<Fr>>(); let v_i = [0, 0, 60619, 30791].iter().map(|e| {
let v_i = [0, 0, 60619, 30791] Fr::from_str(&format!("{}", e)).unwrap()
.iter() }).collect::<Vec<Fr>>();
.map(|e| Fr::from_str(&format!("{}", e)).unwrap()) let w_i = [0, 23320, 41193, 41193].iter().map(|e| {
.collect::<Vec<Fr>>(); Fr::from_str(&format!("{}", e)).unwrap()
let w_i = [0, 23320, 41193, 41193] }).collect::<Vec<Fr>>();
.iter()
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
.collect::<Vec<Fr>>();
for (u, a) in u_i.iter().zip(&params.a[..]) { for (u, a) in u_i.iter()
.zip(&params.a[..])
{
assert_eq!(u, a); assert_eq!(u, a);
} }
for (v, b) in v_i for (v, b) in v_i.iter()
.iter() .filter(|&&e| e != Fr::zero())
.filter(|&&e| e != Fr::zero()) .zip(&params.b_g1[..])
.zip(&params.b_g1[..])
{ {
assert_eq!(v, b); assert_eq!(v, b);
} }
for (v, b) in v_i for (v, b) in v_i.iter()
.iter() .filter(|&&e| e != Fr::zero())
.filter(|&&e| e != Fr::zero()) .zip(&params.b_g2[..])
.zip(&params.b_g2[..])
{ {
assert_eq!(v, b); assert_eq!(v, b);
} }
@@ -286,10 +299,15 @@ 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(c, &params, r, s).unwrap() create_proof(
c,
&params,
r,
s
).unwrap()
}; };
// A(x) = // A(x) =
@@ -305,7 +323,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);
} }
@@ -322,7 +340,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);
} }
@@ -363,10 +381,7 @@ 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] for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() {
.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];
@@ -377,5 +392,9 @@ fn test_xordemo() {
assert_eq!(expected_c, proof.c); assert_eq!(expected_c, proof.c);
} }
assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap()); assert!(verify_proof(
&pvk,
&proof,
&[Fr::one()]
).unwrap());
} }

View File

@@ -1,12 +1,24 @@
use ff::PrimeField; use pairing::{
use group::{CurveAffine, CurveProjective}; Engine,
use pairing::{Engine, PairingCurveAffine}; CurveProjective,
CurveAffine,
PrimeField
};
use super::{PreparedVerifyingKey, Proof, VerifyingKey}; use super::{
Proof,
VerifyingKey,
PreparedVerifyingKey
};
use crate::SynthesisError; use ::{
SynthesisError
};
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> { pub fn prepare_verifying_key<E: Engine>(
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;
@@ -16,15 +28,16 @@ pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyi
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);
} }
@@ -43,14 +56,11 @@ 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(&E::miller_loop( Ok(E::final_exponentiation(
[ &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())
.iter(), ).unwrap() == pvk.alpha_g1_beta_g2)
))
.unwrap()
== pvk.alpha_g1_beta_g2)
} }

View File

@@ -1,162 +1,35 @@
//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit extern crate pairing;
//! traits and and primitive structures, as well as basic gadget implementations extern crate rand;
//! such as booleans and number abstractions. extern crate num_cpus;
//! extern crate futures;
//! # Example circuit extern crate futures_cpupool;
//! extern crate bit_vec;
//! Say we want to write a circuit that proves we know the preimage to some hash extern crate crossbeam;
//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a extern crate byteorder;
//! fixed length known in advance (because the circuit parameters will depend on
//! it), but can otherwise have any value. We take the following strategy:
//!
//! - Witness each bit of the preimage.
//! - Compute `hash = SHA-256d(preimage)` inside the circuit.
//! - Expose `hash` as a public input using multiscalar packing.
//!
//! ```
//! use bellman::{
//! gadgets::{
//! boolean::{AllocatedBit, Boolean},
//! multipack,
//! sha256::sha256,
//! },
//! groth16, Circuit, ConstraintSystem, SynthesisError,
//! };
//! use pairing::{bls12_381::Bls12, Engine};
//! use rand::rngs::OsRng;
//! use sha2::{Digest, Sha256};
//!
//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order.
//! fn sha256d<E: Engine, CS: ConstraintSystem<E>>(
//! mut cs: CS,
//! data: &[Boolean],
//! ) -> Result<Vec<Boolean>, SynthesisError> {
//! // Flip endianness of each input byte
//! let input: Vec<_> = data
//! .chunks(8)
//! .map(|c| c.iter().rev())
//! .flatten()
//! .cloned()
//! .collect();
//!
//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;
//!
//! // Flip endianness of each output byte
//! Ok(res
//! .chunks(8)
//! .map(|c| c.iter().rev())
//! .flatten()
//! .cloned()
//! .collect())
//! }
//!
//! struct MyCircuit {
//! /// The input to SHA-256d we are proving that we know. Set to `None` when we
//! /// are verifying a proof (and do not have the witness data).
//! preimage: Option<[u8; 80]>,
//! }
//!
//! impl<E: Engine> Circuit<E> for MyCircuit {
//! fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
//! // Compute the values for the bits of the preimage. If we are verifying a proof,
//! // we still need to create the same constraints, so we return an equivalent-size
//! // Vec of None (indicating that the value of each bit is unknown).
//! let bit_values = if let Some(preimage) = self.preimage {
//! preimage
//! .into_iter()
//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
//! .flatten()
//! .map(|b| Some(b))
//! .collect()
//! } else {
//! vec![None; 80 * 8]
//! };
//! assert_eq!(bit_values.len(), 80 * 8);
//!
//! // Witness the bits of the preimage.
//! let preimage_bits = bit_values
//! .into_iter()
//! .enumerate()
//! // Allocate each bit.
//! .map(|(i, b)| {
//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b)
//! })
//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
//! .map(|b| b.map(Boolean::from))
//! .collect::<Result<Vec<_>, _>>()?;
//!
//! // Compute hash = SHA-256d(preimage).
//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;
//!
//! // Expose the vector of 32 boolean variables as compact public inputs.
//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
//! }
//! }
//!
//! // Create parameters for our circuit. In a production deployment these would
//! // be generated securely using a multiparty computation.
//! let params = {
//! let c = MyCircuit { preimage: None };
//! groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
//! };
//!
//! // Prepare the verification key (for proof verification).
//! let pvk = groth16::prepare_verifying_key(&params.vk);
//!
//! // Pick a preimage and compute its hash.
//! let preimage = [42; 80];
//! let hash = Sha256::digest(&Sha256::digest(&preimage));
//!
//! // Create an instance of our circuit (with the preimage as a witness).
//! let c = MyCircuit {
//! preimage: Some(preimage),
//! };
//!
//! // Create a Groth16 proof with our parameters.
//! let proof = groth16::create_random_proof(c, &params, &mut OsRng).unwrap();
//!
//! // Pack the hash as inputs for proof verification.
//! let hash_bits = multipack::bytes_to_bits_le(&hash);
//! let inputs = multipack::compute_multipacking::<Bls12>(&hash_bits);
//!
//! // Check the proof!
//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap());
//! ```
//!
//! # Roadmap
//!
//! `bellman` is being refactored into a generic proving library. Currently it
//! is pairing-specific, and different types of proving systems need to be
//! implemented as sub-modules. After the refactor, `bellman` will be generic
//! using the [`ff`] and [`group`] crates, while specific proving systems will
//! be separate crates that pull in the dependencies they require.
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
pub mod domain;
pub mod gadgets;
#[cfg(feature = "groth16")]
pub mod groth16;
pub mod multicore; pub mod multicore;
mod multiexp; mod multiexp;
pub mod domain;
pub mod groth16;
use ff::{Field, ScalarEngine}; use pairing::{Engine, Field};
use std::error::Error; use std::ops::{Add, Sub};
use std::fmt; use std::fmt;
use std::error::Error;
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
/// circuit that can be synthesized. The `synthesize` method is called during /// circuit that can be synthesized. The `synthesize` method is called during
/// CRS generation and during proving. /// CRS generation and during proving.
pub trait Circuit<E: ScalarEngine> { pub trait Circuit<E: Engine> {
/// 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>>(self, cs: &mut CS) -> Result<(), SynthesisError>; fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>;
} }
/// Represents a variable in our constraint system. /// Represents a variable in our constraint system.
@@ -178,31 +51,31 @@ impl Variable {
} }
/// Represents the index of either an input variable or /// Represents the index of either an input variable or
/// auxiliary variable. /// auxillary 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
/// in the scalar field of a pairing-friendly elliptic curve group. /// in the scalar field of a pairing-friendly elliptic curve group.
#[derive(Clone)] #[derive(Clone)]
pub struct LinearCombination<E: ScalarEngine>(Vec<(Variable, E::Fr)>); pub struct LinearCombination<E: Engine>(Vec<(Variable, E::Fr)>);
impl<E: ScalarEngine> AsRef<[(Variable, E::Fr)]> for LinearCombination<E> { impl<E: Engine> AsRef<[(Variable, E::Fr)]> for LinearCombination<E> {
fn as_ref(&self) -> &[(Variable, E::Fr)] { fn as_ref(&self) -> &[(Variable, E::Fr)] {
&self.0 &self.0
} }
} }
impl<E: ScalarEngine> LinearCombination<E> { impl<E: Engine> LinearCombination<E> {
pub fn zero() -> LinearCombination<E> { pub fn zero() -> LinearCombination<E> {
LinearCombination(vec![]) LinearCombination(vec![])
} }
} }
impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> { impl<E: Engine> Add<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<E> { fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
@@ -212,10 +85,9 @@ impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
} }
} }
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> { impl<E: Engine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> { fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
coeff.negate(); coeff.negate();
@@ -223,7 +95,7 @@ impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
} }
} }
impl<E: ScalarEngine> Add<Variable> for LinearCombination<E> { impl<E: Engine> Add<Variable> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
fn add(self, other: Variable) -> LinearCombination<E> { fn add(self, other: Variable) -> LinearCombination<E> {
@@ -231,7 +103,7 @@ impl<E: ScalarEngine> Add<Variable> for LinearCombination<E> {
} }
} }
impl<E: ScalarEngine> Sub<Variable> for LinearCombination<E> { impl<E: Engine> Sub<Variable> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
fn sub(self, other: Variable) -> LinearCombination<E> { fn sub(self, other: Variable) -> LinearCombination<E> {
@@ -239,7 +111,7 @@ impl<E: ScalarEngine> Sub<Variable> for LinearCombination<E> {
} }
} }
impl<'a, E: ScalarEngine> Add<&'a LinearCombination<E>> for LinearCombination<E> { impl<'a, E: Engine> Add<&'a LinearCombination<E>> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
fn add(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> { fn add(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
@@ -251,7 +123,7 @@ impl<'a, E: ScalarEngine> Add<&'a LinearCombination<E>> for LinearCombination<E>
} }
} }
impl<'a, E: ScalarEngine> Sub<&'a LinearCombination<E>> for LinearCombination<E> { impl<'a, E: Engine> Sub<&'a LinearCombination<E>> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
fn sub(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> { fn sub(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
@@ -263,7 +135,7 @@ impl<'a, E: ScalarEngine> Sub<&'a LinearCombination<E>> for LinearCombination<E>
} }
} }
impl<'a, E: ScalarEngine> Add<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> { impl<'a, E: Engine> Add<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> { fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
@@ -277,7 +149,7 @@ impl<'a, E: ScalarEngine> Add<(E::Fr, &'a LinearCombination<E>)> for LinearCombi
} }
} }
impl<'a, E: ScalarEngine> Sub<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> { impl<'a, E: Engine> Sub<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> { fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
@@ -309,8 +181,8 @@ pub enum SynthesisError {
IoError(io::Error), IoError(io::Error),
/// 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 auxillary variable
UnconstrainedVariable, UnconstrainedVariable
} }
impl From<io::Error> for SynthesisError { impl From<io::Error> for SynthesisError {
@@ -322,23 +194,21 @@ 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 => { SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed",
"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 => "auxillary variable was unconstrained"
} }
} }
} }
impl fmt::Display for SynthesisError { impl fmt::Display for SynthesisError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
if let SynthesisError::IoError(ref e) = *self { if let &SynthesisError::IoError(ref e) = self {
write!(f, "I/O error: ")?; write!(f, "I/O error: ")?;
e.fmt(f) e.fmt(f)
} else { } else {
@@ -349,7 +219,7 @@ impl fmt::Display for SynthesisError {
/// Represents a constraint system which can have new variables /// Represents a constraint system which can have new variables
/// allocated and constrains between them formed. /// allocated and constrains between them formed.
pub trait ConstraintSystem<E: ScalarEngine>: Sized { pub trait ConstraintSystem<E: Engine>: Sized {
/// Represents the type of the "root" of this constraint system /// Represents the type of the "root" of this constraint system
/// so that nested namespaces can minimize indirection. /// so that nested namespaces can minimize indirection.
type Root: ConstraintSystem<E>; type Root: ConstraintSystem<E>;
@@ -363,36 +233,40 @@ 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>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>; ) -> Result<Variable, SynthesisError>
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>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>; ) -> Result<Variable, SynthesisError>
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>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, b: LB,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, c: LC
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 where NR: Into<String>, N: FnOnce() -> NR;
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.
@@ -403,10 +277,11 @@ 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<NR, N>(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root> fn namespace<'a, NR, N>(
where &'a mut self,
NR: Into<String>, name_fn: N
N: FnOnce() -> NR, ) -> Namespace<'a, E, Self::Root>
where NR: Into<String>, N: FnOnce() -> NR
{ {
self.get_root().push_namespace(name_fn); self.get_root().push_namespace(name_fn);
@@ -416,40 +291,46 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// This is a "namespaced" constraint system which borrows a constraint system (pushing /// This is a "namespaced" constraint system which borrows a constraint system (pushing
/// a namespace context) and, when dropped, pops out of the namespace context. /// a namespace context) and, when dropped, pops out of the namespace context.
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E>>(&'a mut CS, PhantomData<E>); pub struct Namespace<'a, E: Engine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>);
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> { impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
type Root = CS::Root; type Root = CS::Root;
fn one() -> Variable { fn one() -> Variable {
CS::one() CS::one()
} }
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, b: LB,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, c: LC
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)
} }
@@ -459,23 +340,23 @@ 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 where NR: Into<String>, N: FnOnce() -> NR
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()
} }
} }
impl<'a, E: ScalarEngine, CS: ConstraintSystem<E>> Drop for Namespace<'a, E, CS> { impl<'a, E: Engine, CS: ConstraintSystem<E>> Drop for Namespace<'a, E, CS> {
fn drop(&mut self) { fn drop(&mut self) {
self.get_root().pop_namespace() self.get_root().pop_namespace()
} }
@@ -483,55 +364,61 @@ impl<'a, E: ScalarEngine, CS: ConstraintSystem<E>> Drop for Namespace<'a, E, CS>
/// Convenience implementation of ConstraintSystem<E> for mutable references to /// Convenience implementation of ConstraintSystem<E> for mutable references to
/// constraint systems. /// constraint systems.
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs mut CS { impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs mut CS {
type Root = CS::Root; type Root = CS::Root;
fn one() -> Variable { fn one() -> Variable {
CS::one() CS::one()
} }
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, b: LB,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, c: LC
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 where NR: Into<String>, N: FnOnce() -> NR
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

@@ -1,164 +1,106 @@
//! An interface for dealing with the kinds of parallel computations involved in //! This is an interface for dealing with the kinds of
//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and //! parallel computations involved in bellman. It's
//! [`crossbeam`] but may be extended in the future to allow for various //! currently just a thin wrapper around CpuPool and
//! parallelism strategies. //! crossbeam but may be extended in the future to
//! //! allow for various parallelism strategies.
//! [`CpuPool`]: futures_cpupool::CpuPool
#[cfg(feature = "multicore")] use num_cpus;
mod implementation { use futures::{Future, IntoFuture, Poll};
use crossbeam::{self, thread::Scope}; use futures_cpupool::{CpuPool, CpuFuture};
use futures::{Future, IntoFuture, Poll}; use crossbeam::{self, Scope};
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 {
// We don't expose this outside the library so that // We don't expose this outside the library so that
// all `Worker` instances have the same number of // all `Worker` instances have the same number of
// CPUs configured. // CPUs configured.
pub(crate) fn new_with_cpus(cpus: usize) -> Worker { pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
Worker { Worker {
cpus, cpus: cpus,
pool: CpuPool::new(cpus), pool: CpuPool::new(cpus)
}
}
pub fn new() -> Worker {
Self::new_with_cpus(num_cpus::get())
}
pub fn log_num_cpus(&self) -> u32 {
log2_floor(self.cpus)
}
pub fn compute<F, R>(&self, f: F) -> WorkerFuture<R::Item, R::Error>
where
F: FnOnce() -> R + Send + 'static,
R: IntoFuture + 'static,
R::Future: Send + 'static,
R::Item: Send + 'static,
R::Error: Send + 'static,
{
WorkerFuture {
future: self.pool.spawn_fn(f),
}
}
pub fn scope<'a, F, R>(&self, elements: usize, f: F) -> R
where
F: FnOnce(&Scope<'a>, usize) -> R,
{
let chunk_size = if elements < self.cpus {
1
} else {
elements / self.cpus
};
// TODO: Handle case where threads fail
crossbeam::scope(|scope| f(scope, chunk_size))
.expect("Threads aren't allowed to fail yet")
} }
} }
pub struct WorkerFuture<T, E> { pub fn new() -> Worker {
future: CpuFuture<T, E>, Self::new_with_cpus(num_cpus::get())
} }
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> { pub fn log_num_cpus(&self) -> u32 {
type Item = T; log2_floor(self.cpus)
type Error = E; }
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { pub fn compute<F, R>(
self.future.poll() &self, f: F
) -> WorkerFuture<R::Item, R::Error>
where F: FnOnce() -> R + Send + 'static,
R: IntoFuture + 'static,
R::Future: Send + 'static,
R::Item: Send + 'static,
R::Error: Send + 'static
{
WorkerFuture {
future: self.pool.spawn_fn(f)
} }
} }
fn log2_floor(num: usize) -> u32 { pub fn scope<'a, F, R>(
assert!(num > 0); &self,
elements: usize,
f: F
) -> R
where F: FnOnce(&Scope<'a>, usize) -> R
{
let chunk_size = if elements < self.cpus {
1
} else {
elements / self.cpus
};
let mut pow = 0; crossbeam::scope(|scope| {
f(scope, chunk_size)
while (1 << (pow + 1)) <= num { })
pow += 1;
}
pow
}
#[test]
fn test_log2_floor() {
assert_eq!(log2_floor(1), 0);
assert_eq!(log2_floor(2), 1);
assert_eq!(log2_floor(3), 1);
assert_eq!(log2_floor(4), 2);
assert_eq!(log2_floor(5), 2);
assert_eq!(log2_floor(6), 2);
assert_eq!(log2_floor(7), 2);
assert_eq!(log2_floor(8), 3);
} }
} }
#[cfg(not(feature = "multicore"))] pub struct WorkerFuture<T, E> {
mod implementation { future: CpuFuture<T, E>
use futures::{future, Future, IntoFuture, Poll}; }
#[derive(Clone)] impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
pub struct Worker; type Item = T;
type Error = E;
impl Worker { fn poll(&mut self) -> Poll<Self::Item, Self::Error>
pub fn new() -> Worker { {
Worker self.future.poll()
}
pub fn log_num_cpus(&self) -> u32 {
0
}
pub fn compute<F, R>(&self, f: F) -> R::Future
where
F: FnOnce() -> R + Send + 'static,
R: IntoFuture + 'static,
R::Future: Send + 'static,
R::Item: Send + 'static,
R::Error: Send + 'static,
{
f().into_future()
}
pub fn scope<F, R>(&self, elements: usize, f: F) -> R
where
F: FnOnce(&DummyScope, usize) -> R,
{
f(&DummyScope, elements)
}
}
pub struct WorkerFuture<T, E> {
future: future::FutureResult<T, E>,
}
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
type Item = T;
type Error = E;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.future.poll()
}
}
pub struct DummyScope;
impl DummyScope {
pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
f(self);
}
} }
} }
pub use self::implementation::*; fn log2_floor(num: usize) -> u32 {
assert!(num > 0);
let mut pow = 0;
while (1 << (pow+1)) <= num {
pow += 1;
}
pow
}
#[test]
fn test_log2_floor() {
assert_eq!(log2_floor(1), 0);
assert_eq!(log2_floor(2), 1);
assert_eq!(log2_floor(3), 1);
assert_eq!(log2_floor(4), 2);
assert_eq!(log2_floor(5), 2);
assert_eq!(log2_floor(6), 2);
assert_eq!(log2_floor(7), 2);
assert_eq!(log2_floor(8), 3);
}

View File

@@ -1,11 +1,17 @@
use super::multicore::Worker; use pairing::{
use bit_vec::{self, BitVec}; CurveAffine,
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; CurveProjective,
use futures::Future; Engine,
use group::{CurveAffine, CurveProjective}; PrimeField,
use std::io; Field,
use std::iter; PrimeFieldRepr
};
use std::sync::Arc; use std::sync::Arc;
use std::io;
use bit_vec::{self, BitVec};
use std::iter;
use futures::{Future};
use super::multicore::Worker;
use super::SynthesisError; use super::SynthesisError;
@@ -19,10 +25,7 @@ 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( fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError>;
&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>;
@@ -37,20 +40,13 @@ 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( fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError> {
&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( return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
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]);
@@ -62,11 +58,7 @@ 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( return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
io::ErrorKind::UnexpectedEof,
"expected more bases from source",
)
.into());
} }
self.1 += amt; self.1 += amt;
@@ -77,7 +69,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>;
@@ -106,7 +98,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 {
@@ -125,7 +117,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
} }
} }
@@ -149,16 +141,15 @@ fn multiexp_inner<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 Engine>::Fr as PrimeField>::Repr>>,
mut skip: u32, mut skip: u32,
c: u32, c: u32,
handle_trivial: bool, handle_trivial: bool
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>> ) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where where for<'a> &'a Q: QueryDensity,
for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef<Q>,
D: Send + Sync + 'static + Clone + AsRef<Q>, G: CurveAffine,
G: CurveAffine, S: SourceBuilder<G>
S: SourceBuilder<G>,
{ {
// Perform this region of the multiexp // Perform this region of the multiexp
let this = { let this = {
@@ -176,8 +167,8 @@ where
// Create space for the buckets // Create space for the buckets
let mut buckets = vec![<G as CurveAffine>::Projective::zero(); (1 << c) - 1]; let mut buckets = vec![<G as CurveAffine>::Projective::zero(); (1 << c) - 1];
let zero = <G::Engine as ScalarEngine>::Fr::zero().into_repr(); let zero = <G::Engine as Engine>::Fr::zero().into_repr();
let one = <G::Engine as ScalarEngine>::Fr::one().into_repr(); let one = <G::Engine as Engine>::Fr::one().into_repr();
// Sort the bases into buckets // Sort the bases into buckets
for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) { for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) {
@@ -220,31 +211,23 @@ where
skip += c; skip += c;
if skip >= <G::Engine as ScalarEngine>::Fr::NUM_BITS { if skip >= <G::Engine as Engine>::Fr::NUM_BITS {
// There isn't another region. // There isn't another region.
Box::new(this) Box::new(this)
} else { } else {
// 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( this.join(multiexp_inner(pool, bases, density_map, exponents, skip, c, false))
pool, .map(move |(this, mut higher)| {
bases, for _ in 0..c {
density_map, higher.double();
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
}), })
) )
} }
} }
@@ -255,13 +238,12 @@ 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 Engine>::Fr as PrimeField>::Repr>>
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>> ) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where where for<'a> &'a Q: QueryDensity,
for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef<Q>,
D: Send + Sync + 'static + Clone + AsRef<Q>, G: CurveAffine,
G: CurveAffine, S: SourceBuilder<G>
S: SourceBuilder<G>,
{ {
let c = if exponents.len() < 32 { let c = if exponents.len() < 32 {
3u32 3u32
@@ -279,13 +261,13 @@ where
multiexp_inner(pool, bases, density_map, exponents, 0, c, true) multiexp_inner(pool, bases, density_map, exponents, 0, c, true)
} }
#[cfg(feature = "pairing")]
#[test] #[test]
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();
@@ -297,28 +279,25 @@ fn test_with_bls12() {
acc acc
} }
use pairing::{bls12_381::Bls12, Engine}; use rand::{self, Rand};
use rand; use pairing::bls12_381::Bls12;
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( let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>());
(0..SAMPLES) let g = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::G1::rand(rng).into_affine()).collect::<Vec<_>>());
.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(&pool, (g, 0), FullDensity, v).wait().unwrap(); let fast = multiexp(
&pool,
(g, 0),
FullDensity,
v
).wait().unwrap();
assert_eq!(naive, fast); assert_eq!(naive, fast);
} }

View File

@@ -1,22 +1,38 @@
extern crate bellman;
extern crate pairing;
extern crate rand;
// For randomness (during paramgen and proof generation) // For randomness (during paramgen and proof generation)
use rand::thread_rng; use rand::{thread_rng, Rng};
// For benchmarking // For benchmarking
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
// Bring in some tools for using pairing-friendly curves // Bring in some tools for using pairing-friendly curves
use ff::{Field, ScalarEngine}; use pairing::{
use pairing::Engine; Engine,
Field
};
// 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::Bls12; use pairing::bls12_381::{
Bls12
};
// We'll use these interfaces to construct our circuit. // We'll use these interfaces to construct our circuit.
use bellman::{Circuit, ConstraintSystem, SynthesisError}; use bellman::{
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::{
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof, Proof,
generate_random_parameters,
prepare_verifying_key,
create_random_proof,
verify_proof,
}; };
const MIMC_ROUNDS: usize = 322; const MIMC_ROUNDS: usize = 322;
@@ -34,7 +50,12 @@ const MIMC_ROUNDS: usize = 322;
/// return xL /// return xL
/// } /// }
/// ``` /// ```
fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr { fn mimc<E: Engine>(
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 {
@@ -56,81 +77,80 @@ fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr {
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>>(self, cs: &mut CS) -> Result<(), SynthesisError> { fn synthesize<CS: ConstraintSystem<E>>(
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( let mut xl = cs.alloc(|| "preimage xl", || {
|| "preimage xl", xl_value.ok_or(SynthesisError::AssignmentMissing)
|| 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( let mut xr = cs.alloc(|| "preimage xr", || {
|| "preimage xr", xr_value.ok_or(SynthesisError::AssignmentMissing)
|| 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
let cs = &mut cs.namespace(|| format!("round {}", i)); let cs = &mut cs.namespace(|| format!("round {}", i));
// tmp = (xL + Ci)^2 // tmp = (xL + Ci)^2
let tmp_value = xl_value.map(|mut e| { let mut tmp_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]); e.add_assign(&self.constants[i]);
e.square(); e.square();
e e
}); });
let tmp = cs.alloc( let mut tmp = cs.alloc(|| "tmp", || {
|| "tmp", tmp_value.ok_or(SynthesisError::AssignmentMissing)
|| 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
// new_xL = xR + tmp * (xL + Ci) // new_xL = xR + tmp * (xL + Ci)
// new_xL - xR = tmp * (xL + Ci) // new_xL - xR = tmp * (xL + Ci)
let new_xl_value = xl_value.map(|mut e| { let mut new_xl_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]); e.add_assign(&self.constants[i]);
e.mul_assign(&tmp_value.unwrap()); e.mul_assign(&tmp_value.unwrap());
e.add_assign(&xr_value.unwrap()); e.add_assign(&xr_value.unwrap());
e e
}); });
let 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( cs.alloc_input(|| "image", || {
|| "image", new_xl_value.ok_or(SynthesisError::AssignmentMissing)
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing), })?
)?
} else { } else {
cs.alloc( cs.alloc(|| "new_xl", || {
|| "new_xl", new_xl_value.ok_or(SynthesisError::AssignmentMissing)
|| 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
@@ -153,9 +173,7 @@ 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) let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng))
.collect::<Vec<_>>();
println!("Creating parameters..."); println!("Creating parameters...");
@@ -164,7 +182,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()
@@ -186,8 +204,8 @@ fn test_mimc() {
for _ in 0..SAMPLES { for _ in 0..SAMPLES {
// Generate a random preimage and compute the image // Generate a random preimage and compute the image
let xl = <Bls12 as ScalarEngine>::Fr::random(rng); let xl = rng.gen();
let xr = <Bls12 as ScalarEngine>::Fr::random(rng); let xr = rng.gen();
let image = mimc::<Bls12>(xl, xr, &constants); let image = mimc::<Bls12>(xl, xr, &constants);
proof_vec.truncate(0); proof_vec.truncate(0);
@@ -199,7 +217,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.
@@ -213,16 +231,20 @@ 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(&pvk, &proof, &[image]).unwrap()); assert!(verify_proof(
&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 = let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64
proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64); + (proving_avg.as_secs() as f64);
let verifying_avg = total_verifying / SAMPLES; let verifying_avg = total_verifying / SAMPLES;
let verifying_avg = let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64
verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64); + (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

@@ -1,23 +0,0 @@
[package]
name = "ff"
version = "0.5.2"
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
description = "Library for building and interfacing with finite fields"
readme = "README.md"
documentation = "https://docs.rs/ff/"
homepage = "https://github.com/ebfull/ff"
license = "MIT/Apache-2.0"
repository = "https://github.com/ebfull/ff"
edition = "2018"
[dependencies]
byteorder = "1"
ff_derive = { version = "^0.4.1", path = "ff_derive", optional = true }
rand_core = "0.5"
[features]
default = []
derive = ["ff_derive"]
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -1,67 +0,0 @@
# ff
`ff` is a finite field library written in pure Rust, with no `unsafe{}` code.
## Disclaimers
* This library does not provide constant-time guarantees.
## Usage
Add the `ff` crate to your `Cargo.toml`:
```toml
[dependencies]
ff = "0.5"
```
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits.
See the **[documentation](https://docs.rs/ff/)** for more.
### #![derive(PrimeField)]
If you need an implementation of a prime field, this library also provides a procedural
macro that will expand into an efficient implementation of a prime field when supplied
with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is
also quadratic nonresidue.
First, enable the `derive` crate feature:
```toml
[dependencies]
ff = { version = "0.4", features = ["derive"] }
```
And then use the macro like so:
```rust
extern crate rand;
#[macro_use]
extern crate ff;
#[derive(PrimeField)]
#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"]
#[PrimeFieldGenerator = "7"]
struct Fp(FpRepr);
```
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement
`SqrtField` if supported. The library implements `FpRepr` itself and derives
`PrimeFieldRepr` for it.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

View File

@@ -1,24 +0,0 @@
[package]
name = "ff_derive"
version = "0.4.1"
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
description = "Procedural macro library used to build custom prime field implementations"
documentation = "https://docs.rs/ff/"
homepage = "https://github.com/ebfull/ff"
license = "MIT/Apache-2.0"
repository = "https://github.com/ebfull/ff"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
num-bigint = "0.2"
num-traits = "0.2"
num-integer = "0.1"
proc-macro2 = "1"
quote = "1"
syn = "1"
[badges]
maintenance = { status = "passively-maintained" }

File diff suppressed because it is too large Load Diff

View File

@@ -1,393 +0,0 @@
//! This crate provides traits for working with finite fields.
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
#![allow(unused_imports)]
#[cfg(feature = "derive")]
pub use ff_derive::*;
use rand_core::RngCore;
use std::error::Error;
use std::fmt;
use std::io::{self, Read, Write};
/// This trait represents an element of a field.
pub trait Field:
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
{
/// Returns an element chosen uniformly at random using a user-provided RNG.
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self;
/// Returns the zero element of the field, the additive identity.
fn zero() -> Self;
/// Returns the one element of the field, the multiplicative identity.
fn one() -> Self;
/// Returns true iff this element is zero.
fn is_zero(&self) -> bool;
/// Squares this element.
fn square(&mut self);
/// Doubles this element.
fn double(&mut self);
/// Negates this element.
fn negate(&mut self);
/// Adds another element to this element.
fn add_assign(&mut self, other: &Self);
/// Subtracts another element from this element.
fn sub_assign(&mut self, other: &Self);
/// Multiplies another element by this element.
fn mul_assign(&mut self, other: &Self);
/// Computes the multiplicative inverse of this element, if nonzero.
fn inverse(&self) -> Option<Self>;
/// Exponentiates this element by a power of the base prime modulus via
/// the Frobenius automorphism.
fn frobenius_map(&mut self, power: usize);
/// Exponentiates this element by a number represented with `u64` limbs,
/// least significant digit first.
fn pow<S: AsRef<[u64]>>(&self, exp: S) -> Self {
let mut res = Self::one();
let mut found_one = false;
for i in BitIterator::new(exp) {
if found_one {
res.square();
} else {
found_one = i;
}
if i {
res.mul_assign(self);
}
}
res
}
}
/// This trait represents an element of a field that has a square root operation described for it.
pub trait SqrtField: Field {
/// Returns the Legendre symbol of the field element.
fn legendre(&self) -> LegendreSymbol;
/// Returns the square root of the field element, if it is
/// quadratic residue.
fn sqrt(&self) -> Option<Self>;
}
/// This trait represents a wrapper around a biginteger which can encode any element of a particular
/// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit
/// first.
pub trait PrimeFieldRepr:
Sized
+ Copy
+ Clone
+ Eq
+ Ord
+ Send
+ Sync
+ Default
+ fmt::Debug
+ fmt::Display
+ 'static
+ AsRef<[u64]>
+ AsMut<[u64]>
+ From<u64>
{
/// Subtract another represetation from this one.
fn sub_noborrow(&mut self, other: &Self);
/// Add another representation to this one.
fn add_nocarry(&mut self, other: &Self);
/// Compute the number of bits needed to encode this number. Always a
/// multiple of 64.
fn num_bits(&self) -> u32;
/// Returns true iff this number is zero.
fn is_zero(&self) -> bool;
/// Returns true iff this number is odd.
fn is_odd(&self) -> bool;
/// Returns true iff this number is even.
fn is_even(&self) -> bool;
/// Performs a rightwise bitshift of this number, effectively dividing
/// it by 2.
fn div2(&mut self);
/// Performs a rightwise bitshift of this number by some amount.
fn shr(&mut self, amt: u32);
/// Performs a leftwise bitshift of this number, effectively multiplying
/// it by 2. Overflow is ignored.
fn mul2(&mut self);
/// Performs a leftwise bitshift of this number by some amount.
fn shl(&mut self, amt: u32);
/// Writes this `PrimeFieldRepr` as a big endian integer.
fn write_be<W: Write>(&self, mut writer: W) -> io::Result<()> {
use byteorder::{BigEndian, WriteBytesExt};
for digit in self.as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit)?;
}
Ok(())
}
/// Reads a big endian integer into this representation.
fn read_be<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
use byteorder::{BigEndian, ReadBytesExt};
for digit in self.as_mut().iter_mut().rev() {
*digit = reader.read_u64::<BigEndian>()?;
}
Ok(())
}
/// Writes this `PrimeFieldRepr` as a little endian integer.
fn write_le<W: Write>(&self, mut writer: W) -> io::Result<()> {
use byteorder::{LittleEndian, WriteBytesExt};
for digit in self.as_ref().iter() {
writer.write_u64::<LittleEndian>(*digit)?;
}
Ok(())
}
/// Reads a little endian integer into this representation.
fn read_le<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
use byteorder::{LittleEndian, ReadBytesExt};
for digit in self.as_mut().iter_mut() {
*digit = reader.read_u64::<LittleEndian>()?;
}
Ok(())
}
}
#[derive(Debug, PartialEq)]
pub enum LegendreSymbol {
Zero = 0,
QuadraticResidue = 1,
QuadraticNonResidue = -1,
}
/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
/// `PrimeField` element.
#[derive(Debug)]
pub enum PrimeFieldDecodingError {
/// The encoded value is not in the field
NotInField(String),
}
impl Error for PrimeFieldDecodingError {
fn description(&self) -> &str {
match *self {
PrimeFieldDecodingError::NotInField(..) => "not an element of the field",
}
}
}
impl fmt::Display for PrimeFieldDecodingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self {
PrimeFieldDecodingError::NotInField(ref repr) => {
write!(f, "{} is not an element of the field", repr)
}
}
}
}
/// This represents an element of a prime field.
pub trait PrimeField: Field {
/// The prime field can be converted back and forth into this biginteger
/// representation.
type Repr: PrimeFieldRepr + From<Self>;
/// Interpret a string of numbers as a (congruent) prime field element.
/// Does not accept unnecessary leading zeroes or a blank string.
fn from_str(s: &str) -> Option<Self> {
if s.is_empty() {
return None;
}
if s == "0" {
return Some(Self::zero());
}
let mut res = Self::zero();
let ten = Self::from_repr(Self::Repr::from(10)).unwrap();
let mut first_digit = true;
for c in s.chars() {
match c.to_digit(10) {
Some(c) => {
if first_digit {
if c == 0 {
return None;
}
first_digit = false;
}
res.mul_assign(&ten);
res.add_assign(&Self::from_repr(Self::Repr::from(u64::from(c))).unwrap());
}
None => {
return None;
}
}
}
Some(res)
}
/// Convert this prime field element into a biginteger representation.
fn from_repr(_: Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
/// Convert a biginteger representation into a prime field element, if
/// the number is an element of the field.
fn into_repr(&self) -> Self::Repr;
/// Returns the field characteristic; the modulus.
fn char() -> Self::Repr;
/// How many bits are needed to represent an element of this field.
const NUM_BITS: u32;
/// How many bits of information can be reliably stored in the field element.
const CAPACITY: u32;
/// Returns the multiplicative generator of `char()` - 1 order. This element
/// must also be quadratic nonresidue.
fn multiplicative_generator() -> Self;
/// 2^s * t = `char()` - 1 with t odd.
const S: u32;
/// Returns the 2^s root of unity computed by exponentiating the `multiplicative_generator()`
/// by t.
fn root_of_unity() -> Self;
}
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
/// with well-defined relationships. Specific relationships (for example, a
/// pairing-friendly curve) can be defined in a subtrait.
pub trait ScalarEngine: Sized + 'static + Clone {
/// This is the scalar field of the engine's groups.
type Fr: PrimeField + SqrtField;
}
#[derive(Debug)]
pub struct BitIterator<E> {
t: E,
n: usize,
}
impl<E: AsRef<[u64]>> BitIterator<E> {
pub fn new(t: E) -> Self {
let n = t.as_ref().len() * 64;
BitIterator { t, n }
}
}
impl<E: AsRef<[u64]>> Iterator for BitIterator<E> {
type Item = bool;
fn next(&mut self) -> Option<bool> {
if self.n == 0 {
None
} else {
self.n -= 1;
let part = self.n / 64;
let bit = self.n - (64 * part);
Some(self.t.as_ref()[part] & (1 << bit) > 0)
}
}
}
#[test]
fn test_bit_iterator() {
let mut a = BitIterator::new([0xa953d79b83f6ab59, 0x6dea2059e200bd39]);
let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001";
for e in expected.chars() {
assert!(a.next().unwrap() == (e == '1'));
}
assert!(a.next().is_none());
let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001";
let mut a = BitIterator::new([
0x429d5f3ac3a3b759,
0xb10f4c66768b1c92,
0x92368b6d16ecd3b4,
0xa57ea85ae8775219,
]);
for e in expected.chars() {
assert!(a.next().unwrap() == (e == '1'));
}
assert!(a.next().is_none());
}
pub use self::arith_impl::*;
mod arith_impl {
/// Calculate a - b - borrow, returning the result and modifying
/// the borrow value.
#[inline(always)]
pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 {
let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow);
*borrow = if tmp >> 64 == 0 { 1 } else { 0 };
tmp as u64
}
/// Calculate a + b + carry, returning the sum and modifying the
/// carry value.
#[inline(always)]
pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 {
let tmp = u128::from(a) + u128::from(b) + u128::from(*carry);
*carry = (tmp >> 64) as u64;
tmp as u64
}
/// Calculate a + (b * c) + carry, returning the least significant digit
/// and setting carry to the most significant digit.
#[inline(always)]
pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry);
*carry = (tmp >> 64) as u64;
tmp as u64
}
}

View File

@@ -1,23 +0,0 @@
[package]
name = "group"
version = "0.2.0"
authors = [
"Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>",
]
readme = "README.md"
license = "MIT/Apache-2.0"
description = "Elliptic curve group traits and utilities"
documentation = "https://docs.rs/group/"
homepage = "https://github.com/ebfull/group"
repository = "https://github.com/ebfull/group"
edition = "2018"
[dependencies]
ff = { version = "0.5.0", path = "../ff" }
rand = "0.7"
rand_xorshift = "0.2"
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -1,190 +0,0 @@
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
use rand::RngCore;
use std::error::Error;
use std::fmt;
pub mod tests;
mod wnaf;
pub use self::wnaf::Wnaf;
/// Projective representation of an elliptic curve point guaranteed to be
/// in the correct prime order subgroup.
pub trait CurveProjective:
PartialEq + Eq + Sized + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
{
type Engine: ScalarEngine<Fr = Self::Scalar>;
type Scalar: PrimeField + SqrtField;
type Base: SqrtField;
type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>;
/// Returns an element chosen uniformly at random using a user-provided RNG.
fn random<R: RngCore>(rng: &mut R) -> Self;
/// Returns the additive identity.
fn zero() -> Self;
/// Returns a fixed generator of unknown exponent.
fn one() -> Self;
/// Determines if this point is the point at infinity.
fn is_zero(&self) -> bool;
/// Normalizes a slice of projective elements so that
/// conversion to affine is cheap.
fn batch_normalization(v: &mut [Self]);
/// Checks if the point is already "normalized" so that
/// cheap affine conversion is possible.
fn is_normalized(&self) -> bool;
/// Doubles this element.
fn double(&mut self);
/// Adds another element to this element.
fn add_assign(&mut self, other: &Self);
/// Subtracts another element from this element.
fn sub_assign(&mut self, other: &Self) {
let mut tmp = *other;
tmp.negate();
self.add_assign(&tmp);
}
/// Adds an affine element to this element.
fn add_assign_mixed(&mut self, other: &Self::Affine);
/// Negates this element.
fn negate(&mut self);
/// Performs scalar multiplication of this element.
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S);
/// Converts this element into its affine representation.
fn into_affine(&self) -> Self::Affine;
/// Recommends a wNAF window table size given a scalar. Always returns a number
/// between 2 and 22, inclusive.
fn recommended_wnaf_for_scalar(scalar: <Self::Scalar as PrimeField>::Repr) -> usize;
/// Recommends a wNAF window size given the number of scalars you intend to multiply
/// a base by. Always returns a number between 2 and 22, inclusive.
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize;
}
/// Affine representation of an elliptic curve point guaranteed to be
/// in the correct prime order subgroup.
pub trait CurveAffine:
Copy + Clone + Sized + Send + Sync + fmt::Debug + fmt::Display + PartialEq + Eq + 'static
{
type Engine: ScalarEngine<Fr = Self::Scalar>;
type Scalar: PrimeField + SqrtField;
type Base: SqrtField;
type Projective: CurveProjective<Affine = Self, Scalar = Self::Scalar>;
type Uncompressed: EncodedPoint<Affine = Self>;
type Compressed: EncodedPoint<Affine = Self>;
/// Returns the additive identity.
fn zero() -> Self;
/// Returns a fixed generator of unknown exponent.
fn one() -> Self;
/// Determines if this point represents the point at infinity; the
/// additive identity.
fn is_zero(&self) -> bool;
/// Negates this element.
fn negate(&mut self);
/// Performs scalar multiplication of this element with mixed addition.
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective;
/// Converts this element into its affine representation.
fn into_projective(&self) -> Self::Projective;
/// Converts this element into its compressed encoding, so long as it's not
/// the point at infinity.
fn into_compressed(&self) -> Self::Compressed {
<Self::Compressed as EncodedPoint>::from_affine(*self)
}
/// Converts this element into its uncompressed encoding, so long as it's not
/// the point at infinity.
fn into_uncompressed(&self) -> Self::Uncompressed {
<Self::Uncompressed as EncodedPoint>::from_affine(*self)
}
}
/// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`.
pub trait EncodedPoint:
Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + 'static
{
type Affine: CurveAffine;
/// Creates an empty representation.
fn empty() -> Self;
/// Returns the number of bytes consumed by this representation.
fn size() -> usize;
/// Converts an `EncodedPoint` into a `CurveAffine` element,
/// if the encoding represents a valid element.
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError>;
/// Converts an `EncodedPoint` into a `CurveAffine` element,
/// without guaranteeing that the encoding represents a valid
/// element. This is useful when the caller knows the encoding is
/// valid already.
///
/// If the encoding is invalid, this can break API invariants,
/// so caution is strongly encouraged.
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError>;
/// Creates an `EncodedPoint` from an affine point, as long as the
/// point is not the point at infinity.
fn from_affine(affine: Self::Affine) -> Self;
}
/// An error that may occur when trying to decode an `EncodedPoint`.
#[derive(Debug)]
pub enum GroupDecodingError {
/// The coordinate(s) do not lie on the curve.
NotOnCurve,
/// The element is not part of the r-order subgroup.
NotInSubgroup,
/// One of the coordinates could not be decoded
CoordinateDecodingError(&'static str, PrimeFieldDecodingError),
/// The compression mode of the encoded element was not as expected
UnexpectedCompressionMode,
/// The encoding contained bits that should not have been set
UnexpectedInformation,
}
impl Error for GroupDecodingError {
fn description(&self) -> &str {
match *self {
GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve",
GroupDecodingError::NotInSubgroup => "the element is not part of an r-order subgroup",
GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded",
GroupDecodingError::UnexpectedCompressionMode => {
"encoding has unexpected compression mode"
}
GroupDecodingError::UnexpectedInformation => "encoding has unexpected information",
}
}
}
impl fmt::Display for GroupDecodingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self {
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
write!(f, "{} decoding error: {}", description, err)
}
_ => write!(f, "{}", self.description()),
}
}
}

View File

@@ -1,18 +1,12 @@
[package] [package]
name = "librustzcash" name = "librustzcash"
description = "Rust FFI used by the zcashd binary. Not an official API." version = "0.1.0"
version = "0.2.0"
authors = [ authors = [
"Sean Bowe <ewillbefull@gmail.com>", "Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>", "Jack Grigg <jack@z.cash>",
"Jay Graber <jay@z.cash>", "Jay Graber <jay@z.cash>",
"Simon Liu <simon@z.cash>" "Simon Liu <simon@z.cash>"
] ]
homepage = "https://github.com/zcash/librustzcash"
repository = "https://github.com/zcash/librustzcash"
readme = "README.md"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib] [lib]
name = "rustzcash" name = "rustzcash"
@@ -20,17 +14,15 @@ path = "src/rustzcash.rs"
crate-type = ["staticlib"] crate-type = ["staticlib"]
[dependencies] [dependencies]
bellman = { version = "0.2.0", path = "../bellman" } bellman = { path = "../bellman" }
blake2b_simd = "0.5"
blake2s_simd = "0.5"
ff = { version = "0.5.0", path = "../ff" }
libc = "0.2" libc = "0.2"
pairing = { version = "0.15.0", path = "../pairing" } pairing = { path = "../pairing" }
lazy_static = "1" lazy_static = "1"
rand_core = "0.5.1" byteorder = "1"
zcash_history = { version = "0.0.1", path = "../zcash_history" } rand = "0.4"
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" } sapling-crypto = { path = "../sapling-crypto" }
zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" } zip32 = { path = "../zip32" }
[badges] [dependencies.blake2-rfc]
maintenance = { status = "deprecated" } git = "https://github.com/gtank/blake2-rfc"
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"

View File

@@ -1,17 +1,12 @@
# librustzcash # librustzcash
`librustzcash` is an FFI library crate that exposes the Zcash Rust components to This repository contains librustzcash, a static library for Zcash code assets written in Rust.
the `zcashd` full node.
The FFI API does not have any stability guarantees, and will change as required
by `zcashd`.
## License ## License
Licensed under either of Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option. at your option.

View File

@@ -4,12 +4,6 @@
#include <stdint.h> #include <stdint.h>
extern "C" { extern "C" {
#ifdef WIN32
typedef uint16_t codeunit;
#else
typedef uint8_t codeunit;
#endif
void librustzcash_to_scalar(const unsigned char *input, unsigned char *result); void librustzcash_to_scalar(const unsigned char *input, unsigned char *result);
void librustzcash_ask_to_ak(const unsigned char *ask, unsigned char *result); void librustzcash_ask_to_ak(const unsigned char *ask, unsigned char *result);
@@ -25,14 +19,11 @@ extern "C" {
/// Loads the zk-SNARK parameters into memory and saves /// Loads the zk-SNARK parameters into memory and saves
/// paths as necessary. Only called once. /// paths as necessary. Only called once.
void librustzcash_init_zksnark_params( void librustzcash_init_zksnark_params(
const codeunit* spend_path, const char* spend_path,
size_t spend_path_len,
const char* spend_hash, const char* spend_hash,
const codeunit* output_path, const char* output_path,
size_t output_path_len,
const char* output_hash, const char* output_hash,
const codeunit* sprout_path, const char* sprout_path,
size_t sprout_path_len,
const char* sprout_hash const char* sprout_hash
); );
@@ -112,7 +103,8 @@ extern "C" {
bool librustzcash_sapling_output_proof( bool librustzcash_sapling_output_proof(
void *ctx, void *ctx,
const unsigned char *esk, const unsigned char *esk,
const unsigned char *payment_address, const unsigned char *diversifier,
const unsigned char *pk_d,
const unsigned char *rcm, const unsigned char *rcm,
const uint64_t value, const uint64_t value,
unsigned char *cv, unsigned char *cv,
@@ -307,33 +299,6 @@ extern "C" {
unsigned char *j_ret, unsigned char *j_ret,
unsigned char *addr_ret unsigned char *addr_ret
); );
uint32_t librustzcash_mmr_append(
uint32_t cbranch,
uint32_t t_len,
const uint32_t *ni_ptr,
const unsigned char *n_ptr,
size_t p_len,
const unsigned char *nn_ptr,
unsigned char *rt_ret,
unsigned char *buf_ret
);
uint32_t librustzcash_mmr_delete(
uint32_t cbranch,
uint32_t t_len,
const uint32_t *ni_ptr,
const unsigned char *n_ptr,
size_t p_len,
size_t e_len,
unsigned char *rt_ret
);
uint32_t librustzcash_mmr_hash_node(
uint32_t cbranch,
const unsigned char *n_ptr,
unsigned char *h_ret
);
} }
#endif // LIBRUSTZCASH_INCLUDE_H_ #endif // LIBRUSTZCASH_INCLUDE_H_

View File

@@ -1,10 +1,5 @@
//! Verification functions for the [Equihash] proof-of-work algorithm. use blake2_rfc::blake2b::{Blake2b, Blake2bResult};
//!
//! [Equihash]: https://zips.z.cash/protocol/protocol.pdf#equihash
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams, State as Blake2bState};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use log::error;
use std::io::Cursor; use std::io::Cursor;
use std::mem::size_of; use std::mem::size_of;
@@ -38,7 +33,7 @@ impl Params {
} }
impl Node { impl Node {
fn new(p: &Params, state: &Blake2bState, i: u32) -> Self { fn new(p: &Params, state: &Blake2b, i: u32) -> Self {
let hash = generate_hash(state, i / p.indices_per_hash_output()); let hash = generate_hash(state, i / p.indices_per_hash_output());
let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize; let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize;
let end = start + (p.n as usize) / 8; let end = start + (p.n as usize) / 8;
@@ -65,7 +60,10 @@ impl Node {
indices.extend(a.indices.iter()); indices.extend(a.indices.iter());
indices indices
}; };
Node { hash, indices } Node {
hash: hash,
indices: indices,
}
} }
fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self { fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self {
@@ -84,7 +82,10 @@ impl Node {
indices.extend(b.indices.iter()); indices.extend(b.indices.iter());
indices.extend(a.indices.iter()); indices.extend(a.indices.iter());
} }
Node { hash, indices } Node {
hash: hash,
indices: indices,
}
} }
fn indices_before(&self, other: &Node) -> bool { fn indices_before(&self, other: &Node) -> bool {
@@ -98,18 +99,15 @@ impl Node {
} }
} }
fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2bState { fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2b {
let mut personalization: Vec<u8> = Vec::from("ZcashPoW"); let mut personalization: Vec<u8> = Vec::from("ZcashPoW");
personalization.write_u32::<LittleEndian>(n).unwrap(); personalization.write_u32::<LittleEndian>(n).unwrap();
personalization.write_u32::<LittleEndian>(k).unwrap(); personalization.write_u32::<LittleEndian>(k).unwrap();
Blake2bParams::new() Blake2b::with_params(digest_len as usize, &[], &[], &personalization)
.hash_length(digest_len as usize)
.personal(&personalization)
.to_state()
} }
fn generate_hash(base_state: &Blake2bState, i: u32) -> Blake2bHash { fn generate_hash(base_state: &Blake2b, i: u32) -> Blake2bResult {
let mut lei = [0u8; 4]; let mut lei = [0u8; 4];
(&mut lei[..]).write_u32::<LittleEndian>(i).unwrap(); (&mut lei[..]).write_u32::<LittleEndian>(i).unwrap();
@@ -140,7 +138,7 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec<u8> {
let mut j = 0; let mut j = 0;
for b in vin { for b in vin {
acc_value = (acc_value << 8) | u32::from(*b); acc_value = (acc_value << 8) | *b as u32;
acc_bits += 8; acc_bits += 8;
// When we have bit_len or more bits in the accumulator, write the next // When we have bit_len or more bits in the accumulator, write the next
@@ -151,7 +149,8 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec<u8> {
vout[j + x] = (( vout[j + x] = ((
// Big-endian // Big-endian
acc_value >> (acc_bits + (8 * (out_width - x - 1))) acc_value >> (acc_bits + (8 * (out_width - x - 1)))
) & ( )
& (
// Apply bit_len_mask across byte boundaries // Apply bit_len_mask across byte boundaries
(bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF (bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF
)) as u8; )) as u8;
@@ -196,18 +195,18 @@ fn distinct_indices(a: &Node, b: &Node) -> bool {
} }
} }
} }
true return true;
} }
fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> bool { fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> bool {
if !has_collision(a, b, p.collision_byte_length()) { if !has_collision(a, b, p.collision_byte_length()) {
error!("Invalid solution: invalid collision length between StepRows"); // error!("Invalid solution: invalid collision length between StepRows");
false false
} else if b.indices_before(a) { } else if b.indices_before(a) {
error!("Invalid solution: Index tree incorrectly ordered"); // error!("Invalid solution: Index tree incorrectly ordered");
false false
} else if !distinct_indices(a, b) { } else if !distinct_indices(a, b) {
error!("Invalid solution: duplicate indices"); // error!("Invalid solution: duplicate indices");
false false
} else { } else {
true true
@@ -221,7 +220,7 @@ pub fn is_valid_solution_iterative(
nonce: &[u8], nonce: &[u8],
indices: &[u32], indices: &[u32],
) -> bool { ) -> bool {
let p = Params { n, k }; let p = Params { n: n, k: k };
let mut state = initialise_state(p.n, p.k, p.hash_output()); let mut state = initialise_state(p.n, p.k, p.hash_output());
state.update(input); state.update(input);
@@ -248,25 +247,25 @@ pub fn is_valid_solution_iterative(
} }
assert!(rows.len() == 1); assert!(rows.len() == 1);
rows[0].is_zero(hash_len) return rows[0].is_zero(hash_len);
} }
fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Option<Node> { fn tree_validator(p: &Params, state: &Blake2b, indices: &[u32]) -> Option<Node> {
if indices.len() > 1 { if indices.len() > 1 {
let end = indices.len(); let end = indices.len();
let mid = end / 2; let mid = end / 2;
match ( match tree_validator(p, state, &indices[0..mid]) {
tree_validator(p, state, &indices[0..mid]), Some(a) => match tree_validator(p, state, &indices[mid..end]) {
tree_validator(p, state, &indices[mid..end]), Some(b) => {
) { if validate_subtrees(p, &a, &b) {
(Some(a), Some(b)) => { Some(Node::from_children(a, b, p.collision_byte_length()))
if validate_subtrees(p, &a, &b) { } else {
Some(Node::from_children(a, b, p.collision_byte_length())) None
} else { }
None
} }
} None => None,
_ => None, },
None => None,
} }
} else { } else {
Some(Node::new(&p, &state, indices[0])) Some(Node::new(&p, &state, indices[0]))
@@ -280,7 +279,7 @@ pub fn is_valid_solution_recursive(
nonce: &[u8], nonce: &[u8],
indices: &[u32], indices: &[u32],
) -> bool { ) -> bool {
let p = Params { n, k }; let p = Params { n: n, k: k };
let mut state = initialise_state(p.n, p.k, p.hash_output()); let mut state = initialise_state(p.n, p.k, p.hash_output());
state.update(input); state.update(input);
@@ -296,7 +295,7 @@ pub fn is_valid_solution_recursive(
} }
pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool { pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool {
let p = Params { n, k }; let p = Params { n: n, k: k };
let indices = indices_from_minimal(soln, p.collision_bit_length()); let indices = indices_from_minimal(soln, p.collision_bit_length());
// Recursive validation is faster // Recursive validation is faster

View File

@@ -1,18 +1,18 @@
use blake2b_simd::State; use blake2_rfc::blake2b::Blake2b;
use std::io::{self, Read}; use std::io::{self, Read};
/// Abstraction over a reader which hashes the data being read. /// Abstraction over a reader which hashes the data being read.
pub struct HashReader<R: Read> { pub struct HashReader<R: Read> {
reader: R, reader: R,
hasher: State, hasher: Blake2b,
} }
impl<R: Read> HashReader<R> { impl<R: Read> HashReader<R> {
/// Construct a new `HashReader` given an existing `reader` by value. /// Construct a new `HashReader` given an existing `reader` by value.
pub fn new(reader: R) -> Self { pub fn new(reader: R) -> Self {
HashReader { HashReader {
reader, reader: reader,
hasher: State::new(), hasher: Blake2b::new(64),
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::{OsRng, RngCore}; use pairing::{PrimeField, PrimeFieldRepr};
use zcash_primitives::jubjub::{edwards, JubjubBls12}; use rand::{OsRng, Rng};
use zcash_primitives::primitives::{Diversifier, ViewingKey}; use sapling_crypto::jubjub::{edwards, JubjubBls12};
use sapling_crypto::primitives::{Diversifier, ViewingKey};
use crate::{ use {
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree, librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
librustzcash_sapling_ka_derivepublic, librustzcash_sapling_ka_derivepublic,
}; };
@@ -12,7 +12,7 @@ use crate::{
#[test] #[test]
fn test_key_agreement() { fn test_key_agreement() {
let params = JubjubBls12::new(); let params = JubjubBls12::new();
let mut rng = OsRng; let mut rng = OsRng::new().unwrap();
// Create random viewing key // Create random viewing key
let vk = ViewingKey::<Bls12> { let vk = ViewingKey::<Bls12> {
@@ -22,9 +22,7 @@ fn test_key_agreement() {
// Create a random address with the viewing key // Create a random address with the viewing key
let addr = loop { let addr = loop {
let mut d = [0; 11]; match vk.into_payment_address(Diversifier(rng.gen()), &params) {
rng.fill_bytes(&mut d);
match vk.to_payment_address(Diversifier(d), &params) {
Some(a) => break a, Some(a) => break a,
None => {} None => {}
} }
@@ -46,7 +44,7 @@ fn test_key_agreement() {
// Serialize pk_d for the call to librustzcash_sapling_ka_agree // Serialize pk_d for the call to librustzcash_sapling_ka_agree
let mut addr_pk_d = [0u8; 32]; let mut addr_pk_d = [0u8; 32];
addr.pk_d().write(&mut addr_pk_d[..]).unwrap(); addr.pk_d.write(&mut addr_pk_d[..]).unwrap();
assert!(librustzcash_sapling_ka_agree( assert!(librustzcash_sapling_ka_agree(
&addr_pk_d, &addr_pk_d,
@@ -58,7 +56,7 @@ fn test_key_agreement() {
// using the diversifier and esk. // using the diversifier and esk.
let mut epk = [0u8; 32]; let mut epk = [0u8; 32];
assert!(librustzcash_sapling_ka_derivepublic( assert!(librustzcash_sapling_ka_derivepublic(
&addr.diversifier().0, &addr.diversifier.0,
&esk, &esk,
&mut epk &mut epk
)); ));

View File

@@ -1,13 +1,12 @@
use ff::{PrimeField, PrimeFieldRepr}; use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use sapling_crypto::{
use zcash_primitives::{
jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams}, jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams},
primitives::{Diversifier, ProofGenerationKey}, primitives::{Diversifier, ProofGenerationKey},
}; };
use super::JUBJUB; use super::JUBJUB;
use crate::{ use {
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk, librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk, librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
}; };
@@ -28,8 +27,6 @@ fn key_components() {
note_v: u64, note_v: u64,
note_r: [u8; 32], note_r: [u8; 32],
note_cm: [u8; 32], note_cm: [u8; 32],
note_pos: u64,
note_nf: [u8; 32],
}; };
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
@@ -89,12 +86,6 @@ fn key_components() {
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2, 0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
0xdd, 0x07, 0x64, 0x39, 0xdd, 0x07, 0x64, 0x39,
], ],
note_pos: 0,
note_nf: [
0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86,
0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27,
0x47, 0xab, 0x40, 0x63,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -151,12 +142,6 @@ fn key_components() {
0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72, 0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72,
0xca, 0xd4, 0x69, 0x50, 0xca, 0xd4, 0x69, 0x50,
], ],
note_pos: 763714296,
note_nf: [
0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2,
0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17,
0x4c, 0x2e, 0x9d, 0x93,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -213,12 +198,6 @@ fn key_components() {
0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f, 0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f,
0x4e, 0x55, 0xf1, 0x51, 0x4e, 0x55, 0xf1, 0x51,
], ],
note_pos: 1527428592,
note_nf: [
0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f,
0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f,
0x59, 0xae, 0x1d, 0x14,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -275,12 +254,6 @@ fn key_components() {
0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa, 0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa,
0x60, 0xd1, 0x9b, 0x6c, 0x60, 0xd1, 0x9b, 0x6c,
], ],
note_pos: 2291142888,
note_nf: [
0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56,
0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75,
0x8b, 0xec, 0x47, 0xa1,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -337,12 +310,6 @@ fn key_components() {
0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b, 0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b,
0xc7, 0x1b, 0x7f, 0x36, 0xc7, 0x1b, 0x7f, 0x36,
], ],
note_pos: 3054857184,
note_nf: [
0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f,
0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03,
0x01, 0xbf, 0x3d, 0x13,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -399,12 +366,6 @@ fn key_components() {
0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69, 0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69,
0xde, 0x1a, 0x5b, 0x4c, 0xde, 0x1a, 0x5b, 0x4c,
], ],
note_pos: 3818571480,
note_nf: [
0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2,
0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20,
0xb6, 0xc9, 0xa7, 0xe1,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -461,12 +422,6 @@ fn key_components() {
0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b, 0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b,
0x78, 0x3a, 0x1e, 0x55, 0x78, 0x3a, 0x1e, 0x55,
], ],
note_pos: 287318480,
note_nf: [
0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae,
0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4,
0x07, 0xd5, 0x1e, 0x11,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -523,12 +478,6 @@ fn key_components() {
0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36, 0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36,
0xf7, 0x8c, 0x2b, 0x23, 0xf7, 0x8c, 0x2b, 0x23,
], ],
note_pos: 1051032776,
note_nf: [
0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f,
0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc,
0x8b, 0xb6, 0x98, 0x90,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -585,12 +534,6 @@ fn key_components() {
0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07, 0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07,
0x8f, 0xea, 0x4d, 0x04, 0x8f, 0xea, 0x4d, 0x04,
], ],
note_pos: 1814747072,
note_nf: [
0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20,
0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe,
0xb1, 0x7c, 0xd6, 0x20,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -647,12 +590,6 @@ fn key_components() {
0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63, 0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63,
0xe4, 0x1d, 0xeb, 0x37, 0xe4, 0x1d, 0xeb, 0x37,
], ],
note_pos: 2578461368,
note_nf: [
0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe,
0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71,
0x0d, 0x93, 0xc9, 0xe9,
],
}, },
]; ];
@@ -678,7 +615,7 @@ fn key_components() {
} }
let pgk = ProofGenerationKey { ak, nsk }; let pgk = ProofGenerationKey { ak, nsk };
let fvk = pgk.to_viewing_key(&JUBJUB); let fvk = pgk.into_viewing_key(&JUBJUB);
{ {
let mut vec = Vec::new(); let mut vec = Vec::new();
fvk.nk.write(&mut vec).unwrap(); fvk.nk.write(&mut vec).unwrap();
@@ -704,10 +641,10 @@ fn key_components() {
let diversifier = Diversifier(tv.default_d); let diversifier = Diversifier(tv.default_d);
assert!(librustzcash_check_diversifier(&tv.default_d)); assert!(librustzcash_check_diversifier(&tv.default_d));
let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap(); let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap();
{ {
let mut vec = Vec::new(); let mut vec = Vec::new();
addr.pk_d().write(&mut vec).unwrap(); addr.pk_d.write(&mut vec).unwrap();
assert_eq!(&vec, &tv.default_pk_d); assert_eq!(&vec, &tv.default_pk_d);
} }
{ {
@@ -725,7 +662,5 @@ fn key_components() {
note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap(); note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap();
assert_eq!(&vec, &tv.note_cm); assert_eq!(&vec, &tv.note_cm);
} }
assert_eq!(note.nf(&fvk, tv.note_pos, &JUBJUB), tv.note_nf);
} }
} }

View File

@@ -1,225 +0,0 @@
use zcash_history::{Entry, EntryLink, NodeData};
use crate::{librustzcash_mmr_append, librustzcash_mmr_delete};
const NODE_DATA_16L: &[u8] = include_bytes!("./res/tree16.dat");
const NODE_DATA_1023L: &[u8] = include_bytes!("./res/tree1023.dat");
struct TreeView {
peaks: Vec<(u32, Entry)>,
extra: Vec<(u32, Entry)>,
}
fn draft(into: &mut Vec<(u32, Entry)>, nodes: &[NodeData], peak_pos: usize, h: u32) {
let node_data = nodes[peak_pos - 1].clone();
let peak: Entry = match h {
0 => node_data.into(),
_ => Entry::new(
node_data,
EntryLink::Stored((peak_pos - (1 << h) - 1) as u32),
EntryLink::Stored((peak_pos - 2) as u32),
),
};
into.push(((peak_pos - 1) as u32, peak));
}
fn prepare_tree(nodes: &[NodeData]) -> TreeView {
assert!(!nodes.is_empty());
// integer log2 of (nodes.len()+1), -1
let mut h = (32 - ((nodes.len() + 1) as u32).leading_zeros() - 1) - 1;
let mut peak_pos = (1 << (h + 1)) - 1;
let mut peaks = Vec::new();
// used later
let mut last_peak_pos = 0;
let mut last_peak_h = 0;
loop {
if peak_pos > nodes.len() {
// left child, -2^h
peak_pos -= 1 << h;
h -= 1;
}
if peak_pos <= nodes.len() {
draft(&mut peaks, nodes, peak_pos, h);
// save to be used in next loop
last_peak_pos = peak_pos;
last_peak_h = h;
// right sibling
peak_pos += (1 << (h + 1)) - 1;
}
if h == 0 {
break;
}
}
// for deletion, everything on the right slope of the last peak should be pre-loaded
let mut extra = Vec::new();
let mut h = last_peak_h;
let mut peak_pos = last_peak_pos;
while h > 0 {
let left_pos = peak_pos - (1 << h);
let right_pos = peak_pos - 1;
h -= 1;
// drafting left child
draft(&mut extra, nodes, left_pos, h);
// drafting right child
draft(&mut extra, nodes, right_pos, h);
// continuing on right slope
peak_pos = right_pos;
}
TreeView { peaks, extra }
}
fn preload_tree_append(nodes: &[NodeData]) -> (Vec<u32>, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>) {
assert!(!nodes.is_empty());
let tree_view = prepare_tree(nodes);
let mut indices = Vec::new();
let mut bytes = Vec::new();
for (idx, entry) in tree_view.peaks.into_iter() {
let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE];
entry
.write(&mut &mut buf[..])
.expect("Cannot fail if enough buffer length");
indices.push(idx);
bytes.push(buf);
}
(indices, bytes)
}
// also returns number of peaks
fn preload_tree_delete(
nodes: &[NodeData],
) -> (Vec<u32>, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>, usize) {
assert!(!nodes.is_empty());
let tree_view = prepare_tree(nodes);
let mut indices = Vec::new();
let mut bytes = Vec::new();
let peak_count = tree_view.peaks.len();
for (idx, entry) in tree_view
.peaks
.into_iter()
.chain(tree_view.extra.into_iter())
{
let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE];
entry
.write(&mut &mut buf[..])
.expect("Cannot fail if enough buffer length");
indices.push(idx);
bytes.push(buf);
}
(indices, bytes, peak_count)
}
fn load_nodes(bytes: &'static [u8]) -> Vec<NodeData> {
let mut res = Vec::new();
let mut cursor = std::io::Cursor::new(bytes);
while (cursor.position() as usize) < bytes.len() {
let node_data = zcash_history::NodeData::read(0, &mut cursor)
.expect("Statically checked to be correct");
res.push(node_data);
}
res
}
#[test]
fn append() {
let nodes = load_nodes(NODE_DATA_16L);
let (indices, peaks) = preload_tree_append(&nodes);
let mut rt_ret = [0u8; 32];
let mut buf_ret = Vec::<[u8; zcash_history::MAX_NODE_DATA_SIZE]>::with_capacity(32);
let mut new_node_data = [0u8; zcash_history::MAX_NODE_DATA_SIZE];
let new_node = NodeData {
consensus_branch_id: 0,
subtree_commitment: [0u8; 32],
start_time: 101,
end_time: 110,
start_target: 190,
end_target: 200,
start_sapling_root: [0u8; 32],
end_sapling_root: [0u8; 32],
subtree_total_work: Default::default(),
start_height: 10,
end_height: 10,
sapling_tx: 13,
};
new_node
.write(&mut &mut new_node_data[..])
.expect("Failed to write node data");
let result = librustzcash_mmr_append(
0,
nodes.len() as u32,
indices.as_ptr(),
peaks.as_ptr(),
peaks.len(),
&new_node_data,
&mut rt_ret,
buf_ret.as_mut_ptr(),
);
unsafe {
buf_ret.set_len(result as usize);
}
assert_eq!(result, 2);
let new_node_1 =
NodeData::from_bytes(0, &buf_ret[0][..]).expect("Failed to reconstruct return node #1");
let new_node_2 =
NodeData::from_bytes(0, &buf_ret[1][..]).expect("Failed to reconstruct return node #2");
assert_eq!(new_node_1.start_height, 10);
assert_eq!(new_node_1.end_height, 10);
// this is combined new node (which is `new_node_1`) + the one which was there before (for block #9)
assert_eq!(new_node_2.start_height, 9);
assert_eq!(new_node_2.end_height, 10);
assert_eq!(new_node_2.sapling_tx, 27);
}
#[test]
fn delete() {
let nodes = load_nodes(NODE_DATA_1023L);
let (indices, nodes, peak_count) = preload_tree_delete(&nodes);
let mut rt_ret = [0u8; 32];
let result = librustzcash_mmr_delete(
0,
nodes.len() as u32,
indices.as_ptr(),
nodes.as_ptr(),
peak_count,
indices.len() - peak_count,
&mut rt_ret,
);
// Deleting from full tree of 9 height would result in cascade deleting of 10 nodes
assert_eq!(result, 10);
}

View File

@@ -1,10 +1,9 @@
use zcash_primitives::jubjub::{FixedGenerators, JubjubParams}; use sapling_crypto::jubjub::{FixedGenerators, JubjubParams};
use super::JUBJUB; use super::JUBJUB;
mod key_agreement; mod key_agreement;
mod key_components; mod key_components;
mod mmr;
mod notes; mod notes;
mod signatures; mod signatures;

View File

@@ -1,5 +1,5 @@
use crate::librustzcash_sapling_compute_cm; use librustzcash_sapling_compute_cm;
use crate::librustzcash_sapling_compute_nf; use librustzcash_sapling_compute_nf;
#[test] #[test]
fn notes() { fn notes() {

View File

@@ -1,7 +1,8 @@
use ff::{PrimeField, PrimeFieldRepr}; use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use sapling_crypto::{
use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine}; jubjub::{FixedGenerators, JubjubEngine},
use zcash_primitives::redjubjub::{PrivateKey, PublicKey, Signature}; redjubjub::{PrivateKey, PublicKey, Signature},
};
use super::JUBJUB; use super::JUBJUB;

View File

@@ -2,33 +2,22 @@
name = "pairing" name = "pairing"
# Remember to change version string in README.md. # Remember to change version string in README.md.
version = "0.15.1" version = "0.14.2"
authors = [ authors = ["Sean Bowe <ewillbefull@gmail.com>"]
"Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>",
]
readme = "README.md"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
description = "Pairing-friendly elliptic curve library" description = "Pairing-friendly elliptic curve library"
documentation = "https://docs.rs/pairing/" documentation = "https://docs.rs/pairing/"
homepage = "https://github.com/ebfull/pairing" homepage = "https://github.com/ebfull/pairing"
repository = "https://github.com/ebfull/pairing" repository = "https://github.com/ebfull/pairing"
edition ="2018"
[dependencies] [dependencies]
rand = "0.4"
byteorder = "1" byteorder = "1"
ff = { version = "^0.5.2", path = "../ff", features = ["derive"] } clippy = { version = "0.0.200", optional = true }
group = { version = "0.2.0", path = "../group" }
rand_core = "0.5"
[dev-dependencies]
rand_xorshift = "0.2"
[features] [features]
unstable-features = ["expose-arith"] unstable-features = ["expose-arith"]
expose-arith = [] expose-arith = []
u128-support = []
default = [] default = []
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -1,32 +1,28 @@
# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) # # pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) #
`pairing` is a crate for using pairing-friendly elliptic curves. This is a Rust crate for using pairing-friendly elliptic curves. Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) construction is implemented.
Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html)
construction is implemented.
## Roadmap
`pairing` is being refactored into a generic library for working with
pairing-friendly curves. After the refactor, `pairing` will provide basic traits
for pairing-friendly elliptic curve constructions, while specific curves will be
in separate crates.
## [Documentation](https://docs.rs/pairing/) ## [Documentation](https://docs.rs/pairing/)
Bring the `pairing` crate into your project just as you normally would. Bring the `pairing` crate into your project just as you normally would.
If you're using a supported platform and the nightly Rust compiler, you can enable the `u128-support` feature for faster arithmetic.
```toml
[dependencies.pairing]
version = "0.14"
features = ["u128-support"]
```
## Security Warnings ## Security Warnings
This library does not make any guarantees about constant-time operations, memory This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks.
access patterns, or resistance to side-channel attacks.
## License ## License
Licensed under either of Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option. at your option.

View File

@@ -1,22 +1,17 @@
mod g1 { mod g1 {
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
use ff::Field;
use group::CurveProjective;
use pairing::bls12_381::*; use pairing::bls12_381::*;
use pairing::CurveProjective;
#[bench] #[bench]
fn bench_g1_mul_assign(b: &mut ::test::Bencher) { fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let v: Vec<(G1, Fr)> = (0..SAMPLES) let v: Vec<(G1, Fr)> = (0..SAMPLES)
.map(|_| (G1::random(&mut rng), Fr::random(&mut rng))) .map(|_| (G1::rand(&mut rng), Fr::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -32,13 +27,10 @@ mod g1 {
fn bench_g1_add_assign(b: &mut ::test::Bencher) { fn bench_g1_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let v: Vec<(G1, G1)> = (0..SAMPLES) let v: Vec<(G1, G1)> = (0..SAMPLES)
.map(|_| (G1::random(&mut rng), G1::random(&mut rng))) .map(|_| (G1::rand(&mut rng), G1::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -54,13 +46,10 @@ mod g1 {
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) { fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let v: Vec<(G1, G1Affine)> = (0..SAMPLES) let v: Vec<(G1, G1Affine)> = (0..SAMPLES)
.map(|_| (G1::random(&mut rng), G1::random(&mut rng).into())) .map(|_| (G1::rand(&mut rng), G1::rand(&mut rng).into()))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -74,24 +63,19 @@ mod g1 {
} }
mod g2 { mod g2 {
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
use ff::Field;
use group::CurveProjective;
use pairing::bls12_381::*; use pairing::bls12_381::*;
use pairing::CurveProjective;
#[bench] #[bench]
fn bench_g2_mul_assign(b: &mut ::test::Bencher) { fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let v: Vec<(G2, Fr)> = (0..SAMPLES) let v: Vec<(G2, Fr)> = (0..SAMPLES)
.map(|_| (G2::random(&mut rng), Fr::random(&mut rng))) .map(|_| (G2::rand(&mut rng), Fr::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -107,13 +91,10 @@ mod g2 {
fn bench_g2_add_assign(b: &mut ::test::Bencher) { fn bench_g2_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let v: Vec<(G2, G2)> = (0..SAMPLES) let v: Vec<(G2, G2)> = (0..SAMPLES)
.map(|_| (G2::random(&mut rng), G2::random(&mut rng))) .map(|_| (G2::rand(&mut rng), G2::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -129,13 +110,10 @@ mod g2 {
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) { fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let v: Vec<(G2, G2Affine)> = (0..SAMPLES) let v: Vec<(G2, G2Affine)> = (0..SAMPLES)
.map(|_| (G2::random(&mut rng), G2::random(&mut rng).into())) .map(|_| (G2::rand(&mut rng), G2::rand(&mut rng).into()))
.collect(); .collect();
let mut count = 0; let mut count = 0;

View File

@@ -1,22 +1,18 @@
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
use pairing::bls12_381::*; use pairing::bls12_381::*;
use pairing::{Field, PrimeField, PrimeFieldRepr, SqrtField};
#[bench] #[bench]
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) { fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
.map(|_| { .map(|_| {
let mut tmp1 = Fq::random(&mut rng).into_repr(); let mut tmp1 = FqRepr::rand(&mut rng);
let mut tmp2 = Fq::random(&mut rng).into_repr(); let mut tmp2 = FqRepr::rand(&mut rng);
// Shave a few bits off to avoid overflow. // Shave a few bits off to avoid overflow.
for _ in 0..3 { for _ in 0..3 {
tmp1.div2(); tmp1.div2();
@@ -39,14 +35,11 @@ fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) { fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
.map(|_| { .map(|_| {
let tmp1 = Fq::random(&mut rng).into_repr(); let tmp1 = FqRepr::rand(&mut rng);
let mut tmp2 = tmp1; let mut tmp2 = tmp1;
// Ensure tmp2 is smaller than tmp1. // Ensure tmp2 is smaller than tmp1.
for _ in 0..10 { for _ in 0..10 {
@@ -69,14 +62,9 @@ fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) { fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<FqRepr> = (0..SAMPLES) let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
.map(|_| Fq::random(&mut rng).into_repr())
.collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -90,14 +78,9 @@ fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) { fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<FqRepr> = (0..SAMPLES) let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
.map(|_| Fq::random(&mut rng).into_repr())
.collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -112,14 +95,9 @@ fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
fn bench_fq_repr_div2(b: &mut ::test::Bencher) { fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<FqRepr> = (0..SAMPLES) let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
.map(|_| Fq::random(&mut rng).into_repr())
.collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -134,13 +112,10 @@ fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
fn bench_fq_add_assign(b: &mut ::test::Bencher) { fn bench_fq_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fq, Fq)> = (0..SAMPLES) let v: Vec<(Fq, Fq)> = (0..SAMPLES)
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -156,13 +131,10 @@ fn bench_fq_add_assign(b: &mut ::test::Bencher) {
fn bench_fq_sub_assign(b: &mut ::test::Bencher) { fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fq, Fq)> = (0..SAMPLES) let v: Vec<(Fq, Fq)> = (0..SAMPLES)
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -178,13 +150,10 @@ fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
fn bench_fq_mul_assign(b: &mut ::test::Bencher) { fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fq, Fq)> = (0..SAMPLES) let v: Vec<(Fq, Fq)> = (0..SAMPLES)
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -200,12 +169,9 @@ fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
fn bench_fq_square(b: &mut ::test::Bencher) { fn bench_fq_square(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -220,12 +186,9 @@ fn bench_fq_square(b: &mut ::test::Bencher) {
fn bench_fq_inverse(b: &mut ::test::Bencher) { fn bench_fq_inverse(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -238,12 +201,9 @@ fn bench_fq_inverse(b: &mut ::test::Bencher) {
fn bench_fq_negate(b: &mut ::test::Bencher) { fn bench_fq_negate(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -258,14 +218,11 @@ fn bench_fq_negate(b: &mut ::test::Bencher) {
fn bench_fq_sqrt(b: &mut ::test::Bencher) { fn bench_fq_sqrt(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq> = (0..SAMPLES) let v: Vec<Fq> = (0..SAMPLES)
.map(|_| { .map(|_| {
let mut tmp = Fq::random(&mut rng); let mut tmp = Fq::rand(&mut rng);
tmp.square(); tmp.square();
tmp tmp
}) })
@@ -282,12 +239,9 @@ fn bench_fq_sqrt(b: &mut ::test::Bencher) {
fn bench_fq_into_repr(b: &mut ::test::Bencher) { fn bench_fq_into_repr(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -300,13 +254,10 @@ fn bench_fq_into_repr(b: &mut ::test::Bencher) {
fn bench_fq_from_repr(b: &mut ::test::Bencher) { fn bench_fq_from_repr(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<FqRepr> = (0..SAMPLES) let v: Vec<FqRepr> = (0..SAMPLES)
.map(|_| Fq::random(&mut rng).into_repr()) .map(|_| Fq::rand(&mut rng).into_repr())
.collect(); .collect();
let mut count = 0; let mut count = 0;

View File

@@ -1,20 +1,16 @@
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
use ff::Field;
use pairing::bls12_381::*; use pairing::bls12_381::*;
use pairing::Field;
#[bench] #[bench]
fn bench_fq12_add_assign(b: &mut ::test::Bencher) { fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -30,13 +26,10 @@ fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) { fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -52,13 +45,10 @@ fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) { fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -74,12 +64,9 @@ fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
fn bench_fq12_squaring(b: &mut ::test::Bencher) { fn bench_fq12_squaring(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -94,12 +81,9 @@ fn bench_fq12_squaring(b: &mut ::test::Bencher) {
fn bench_fq12_inverse(b: &mut ::test::Bencher) { fn bench_fq12_inverse(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {

View File

@@ -1,20 +1,16 @@
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
use ff::{Field, SqrtField};
use pairing::bls12_381::*; use pairing::bls12_381::*;
use pairing::{Field, SqrtField};
#[bench] #[bench]
fn bench_fq2_add_assign(b: &mut ::test::Bencher) { fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -30,13 +26,10 @@ fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) { fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -52,13 +45,10 @@ fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) { fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -74,12 +64,9 @@ fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
fn bench_fq2_squaring(b: &mut ::test::Bencher) { fn bench_fq2_squaring(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -94,12 +81,9 @@ fn bench_fq2_squaring(b: &mut ::test::Bencher) {
fn bench_fq2_inverse(b: &mut ::test::Bencher) { fn bench_fq2_inverse(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -113,12 +97,9 @@ fn bench_fq2_inverse(b: &mut ::test::Bencher) {
fn bench_fq2_sqrt(b: &mut ::test::Bencher) { fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {

View File

@@ -1,22 +1,18 @@
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
use pairing::bls12_381::*; use pairing::bls12_381::*;
use pairing::{Field, PrimeField, PrimeFieldRepr, SqrtField};
#[bench] #[bench]
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) { fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
.map(|_| { .map(|_| {
let mut tmp1 = Fr::random(&mut rng).into_repr(); let mut tmp1 = FrRepr::rand(&mut rng);
let mut tmp2 = Fr::random(&mut rng).into_repr(); let mut tmp2 = FrRepr::rand(&mut rng);
// Shave a few bits off to avoid overflow. // Shave a few bits off to avoid overflow.
for _ in 0..3 { for _ in 0..3 {
tmp1.div2(); tmp1.div2();
@@ -39,14 +35,11 @@ fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) { fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
.map(|_| { .map(|_| {
let tmp1 = Fr::random(&mut rng).into_repr(); let tmp1 = FrRepr::rand(&mut rng);
let mut tmp2 = tmp1; let mut tmp2 = tmp1;
// Ensure tmp2 is smaller than tmp1. // Ensure tmp2 is smaller than tmp1.
for _ in 0..10 { for _ in 0..10 {
@@ -69,14 +62,9 @@ fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) { fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<FrRepr> = (0..SAMPLES) let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
.map(|_| Fr::random(&mut rng).into_repr())
.collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -90,14 +78,9 @@ fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) { fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<FrRepr> = (0..SAMPLES) let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
.map(|_| Fr::random(&mut rng).into_repr())
.collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -112,14 +95,9 @@ fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
fn bench_fr_repr_div2(b: &mut ::test::Bencher) { fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<FrRepr> = (0..SAMPLES) let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
.map(|_| Fr::random(&mut rng).into_repr())
.collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -134,13 +112,10 @@ fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
fn bench_fr_add_assign(b: &mut ::test::Bencher) { fn bench_fr_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fr, Fr)> = (0..SAMPLES) let v: Vec<(Fr, Fr)> = (0..SAMPLES)
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -156,13 +131,10 @@ fn bench_fr_add_assign(b: &mut ::test::Bencher) {
fn bench_fr_sub_assign(b: &mut ::test::Bencher) { fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fr, Fr)> = (0..SAMPLES) let v: Vec<(Fr, Fr)> = (0..SAMPLES)
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -178,13 +150,10 @@ fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
fn bench_fr_mul_assign(b: &mut ::test::Bencher) { fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(Fr, Fr)> = (0..SAMPLES) let v: Vec<(Fr, Fr)> = (0..SAMPLES)
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;
@@ -200,12 +169,9 @@ fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
fn bench_fr_square(b: &mut ::test::Bencher) { fn bench_fr_square(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -220,12 +186,9 @@ fn bench_fr_square(b: &mut ::test::Bencher) {
fn bench_fr_inverse(b: &mut ::test::Bencher) { fn bench_fr_inverse(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -238,12 +201,9 @@ fn bench_fr_inverse(b: &mut ::test::Bencher) {
fn bench_fr_negate(b: &mut ::test::Bencher) { fn bench_fr_negate(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -258,14 +218,11 @@ fn bench_fr_negate(b: &mut ::test::Bencher) {
fn bench_fr_sqrt(b: &mut ::test::Bencher) { fn bench_fr_sqrt(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fr> = (0..SAMPLES) let v: Vec<Fr> = (0..SAMPLES)
.map(|_| { .map(|_| {
let mut tmp = Fr::random(&mut rng); let mut tmp = Fr::rand(&mut rng);
tmp.square(); tmp.square();
tmp tmp
}) })
@@ -282,12 +239,9 @@ fn bench_fr_sqrt(b: &mut ::test::Bencher) {
fn bench_fr_into_repr(b: &mut ::test::Bencher) { fn bench_fr_into_repr(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -300,13 +254,10 @@ fn bench_fr_into_repr(b: &mut ::test::Bencher) {
fn bench_fr_from_repr(b: &mut ::test::Bencher) { fn bench_fr_from_repr(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<FrRepr> = (0..SAMPLES) let v: Vec<FrRepr> = (0..SAMPLES)
.map(|_| Fr::random(&mut rng).into_repr()) .map(|_| Fr::rand(&mut rng).into_repr())
.collect(); .collect();
let mut count = 0; let mut count = 0;

View File

@@ -4,23 +4,18 @@ mod fq12;
mod fq2; mod fq2;
mod fr; mod fr;
use rand_core::SeedableRng; use rand::{Rand, SeedableRng, XorShiftRng};
use rand_xorshift::XorShiftRng;
use group::CurveProjective;
use pairing::bls12_381::*; use pairing::bls12_381::*;
use pairing::{Engine, PairingCurveAffine}; use pairing::{CurveAffine, Engine};
#[bench] #[bench]
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) { fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect(); let v: Vec<G1> = (0..SAMPLES).map(|_| G1::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -34,12 +29,9 @@ fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) { fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect(); let v: Vec<G2> = (0..SAMPLES).map(|_| G2::rand(&mut rng)).collect();
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
@@ -53,16 +45,13 @@ fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) { fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES) let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES)
.map(|_| { .map(|_| {
( (
G1Affine::from(G1::random(&mut rng)).prepare(), G1Affine::from(G1::rand(&mut rng)).prepare(),
G2Affine::from(G2::random(&mut rng)).prepare(), G2Affine::from(G2::rand(&mut rng)).prepare(),
) )
}) })
.collect(); .collect();
@@ -79,16 +68,13 @@ fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) { fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<Fq12> = (0..SAMPLES) let v: Vec<Fq12> = (0..SAMPLES)
.map(|_| { .map(|_| {
( (
G1Affine::from(G1::random(&mut rng)).prepare(), G1Affine::from(G1::rand(&mut rng)).prepare(),
G2Affine::from(G2::random(&mut rng)).prepare(), G2Affine::from(G2::rand(&mut rng)).prepare(),
) )
}) })
.map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)])) .map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)]))
@@ -106,13 +92,10 @@ fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
fn bench_pairing_full(b: &mut ::test::Bencher) { fn bench_pairing_full(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let v: Vec<(G1, G2)> = (0..SAMPLES) let v: Vec<(G1, G2)> = (0..SAMPLES)
.map(|_| (G1::random(&mut rng), G2::random(&mut rng))) .map(|_| (G1::rand(&mut rng), G2::rand(&mut rng)))
.collect(); .collect();
let mut count = 0; let mut count = 0;

View File

@@ -1,10 +1,7 @@
#![feature(test)] #![feature(test)]
extern crate ff;
extern crate group;
extern crate pairing; extern crate pairing;
extern crate rand_core; extern crate rand;
extern crate rand_xorshift;
extern crate test; extern crate test;
mod bls12_381; mod bls12_381;

View File

@@ -14,11 +14,12 @@ 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)
} else { } else {
@@ -29,13 +30,14 @@ 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())
} }
} }
@@ -87,9 +89,7 @@ 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 { if i { res.add_assign_mixed(self) }
res.add_assign_mixed(self)
}
} }
res res
} }
@@ -112,8 +112,12 @@ macro_rules! curve_impl {
$affine { $affine {
x: x, x: x,
y: if (y < negy) ^ greatest { y } else { negy }, y: if (y < negy) ^ greatest {
infinity: false, y
} else {
negy
},
infinity: false
} }
}) })
} }
@@ -144,15 +148,18 @@ macro_rules! curve_impl {
type Engine = Bls12; type Engine = Bls12;
type Scalar = $scalarfield; type Scalar = $scalarfield;
type Base = $basefield; type Base = $basefield;
type Prepared = $prepared;
type Projective = $projective; type Projective = $projective;
type Uncompressed = $uncompressed; type Uncompressed = $uncompressed;
type Compressed = $compressed; type Compressed = $compressed;
type Pair = $pairing;
type PairingResult = Fq12;
fn zero() -> Self { fn zero() -> Self {
$affine { $affine {
x: $basefield::zero(), x: $basefield::zero(),
y: $basefield::one(), y: $basefield::one(),
infinity: true, infinity: true
} }
} }
@@ -175,16 +182,6 @@ macro_rules! curve_impl {
} }
} }
fn into_projective(&self) -> $projective {
(*self).into()
}
}
impl PairingCurveAffine for $affine {
type Prepared = $prepared;
type Pair = $pairing;
type PairingResult = Fq12;
fn prepare(&self) -> Self::Prepared { fn prepare(&self) -> Self::Prepared {
$prepared::from_affine(*self) $prepared::from_affine(*self)
} }
@@ -192,18 +189,18 @@ 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)
} }
fn into_projective(&self) -> $projective {
(*self).into()
}
} }
impl CurveProjective for $projective { impl Rand for $projective {
type Engine = Bls12; fn rand<R: Rng>(rng: &mut R) -> Self {
type Scalar = $scalarfield;
type Base = $basefield;
type Affine = $affine;
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
loop { loop {
let x = $basefield::random(rng); let x = rng.gen();
let greatest = rng.next_u32() % 2 != 0; let greatest = rng.gen();
if let Some(p) = $affine::get_point_from_x(x, greatest) { if let Some(p) = $affine::get_point_from_x(x, greatest) {
let p = p.scale_by_cofactor(); let p = p.scale_by_cofactor();
@@ -214,6 +211,13 @@ macro_rules! curve_impl {
} }
} }
} }
}
impl CurveProjective for $projective {
type Engine = Bls12;
type Scalar = $scalarfield;
type Base = $basefield;
type Affine = $affine;
// The point at infinity is always represented by // The point at infinity is always represented by
// Z = 0. // Z = 0.
@@ -221,7 +225,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()
} }
} }
@@ -239,7 +243,8 @@ 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
@@ -247,10 +252,9 @@ 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 for g in v.iter_mut()
.iter_mut() // Ignore normalized elements
// Ignore normalized elements .filter(|g| !g.is_normalized())
.filter(|g| !g.is_normalized())
{ {
tmp.mul_assign(&g.z); tmp.mul_assign(&g.z);
prod.push(tmp); prod.push(tmp);
@@ -260,19 +264,13 @@ 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 for (g, s) in v.iter_mut()
.iter_mut() // Backwards
// Backwards .rev()
.rev() // Ignore normalized elements
// Ignore normalized elements .filter(|g| !g.is_normalized())
.filter(|g| !g.is_normalized()) // Backwards, skip last element, fill in one for last term.
// Backwards, skip last element, fill in one for last term. .zip(prod.into_iter().rev().skip(1).chain(Some($basefield::one())))
.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,7 +281,9 @@ macro_rules! curve_impl {
} }
// Perform affine transformations // Perform affine transformations
for g in v.iter_mut().filter(|g| !g.is_normalized()) { for g in v.iter_mut()
.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
@@ -536,7 +536,8 @@ 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 {
@@ -574,7 +575,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()
} }
} }
} }
@@ -591,7 +592,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.
@@ -611,22 +612,23 @@ macro_rules! curve_impl {
$affine { $affine {
x: x, x: x,
y: y, y: y,
infinity: false, infinity: false
} }
} }
} }
} }
}; }
} }
pub mod g1 { pub mod g1 {
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr}; use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
use super::g2::G2Affine; use super::g2::G2Affine;
use crate::{Engine, PairingCurveAffine}; use rand::{Rand, Rng};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand_core::RngCore;
use std::fmt; use std::fmt;
use {
BitIterator, CurveAffine, CurveProjective, EncodedPoint, Engine, Field, GroupDecodingError,
PrimeField, PrimeFieldRepr, SqrtField,
};
curve_impl!( curve_impl!(
"G1", "G1",
@@ -656,7 +658,7 @@ pub mod g1 {
} }
impl fmt::Debug for G1Uncompressed { impl fmt::Debug for G1Uncompressed {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -766,7 +768,7 @@ pub mod g1 {
} }
impl fmt::Debug for G1Compressed { impl fmt::Debug for G1Compressed {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -934,7 +936,7 @@ pub mod g1 {
#[test] #[test]
fn g1_generator() { fn g1_generator() {
use crate::SqrtField; use SqrtField;
let mut x = Fq::zero(); let mut x = Fq::zero();
let mut i = 0; let mut i = 0;
@@ -952,7 +954,7 @@ pub mod g1 {
let negyrepr = negy.into_repr(); let negyrepr = negy.into_repr();
let p = G1Affine { let p = G1Affine {
x, x: x,
y: if yrepr < negyrepr { y } else { negy }, y: if yrepr < negyrepr { y } else { negy },
infinity: false, infinity: false,
}; };
@@ -987,8 +989,7 @@ 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,
@@ -996,8 +997,7 @@ 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());
@@ -1014,8 +1014,7 @@ 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,8 +1022,7 @@ 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());
@@ -1042,8 +1040,7 @@ 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,
@@ -1051,8 +1048,7 @@ 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());
@@ -1070,8 +1066,7 @@ 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,
@@ -1079,8 +1074,7 @@ pub mod g1 {
0xcefca68333c35288, 0xcefca68333c35288,
0x570c8005f8573fa6, 0x570c8005f8573fa6,
0x152ca696fe034442, 0x152ca696fe034442,
])) ])).unwrap(),
.unwrap(),
z: Fq::one(), z: Fq::one(),
}; };
@@ -1092,8 +1086,7 @@ 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,
@@ -1101,8 +1094,7 @@ pub mod g1 {
0xd65104c6f95a872a, 0xd65104c6f95a872a,
0x1f2998a5a9c61253, 0x1f2998a5a9c61253,
0xe74846154a9e44, 0xe74846154a9e44,
])) ])).unwrap(),
.unwrap(),
z: Fq::one(), z: Fq::one(),
}); });
@@ -1118,8 +1110,7 @@ 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,
@@ -1127,8 +1118,7 @@ pub mod g1 {
0xeec8d1a5b7466c58, 0xeec8d1a5b7466c58,
0x4bc362649dce6376, 0x4bc362649dce6376,
0x430cbdc5455b00a 0x430cbdc5455b00a
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
} }
); );
@@ -1144,8 +1134,7 @@ 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,
@@ -1153,8 +1142,7 @@ pub mod g1 {
0xcefca68333c35288, 0xcefca68333c35288,
0x570c8005f8573fa6, 0x570c8005f8573fa6,
0x152ca696fe034442, 0x152ca696fe034442,
])) ])).unwrap(),
.unwrap(),
z: Fq::one(), z: Fq::one(),
}; };
@@ -1172,8 +1160,7 @@ 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,
@@ -1181,8 +1168,7 @@ pub mod g1 {
0x1771a65b60572f4e, 0x1771a65b60572f4e,
0x8b547c1313b27555, 0x8b547c1313b27555,
0x135075589a687b1e 0x135075589a687b1e
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
} }
); );
@@ -1205,8 +1191,7 @@ 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,
@@ -1214,8 +1199,7 @@ pub mod g1 {
0x520f74773e74c8c3, 0x520f74773e74c8c3,
0x484c8fc982008f0, 0x484c8fc982008f0,
0xee2c3d922008cc6, 0xee2c3d922008cc6,
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
}; };
@@ -1227,8 +1211,7 @@ 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,
@@ -1236,8 +1219,7 @@ pub mod g1 {
0x520f74773e74c8c3, 0x520f74773e74c8c3,
0x484c8fc982008f0, 0x484c8fc982008f0,
0xee2c3d922008cc6, 0xee2c3d922008cc6,
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
}; };
@@ -1252,8 +1234,7 @@ 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,
@@ -1261,8 +1242,7 @@ pub mod g1 {
0x1267d70db51049fb, 0x1267d70db51049fb,
0x4696deb9ab2ba3e7, 0x4696deb9ab2ba3e7,
0xb1e4e11177f59d4, 0xb1e4e11177f59d4,
])) ])).unwrap(),
.unwrap(),
infinity: false, infinity: false,
}; };
@@ -1283,19 +1263,19 @@ pub mod g1 {
#[test] #[test]
fn g1_curve_tests() { fn g1_curve_tests() {
use group::tests::curve_tests; ::tests::curve::curve_tests::<G1>();
curve_tests::<G1>();
} }
} }
pub mod g2 { pub mod g2 {
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr}; use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
use super::g1::G1Affine; use super::g1::G1Affine;
use crate::{Engine, PairingCurveAffine}; use rand::{Rand, Rng};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand_core::RngCore;
use std::fmt; use std::fmt;
use {
BitIterator, CurveAffine, CurveProjective, EncodedPoint, Engine, Field, GroupDecodingError,
PrimeField, PrimeFieldRepr, SqrtField,
};
curve_impl!( curve_impl!(
"G2", "G2",
@@ -1325,7 +1305,7 @@ pub mod g2 {
} }
impl fmt::Debug for G2Uncompressed { impl fmt::Debug for G2Uncompressed {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -1451,7 +1431,7 @@ pub mod g2 {
} }
impl fmt::Debug for G2Compressed { impl fmt::Debug for G2Compressed {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -1640,7 +1620,7 @@ pub mod g2 {
#[test] #[test]
fn g2_generator() { fn g2_generator() {
use crate::SqrtField; use SqrtField;
let mut x = Fq2::zero(); let mut x = Fq2::zero();
let mut i = 0; let mut i = 0;
@@ -1656,7 +1636,7 @@ pub mod g2 {
negy.negate(); negy.negate();
let p = G2Affine { let p = G2Affine {
x, x: x,
y: if y < negy { y } else { negy }, y: if y < negy { y } else { negy },
infinity: false, infinity: false,
}; };
@@ -1692,8 +1672,7 @@ 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,
@@ -1701,8 +1680,7 @@ 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([
@@ -1712,8 +1690,7 @@ 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,
@@ -1721,8 +1698,7 @@ pub mod g2 {
0x15008b1dc399e8df, 0x15008b1dc399e8df,
0x68128fd0548a3829, 0x68128fd0548a3829,
0x16a613db5c873aaa, 0x16a613db5c873aaa,
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@@ -1741,8 +1717,7 @@ 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,
@@ -1750,8 +1725,7 @@ 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([
@@ -1761,8 +1735,7 @@ 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,
@@ -1770,8 +1743,7 @@ pub mod g2 {
0x55f2b8efad953e04, 0x55f2b8efad953e04,
0x7379345eda55265e, 0x7379345eda55265e,
0x377f2e6208fd4cb, 0x377f2e6208fd4cb,
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@@ -1791,8 +1763,7 @@ 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,
@@ -1800,8 +1771,7 @@ 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([
@@ -1811,8 +1781,7 @@ 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,
@@ -1820,8 +1789,7 @@ pub mod g2 {
0x3940a2dbb914b529, 0x3940a2dbb914b529,
0xbeb88137cf34f3e7, 0xbeb88137cf34f3e7,
0x1699ee577c61b694, 0x1699ee577c61b694,
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@@ -1841,8 +1809,7 @@ 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,
@@ -1850,8 +1817,7 @@ 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([
@@ -1861,8 +1827,7 @@ 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,
@@ -1870,8 +1835,7 @@ pub mod g2 {
0x9eaa6d19de569196, 0x9eaa6d19de569196,
0xf6a03d31e2ec2183, 0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b, 0x3bdafaf7ca9b39b,
])) ])).unwrap(),
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}; };
@@ -1885,8 +1849,7 @@ 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,
@@ -1894,8 +1857,7 @@ 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([
@@ -1905,8 +1867,7 @@ 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,
@@ -1914,8 +1875,7 @@ pub mod g2 {
0x31898db63f87363a, 0x31898db63f87363a,
0xabab040ddbd097cc, 0xabab040ddbd097cc,
0x11ad236b9ba02990, 0x11ad236b9ba02990,
])) ])).unwrap(),
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}); });
@@ -1933,8 +1893,7 @@ 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,
@@ -1942,8 +1901,7 @@ 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([
@@ -1953,8 +1911,7 @@ 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,
@@ -1962,8 +1919,7 @@ pub mod g2 {
0x38528f92b689644d, 0x38528f92b689644d,
0xb6884deec59fb21f, 0xb6884deec59fb21f,
0x3c075d3ec52ba90 0x3c075d3ec52ba90
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
} }
@@ -1981,8 +1937,7 @@ 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,
@@ -1990,8 +1945,7 @@ 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([
@@ -2001,8 +1955,7 @@ 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,
@@ -2010,8 +1963,7 @@ pub mod g2 {
0x9eaa6d19de569196, 0x9eaa6d19de569196,
0xf6a03d31e2ec2183, 0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b, 0x3bdafaf7ca9b39b,
])) ])).unwrap(),
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}; };
@@ -2031,8 +1983,7 @@ 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,
@@ -2040,8 +1991,7 @@ 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([
@@ -2051,8 +2001,7 @@ 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,
@@ -2060,8 +2009,7 @@ pub mod g2 {
0xb271f52f12ead742, 0xb271f52f12ead742,
0x244e6c2015c83348, 0x244e6c2015c83348,
0x19e2deae6eb9b441 0x19e2deae6eb9b441
])) ])).unwrap(),
.unwrap(),
}, },
infinity: false, infinity: false,
} }
@@ -2070,8 +2018,7 @@ pub mod g2 {
#[test] #[test]
fn g2_curve_tests() { fn g2_curve_tests() {
use group::tests::curve_tests; ::tests::curve::curve_tests::<G2>();
curve_tests::<G2>();
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
use super::fq::FROBENIUS_COEFF_FQ12_C1; 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 rand::{Rand, Rng};
use rand_core::RngCore; use Field;
/// 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)]
@@ -12,11 +12,20 @@ pub struct Fq12 {
} }
impl ::std::fmt::Display for Fq12 { impl ::std::fmt::Display for Fq12 {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "Fq12({} + {} * w)", self.c0, self.c1) write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
} }
} }
impl Rand for Fq12 {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fq12 {
c0: rng.gen(),
c1: rng.gen(),
}
}
}
impl Fq12 { impl Fq12 {
pub fn conjugate(&mut self) { pub fn conjugate(&mut self) {
self.c1.negate(); self.c1.negate();
@@ -40,13 +49,6 @@ impl Fq12 {
} }
impl Field for Fq12 { impl Field for Fq12 {
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fq12 {
c0: Fq6::random(rng),
c1: Fq6::random(rng),
}
}
fn zero() -> Self { fn zero() -> Self {
Fq12 { Fq12 {
c0: Fq6::zero(), c0: Fq6::zero(),
@@ -147,29 +149,24 @@ impl Field for Fq12 {
} }
#[cfg(test)] #[cfg(test)]
use rand_core::SeedableRng; use rand::{SeedableRng, XorShiftRng};
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq12_mul_by_014() { fn test_fq12_mul_by_014() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let c0 = Fq2::random(&mut rng); let c0 = Fq2::rand(&mut rng);
let c1 = Fq2::random(&mut rng); let c1 = Fq2::rand(&mut rng);
let c5 = Fq2::random(&mut rng); let c5 = Fq2::rand(&mut rng);
let mut a = Fq12::random(&mut rng); let mut a = Fq12::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_014(&c0, &c1, &c5); a.mul_by_014(&c0, &c1, &c5);
b.mul_assign(&Fq12 { b.mul_assign(&Fq12 {
c0: Fq6 { c0: Fq6 {
c0, c0: c0,
c1, c1: c1,
c2: Fq2::zero(), c2: Fq2::zero(),
}, },
c1: Fq6 { c1: Fq6 {
@@ -185,8 +182,8 @@ fn test_fq12_mul_by_014() {
#[test] #[test]
fn fq12_field_tests() { fn fq12_field_tests() {
use ff::PrimeField; use PrimeField;
crate::tests::field::random_field_tests::<Fq12>(); ::tests::field::random_field_tests::<Fq12>();
crate::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13); ::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
} }

View File

@@ -1,6 +1,6 @@
use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE};
use ff::{Field, SqrtField}; use rand::{Rand, Rng};
use rand_core::RngCore; use {Field, SqrtField};
use std::cmp::Ordering; use std::cmp::Ordering;
@@ -12,7 +12,7 @@ pub struct Fq2 {
} }
impl ::std::fmt::Display for Fq2 { impl ::std::fmt::Display for Fq2 {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "Fq2({} + {} * u)", self.c0, self.c1) write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
} }
} }
@@ -56,14 +56,16 @@ impl Fq2 {
} }
} }
impl Field for Fq2 { impl Rand for Fq2 {
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self { fn rand<R: Rng>(rng: &mut R) -> Self {
Fq2 { Fq2 {
c0: Fq::random(rng), c0: rng.gen(),
c1: Fq::random(rng), c1: rng.gen(),
} }
} }
}
impl Field for Fq2 {
fn zero() -> Self { fn zero() -> Self {
Fq2 { Fq2 {
c0: Fq::zero(), c0: Fq::zero(),
@@ -158,7 +160,7 @@ impl Field for Fq2 {
} }
impl SqrtField for Fq2 { impl SqrtField for Fq2 {
fn legendre(&self) -> ::ff::LegendreSymbol { fn legendre(&self) -> ::LegendreSymbol {
self.norm().legendre() self.norm().legendre()
} }
@@ -264,14 +266,13 @@ fn test_fq2_basics() {
assert!(!Fq2 { assert!(!Fq2 {
c0: Fq::zero(), c0: Fq::zero(),
c1: Fq::one(), c1: Fq::one(),
} }.is_zero());
.is_zero());
} }
#[test] #[test]
fn test_fq2_squaring() { fn test_fq2_squaring() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use PrimeField;
let mut a = Fq2 { let mut a = Fq2 {
c0: Fq::one(), c0: Fq::one(),
@@ -308,8 +309,7 @@ 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,8 +317,7 @@ fn test_fq2_squaring() {
0xbfb99020604137a0, 0xbfb99020604137a0,
0xfc58a7b7be815407, 0xfc58a7b7be815407,
0x10d1615e75250a21, 0x10d1615e75250a21,
])) ])).unwrap(),
.unwrap(),
}; };
a.square(); a.square();
assert_eq!( assert_eq!(
@@ -331,8 +330,7 @@ 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,
@@ -340,8 +338,7 @@ fn test_fq2_squaring() {
0x38d0d7275a9689e1, 0x38d0d7275a9689e1,
0x739c983042779a65, 0x739c983042779a65,
0x1542a61c8a8db994 0x1542a61c8a8db994
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -349,7 +346,7 @@ fn test_fq2_squaring() {
#[test] #[test]
fn test_fq2_mul() { fn test_fq2_mul() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use PrimeField;
let mut a = Fq2 { let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -359,8 +356,7 @@ 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,
@@ -368,8 +364,7 @@ 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([
@@ -379,8 +374,7 @@ 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,
@@ -388,8 +382,7 @@ fn test_fq2_mul() {
0x57a06d3135a752ae, 0x57a06d3135a752ae,
0x634cd3c6c565096d, 0x634cd3c6c565096d,
0x19e17334d4e93558, 0x19e17334d4e93558,
])) ])).unwrap(),
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@@ -401,8 +394,7 @@ 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,
@@ -410,8 +402,7 @@ fn test_fq2_mul() {
0xc975106579c275ee, 0xc975106579c275ee,
0x33a9ac82ce4c5083, 0x33a9ac82ce4c5083,
0x1ef1a36c201589d 0x1ef1a36c201589d
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -419,7 +410,7 @@ fn test_fq2_mul() {
#[test] #[test]
fn test_fq2_inverse() { fn test_fq2_inverse() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use PrimeField;
assert!(Fq2::zero().inverse().is_none()); assert!(Fq2::zero().inverse().is_none());
@@ -431,8 +422,7 @@ 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,
@@ -440,8 +430,7 @@ 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!(
@@ -454,8 +443,7 @@ 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,
@@ -463,8 +451,7 @@ fn test_fq2_inverse() {
0xcbf651a0f367afb2, 0xcbf651a0f367afb2,
0xdf4e54f0d3ef15a6, 0xdf4e54f0d3ef15a6,
0x103bdf241afb0019 0x103bdf241afb0019
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -472,7 +459,7 @@ fn test_fq2_inverse() {
#[test] #[test]
fn test_fq2_addition() { fn test_fq2_addition() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use PrimeField;
let mut a = Fq2 { let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -482,8 +469,7 @@ 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,
@@ -491,8 +477,7 @@ 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([
@@ -502,8 +487,7 @@ 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,
@@ -511,8 +495,7 @@ fn test_fq2_addition() {
0x2236f55246d0d44d, 0x2236f55246d0d44d,
0x4c8c1800eb104566, 0x4c8c1800eb104566,
0x11d6e20e986c2085, 0x11d6e20e986c2085,
])) ])).unwrap(),
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@@ -524,8 +507,7 @@ 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,
@@ -533,8 +515,7 @@ fn test_fq2_addition() {
0xd62fa51334f560fa, 0xd62fa51334f560fa,
0x9ad265eb46e01984, 0x9ad265eb46e01984,
0x1303f3465112c8bc 0x1303f3465112c8bc
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -542,7 +523,7 @@ fn test_fq2_addition() {
#[test] #[test]
fn test_fq2_subtraction() { fn test_fq2_subtraction() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use PrimeField;
let mut a = Fq2 { let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -552,8 +533,7 @@ 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,
@@ -561,8 +541,7 @@ 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([
@@ -572,8 +551,7 @@ 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,
@@ -581,8 +559,7 @@ fn test_fq2_subtraction() {
0x2236f55246d0d44d, 0x2236f55246d0d44d,
0x4c8c1800eb104566, 0x4c8c1800eb104566,
0x11d6e20e986c2085, 0x11d6e20e986c2085,
])) ])).unwrap(),
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@@ -594,8 +571,7 @@ 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,
@@ -603,8 +579,7 @@ fn test_fq2_subtraction() {
0xf63905f39ad8cb1f, 0xf63905f39ad8cb1f,
0x4cd5dd9fb40b3b8f, 0x4cd5dd9fb40b3b8f,
0x957411359ba6e4c 0x957411359ba6e4c
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -612,7 +587,7 @@ fn test_fq2_subtraction() {
#[test] #[test]
fn test_fq2_negation() { fn test_fq2_negation() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use PrimeField;
let mut a = Fq2 { let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -622,8 +597,7 @@ 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,
@@ -631,8 +605,7 @@ fn test_fq2_negation() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])) ])).unwrap(),
.unwrap(),
}; };
a.negate(); a.negate();
assert_eq!( assert_eq!(
@@ -645,8 +618,7 @@ 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,
@@ -654,8 +626,7 @@ fn test_fq2_negation() {
0xb07e9bc405608611, 0xb07e9bc405608611,
0xfcd559cbe77bd8b8, 0xfcd559cbe77bd8b8,
0x18d400b280d93e62 0x18d400b280d93e62
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -663,7 +634,7 @@ fn test_fq2_negation() {
#[test] #[test]
fn test_fq2_doubling() { fn test_fq2_doubling() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use PrimeField;
let mut a = Fq2 { let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -673,8 +644,7 @@ 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,
@@ -682,8 +652,7 @@ fn test_fq2_doubling() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])) ])).unwrap(),
.unwrap(),
}; };
a.double(); a.double();
assert_eq!( assert_eq!(
@@ -696,8 +665,7 @@ 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,
@@ -705,8 +673,7 @@ fn test_fq2_doubling() {
0x67f15f81dc49195b, 0x67f15f81dc49195b,
0x9c8c9bd4b79fa83d, 0x9c8c9bd4b79fa83d,
0x25a226f714d506e 0x25a226f714d506e
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -714,7 +681,7 @@ fn test_fq2_doubling() {
#[test] #[test]
fn test_fq2_frobenius_map() { fn test_fq2_frobenius_map() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use PrimeField;
let mut a = Fq2 { let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -724,8 +691,7 @@ 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,
@@ -733,8 +699,7 @@ 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!(
@@ -747,8 +712,7 @@ 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,
@@ -756,8 +720,7 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])) ])).unwrap(),
.unwrap(),
} }
); );
a.frobenius_map(1); a.frobenius_map(1);
@@ -771,8 +734,7 @@ 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,
@@ -780,8 +742,7 @@ fn test_fq2_frobenius_map() {
0xb07e9bc405608611, 0xb07e9bc405608611,
0xfcd559cbe77bd8b8, 0xfcd559cbe77bd8b8,
0x18d400b280d93e62 0x18d400b280d93e62
])) ])).unwrap(),
.unwrap(),
} }
); );
a.frobenius_map(1); a.frobenius_map(1);
@@ -795,8 +756,7 @@ 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,
@@ -804,8 +764,7 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])) ])).unwrap(),
.unwrap(),
} }
); );
a.frobenius_map(2); a.frobenius_map(2);
@@ -819,8 +778,7 @@ 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,
@@ -828,8 +786,7 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
@@ -837,7 +794,7 @@ fn test_fq2_frobenius_map() {
#[test] #[test]
fn test_fq2_sqrt() { fn test_fq2_sqrt() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use PrimeField;
assert_eq!( assert_eq!(
Fq2 { Fq2 {
@@ -848,8 +805,7 @@ 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,
@@ -857,11 +813,9 @@ 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,
@@ -870,8 +824,7 @@ 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,
@@ -879,8 +832,7 @@ fn test_fq2_sqrt() {
0x6b852aeaf2afcb1b, 0x6b852aeaf2afcb1b,
0xa4c93b08105d71a9, 0xa4c93b08105d71a9,
0x8d7cfff94216330 0x8d7cfff94216330
])) ])).unwrap(),
.unwrap(),
} }
); );
@@ -893,12 +845,10 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf, 0x64774b84f38512bf,
0x4b1ba7b6434bacd7, 0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a 0x1a0111ea397fe69a
])) ])).unwrap(),
.unwrap(),
c1: Fq::zero(), c1: Fq::zero(),
} }.sqrt()
.sqrt() .unwrap(),
.unwrap(),
Fq2 { Fq2 {
c0: Fq::zero(), c0: Fq::zero(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
@@ -908,15 +858,14 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf, 0x64774b84f38512bf,
0x4b1ba7b6434bacd7, 0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a 0x1a0111ea397fe69a
])) ])).unwrap(),
.unwrap(),
} }
); );
} }
#[test] #[test]
fn test_fq2_legendre() { fn test_fq2_legendre() {
use ff::LegendreSymbol::*; use LegendreSymbol::*;
assert_eq!(Zero, Fq2::zero().legendre()); assert_eq!(Zero, Fq2::zero().legendre());
// i^2 = -1 // i^2 = -1
@@ -928,16 +877,11 @@ fn test_fq2_legendre() {
} }
#[cfg(test)] #[cfg(test)]
use rand_core::SeedableRng; use rand::{SeedableRng, XorShiftRng};
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq2_mul_nonresidue() { fn test_fq2_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let nqr = Fq2 { let nqr = Fq2 {
c0: Fq::one(), c0: Fq::one(),
@@ -945,7 +889,7 @@ fn test_fq2_mul_nonresidue() {
}; };
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fq2::random(&mut rng); let mut a = Fq2::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_nonresidue(); a.mul_by_nonresidue();
b.mul_assign(&nqr); b.mul_assign(&nqr);
@@ -956,9 +900,9 @@ fn test_fq2_mul_nonresidue() {
#[test] #[test]
fn fq2_field_tests() { fn fq2_field_tests() {
use ff::PrimeField; use PrimeField;
crate::tests::field::random_field_tests::<Fq2>(); ::tests::field::random_field_tests::<Fq2>();
crate::tests::field::random_sqrt_tests::<Fq2>(); ::tests::field::random_sqrt_tests::<Fq2>();
crate::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13); ::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
} }

View File

@@ -1,7 +1,7 @@
use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2};
use super::fq2::Fq2; use super::fq2::Fq2;
use ff::Field; use rand::{Rand, Rng};
use rand_core::RngCore; use Field;
/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). /// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2).
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -12,11 +12,21 @@ pub struct Fq6 {
} }
impl ::std::fmt::Display for Fq6 { impl ::std::fmt::Display for Fq6 {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2) write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
} }
} }
impl Rand for Fq6 {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fq6 {
c0: rng.gen(),
c1: rng.gen(),
c2: rng.gen(),
}
}
}
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) {
@@ -100,14 +110,6 @@ impl Fq6 {
} }
impl Field for Fq6 { impl Field for Fq6 {
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fq6 {
c0: Fq2::random(rng),
c1: Fq2::random(rng),
c2: Fq2::random(rng),
}
}
fn zero() -> Self { fn zero() -> Self {
Fq6 { Fq6 {
c0: Fq2::zero(), c0: Fq2::zero(),
@@ -300,16 +302,11 @@ impl Field for Fq6 {
} }
#[cfg(test)] #[cfg(test)]
use rand_core::SeedableRng; use rand::{SeedableRng, XorShiftRng};
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq6_mul_nonresidue() { fn test_fq6_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let nqr = Fq6 { let nqr = Fq6 {
c0: Fq2::zero(), c0: Fq2::zero(),
@@ -318,7 +315,7 @@ fn test_fq6_mul_nonresidue() {
}; };
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fq6::random(&mut rng); let mut a = Fq6::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_nonresidue(); a.mul_by_nonresidue();
b.mul_assign(&nqr); b.mul_assign(&nqr);
@@ -329,20 +326,17 @@ fn test_fq6_mul_nonresidue() {
#[test] #[test]
fn test_fq6_mul_by_1() { fn test_fq6_mul_by_1() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let c1 = Fq2::random(&mut rng); let c1 = Fq2::rand(&mut rng);
let mut a = Fq6::random(&mut rng); let mut a = Fq6::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_1(&c1); a.mul_by_1(&c1);
b.mul_assign(&Fq6 { b.mul_assign(&Fq6 {
c0: Fq2::zero(), c0: Fq2::zero(),
c1, c1: c1,
c2: Fq2::zero(), c2: Fq2::zero(),
}); });
@@ -352,21 +346,18 @@ fn test_fq6_mul_by_1() {
#[test] #[test]
fn test_fq6_mul_by_01() { fn test_fq6_mul_by_01() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let c0 = Fq2::random(&mut rng); let c0 = Fq2::rand(&mut rng);
let c1 = Fq2::random(&mut rng); let c1 = Fq2::rand(&mut rng);
let mut a = Fq6::random(&mut rng); let mut a = Fq6::rand(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_01(&c0, &c1); a.mul_by_01(&c0, &c1);
b.mul_assign(&Fq6 { b.mul_assign(&Fq6 {
c0, c0: c0,
c1, c1: c1,
c2: Fq2::zero(), c2: Fq2::zero(),
}); });
@@ -376,8 +367,8 @@ fn test_fq6_mul_by_01() {
#[test] #[test]
fn fq6_field_tests() { fn fq6_field_tests() {
use ff::PrimeField; use PrimeField;
crate::tests::field::random_field_tests::<Fq6>(); ::tests::field::random_field_tests::<Fq6>();
crate::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13); ::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,3 @@
//! An implementation of the BLS12-381 pairing-friendly elliptic curve
//! construction.
mod ec; mod ec;
mod fq; mod fq;
mod fq12; mod fq12;
@@ -12,8 +9,8 @@ mod fr;
mod tests; mod tests;
pub use self::ec::{ pub use self::ec::{
G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared, G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2, G2Affine, G2Compressed, G2Prepared,
G2Uncompressed, G1, G2, G2Uncompressed,
}; };
pub use self::fq::{Fq, FqRepr}; pub use self::fq::{Fq, FqRepr};
pub use self::fq12::Fq12; pub use self::fq12::Fq12;
@@ -21,10 +18,7 @@ pub use self::fq2::Fq2;
pub use self::fq6::Fq6; pub use self::fq6::Fq6;
pub use self::fr::{Fr, FrRepr}; pub use self::fr::{Fr, FrRepr};
use super::{Engine, PairingCurveAffine}; use super::{BitIterator, CurveAffine, Engine, Field};
use ff::{BitIterator, Field, ScalarEngine};
use group::CurveAffine;
// The BLS parameter x for BLS12-381 is -0xd201000000010000 // The BLS parameter x for BLS12-381 is -0xd201000000010000
const BLS_X: u64 = 0xd201000000010000; const BLS_X: u64 = 0xd201000000010000;
@@ -33,11 +27,8 @@ const BLS_X_IS_NEGATIVE: bool = true;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Bls12; pub struct Bls12;
impl ScalarEngine for Bls12 {
type Fr = Fr;
}
impl Engine for Bls12 { impl Engine for Bls12 {
type Fr = Fr;
type G1 = G1; type G1 = G1;
type G1Affine = G1Affine; type G1Affine = G1Affine;
type G2 = G2; type G2 = G2;
@@ -50,8 +41,8 @@ impl Engine for Bls12 {
where where
I: IntoIterator< I: IntoIterator<
Item = &'a ( Item = &'a (
&'a <Self::G1Affine as PairingCurveAffine>::Prepared, &'a <Self::G1Affine as CurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared, &'a <Self::G2Affine as CurveAffine>::Prepared,
), ),
>, >,
{ {
@@ -369,5 +360,5 @@ impl G2Prepared {
#[test] #[test]
fn bls12_engine_tests() { fn bls12_engine_tests() {
crate::tests::engine::engine_tests::<Bls12>(); ::tests::engine::engine_tests::<Bls12>();
} }

View File

@@ -1,8 +1,5 @@
use ff::PrimeFieldRepr;
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use super::*; use super::*;
use crate::*; use *;
#[test] #[test]
fn test_pairing_result_against_relic() { fn test_pairing_result_against_relic() {

View File

@@ -1,49 +1,59 @@
//! A library for working with pairing-friendly curves.
// `clippy` is a code linting tool for improving code quality by catching // `clippy` is a code linting tool for improving code quality by catching
// common mistakes or strange code patterns. If the `cargo-clippy` feature // common mistakes or strange code patterns. If the `clippy` feature is
// is provided, all compiler warnings are prohibited. // provided, it is enabled and all compiler warnings are prohibited.
#![cfg_attr(feature = "cargo-clippy", deny(warnings))] #![cfg_attr(feature = "clippy", deny(warnings))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))] #![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] #![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] #![cfg_attr(feature = "clippy", allow(inline_always))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))] #![cfg_attr(feature = "clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] #![cfg_attr(feature = "clippy", allow(unreadable_literal))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))] #![cfg_attr(feature = "clippy", allow(many_single_char_names))]
// Catch documentation errors caused by code changes. #![cfg_attr(feature = "clippy", allow(new_without_default_derive))]
#![deny(intra_doc_link_resolution_failure)] #![cfg_attr(feature = "clippy", allow(write_literal))]
// Force public structures to implement Debug // Force public structures to implement Debug
#![deny(missing_debug_implementations)] #![deny(missing_debug_implementations)]
extern crate byteorder;
extern crate rand;
#[cfg(test)] #[cfg(test)]
pub mod tests; pub mod tests;
pub mod bls12_381; pub mod bls12_381;
use ff::{Field, PrimeField, ScalarEngine, SqrtField}; mod wnaf;
use group::{CurveAffine, CurveProjective}; pub use self::wnaf::Wnaf;
use std::error::Error;
use std::fmt;
use std::io::{self, Read, Write};
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.) /// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
/// with well-defined relationships. In particular, the G1/G2 curve groups are /// with well-defined relationships. In particular, the G1/G2 curve groups are
/// of prime order `r`, and are equipped with a bilinear pairing function. /// of prime order `r`, and are equipped with a bilinear pairing function.
pub trait Engine: ScalarEngine { pub trait Engine: Sized + 'static + Clone {
/// This is the scalar field of the G1/G2 groups.
type Fr: PrimeField + SqrtField;
/// The projective representation of an element in G1. /// The projective representation of an element in G1.
type G1: CurveProjective< type G1: CurveProjective<
Engine = Self, Engine = Self,
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: CurveAffine<
Engine = Self, Engine = Self,
Base = Self::Fq, Base = Self::Fq,
Scalar = Self::Fr, Scalar = Self::Fr,
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<
@@ -51,17 +61,19 @@ 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: CurveAffine<
Engine = Self, Engine = Self,
Base = Self::Fqe, Base = Self::Fqe,
Scalar = Self::Fr, Scalar = Self::Fr,
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;
@@ -77,13 +89,13 @@ pub trait Engine: ScalarEngine {
where where
I: IntoIterator< I: IntoIterator<
Item = &'a ( Item = &'a (
&'a <Self::G1Affine as PairingCurveAffine>::Prepared, &'a <Self::G1Affine as CurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared, &'a <Self::G2Affine as CurveAffine>::Prepared,
), ),
>; >;
/// Perform final exponentiation of the result of a miller loop. /// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk>; fn final_exponentiation(&Self::Fqk) -> Option<Self::Fqk>;
/// Performs a complete pairing operation `(p, q)`. /// Performs a complete pairing operation `(p, q)`.
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
@@ -92,22 +104,655 @@ pub trait Engine: ScalarEngine {
G2: Into<Self::G2Affine>, G2: Into<Self::G2Affine>,
{ {
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()))].into_iter(),
)) )).unwrap()
.unwrap()
} }
} }
/// Affine representation of an elliptic curve point that can be used /// Projective representation of an elliptic curve point guaranteed to be
/// to perform pairings. /// in the correct prime order subgroup.
pub trait PairingCurveAffine: CurveAffine { pub trait CurveProjective:
PartialEq
+ Eq
+ Sized
+ Copy
+ Clone
+ Send
+ Sync
+ fmt::Debug
+ fmt::Display
+ rand::Rand
+ 'static
{
type Engine: Engine<Fr = Self::Scalar>;
type Scalar: PrimeField + SqrtField;
type Base: SqrtField;
type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>;
/// Returns the additive identity.
fn zero() -> Self;
/// Returns a fixed generator of unknown exponent.
fn one() -> Self;
/// Determines if this point is the point at infinity.
fn is_zero(&self) -> bool;
/// Normalizes a slice of projective elements so that
/// conversion to affine is cheap.
fn batch_normalization(v: &mut [Self]);
/// Checks if the point is already "normalized" so that
/// cheap affine conversion is possible.
fn is_normalized(&self) -> bool;
/// Doubles this element.
fn double(&mut self);
/// Adds another element to this element.
fn add_assign(&mut self, other: &Self);
/// Subtracts another element from this element.
fn sub_assign(&mut self, other: &Self) {
let mut tmp = *other;
tmp.negate();
self.add_assign(&tmp);
}
/// Adds an affine element to this element.
fn add_assign_mixed(&mut self, other: &Self::Affine);
/// Negates this element.
fn negate(&mut self);
/// Performs scalar multiplication of this element.
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S);
/// Converts this element into its affine representation.
fn into_affine(&self) -> Self::Affine;
/// Recommends a wNAF window table size given a scalar. Always returns a number
/// between 2 and 22, inclusive.
fn recommended_wnaf_for_scalar(scalar: <Self::Scalar as PrimeField>::Repr) -> usize;
/// Recommends a wNAF window size given the number of scalars you intend to multiply
/// a base by. Always returns a number between 2 and 22, inclusive.
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize;
}
/// Affine representation of an elliptic curve point guaranteed to be
/// in the correct prime order subgroup.
pub trait CurveAffine:
Copy + Clone + Sized + Send + Sync + fmt::Debug + fmt::Display + PartialEq + Eq + 'static
{
type Engine: Engine<Fr = Self::Scalar>;
type Scalar: PrimeField + SqrtField;
type Base: SqrtField;
type Projective: CurveProjective<Affine = Self, Scalar = Self::Scalar>;
type Prepared: Clone + Send + Sync + 'static; type Prepared: Clone + Send + Sync + 'static;
type Pair: PairingCurveAffine<Pair = Self>; type Uncompressed: EncodedPoint<Affine = Self>;
type Compressed: EncodedPoint<Affine = Self>;
type Pair: CurveAffine<Pair = Self>;
type PairingResult: Field; type PairingResult: Field;
/// Returns the additive identity.
fn zero() -> Self;
/// Returns a fixed generator of unknown exponent.
fn one() -> Self;
/// Determines if this point represents the point at infinity; the
/// additive identity.
fn is_zero(&self) -> bool;
/// Negates this element.
fn negate(&mut self);
/// Performs scalar multiplication of this element with mixed addition.
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective;
/// Prepares this element for pairing purposes. /// Prepares this element for pairing purposes.
fn prepare(&self) -> Self::Prepared; fn prepare(&self) -> Self::Prepared;
/// Perform a pairing /// Perform a pairing
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult;
/// Converts this element into its affine representation.
fn into_projective(&self) -> Self::Projective;
/// Converts this element into its compressed encoding, so long as it's not
/// the point at infinity.
fn into_compressed(&self) -> Self::Compressed {
<Self::Compressed as EncodedPoint>::from_affine(*self)
}
/// Converts this element into its uncompressed encoding, so long as it's not
/// the point at infinity.
fn into_uncompressed(&self) -> Self::Uncompressed {
<Self::Uncompressed as EncodedPoint>::from_affine(*self)
}
}
/// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`.
pub trait EncodedPoint:
Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + 'static
{
type Affine: CurveAffine;
/// Creates an empty representation.
fn empty() -> Self;
/// Returns the number of bytes consumed by this representation.
fn size() -> usize;
/// Converts an `EncodedPoint` into a `CurveAffine` element,
/// if the encoding represents a valid element.
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError>;
/// Converts an `EncodedPoint` into a `CurveAffine` element,
/// without guaranteeing that the encoding represents a valid
/// element. This is useful when the caller knows the encoding is
/// valid already.
///
/// If the encoding is invalid, this can break API invariants,
/// so caution is strongly encouraged.
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError>;
/// Creates an `EncodedPoint` from an affine point, as long as the
/// point is not the point at infinity.
fn from_affine(affine: Self::Affine) -> Self;
}
/// This trait represents an element of a field.
pub trait Field:
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static + rand::Rand
{
/// Returns the zero element of the field, the additive identity.
fn zero() -> Self;
/// Returns the one element of the field, the multiplicative identity.
fn one() -> Self;
/// Returns true iff this element is zero.
fn is_zero(&self) -> bool;
/// Squares this element.
fn square(&mut self);
/// Doubles this element.
fn double(&mut self);
/// Negates this element.
fn negate(&mut self);
/// Adds another element to this element.
fn add_assign(&mut self, other: &Self);
/// Subtracts another element from this element.
fn sub_assign(&mut self, other: &Self);
/// Multiplies another element by this element.
fn mul_assign(&mut self, other: &Self);
/// Computes the multiplicative inverse of this element, if nonzero.
fn inverse(&self) -> Option<Self>;
/// Exponentiates this element by a power of the base prime modulus via
/// the Frobenius automorphism.
fn frobenius_map(&mut self, power: usize);
/// Exponentiates this element by a number represented with `u64` limbs,
/// least significant digit first.
fn pow<S: AsRef<[u64]>>(&self, exp: S) -> Self {
let mut res = Self::one();
let mut found_one = false;
for i in BitIterator::new(exp) {
if found_one {
res.square();
} else {
found_one = i;
}
if i {
res.mul_assign(self);
}
}
res
}
}
/// This trait represents an element of a field that has a square root operation described for it.
pub trait SqrtField: Field {
/// Returns the Legendre symbol of the field element.
fn legendre(&self) -> LegendreSymbol;
/// Returns the square root of the field element, if it is
/// quadratic residue.
fn sqrt(&self) -> Option<Self>;
}
/// This trait represents a wrapper around a biginteger which can encode any element of a particular
/// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit
/// first.
pub trait PrimeFieldRepr:
Sized
+ Copy
+ Clone
+ Eq
+ Ord
+ Send
+ Sync
+ Default
+ fmt::Debug
+ fmt::Display
+ 'static
+ rand::Rand
+ AsRef<[u64]>
+ AsMut<[u64]>
+ From<u64>
{
/// Subtract another represetation from this one.
fn sub_noborrow(&mut self, other: &Self);
/// Add another representation to this one.
fn add_nocarry(&mut self, other: &Self);
/// Compute the number of bits needed to encode this number. Always a
/// multiple of 64.
fn num_bits(&self) -> u32;
/// Returns true iff this number is zero.
fn is_zero(&self) -> bool;
/// Returns true iff this number is odd.
fn is_odd(&self) -> bool;
/// Returns true iff this number is even.
fn is_even(&self) -> bool;
/// Performs a rightwise bitshift of this number, effectively dividing
/// it by 2.
fn div2(&mut self);
/// Performs a rightwise bitshift of this number by some amount.
fn shr(&mut self, amt: u32);
/// Performs a leftwise bitshift of this number, effectively multiplying
/// it by 2. Overflow is ignored.
fn mul2(&mut self);
/// Performs a leftwise bitshift of this number by some amount.
fn shl(&mut self, amt: u32);
/// Writes this `PrimeFieldRepr` as a big endian integer.
fn write_be<W: Write>(&self, mut writer: W) -> io::Result<()> {
use byteorder::{BigEndian, WriteBytesExt};
for digit in self.as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit)?;
}
Ok(())
}
/// Reads a big endian integer into this representation.
fn read_be<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
use byteorder::{BigEndian, ReadBytesExt};
for digit in self.as_mut().iter_mut().rev() {
*digit = reader.read_u64::<BigEndian>()?;
}
Ok(())
}
/// Writes this `PrimeFieldRepr` as a little endian integer.
fn write_le<W: Write>(&self, mut writer: W) -> io::Result<()> {
use byteorder::{LittleEndian, WriteBytesExt};
for digit in self.as_ref().iter() {
writer.write_u64::<LittleEndian>(*digit)?;
}
Ok(())
}
/// Reads a little endian integer into this representation.
fn read_le<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
use byteorder::{LittleEndian, ReadBytesExt};
for digit in self.as_mut().iter_mut() {
*digit = reader.read_u64::<LittleEndian>()?;
}
Ok(())
}
}
#[derive(Debug, PartialEq)]
pub enum LegendreSymbol {
Zero = 0,
QuadraticResidue = 1,
QuadraticNonResidue = -1,
}
/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
/// `PrimeField` element.
#[derive(Debug)]
pub enum PrimeFieldDecodingError {
/// The encoded value is not in the field
NotInField(String),
}
impl Error for PrimeFieldDecodingError {
fn description(&self) -> &str {
match *self {
PrimeFieldDecodingError::NotInField(..) => "not an element of the field",
}
}
}
impl fmt::Display for PrimeFieldDecodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
PrimeFieldDecodingError::NotInField(ref repr) => {
write!(f, "{} is not an element of the field", repr)
}
}
}
}
/// An error that may occur when trying to decode an `EncodedPoint`.
#[derive(Debug)]
pub enum GroupDecodingError {
/// The coordinate(s) do not lie on the curve.
NotOnCurve,
/// The element is not part of the r-order subgroup.
NotInSubgroup,
/// One of the coordinates could not be decoded
CoordinateDecodingError(&'static str, PrimeFieldDecodingError),
/// The compression mode of the encoded element was not as expected
UnexpectedCompressionMode,
/// The encoding contained bits that should not have been set
UnexpectedInformation,
}
impl Error for GroupDecodingError {
fn description(&self) -> &str {
match *self {
GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve",
GroupDecodingError::NotInSubgroup => "the element is not part of an r-order subgroup",
GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded",
GroupDecodingError::UnexpectedCompressionMode => {
"encoding has unexpected compression mode"
}
GroupDecodingError::UnexpectedInformation => "encoding has unexpected information",
}
}
}
impl fmt::Display for GroupDecodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
write!(f, "{} decoding error: {}", description, err)
}
_ => write!(f, "{}", self.description()),
}
}
}
/// This represents an element of a prime field.
pub trait PrimeField: Field {
/// The prime field can be converted back and forth into this biginteger
/// representation.
type Repr: PrimeFieldRepr + From<Self>;
/// Interpret a string of numbers as a (congruent) prime field element.
/// Does not accept unnecessary leading zeroes or a blank string.
fn from_str(s: &str) -> Option<Self> {
if s.is_empty() {
return None;
}
if s == "0" {
return Some(Self::zero());
}
let mut res = Self::zero();
let ten = Self::from_repr(Self::Repr::from(10)).unwrap();
let mut first_digit = true;
for c in s.chars() {
match c.to_digit(10) {
Some(c) => {
if first_digit {
if c == 0 {
return None;
}
first_digit = false;
}
res.mul_assign(&ten);
res.add_assign(&Self::from_repr(Self::Repr::from(u64::from(c))).unwrap());
}
None => {
return None;
}
}
}
Some(res)
}
/// Convert this prime field element into a biginteger representation.
fn from_repr(Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
/// Convert a biginteger representation into a prime field element, if
/// the number is an element of the field.
fn into_repr(&self) -> Self::Repr;
/// Returns the field characteristic; the modulus.
fn char() -> Self::Repr;
/// How many bits are needed to represent an element of this field.
const NUM_BITS: u32;
/// How many bits of information can be reliably stored in the field element.
const CAPACITY: u32;
/// Returns the multiplicative generator of `char()` - 1 order. This element
/// must also be quadratic nonresidue.
fn multiplicative_generator() -> Self;
/// 2^s * t = `char()` - 1 with t odd.
const S: u32;
/// Returns the 2^s root of unity computed by exponentiating the `multiplicative_generator()`
/// by t.
fn root_of_unity() -> Self;
}
#[derive(Debug)]
pub struct BitIterator<E> {
t: E,
n: usize,
}
impl<E: AsRef<[u64]>> BitIterator<E> {
pub fn new(t: E) -> Self {
let n = t.as_ref().len() * 64;
BitIterator { t, n }
}
}
impl<E: AsRef<[u64]>> Iterator for BitIterator<E> {
type Item = bool;
fn next(&mut self) -> Option<bool> {
if self.n == 0 {
None
} else {
self.n -= 1;
let part = self.n / 64;
let bit = self.n - (64 * part);
Some(self.t.as_ref()[part] & (1 << bit) > 0)
}
}
}
#[test]
fn test_bit_iterator() {
let mut a = BitIterator::new([0xa953d79b83f6ab59, 0x6dea2059e200bd39]);
let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001";
for e in expected.chars() {
assert!(a.next().unwrap() == (e == '1'));
}
assert!(a.next().is_none());
let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001";
let mut a = BitIterator::new([
0x429d5f3ac3a3b759,
0xb10f4c66768b1c92,
0x92368b6d16ecd3b4,
0xa57ea85ae8775219,
]);
for e in expected.chars() {
assert!(a.next().unwrap() == (e == '1'));
}
assert!(a.next().is_none());
}
#[cfg(not(feature = "expose-arith"))]
use self::arith_impl::*;
#[cfg(feature = "expose-arith")]
pub use self::arith_impl::*;
#[cfg(feature = "u128-support")]
mod arith_impl {
/// Calculate a - b - borrow, returning the result and modifying
/// the borrow value.
#[inline(always)]
pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 {
let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow);
*borrow = if tmp >> 64 == 0 { 1 } else { 0 };
tmp as u64
}
/// Calculate a + b + carry, returning the sum and modifying the
/// carry value.
#[inline(always)]
pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 {
let tmp = u128::from(a) + u128::from(b) + u128::from(*carry);
*carry = (tmp >> 64) as u64;
tmp as u64
}
/// Calculate a + (b * c) + carry, returning the least significant digit
/// and setting carry to the most significant digit.
#[inline(always)]
pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry);
*carry = (tmp >> 64) as u64;
tmp as u64
}
}
#[cfg(not(feature = "u128-support"))]
mod arith_impl {
#[inline(always)]
fn split_u64(i: u64) -> (u64, u64) {
(i >> 32, i & 0xFFFFFFFF)
}
#[inline(always)]
fn combine_u64(hi: u64, lo: u64) -> u64 {
(hi << 32) | lo
}
/// Calculate a - b - borrow, returning the result and modifying
/// the borrow value.
#[inline(always)]
pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 {
let (a_hi, a_lo) = split_u64(a);
let (b_hi, b_lo) = split_u64(b);
let (b, r0) = split_u64((1 << 32) + a_lo - b_lo - *borrow);
let (b, r1) = split_u64((1 << 32) + a_hi - b_hi - ((b == 0) as u64));
*borrow = (b == 0) as u64;
combine_u64(r1, r0)
}
/// Calculate a + b + carry, returning the sum and modifying the
/// carry value.
#[inline(always)]
pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 {
let (a_hi, a_lo) = split_u64(a);
let (b_hi, b_lo) = split_u64(b);
let (carry_hi, carry_lo) = split_u64(*carry);
let (t, r0) = split_u64(a_lo + b_lo + carry_lo);
let (t, r1) = split_u64(t + a_hi + b_hi + carry_hi);
*carry = t;
combine_u64(r1, r0)
}
/// Calculate a + (b * c) + carry, returning the least significant digit
/// and setting carry to the most significant digit.
#[inline(always)]
pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
/*
[ b_hi | b_lo ]
[ c_hi | c_lo ] *
-------------------------------------------
[ b_lo * c_lo ] <-- w
[ b_hi * c_lo ] <-- x
[ b_lo * c_hi ] <-- y
[ b_hi * c_lo ] <-- z
[ a_hi | a_lo ]
[ C_hi | C_lo ]
*/
let (a_hi, a_lo) = split_u64(a);
let (b_hi, b_lo) = split_u64(b);
let (c_hi, c_lo) = split_u64(c);
let (carry_hi, carry_lo) = split_u64(*carry);
let (w_hi, w_lo) = split_u64(b_lo * c_lo);
let (x_hi, x_lo) = split_u64(b_hi * c_lo);
let (y_hi, y_lo) = split_u64(b_lo * c_hi);
let (z_hi, z_lo) = split_u64(b_hi * c_hi);
let (t, r0) = split_u64(w_lo + a_lo + carry_lo);
let (t, r1) = split_u64(t + w_hi + x_lo + y_lo + a_hi + carry_hi);
let (t, r2) = split_u64(t + x_hi + y_hi + z_lo);
let (_, r3) = split_u64(t + z_hi);
*carry = combine_u64(r3, r2);
combine_u64(r1, r0)
}
} }

View File

@@ -1,14 +1,9 @@
use ff::{Field, PrimeField}; use rand::{Rand, Rng, SeedableRng, XorShiftRng};
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use crate::{CurveAffine, CurveProjective, EncodedPoint}; use {CurveAffine, CurveProjective, EncodedPoint, Field};
pub fn curve_tests<G: CurveProjective>() { pub fn curve_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
// Negation edge case with zero. // Negation edge case with zero.
{ {
@@ -26,7 +21,7 @@ pub fn curve_tests<G: CurveProjective>() {
// Addition edge cases with zero // Addition edge cases with zero
{ {
let mut r = G::random(&mut rng); let mut r = G::rand(&mut rng);
let rcopy = r; let rcopy = r;
r.add_assign(&G::zero()); r.add_assign(&G::zero());
assert_eq!(r, rcopy); assert_eq!(r, rcopy);
@@ -50,7 +45,7 @@ pub fn curve_tests<G: CurveProjective>() {
// Transformations // Transformations
{ {
let a = G::random(&mut rng); let a = G::rand(&mut rng);
let b = a.into_affine().into_projective(); let b = a.into_affine().into_projective();
let c = a let c = a
.into_affine() .into_affine()
@@ -71,12 +66,10 @@ pub fn curve_tests<G: CurveProjective>() {
} }
fn random_wnaf_tests<G: CurveProjective>() { fn random_wnaf_tests<G: CurveProjective>() {
use crate::wnaf::*; use wnaf::*;
use PrimeField;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
{ {
let mut table = vec![]; let mut table = vec![];
@@ -84,8 +77,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
for w in 2..14 { for w in 2..14 {
for _ in 0..100 { for _ in 0..100 {
let g = G::random(&mut rng); let g = G::rand(&mut rng);
let s = G::Scalar::random(&mut rng).into_repr(); let s = G::Scalar::rand(&mut rng).into_repr();
let mut g1 = g; let mut g1 = g;
g1.mul_assign(s); g1.mul_assign(s);
@@ -102,8 +95,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
fn only_compiles_if_send<S: Send>(_: &S) {} fn only_compiles_if_send<S: Send>(_: &S) {}
for _ in 0..100 { for _ in 0..100 {
let g = G::random(&mut rng); let g = G::rand(&mut rng);
let s = G::Scalar::random(&mut rng).into_repr(); let s = G::Scalar::rand(&mut rng).into_repr();
let mut g1 = g; let mut g1 = g;
g1.mul_assign(s); g1.mul_assign(s);
@@ -136,8 +129,7 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(G::random(&mut rng), 1) wnaf.base(rng.gen(), 1).scalar(rng.gen());
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
wnaf.base(g, 1).scalar(s) wnaf.base(g, 1).scalar(s)
}; };
@@ -145,8 +137,7 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(G::random(&mut rng), 1) wnaf.base(rng.gen(), 1).scalar(rng.gen());
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
wnaf.scalar(s).base(g) wnaf.scalar(s).base(g)
}; };
@@ -154,8 +145,7 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(G::random(&mut rng), 1) wnaf.base(rng.gen(), 1).scalar(rng.gen());
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
let mut shared = wnaf.base(g, 1).shared(); let mut shared = wnaf.base(g, 1).shared();
@@ -167,8 +157,7 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(G::random(&mut rng), 1) wnaf.base(rng.gen(), 1).scalar(rng.gen());
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
let mut shared = wnaf.scalar(s).shared(); let mut shared = wnaf.scalar(s).shared();
@@ -190,15 +179,12 @@ fn random_wnaf_tests<G: CurveProjective>() {
} }
fn random_negation_tests<G: CurveProjective>() { fn random_negation_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let r = G::random(&mut rng); let r = G::rand(&mut rng);
let s = G::Scalar::random(&mut rng); let s = G::Scalar::rand(&mut rng);
let mut sneg = s; let mut sneg = s;
sneg.negate(); sneg.negate();
@@ -222,14 +208,11 @@ fn random_negation_tests<G: CurveProjective>() {
} }
fn random_doubling_tests<G: CurveProjective>() { fn random_doubling_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut a = G::random(&mut rng); let mut a = G::rand(&mut rng);
let mut b = G::random(&mut rng); let mut b = G::rand(&mut rng);
// 2(a + b) // 2(a + b)
let mut tmp1 = a; let mut tmp1 = a;
@@ -252,18 +235,15 @@ fn random_doubling_tests<G: CurveProjective>() {
} }
fn random_multiplication_tests<G: CurveProjective>() { fn random_multiplication_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut a = G::random(&mut rng); let mut a = G::rand(&mut rng);
let mut b = G::random(&mut rng); let mut b = G::rand(&mut rng);
let a_affine = a.into_affine(); let a_affine = a.into_affine();
let b_affine = b.into_affine(); let b_affine = b.into_affine();
let s = G::Scalar::random(&mut rng); let s = G::Scalar::rand(&mut rng);
// s ( a + b ) // s ( a + b )
let mut tmp1 = a; let mut tmp1 = a;
@@ -287,15 +267,12 @@ fn random_multiplication_tests<G: CurveProjective>() {
} }
fn random_addition_tests<G: CurveProjective>() { fn random_addition_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = G::random(&mut rng); let a = G::rand(&mut rng);
let b = G::random(&mut rng); let b = G::rand(&mut rng);
let c = G::random(&mut rng); let c = G::rand(&mut rng);
let a_affine = a.into_affine(); let a_affine = a.into_affine();
let b_affine = b.into_affine(); let b_affine = b.into_affine();
let c_affine = c.into_affine(); let c_affine = c.into_affine();
@@ -368,13 +345,10 @@ fn random_addition_tests<G: CurveProjective>() {
} }
fn random_transformation_tests<G: CurveProjective>() { fn random_transformation_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let g = G::random(&mut rng); let g = G::rand(&mut rng);
let g_affine = g.into_affine(); let g_affine = g.into_affine();
let g_projective = g_affine.into_projective(); let g_projective = g_affine.into_projective();
assert_eq!(g, g_projective); assert_eq!(g, g_projective);
@@ -382,20 +356,20 @@ fn random_transformation_tests<G: CurveProjective>() {
// Batch normalization // Batch normalization
for _ in 0..10 { for _ in 0..10 {
let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::<Vec<_>>(); let mut v = (0..1000).map(|_| G::rand(&mut rng)).collect::<Vec<_>>();
for i in &v { for i in &v {
assert!(!i.is_normalized()); assert!(!i.is_normalized());
} }
use rand::distributions::{Distribution, Uniform}; use rand::distributions::{IndependentSample, Range};
let between = Uniform::new(0, 1000); let between = Range::new(0, 1000);
// Sprinkle in some normalized points // Sprinkle in some normalized points
for _ in 0..5 { for _ in 0..5 {
v[between.sample(&mut rng)] = G::zero(); v[between.ind_sample(&mut rng)] = G::zero();
} }
for _ in 0..5 { for _ in 0..5 {
let s = between.sample(&mut rng); let s = between.ind_sample(&mut rng);
v[s] = v[s].into_affine().into_projective(); v[s] = v[s].into_affine().into_projective();
} }
@@ -414,10 +388,7 @@ fn random_transformation_tests<G: CurveProjective>() {
} }
fn random_encoding_tests<G: CurveAffine>() { fn random_encoding_tests<G: CurveAffine>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
assert_eq!( assert_eq!(
G::zero().into_uncompressed().into_affine().unwrap(), G::zero().into_uncompressed().into_affine().unwrap(),
@@ -430,7 +401,7 @@ fn random_encoding_tests<G: CurveAffine>() {
); );
for _ in 0..1000 { for _ in 0..1000 {
let mut r = G::Projective::random(&mut rng).into_affine(); let mut r = G::Projective::rand(&mut rng).into_affine();
let uncompressed = r.into_uncompressed(); let uncompressed = r.into_uncompressed();
let de_uncompressed = uncompressed.into_affine().unwrap(); let de_uncompressed = uncompressed.into_affine().unwrap();

View File

@@ -1,18 +1,13 @@
use group::{CurveAffine, CurveProjective}; use rand::{Rand, SeedableRng, XorShiftRng};
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use crate::{Engine, Field, PairingCurveAffine, PrimeField}; use {CurveAffine, CurveProjective, Engine, Field, PrimeField};
pub fn engine_tests<E: Engine>() { pub fn engine_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..10 { for _ in 0..10 {
let a = E::G1::random(&mut rng).into_affine(); let a = E::G1::rand(&mut rng).into_affine();
let b = E::G2::random(&mut rng).into_affine(); let b = E::G2::rand(&mut rng).into_affine();
assert!(a.pairing_with(&b) == b.pairing_with(&a)); assert!(a.pairing_with(&b) == b.pairing_with(&a));
assert!(a.pairing_with(&b) == E::pairing(a, b)); assert!(a.pairing_with(&b) == E::pairing(a, b));
@@ -22,10 +17,10 @@ pub fn engine_tests<E: Engine>() {
let z1 = E::G1Affine::zero().prepare(); let z1 = E::G1Affine::zero().prepare();
let z2 = E::G2Affine::zero().prepare(); let z2 = E::G2Affine::zero().prepare();
let a = E::G1::random(&mut rng).into_affine().prepare(); let a = E::G1::rand(&mut rng).into_affine().prepare();
let b = E::G2::random(&mut rng).into_affine().prepare(); let b = E::G2::rand(&mut rng).into_affine().prepare();
let c = E::G1::random(&mut rng).into_affine().prepare(); let c = E::G1::rand(&mut rng).into_affine().prepare();
let d = E::G2::random(&mut rng).into_affine().prepare(); let d = E::G2::rand(&mut rng).into_affine().prepare();
assert_eq!( assert_eq!(
E::Fqk::one(), E::Fqk::one(),
@@ -53,15 +48,12 @@ pub fn engine_tests<E: Engine>() {
} }
fn random_miller_loop_tests<E: Engine>() { fn random_miller_loop_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
// Exercise the miller loop for a reduced pairing // Exercise the miller loop for a reduced pairing
for _ in 0..1000 { for _ in 0..1000 {
let a = E::G1::random(&mut rng); let a = E::G1::rand(&mut rng);
let b = E::G2::random(&mut rng); let b = E::G2::rand(&mut rng);
let p2 = E::pairing(a, b); let p2 = E::pairing(a, b);
@@ -75,10 +67,10 @@ fn random_miller_loop_tests<E: Engine>() {
// Exercise a double miller loop // Exercise a double miller loop
for _ in 0..1000 { for _ in 0..1000 {
let a = E::G1::random(&mut rng); let a = E::G1::rand(&mut rng);
let b = E::G2::random(&mut rng); let b = E::G2::rand(&mut rng);
let c = E::G1::random(&mut rng); let c = E::G1::rand(&mut rng);
let d = E::G2::random(&mut rng); let d = E::G2::rand(&mut rng);
let ab = E::pairing(a, b); let ab = E::pairing(a, b);
let cd = E::pairing(c, d); let cd = E::pairing(c, d);
@@ -99,17 +91,14 @@ fn random_miller_loop_tests<E: Engine>() {
} }
fn random_bilinearity_tests<E: Engine>() { fn random_bilinearity_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = E::G1::random(&mut rng); let a = E::G1::rand(&mut rng);
let b = E::G2::random(&mut rng); let b = E::G2::rand(&mut rng);
let c = E::Fr::random(&mut rng); let c = E::Fr::rand(&mut rng);
let d = E::Fr::random(&mut rng); let d = E::Fr::rand(&mut rng);
let mut ac = a; let mut ac = a;
ac.mul_assign(c); ac.mul_assign(c);

View File

@@ -1,16 +1,12 @@
use ff::{Field, LegendreSymbol, PrimeField, SqrtField}; use rand::{Rng, SeedableRng, XorShiftRng};
use rand_core::{RngCore, SeedableRng}; use {Field, LegendreSymbol, PrimeField, SqrtField};
use rand_xorshift::XorShiftRng;
pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) { pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
for i in 0..(maxpower + 1) { for i in 0..(maxpower + 1) {
let mut a = F::random(&mut rng); let mut a = F::rand(&mut rng);
let mut b = a; let mut b = a;
for _ in 0..i { for _ in 0..i {
@@ -24,13 +20,10 @@ pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxp
} }
pub fn random_sqrt_tests<F: SqrtField>() { pub fn random_sqrt_tests<F: SqrtField>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..10000 { for _ in 0..10000 {
let a = F::random(&mut rng); let a = F::rand(&mut rng);
let mut b = a; let mut b = a;
b.square(); b.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
@@ -61,10 +54,7 @@ pub fn random_sqrt_tests<F: SqrtField>() {
} }
pub fn random_field_tests<F: Field>() { pub fn random_field_tests<F: Field>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
random_multiplication_tests::<F, _>(&mut rng); random_multiplication_tests::<F, _>(&mut rng);
random_addition_tests::<F, _>(&mut rng); random_addition_tests::<F, _>(&mut rng);
@@ -86,14 +76,14 @@ pub fn random_field_tests<F: Field>() {
// Multiplication by zero // Multiplication by zero
{ {
let mut a = F::random(&mut rng); let mut a = F::rand(&mut rng);
a.mul_assign(&F::zero()); a.mul_assign(&F::zero());
assert!(a.is_zero()); assert!(a.is_zero());
} }
// Addition by zero // Addition by zero
{ {
let mut a = F::random(&mut rng); let mut a = F::rand(&mut rng);
let copy = a; let copy = a;
a.add_assign(&F::zero()); a.add_assign(&F::zero());
assert_eq!(a, copy); assert_eq!(a, copy);
@@ -116,13 +106,10 @@ pub fn from_str_tests<F: PrimeField>() {
} }
{ {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let n = rng.next_u64(); let n: u64 = rng.gen();
let a = F::from_str(&format!("{}", n)).unwrap(); let a = F::from_str(&format!("{}", n)).unwrap();
let b = F::from_repr(n.into()).unwrap(); let b = F::from_repr(n.into()).unwrap();
@@ -137,11 +124,11 @@ pub fn from_str_tests<F: PrimeField>() {
assert!(F::from_str("00000000000").is_none()); assert!(F::from_str("00000000000").is_none());
} }
fn random_multiplication_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_multiplication_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::random(rng); let a = F::rand(rng);
let b = F::random(rng); let b = F::rand(rng);
let c = F::random(rng); let c = F::rand(rng);
let mut t0 = a; // (a * b) * c let mut t0 = a; // (a * b) * c
t0.mul_assign(&b); t0.mul_assign(&b);
@@ -160,11 +147,11 @@ fn random_multiplication_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_addition_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_addition_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::random(rng); let a = F::rand(rng);
let b = F::random(rng); let b = F::rand(rng);
let c = F::random(rng); let c = F::rand(rng);
let mut t0 = a; // (a + b) + c let mut t0 = a; // (a + b) + c
t0.add_assign(&b); t0.add_assign(&b);
@@ -183,10 +170,10 @@ fn random_addition_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_subtraction_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_subtraction_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let b = F::random(rng); let a = F::rand(rng);
let a = F::random(rng); let b = F::rand(rng);
let mut t0 = a; // (a - b) let mut t0 = a; // (a - b)
t0.sub_assign(&b); t0.sub_assign(&b);
@@ -201,9 +188,9 @@ fn random_subtraction_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_negation_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_negation_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::random(rng); let a = F::rand(rng);
let mut b = a; let mut b = a;
b.negate(); b.negate();
b.add_assign(&a); b.add_assign(&a);
@@ -212,9 +199,9 @@ fn random_negation_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_doubling_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_doubling_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::random(rng); let mut a = F::rand(rng);
let mut b = a; let mut b = a;
a.add_assign(&b); a.add_assign(&b);
b.double(); b.double();
@@ -223,9 +210,9 @@ fn random_doubling_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_squaring_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_squaring_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::random(rng); let mut a = F::rand(rng);
let mut b = a; let mut b = a;
a.mul_assign(&b); a.mul_assign(&b);
b.square(); b.square();
@@ -234,11 +221,11 @@ fn random_squaring_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_inversion_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_inversion_tests<F: Field, R: Rng>(rng: &mut R) {
assert!(F::zero().inverse().is_none()); assert!(F::zero().inverse().is_none());
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::random(rng); let mut a = F::rand(rng);
let b = a.inverse().unwrap(); // probablistically nonzero let b = a.inverse().unwrap(); // probablistically nonzero
a.mul_assign(&b); a.mul_assign(&b);
@@ -246,14 +233,14 @@ fn random_inversion_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
} }
fn random_expansion_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_expansion_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
let a = F::random(rng); let a = F::rand(rng);
let b = F::random(rng); let b = F::rand(rng);
let c = F::random(rng); let c = F::rand(rng);
let d = F::random(rng); let d = F::rand(rng);
let mut t0 = a; let mut t0 = a;
t0.add_assign(&b); t0.add_assign(&b);

View File

@@ -1,3 +1,4 @@
pub mod curve;
pub mod engine; pub mod engine;
pub mod field; pub mod field;
pub mod repr; pub mod repr;

View File

@@ -1,25 +1,21 @@
use ff::{PrimeField, PrimeFieldRepr}; use rand::{SeedableRng, XorShiftRng};
use rand_core::SeedableRng; use PrimeFieldRepr;
use rand_xorshift::XorShiftRng;
pub fn random_repr_tests<P: PrimeField>() { pub fn random_repr_tests<R: PrimeFieldRepr>() {
random_encoding_tests::<P>(); random_encoding_tests::<R>();
random_shl_tests::<P>(); random_shl_tests::<R>();
random_shr_tests::<P>(); random_shr_tests::<R>();
} }
fn random_encoding_tests<P: PrimeField>() { fn random_encoding_tests<R: PrimeFieldRepr>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let r = P::random(&mut rng).into_repr(); let r = R::rand(&mut rng);
// Big endian // Big endian
{ {
let mut rdecoded = <P as PrimeField>::Repr::default(); let mut rdecoded = R::default();
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
r.write_be(&mut v).unwrap(); r.write_be(&mut v).unwrap();
@@ -30,7 +26,7 @@ fn random_encoding_tests<P: PrimeField>() {
// Little endian // Little endian
{ {
let mut rdecoded = <P as PrimeField>::Repr::default(); let mut rdecoded = R::default();
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
r.write_le(&mut v).unwrap(); r.write_le(&mut v).unwrap();
@@ -40,8 +36,8 @@ fn random_encoding_tests<P: PrimeField>() {
} }
{ {
let mut rdecoded_le = <P as PrimeField>::Repr::default(); let mut rdecoded_le = R::default();
let mut rdecoded_be_flip = <P as PrimeField>::Repr::default(); let mut rdecoded_be_flip = R::default();
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
r.write_le(&mut v).unwrap(); r.write_le(&mut v).unwrap();
@@ -59,14 +55,11 @@ fn random_encoding_tests<P: PrimeField>() {
} }
} }
fn random_shl_tests<P: PrimeField>() { fn random_shl_tests<R: PrimeFieldRepr>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let r = P::random(&mut rng).into_repr(); let r = R::rand(&mut rng);
for shift in 0..(r.num_bits() + 1) { for shift in 0..(r.num_bits() + 1) {
let mut r1 = r; let mut r1 = r;
@@ -83,14 +76,11 @@ fn random_shl_tests<P: PrimeField>() {
} }
} }
fn random_shr_tests<P: PrimeField>() { fn random_shr_tests<R: PrimeFieldRepr>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let r = P::random(&mut rng).into_repr(); let r = R::rand(&mut rng);
for shift in 0..(r.num_bits() + 1) { for shift in 0..(r.num_bits() + 1) {
let mut r1 = r; let mut r1 = r;

View File

@@ -1,6 +1,4 @@
use ff::{PrimeField, PrimeFieldRepr}; use super::{CurveProjective, PrimeField, PrimeFieldRepr};
use super::CurveProjective;
/// Replaces the contents of `table` with a w-NAF window table for the given window size. /// Replaces the contents of `table` with a w-NAF window table for the given window size.
pub(crate) fn wnaf_table<G: CurveProjective>(table: &mut Vec<G>, mut base: G, window: usize) { pub(crate) fn wnaf_table<G: CurveProjective>(table: &mut Vec<G>, mut base: G, window: usize) {

View File

@@ -1,3 +1,3 @@
target/ /target/
**/*.rs.bk **/*.rs.bk
Cargo.lock Cargo.lock

View File

@@ -1,7 +1,7 @@
Copyrights in the "zcash_history" library are retained by their contributors. No Copyrights in the "sapling-crypto" library are retained by their contributors. No
copyright assignment is required to contribute to the "zcash_history" library. copyright assignment is required to contribute to the "sapling-crypto" library.
The "zcash_history" library is licensed under either of The "sapling-crypto" library is licensed under either of
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)

31
sapling-crypto/Cargo.toml Normal file
View File

@@ -0,0 +1,31 @@
[package]
authors = ["Sean Bowe <sean@z.cash>"]
description = "Cryptographic library for Zcash Sapling"
documentation = "https://github.com/zcash-hackworks/sapling"
homepage = "https://github.com/zcash-hackworks/sapling"
license = "MIT/Apache-2.0"
name = "sapling-crypto"
repository = "https://github.com/zcash-hackworks/sapling"
version = "0.0.1"
[dependencies.pairing]
path = "../pairing"
features = ["expose-arith"]
[dependencies]
bellman = { path = "../bellman" }
rand = "0.4"
digest = "0.7"
byteorder = "1"
[dependencies.blake2-rfc]
git = "https://github.com/gtank/blake2-rfc"
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
[dev-dependencies]
hex-literal = "0.1"
rust-crypto = "0.2"
[features]
default = ["u128-support"]
u128-support = ["pairing/u128-support"]

23
sapling-crypto/README.md Normal file
View File

@@ -0,0 +1,23 @@
# sapling-crypto
This repository contains a (work-in-progress) implementation of Zcash's "Sapling" cryptography.
## Security Warnings
This library is currently under development and has not been reviewed.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

View File

@@ -0,0 +1,23 @@
#![feature(test)]
extern crate rand;
extern crate test;
extern crate pairing;
extern crate sapling_crypto;
use rand::{Rand, thread_rng};
use pairing::bls12_381::Bls12;
use sapling_crypto::jubjub::JubjubBls12;
use sapling_crypto::pedersen_hash::{pedersen_hash, Personalization};
#[bench]
fn bench_pedersen_hash(b: &mut test::Bencher) {
let params = JubjubBls12::new();
let rng = &mut thread_rng();
let bits = (0..510).map(|_| bool::rand(rng)).collect::<Vec<_>>();
let personalization = Personalization::MerkleTree(31);
b.iter(|| {
pedersen_hash::<Bls12, _>(personalization, bits.clone(), &params)
});
}

View File

@@ -0,0 +1,102 @@
extern crate sapling_crypto;
extern crate bellman;
extern crate rand;
extern crate pairing;
use std::time::{Duration, Instant};
use sapling_crypto::jubjub::{
JubjubBls12,
edwards,
fs,
};
use sapling_crypto::circuit::sapling::{
Spend
};
use sapling_crypto::primitives::{
Diversifier,
ProofGenerationKey,
ValueCommitment
};
use bellman::groth16::*;
use rand::{XorShiftRng, SeedableRng, Rng};
use pairing::bls12_381::{Bls12, Fr};
const TREE_DEPTH: usize = 32;
fn main() {
let jubjub_params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
println!("Creating sample parameters...");
let groth_params = generate_random_parameters::<Bls12, _, _>(
Spend {
params: jubjub_params,
value_commitment: None,
proof_generation_key: None,
payment_address: None,
commitment_randomness: None,
ar: None,
auth_path: vec![None; TREE_DEPTH],
anchor: None
},
rng
).unwrap();
const SAMPLES: u32 = 50;
let mut total_time = Duration::new(0, 0);
for _ in 0..SAMPLES {
let value_commitment = ValueCommitment {
value: 1,
randomness: rng.gen()
};
let nsk: fs::Fs = rng.gen();
let ak = edwards::Point::rand(rng, jubjub_params).mul_by_cofactor(jubjub_params);
let proof_generation_key = ProofGenerationKey {
ak: ak.clone(),
nsk: nsk.clone()
};
let viewing_key = proof_generation_key.into_viewing_key(jubjub_params);
let payment_address;
loop {
let diversifier = Diversifier(rng.gen());
if let Some(p) = viewing_key.into_payment_address(
diversifier,
jubjub_params
)
{
payment_address = p;
break;
}
}
let commitment_randomness: fs::Fs = rng.gen();
let auth_path = vec![Some((rng.gen(), rng.gen())); TREE_DEPTH];
let ar: fs::Fs = rng.gen();
let anchor: Fr = rng.gen();
let start = Instant::now();
let _ = create_random_proof(Spend {
params: jubjub_params,
value_commitment: Some(value_commitment),
proof_generation_key: Some(proof_generation_key),
payment_address: Some(payment_address),
commitment_randomness: Some(commitment_randomness),
ar: Some(ar),
auth_path: auth_path,
anchor: Some(anchor)
}, &groth_params, rng).unwrap();
total_time += start.elapsed();
}
let avg = total_time / SAMPLES;
let avg = avg.subsec_nanos() as f64 / 1_000_000_000f64
+ (avg.as_secs() as f64);
println!("Average proving time (in seconds): {}", avg);
}

View File

@@ -0,0 +1,438 @@
use pairing::{
Engine,
};
use bellman::{
SynthesisError,
ConstraintSystem
};
use super::boolean::{
Boolean
};
use super::uint32::{
UInt32
};
use super::multieq::MultiEq;
/*
2.1. Parameters
The following table summarizes various parameters and their ranges:
| BLAKE2b | BLAKE2s |
--------------+------------------+------------------+
Bits in word | w = 64 | w = 32 |
Rounds in F | r = 12 | r = 10 |
Block bytes | bb = 128 | bb = 64 |
Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 |
Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 |
Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 |
--------------+------------------+------------------+
G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
constants = | (32, 24, 16, 63) | (16, 12, 8, 7) |
--------------+------------------+------------------+
*/
const R1: usize = 16;
const R2: usize = 12;
const R3: usize = 8;
const R4: usize = 7;
/*
Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
----------+-------------------------------------------------+
SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 |
SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 |
SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 |
SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 |
SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 |
SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 |
SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 |
SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 |
SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 |
----------+-------------------------------------------------+
*/
const SIGMA: [[usize; 16]; 10] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[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],
[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]
];
/*
3.1. Mixing Function G
The G primitive function mixes two input words, "x" and "y", into
four words indexed by "a", "b", "c", and "d" in the working vector
v[0..15]. The full modified vector is returned. The rotation
constants (R1, R2, R3, R4) are given in Section 2.1.
FUNCTION G( v[0..15], a, b, c, d, x, y )
|
| v[a] := (v[a] + v[b] + x) mod 2**w
| v[d] := (v[d] ^ v[a]) >>> R1
| v[c] := (v[c] + v[d]) mod 2**w
| v[b] := (v[b] ^ v[c]) >>> R2
| v[a] := (v[a] + v[b] + y) mod 2**w
| v[d] := (v[d] ^ v[a]) >>> R3
| v[c] := (v[c] + v[d]) mod 2**w
| v[b] := (v[b] ^ v[c]) >>> R4
|
| RETURN v[0..15]
|
END FUNCTION.
*/
fn mixing_g<E: Engine, CS: ConstraintSystem<E>, M>(
mut cs: M,
v: &mut [UInt32],
a: usize,
b: usize,
c: usize,
d: usize,
x: &UInt32,
y: &UInt32
) -> Result<(), SynthesisError>
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[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[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[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[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4);
Ok(())
}
/*
3.2. Compression Function F
Compression function F takes as an argument the state vector "h",
message block vector "m" (last block is padded with zeros to full
block size, if required), 2w-bit offset counter "t", and final block
indicator flag "f". Local vector v[0..15] is used in processing. F
returns a new state vector. The number of rounds, "r", is 12 for
BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
FUNCTION F( h[0..7], m[0..15], t, f )
|
| // Initialize local work vector v[0..15]
| v[0..7] := h[0..7] // First half from state.
| v[8..15] := IV[0..7] // Second half from IV.
|
| v[12] := v[12] ^ (t mod 2**w) // Low word of the offset.
| v[13] := v[13] ^ (t >> w) // High word.
|
| IF f = TRUE THEN // last block flag?
| | v[14] := v[14] ^ 0xFF..FF // Invert all bits.
| END IF.
|
| // Cryptographic mixing
| FOR i = 0 TO r - 1 DO // Ten or twelve rounds.
| |
| | // Message word selection permutation for this round.
| | s[0..15] := SIGMA[i mod 10][0..15]
| |
| | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] )
| | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] )
| | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
| | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
| |
| | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
| | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
| | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] )
| | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] )
| |
| END FOR
|
| FOR i = 0 TO 7 DO // XOR the two halves.
| | h[i] := h[i] ^ v[i] ^ v[i + 8]
| END FOR.
|
| RETURN h[0..7] // New state.
|
END FUNCTION.
*/
fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS,
h: &mut [UInt32],
m: &[UInt32],
t: u64,
f: bool
) -> Result<(), SynthesisError>
{
assert_eq!(h.len(), 8);
assert_eq!(m.len(), 16);
/*
static const uint32_t blake2s_iv[8] =
{
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
*/
let mut v = Vec::with_capacity(16);
v.extend_from_slice(h);
v.push(UInt32::constant(0x6A09E667));
v.push(UInt32::constant(0xBB67AE85));
v.push(UInt32::constant(0x3C6EF372));
v.push(UInt32::constant(0xA54FF53A));
v.push(UInt32::constant(0x510E527F));
v.push(UInt32::constant(0x9B05688C));
v.push(UInt32::constant(0x1F83D9AB));
v.push(UInt32::constant(0x5BE0CD19));
assert_eq!(v.len(), 16);
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))?;
if f {
v[14] = v[14].xor(cs.namespace(|| "third xor"), &UInt32::constant(u32::max_value()))?;
}
{
let mut cs = MultiEq::new(&mut cs);
for i in 0..10 {
let mut cs = cs.namespace(|| format!("round {}", i));
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(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(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 {
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(|| "second xor"), &v[i + 8])?;
}
Ok(())
}
/*
FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
|
| h[0..7] := IV[0..7] // Initialization Vector.
|
| // Parameter block p[0]
| h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
|
| // Process padded key and data blocks
| IF dd > 1 THEN
| | FOR i = 0 TO dd - 2 DO
| | | h := F( h, d[i], (i + 1) * bb, FALSE )
| | END FOR.
| END IF.
|
| // Final block.
| IF kk = 0 THEN
| | h := F( h, d[dd - 1], ll, TRUE )
| ELSE
| | h := F( h, d[dd - 1], ll + bb, TRUE )
| END IF.
|
| RETURN first "nn" bytes from little-endian word array h[].
|
END FUNCTION.
*/
pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS,
input: &[Boolean],
personalization: &[u8]
) -> Result<Vec<Boolean>, SynthesisError>
{
use byteorder::{ByteOrder, LittleEndian};
assert_eq!(personalization.len(), 8);
assert!(input.len() % 8 == 0);
let mut h = Vec::with_capacity(8);
h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32));
h.push(UInt32::constant(0xBB67AE85));
h.push(UInt32::constant(0x3C6EF372));
h.push(UInt32::constant(0xA54FF53A));
h.push(UInt32::constant(0x510E527F));
h.push(UInt32::constant(0x9B05688C));
// Personalization is stored here
h.push(UInt32::constant(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![];
for block in input.chunks(512) {
let mut this_block = Vec::with_capacity(16);
for word in block.chunks(32) {
let mut tmp = word.to_vec();
while tmp.len() < 32 {
tmp.push(Boolean::constant(false));
}
this_block.push(UInt32::from_bits(&tmp));
}
while this_block.len() < 16 {
this_block.push(UInt32::constant(0));
}
blocks.push(this_block);
}
if blocks.len() == 0 {
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
}
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
let cs = cs.namespace(|| format!("block {}", i));
blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
}
{
let cs = cs.namespace(|| "final block");
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())
}
#[cfg(test)]
mod test {
use rand::{XorShiftRng, SeedableRng, Rng};
use pairing::bls12_381::{Bls12};
use ::circuit::boolean::{Boolean, AllocatedBit};
use ::circuit::test::TestConstraintSystem;
use super::blake2s;
use bellman::{ConstraintSystem};
use blake2_rfc::blake2s::Blake2s;
#[test]
fn test_blank_hash() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits = vec![];
let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 0);
// >>> import blake2s from hashlib
// >>> h = blake2s(digest_size=32, person=b'12345678')
// >>> h.hexdigest()
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
let mut out = out.into_iter();
for b in expected.into_iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_constraints() {
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();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518);
}
#[test]
fn test_blake2s_precomp_constraints() {
// Test that 512 fixed leading bits (constants)
// doesn't result in more constraints.
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.gen()))
.chain((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();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518);
}
#[test]
fn test_blake2s_constant_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.gen())).collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert_eq!(cs.num_constraints(), 0);
}
#[test]
fn test_blake2s() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0))
{
let mut h = Blake2s::with_params(32, &[], &[], b"12345678");
let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
h.update(&data);
let hash_result = h.finalize();
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
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());
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let mut s = hash_result.as_ref().iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r {
match b {
Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
},
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
},
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
}
}
}
}
}
}

View File

@@ -1,16 +1,21 @@
//! Window table lookup gadgets. use pairing::{Engine, Field};
use ff::{Field, ScalarEngine};
use super::boolean::Boolean;
use super::num::{AllocatedNum, Num};
use super::*; use super::*;
use crate::ConstraintSystem; use super::num::{
AllocatedNum,
Num
};
use super::boolean::Boolean;
use bellman::{
ConstraintSystem
};
// Synthesize the constants for each base pattern. // Synthesize the constants for each base pattern.
fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr]) fn synth<'a, E: Engine, I>(
where window_size: usize,
I: IntoIterator<Item = &'a E::Fr>, constants: I,
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);
@@ -29,23 +34,19 @@ where
/// Performs a 3-bit window table lookup. `bits` is in /// Performs a 3-bit window table lookup. `bits` is in
/// little-endian order. /// little-endian order.
pub fn lookup3_xy<E: ScalarEngine, 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 where CS: ConstraintSystem<E>
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 = match ( let i =
bits[0].get_value(), match (bits[0].get_value(), bits[1].get_value(), bits[2].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 {
@@ -58,15 +59,25 @@ where
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(cs.namespace(|| "x"), || Ok(coords[*i.get()?].0))?; let res_x = AllocatedNum::alloc(
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(cs.namespace(|| "y"), || Ok(coords[*i.get()?].1))?; let res_y = AllocatedNum::alloc(
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];
@@ -80,38 +91,30 @@ where
cs.enforce( cs.enforce(
|| "x-coordinate lookup", || "x-coordinate lookup",
|lc| { |lc| lc + (x_coeffs[0b001], one)
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| lc + res_x.get_variable()
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| lc + (y_coeffs[0b001], one)
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| lc + res_y.get_variable()
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))
@@ -119,19 +122,19 @@ where
/// Performs a 3-bit window table lookup, where /// Performs a 3-bit window table lookup, where
/// one of the bits is a sign bit. /// one of the bits is a sign bit.
pub fn lookup3_xy_with_conditional_negation<E: ScalarEngine, 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 where CS: ConstraintSystem<E>
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 = match (bits[0].get_value(), bits[1].get_value()) { let i =
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 {
@@ -141,19 +144,22 @@ where
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(cs.namespace(|| "y"), || { let y = AllocatedNum::alloc(
let mut tmp = coords[*i.get()?].1; cs.namespace(|| "y"),
if *bits[2].get_value().get()? { || {
tmp.negate(); let mut tmp = coords[*i.get()?].1;
if *bits[2].get_value().get()? {
tmp.negate();
}
Ok(tmp)
} }
Ok(tmp) )?;
})?;
let one = CS::one(); let one = CS::one();
@@ -166,21 +172,21 @@ where
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()))
@@ -188,52 +194,46 @@ where
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use rand::{SeedableRng, Rand, Rng, XorShiftRng};
use super::*; use super::*;
use crate::gadgets::boolean::{AllocatedBit, Boolean}; use ::circuit::test::*;
use crate::gadgets::test::*; use ::circuit::boolean::{Boolean, AllocatedBit};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_lookup3_xy() { fn test_lookup3_xy() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0656]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
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.gen();
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); let a = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.next_u32() % 2 != 0; let b_val = rng.gen();
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()); let b = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.next_u32() % 2 != 0; let c_val = rng.gen();
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()); let c = Boolean::from(
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) let points: Vec<(Fr, Fr)> = (0..8).map(|_| (rng.gen(), rng.gen())).collect();
.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 { if a_val { index += 1 }
index += 1 if b_val { index += 2 }
} 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);
@@ -242,63 +242,53 @@ 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([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
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.gen();
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); let a = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.next_u32() % 2 != 0; let b_val = rng.gen();
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()); let b = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.next_u32() % 2 != 0; let c_val = rng.gen();
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()); let c = Boolean::from(
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) let points: Vec<(Fr, Fr)> = (0..4).map(|_| (rng.gen(), rng.gen())).collect();
.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 { if a_val { index += 1 }
index += 1 if b_val { index += 2 }
}
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 { if c_val { tmp.negate() }
tmp.negate()
}
assert_eq!(res.1.get_value().unwrap(), tmp); assert_eq!(res.1.get_value().unwrap(), tmp);
} }
} }
#[test] #[test]
fn test_synth() { fn test_synth() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
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)) let constants: Vec<_> = (0..(1 << window_size)).map(|_| Fr::rand(&mut rng)).collect();
.map(|_| Fr::random(&mut rng))
.collect();
synth::<Bls12, _>(window_size, &constants, &mut assignment); synth::<Bls12, _>(window_size, &constants, &mut assignment);

View File

@@ -1,17 +1,23 @@
//! Self-contained sub-circuit implementations for various primitives. #[cfg(test)]
pub mod test; pub mod test;
pub mod blake2s;
pub mod boolean; pub mod boolean;
pub mod lookup;
pub mod multieq; pub mod multieq;
pub mod multipack;
pub mod num;
pub mod sha256;
pub mod uint32; pub mod uint32;
pub mod blake2s;
pub mod num;
pub mod lookup;
pub mod ecc;
pub mod pedersen_hash;
pub mod multipack;
pub mod sha256;
use crate::SynthesisError; pub mod sapling;
pub mod sprout;
use bellman::{
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`
@@ -19,7 +25,7 @@ use crate::SynthesisError;
/// This basically is just an extension to `Option` /// This basically is just an extension to `Option`
/// which allows for a convenient mapping to an /// which allows for a convenient mapping to an
/// error on `None`. /// error on `None`.
pub trait Assignment<T> { trait Assignment<T> {
fn get(&self) -> Result<&T, SynthesisError>; fn get(&self) -> Result<&T, SynthesisError>;
} }
@@ -27,7 +33,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,8 +1,17 @@
use ff::{Field, PrimeField, ScalarEngine}; use pairing::{
Engine,
Field,
PrimeField
};
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
pub struct MultiEq<E: ScalarEngine, 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,
@@ -10,18 +19,19 @@ pub struct MultiEq<E: ScalarEngine, CS: ConstraintSystem<E>> {
rhs: LinearCombination<E>, rhs: LinearCombination<E>,
} }
impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> { impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
pub fn new(cs: CS) -> Self { pub fn new(cs: CS) -> Self {
MultiEq { MultiEq {
cs, cs: 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();
@@ -29,7 +39,7 @@ impl<E: ScalarEngine, 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();
@@ -41,8 +51,9 @@ impl<E: ScalarEngine, 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();
@@ -57,63 +68,70 @@ impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
} }
} }
impl<E: ScalarEngine, 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: ScalarEngine, 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>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, b: LB,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, c: LC
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 where NR: Into<String>, N: FnOnce() -> NR
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

@@ -0,0 +1,113 @@
use pairing::{Engine, Field, PrimeField};
use bellman::{ConstraintSystem, SynthesisError};
use super::boolean::{Boolean};
use super::num::Num;
use super::Assignment;
/// Takes a sequence of booleans and exposes them as compact
/// public inputs
pub fn pack_into_inputs<E, CS>(
mut cs: CS,
bits: &[Boolean]
) -> Result<(), SynthesisError>
where E: Engine, CS: ConstraintSystem<E>
{
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate()
{
let mut num = Num::<E>::zero();
let mut coeff = E::Fr::one();
for bit in bits {
num = num.add_bool_with_coeff(CS::one(), bit, coeff);
coeff.double();
}
let input = cs.alloc_input(|| format!("input {}", i), || {
Ok(*num.get_value().get()?)
})?;
// num * 1 = input
cs.enforce(
|| format!("packing constraint {}", i),
|_| num.lc(E::Fr::one()),
|lc| lc + CS::one(),
|lc| lc + input
);
}
Ok(())
}
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool>
{
bytes.iter()
.flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool>
{
bytes.iter()
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn compute_multipacking<E: Engine>(
bits: &[bool]
) -> Vec<E::Fr>
{
let mut result = vec![];
for bits in bits.chunks(E::Fr::CAPACITY as usize)
{
let mut cur = E::Fr::zero();
let mut coeff = E::Fr::one();
for bit in bits {
if *bit {
cur.add_assign(&coeff);
}
coeff.double();
}
result.push(cur);
}
result
}
#[test]
fn test_multipacking() {
use rand::{SeedableRng, Rng, XorShiftRng};
use bellman::{ConstraintSystem};
use pairing::bls12_381::{Bls12};
use ::circuit::test::*;
use super::boolean::{AllocatedBit, Boolean};
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for num_bits in 0..1500 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let bits: Vec<bool> = (0..num_bits).map(|_| rng.gen()).collect();
let circuit_bits = bits.iter().enumerate()
.map(|(i, &b)| {
Boolean::from(
AllocatedBit::alloc(
cs.namespace(|| format!("bit {}", i)),
Some(b)
).unwrap()
)
})
.collect::<Vec<_>>();
let expected_inputs = compute_multipacking::<Bls12>(&bits);
pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap();
assert!(cs.is_satisfied());
assert!(cs.verify(&expected_inputs));
}
}

View File

@@ -1,62 +1,83 @@
//! Gadgets representing numbers in the scalar field of the underlying curve. use pairing::{
Engine,
Field,
PrimeField,
PrimeFieldRepr,
BitIterator
};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine}; use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; use super::{
Assignment
};
use super::Assignment; use super::boolean::{
self,
Boolean,
AllocatedBit
};
use super::boolean::{self, AllocatedBit, Boolean}; pub struct AllocatedNum<E: Engine> {
pub struct AllocatedNum<E: ScalarEngine> {
value: Option<E::Fr>, value: Option<E::Fr>,
variable: Variable, variable: Variable
} }
impl<E: ScalarEngine> 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: ScalarEngine> AllocatedNum<E> { impl<E: Engine> AllocatedNum<E> {
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError> pub fn alloc<CS, F>(
where mut cs: CS,
CS: ConstraintSystem<E>, value: F,
F: FnOnce() -> Result<E::Fr, SynthesisError>, ) -> Result<Self, 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( let var = cs.alloc(|| "num", || {
|| "num", let tmp = value()?;
|| {
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>(&self, mut cs: CS) -> Result<(), SynthesisError> pub fn inputize<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{ {
let input = cs.alloc_input(|| "input variable", || Ok(*self.value.get()?))?; let input = cs.alloc_input(
|| "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(())
@@ -67,19 +88,20 @@ impl<E: ScalarEngine> 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 to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> pub fn into_bits_le_strict<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> 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 where E: Engine,
E: ScalarEngine, CS: ConstraintSystem<E>
CS: ConstraintSystem<E>,
{ {
assert!(!v.is_empty()); assert!(v.len() > 0);
// Let's keep this simple for now and just AND them all // Let's keep this simple for now and just AND them all
// manually // manually
@@ -92,7 +114,7 @@ impl<E: ScalarEngine> 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
)?); )?);
} }
} }
@@ -128,12 +150,15 @@ impl<E: ScalarEngine> 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(cs.namespace(|| format!("bit {}", i)), a_bit)?; let a_bit = AllocatedBit::alloc(
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);
} else { } else {
if !current_run.is_empty() { if current_run.len() > 0 {
// This is the start of a run of zeros, but we need // This is the start of a run of zeros, but we need
// to k-ary AND against `last_run` first. // to k-ary AND against `last_run` first.
@@ -142,7 +167,7 @@ impl<E: ScalarEngine> 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);
} }
@@ -155,7 +180,7 @@ impl<E: ScalarEngine> 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);
} }
@@ -181,20 +206,30 @@ impl<E: ScalarEngine> AllocatedNum<E> {
lc = lc - self.variable; lc = lc - self.variable;
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); cs.enforce(
|| "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(Boolean::from).rev().collect()) Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect())
} }
/// 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 to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> pub fn into_bits_le<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?; let bits = boolean::field_into_allocated_bits_le(
&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();
@@ -207,91 +242,94 @@ impl<E: ScalarEngine> AllocatedNum<E> {
lc = lc - self.variable; lc = lc - self.variable;
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); cs.enforce(
|| "unpacking constraint",
|lc| lc,
|lc| lc,
|_| lc
);
Ok(bits.into_iter().map(Boolean::from).collect()) Ok(bits.into_iter().map(|b| Boolean::from(b)).collect())
} }
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError> pub fn mul<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS,
other: &Self
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let mut value = None; let mut value = None;
let var = cs.alloc( let var = cs.alloc(|| "product num", || {
|| "product num", let mut tmp = *self.value.get()?;
|| { 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,
variable: var, variable: var
}) })
} }
pub fn square<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError> pub fn square<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let mut value = None; let mut value = None;
let var = cs.alloc( let var = cs.alloc(|| "squared num", || {
|| "squared num", let mut tmp = *self.value.get()?;
|| { 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,
variable: var, variable: var
}) })
} }
pub fn assert_nonzero<CS>(&self, mut cs: CS) -> Result<(), SynthesisError> pub fn assert_nonzero<CS>(
where &self,
CS: ConstraintSystem<E>, mut cs: CS
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{ {
let inv = cs.alloc( let inv = cs.alloc(|| "ephemeral inverse", || {
|| "ephemeral inverse", let tmp = *self.value.get()?;
|| {
let tmp = *self.value.get()?;
if tmp.is_zero() { if tmp.is_zero() {
Err(SynthesisError::DivisionByZero) Err(SynthesisError::DivisionByZero)
} else { } else {
Ok(tmp.inverse().unwrap()) 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
@@ -300,7 +338,7 @@ impl<E: ScalarEngine> 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(())
@@ -313,39 +351,44 @@ impl<E: ScalarEngine> 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 where CS: ConstraintSystem<E>
CS: ConstraintSystem<E>,
{ {
let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || { let c = Self::alloc(
if *condition.get_value().get()? { cs.namespace(|| "conditional reversal result 1"),
Ok(*b.value.get()?) || {
} else { if *condition.get_value().get()? {
Ok(*a.value.get()?) Ok(*b.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(cs.namespace(|| "conditional reversal result 2"), || { let d = Self::alloc(
if *condition.get_value().get()? { cs.namespace(|| "conditional reversal result 2"),
Ok(*a.value.get()?) || {
} else { if *condition.get_value().get()? {
Ok(*b.value.get()?) Ok(*a.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))
@@ -360,25 +403,25 @@ impl<E: ScalarEngine> AllocatedNum<E> {
} }
} }
pub struct Num<E: ScalarEngine> { pub struct Num<E: Engine> {
value: Option<E::Fr>, value: Option<E::Fr>,
lc: LinearCombination<E>, lc: LinearCombination<E>
} }
impl<E: ScalarEngine> 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
} }
} }
} }
impl<E: ScalarEngine> Num<E> { 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()
} }
} }
@@ -390,7 +433,13 @@ impl<E: ScalarEngine> Num<E> {
LinearCombination::zero() + (coeff, &self.lc) LinearCombination::zero() + (coeff, &self.lc)
} }
pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: E::Fr) -> Self { pub fn add_bool_with_coeff(
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 {
@@ -398,27 +447,25 @@ impl<E: ScalarEngine> 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 rand::{SeedableRng, Rand, Rng, XorShiftRng};
use ff::{BitIterator, Field, PrimeField}; use bellman::{ConstraintSystem};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::SeedableRng; use pairing::{Field, PrimeField, BitIterator};
use rand_xorshift::XorShiftRng; use ::circuit::test::*;
use super::{AllocatedNum, Boolean}; use super::{AllocatedNum, Boolean};
use crate::gadgets::test::*;
#[test] #[test]
fn test_allocated_num() { fn test_allocated_num() {
@@ -447,10 +494,8 @@ mod test {
fn test_num_multiplication() { fn test_num_multiplication() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").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 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());
@@ -462,15 +507,12 @@ 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([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
{ {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap(); let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap();
let condition = Boolean::constant(false); let condition = Boolean::constant(false);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
@@ -483,8 +525,8 @@ mod test {
{ {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap(); let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap();
let condition = Boolean::constant(true); let condition = Boolean::constant(true);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
@@ -523,7 +565,7 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap(); let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
n.to_bits_le_strict(&mut cs).unwrap(); n.into_bits_le_strict(&mut cs).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -531,37 +573,28 @@ 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!( assert_eq!(cs.which_is_unsatisfied().unwrap(), "bit 254/boolean constraint");
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([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for i in 0..200 { for i in 0..200 {
let r = Fr::random(&mut rng); let r = Fr::rand(&mut rng);
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
let bits = if i % 2 == 0 { let bits = if i % 2 == 0 {
n.to_bits_le(&mut cs).unwrap() n.into_bits_le(&mut cs).unwrap()
} else { } else {
n.to_bits_le_strict(&mut cs).unwrap() n.into_bits_le_strict(&mut cs).unwrap()
}; };
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
for (b, a) in BitIterator::new(r.into_repr()) for (b, a) in BitIterator::new(r.into_repr()).skip(1).zip(bits.iter().rev()) {
.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 {
@@ -569,7 +602,7 @@ mod test {
} }
} }
cs.set("num", Fr::random(&mut rng)); cs.set("num", Fr::rand(&mut rng));
assert!(!cs.is_satisfied()); assert!(!cs.is_satisfied());
cs.set("num", r); cs.set("num", r);
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());

View File

@@ -0,0 +1,194 @@
use super::*;
use super::ecc::{
MontgomeryPoint,
EdwardsPoint
};
use super::boolean::Boolean;
use ::jubjub::*;
use bellman::{
ConstraintSystem
};
use super::lookup::*;
pub use pedersen_hash::Personalization;
impl Personalization {
fn get_constant_bools(&self) -> Vec<Boolean> {
self.get_bits()
.into_iter()
.map(|e| Boolean::constant(e))
.collect()
}
}
pub fn pedersen_hash<E: JubjubEngine, CS>(
mut cs: CS,
personalization: Personalization,
bits: &[Boolean],
params: &E::Params
) -> Result<EdwardsPoint<E>, SynthesisError>
where CS: ConstraintSystem<E>
{
let personalization = personalization.get_constant_bools();
assert_eq!(personalization.len(), 6);
let mut edwards_result = None;
let mut bits = personalization.iter().chain(bits.iter());
let mut segment_generators = params.pedersen_circuit_generators().iter();
let boolean_false = Boolean::constant(false);
let mut segment_i = 0;
loop {
let mut segment_result = None;
let mut segment_windows = &segment_generators.next()
.expect("enough segments")[..];
let mut window_i = 0;
while let Some(a) = bits.next() {
let b = bits.next().unwrap_or(&boolean_false);
let c = bits.next().unwrap_or(&boolean_false);
let tmp = lookup3_xy_with_conditional_negation(
cs.namespace(|| format!("segment {}, window {}", segment_i, window_i)),
&[a.clone(), b.clone(), c.clone()],
&segment_windows[0]
)?;
let tmp = MontgomeryPoint::interpret_unchecked(tmp.0, tmp.1);
match segment_result {
None => {
segment_result = Some(tmp);
},
Some(ref mut segment_result) => {
*segment_result = tmp.add(
cs.namespace(|| format!("addition of segment {}, window {}", segment_i, window_i)),
segment_result,
params
)?;
}
}
segment_windows = &segment_windows[1..];
if segment_windows.len() == 0 {
break;
}
window_i += 1;
}
match segment_result {
Some(segment_result) => {
// Convert this segment into twisted Edwards form.
let segment_result = segment_result.into_edwards(
cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)),
params
)?;
match edwards_result {
Some(ref mut edwards_result) => {
*edwards_result = segment_result.add(
cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)),
edwards_result,
params
)?;
},
None => {
edwards_result = Some(segment_result);
}
}
},
None => {
// We didn't process any new bits.
break;
}
}
segment_i += 1;
}
Ok(edwards_result.unwrap())
}
#[cfg(test)]
mod test {
use rand::{SeedableRng, Rng, XorShiftRng};
use super::*;
use ::circuit::test::*;
use ::circuit::boolean::{Boolean, AllocatedBit};
use pairing::bls12_381::{Bls12, Fr};
use pairing::PrimeField;
#[test]
fn test_pedersen_hash_constraints() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let params = &JubjubBls12::new();
let mut cs = TestConstraintSystem::<Bls12>::new();
let input: Vec<bool> = (0..(Fr::NUM_BITS * 2)).map(|_| rng.gen()).collect();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| {
Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap()
)
}).collect();
pedersen_hash(
cs.namespace(|| "pedersen hash"),
Personalization::NoteCommitment,
&input_bools,
params
).unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1377);
}
#[test]
fn test_pedersen_hash() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let params = &JubjubBls12::new();
for length in 0..751 {
for _ in 0..5 {
let mut input: Vec<bool> = (0..length).map(|_| rng.gen()).collect();
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| {
Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap()
)
}).collect();
let res = pedersen_hash(
cs.namespace(|| "pedersen hash"),
Personalization::MerkleTree(1),
&input_bools,
params
).unwrap();
assert!(cs.is_satisfied());
let expected = ::pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(1),
input.clone().into_iter(),
params
).into_xy();
assert_eq!(res.get_x().get_value().unwrap(), expected.0);
assert_eq!(res.get_y().get_value().unwrap(), expected.1);
// Test against the output of a different personalization
let unexpected = ::pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(0),
input.into_iter(),
params
).into_xy();
assert!(res.get_x().get_value().unwrap() != unexpected.0);
assert!(res.get_y().get_value().unwrap() != unexpected.1);
}
}
}
}

View File

@@ -1,15 +1,9 @@
//! Circuits for the [SHA-256] hash function and its internal compression
//! function.
//!
//! [SHA-256]: https://tools.ietf.org/html/rfc6234
use super::boolean::Boolean;
use super::multieq::MultiEq;
use super::uint32::UInt32; use super::uint32::UInt32;
use crate::{ConstraintSystem, SynthesisError}; use super::multieq::MultiEq;
use ff::ScalarEngine; use super::boolean::Boolean;
use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine;
#[allow(clippy::unreadable_literal)]
const ROUND_CONSTANTS: [u32; 64] = [ const ROUND_CONSTANTS: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
@@ -18,36 +12,37 @@ 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
]; ];
#[allow(clippy::unreadable_literal)]
const IV: [u32; 8] = [ const IV: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
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 where E: Engine, CS: ConstraintSystem<E>
E: ScalarEngine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(input.len(), 512); assert_eq!(input.len(), 512);
Ok( Ok(sha256_compression_function(
sha256_compression_function(&mut cs, &input, &get_sha256_iv())? &mut cs,
.into_iter() &input,
.flat_map(|e| e.into_bits_be()) &get_sha256_iv()
.collect(), )?
) .into_iter()
.flat_map(|e| e.into_bits_be())
.collect())
} }
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError> pub fn sha256<E, CS>(
where mut cs: CS,
E: ScalarEngine, input: &[Boolean]
CS: ConstraintSystem<E>, ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>
{ {
assert!(input.len() % 8 == 0); assert!(input.len() % 8 == 0);
@@ -67,10 +62,16 @@ where
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(cs.namespace(|| format!("block {}", i)), block, &cur)?; cur = sha256_compression_function(
cs.namespace(|| format!("block {}", i)),
block,
&cur
)?;
} }
Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect()) Ok(cur.into_iter()
.flat_map(|e| e.into_bits_be())
.collect())
} }
fn get_sha256_iv() -> Vec<UInt32> { fn get_sha256_iv() -> Vec<UInt32> {
@@ -80,19 +81,16 @@ 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 where E: Engine, CS: ConstraintSystem<E>
E: ScalarEngine,
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 let mut w = input.chunks(32)
.chunks(32) .map(|e| UInt32::from_bits_be(e))
.map(|e| UInt32::from_bits_be(e)) .collect::<Vec<_>>();
.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
@@ -102,18 +100,30 @@ where
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(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?; s0 = s0.xor(
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?; cs.namespace(|| "first xor for s0"),
&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(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?; s1 = s1.xor(
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?; cs.namespace(|| "first xor for s1"),
&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
@@ -124,21 +134,29 @@ where
enum Maybe { enum Maybe {
Deferred(Vec<UInt32>), Deferred(Vec<UInt32>),
Concrete(UInt32), Concrete(UInt32)
} }
impl Maybe { impl Maybe {
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError> fn compute<E, CS, M>(
where self,
E: ScalarEngine, cs: M,
CS: ConstraintSystem<E>, others: &[UInt32]
M: ConstraintSystem<E, Root = MultiEq<E, CS>>, ) -> Result<UInt32, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root=MultiEq<E, CS>>
{ {
Ok(match self { Ok(match self {
Maybe::Concrete(ref v) => return Ok(v.clone()), Maybe::Concrete(ref v) => {
return Ok(v.clone())
},
Maybe::Deferred(mut v) => { Maybe::Deferred(mut v) => {
v.extend(others.iter().cloned()); v.extend(others.into_iter().cloned());
UInt32::addmany(cs, &v)? UInt32::addmany(
cs,
&v
)?
} }
}) })
} }
@@ -159,11 +177,22 @@ where
// 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(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?; s1 = s1.xor(
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?; cs.namespace(|| "first xor for s1"),
&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(cs.namespace(|| "ch"), &new_e, &f, &g)?; let ch = UInt32::sha256_ch(
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![
@@ -171,17 +200,28 @@ where
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(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?; s0 = s0.xor(
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?; cs.namespace(|| "first xor for s0"),
&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(cs.namespace(|| "maj"), &new_a, &b, &c)?; let maj = UInt32::sha256_maj(
cs.namespace(|| "maj"),
&new_a,
&b,
&c
)?;
// temp2 := S0 + maj // temp2 := S0 + maj
let temp2 = vec![s0, maj]; let temp2 = vec![s0, maj];
@@ -204,13 +244,7 @@ where
d = c; d = c;
c = b; c = b;
b = new_a; b = new_a;
a = Maybe::Deferred( a = Maybe::Deferred(temp1.iter().cloned().chain(temp2.iter().cloned()).collect::<Vec<_>>());
temp1
.iter()
.cloned()
.chain(temp2.iter().cloned())
.collect::<Vec<_>>(),
);
} }
/* /*
@@ -227,42 +261,42 @@ where
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])
@@ -271,12 +305,10 @@ where
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::gadgets::boolean::AllocatedBit; use circuit::boolean::AllocatedBit;
use crate::gadgets::test::TestConstraintSystem;
use hex_literal::hex;
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng}; use circuit::test::TestConstraintSystem;
use rand_xorshift::XorShiftRng; use rand::{XorShiftRng, SeedableRng, Rng};
#[test] #[test]
fn test_blank_hash() { fn test_blank_hash() {
@@ -285,7 +317,11 @@ 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(&mut cs, &input_bits, &iv).unwrap(); let out = sha256_compression_function(
&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());
@@ -294,7 +330,7 @@ mod test {
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
let mut out = out_bits.into_iter(); let mut out = out_bits.into_iter();
for b in expected.iter() { for b in expected.into_iter() {
for i in (0..8).rev() { for i in (0..8).rev() {
let c = out.next().unwrap().get_value().unwrap(); let c = out.next().unwrap().get_value().unwrap();
@@ -305,27 +341,25 @@ mod test {
#[test] #[test]
fn test_full_block() { fn test_full_block() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
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) let input_bits: Vec<_> = (0..512).map(|i| {
.map(|i| { Boolean::from(
Boolean::from( AllocatedBit::alloc(
AllocatedBit::alloc( cs.namespace(|| format!("input bit {}", i)),
cs.namespace(|| format!("input bit {}", i)), Some(rng.gen())
Some(rng.next_u32() % 2 != 0), ).unwrap()
) )
.unwrap(), }).collect();
)
})
.collect();
sha256_compression_function(cs.namespace(|| "sha256"), &input_bits, &iv).unwrap(); sha256_compression_function(
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);
@@ -333,18 +367,18 @@ mod test {
#[test] #[test]
fn test_against_vectors() { fn test_against_vectors() {
use sha2::{Digest, Sha256}; use crypto::sha2::Sha256;
use crypto::digest::Digest;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
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.gen()).collect();
h.input(&data); h.input(&data);
let hash_result = h.result(); let mut hash_result = [0u8; 32];
h.result(&mut hash_result[..]);
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![]; let mut input_bits = vec![];
@@ -353,11 +387,7 @@ 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( input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into());
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
} }
} }
@@ -365,19 +395,17 @@ mod test {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut s = hash_result let mut s = hash_result.as_ref().iter()
.as_ref() .flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
.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,18 +1,20 @@
use bellman::gadgets::boolean::Boolean; use pairing::{Engine};
use bellman::gadgets::sha256::sha256;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine; use circuit::sha256::{
sha256
};
use circuit::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 where E: Engine, CS: ConstraintSystem<E>
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);
@@ -33,5 +35,8 @@ where
image.extend(rho.iter().cloned()); image.extend(rho.iter().cloned());
image.extend(r.iter().cloned()); image.extend(r.iter().cloned());
sha256(cs, &image) sha256(
cs,
&image
)
} }

View File

@@ -1,11 +1,16 @@
use bellman::gadgets::boolean::{AllocatedBit, Boolean}; use pairing::{Engine};
use bellman::gadgets::sha256::sha256_block_no_padding;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine; use circuit::sha256::{
sha256_block_no_padding
};
use circuit::boolean::{
AllocatedBit,
Boolean
};
use super::commitment::note_comm;
use super::prfs::*;
use super::*; use super::*;
use super::prfs::*;
use super::commitment::note_comm;
pub struct InputNote { pub struct InputNote {
pub nf: Vec<Boolean>, pub nf: Vec<Boolean>,
@@ -22,50 +27,66 @@ 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 where E: Engine, CS: ConstraintSystem<E>
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(cs.namespace(|| "rho"), rho.as_ref().map(|rho| &rho.0[..]))?; let rho = witness_u256(
cs.namespace(|| "rho"),
rho.as_ref().map(|rho| &rho.0[..])
)?;
let r = witness_u256(cs.namespace(|| "r"), r.as_ref().map(|r| &r.0[..]))?; let r = witness_u256(
cs.namespace(|| "r"),
r.as_ref().map(|r| &r.0[..])
)?;
let a_pk = prf_a_pk(cs.namespace(|| "a_pk computation"), &a_sk)?; let a_pk = prf_a_pk(
cs.namespace(|| "a_pk computation"),
&a_sk
)?;
let nf = prf_nf(cs.namespace(|| "nf computation"), &a_sk, &rho)?; let nf = prf_nf(
cs.namespace(|| "nf computation"),
&a_sk,
&rho
)?;
let mac = prf_pk(cs.namespace(|| "mac computation"), &a_sk, h_sig, nonce)?; let mac = prf_pk(
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
let mut cur = cm.clone(); let mut cur = cm.clone();
for (i, layer) in auth_path.iter().enumerate() { for (i, layer) in auth_path.into_iter().enumerate() {
let cs = &mut cs.namespace(|| format!("layer {}", i)); let cs = &mut cs.namespace(|| format!("layer {}", i));
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
@@ -73,16 +94,19 @@ 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(cs.namespace(|| "hash of this layer"), &preimage)?; cur = sha256_block_no_padding(
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
@@ -92,7 +116,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());
@@ -108,11 +132,14 @@ 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 { mac, nf }) Ok(InputNote {
mac: mac,
nf: nf
})
} }
} }
@@ -122,11 +149,9 @@ 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 where E: Engine, CS: ConstraintSystem<E>,
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);
@@ -139,9 +164,13 @@ where
let x = Boolean::from(AllocatedBit::alloc( let x = Boolean::from(AllocatedBit::alloc(
cs.namespace(|| "x"), cs.namespace(|| "x"),
condition condition.get_value().and_then(|v| {
.get_value() if v {
.and_then(|v| if v { rhs.get_value() } else { lhs.get_value() }), rhs.get_value()
} else {
lhs.get_value()
}
})
)?); )?);
// x = (1-condition)lhs + (condition)rhs // x = (1-condition)lhs + (condition)rhs
@@ -155,25 +184,33 @@ where
// 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()) - &lhs.lc(CS::one(), E::Fr::one()), |lc| lc + &rhs.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()) - &lhs.lc(CS::one(), E::Fr::one()), |lc| lc + &x.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 condition.get_value().and_then(|v| {
.get_value() if v {
.and_then(|v| if v { lhs.get_value() } else { rhs.get_value() }), lhs.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()) - &rhs.lc(CS::one(), E::Fr::one()), |lc| lc + &lhs.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()) - &rhs.lc(CS::one(), E::Fr::one()), |lc| lc + &y.lc(CS::one(), E::Fr::one())
- &rhs.lc(CS::one(), E::Fr::one())
); );
new_lhs.push(x); new_lhs.push(x);

View File

@@ -1,25 +1,15 @@
//! The "hybrid Sprout" circuit. use pairing::{Engine, Field};
//! use bellman::{ConstraintSystem, SynthesisError, Circuit, LinearCombination};
//! "Hybrid Sprout" refers to the implementation of the [Sprout statement] in use circuit::boolean::{
//! `bellman` for [`groth16`], instead of the [original implementation][oldimpl] AllocatedBit,
//! using [`libsnark`] for [BCTV14]. Boolean
//! };
//! [Sprout statement]: https://zips.z.cash/protocol/protocol.pdf#joinsplitstatement use circuit::multipack::pack_into_inputs;
//! [`groth16`]: bellman::groth16
//! [oldimpl]: https://github.com/zcash/zcash/tree/v2.0.7/src/zcash/circuit
//! [`libsnark`]: https://github.com/scipr-lab/libsnark
//! [BCTV14]: https://eprint.iacr.org/2013/879
use bellman::gadgets::boolean::{AllocatedBit, Boolean};
use bellman::gadgets::multipack::pack_into_inputs;
use bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError};
use ff::Field;
use pairing::Engine;
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::*;
@@ -46,29 +36,39 @@ 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>>(self, cs: &mut CS) -> Result<(), SynthesisError> { fn synthesize<CS: ConstraintSystem<E>>(
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(cs.namespace(|| "vpub_old"), self.vpub_old)?; let vpub_old = NoteValue::new(
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(cs.namespace(|| "vpub_new"), self.vpub_new)?; let vpub_new = NoteValue::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
@@ -79,17 +79,22 @@ 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(cs.namespace(|| "rt"), self.rt.as_ref().map(|v| &v[..])).unwrap(); let rt = witness_u256(
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(cs.namespace(|| "phi"), self.phi.as_ref().map(|v| &v[..])).unwrap(); let phi = witness_u252(
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;
@@ -104,14 +109,17 @@ impl<E: Engine> Circuit<E> for JoinSplit {
} }
// Allocate the value of the note // Allocate the value of the note
let value = NoteValue::new(cs.namespace(|| "value"), input.value)?; let value = NoteValue::new(
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
@@ -124,7 +132,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
@@ -139,8 +147,10 @@ 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 = let lhs_total = NoteValue::new(
NoteValue::new(cs.namespace(|| "total value of left hand side"), lhs_total)?; cs.namespace(|| "total value of left hand side"),
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
@@ -148,7 +158,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()
); );
} }
@@ -158,14 +168,17 @@ 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(cs.namespace(|| "value"), output.value)?; let value = NoteValue::new(
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
@@ -176,7 +189,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
@@ -189,7 +202,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![];
@@ -215,14 +228,15 @@ 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>(mut cs: CS, value: Option<u64>) -> Result<NoteValue, SynthesisError> fn new<E, CS>(
where mut cs: CS,
E: Engine, value: Option<u64>
CS: ConstraintSystem<E>, ) -> Result<NoteValue, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>,
{ {
let mut values; let mut values;
match value { match value {
@@ -232,7 +246,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];
} }
@@ -240,24 +254,28 @@ 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(AllocatedBit::alloc( bits.push(
cs.namespace(|| format!("bit {}", i)), AllocatedBit::alloc(
value, cs.namespace(|| format!("bit {}", i)),
)?); value
)?
);
} }
Ok(NoteValue { value, bits }) Ok(NoteValue {
value: value,
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 self.bits.chunks(8)
.chunks(8) .flat_map(|v| v.iter().rev())
.flat_map(|v| v.iter().rev()) .cloned()
.cloned() .map(|e| Boolean::from(e))
.map(Boolean::from) .collect()
.collect()
} }
/// Computes this value as a linear combination of /// Computes this value as a linear combination of
@@ -285,18 +303,15 @@ 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 where E: Engine, CS: ConstraintSystem<E>,
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 for b in value.iter()
.iter() .flat_map(|&m| (0..8).rev().map(move |i| m >> i & 1 == 1))
.flat_map(|&m| (0..8).rev().map(move |i| m >> i & 1 == 1)) .skip(skip_bits)
.skip(skip_bits)
{ {
tmp.push(Some(b)); tmp.push(Some(b));
} }
@@ -311,36 +326,37 @@ where
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>(cs: CS, value: Option<&[u8]>) -> Result<Vec<Boolean>, SynthesisError> fn witness_u256<E, CS>(
where cs: CS,
E: Engine, value: Option<&[u8]>,
CS: ConstraintSystem<E>, ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>,
{ {
witness_bits(cs, value, 256, 0) witness_bits(cs, value, 256, 0)
} }
fn witness_u252<E, CS>(cs: CS, value: Option<&[u8]>) -> Result<Vec<Boolean>, SynthesisError> fn witness_u252<E, CS>(
where cs: CS,
E: Engine, value: Option<&[u8]>,
CS: ConstraintSystem<E>, ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>,
{ {
witness_bits(cs, value, 252, 4) witness_bits(cs, value, 252, 4)
} }
#[test] #[test]
#[ignore]
fn test_sprout_constraints() { fn test_sprout_constraints() {
use bellman::gadgets::test::*; use pairing::bls12_381::{Bls12};
use pairing::bls12_381::Bls12; use ::circuit::test::*;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian};
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[..];
@@ -376,7 +392,9 @@ 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| p.1 = (position & 1) == 1); auth_path[i].as_mut().map(|p| {
p.1 = (position & 1) == 1
});
position >>= 1; position >>= 1;
} }
@@ -388,13 +406,15 @@ 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(JSInput { inputs.push(
value, JSInput {
a_sk, value: value,
rho, a_sk: a_sk,
r, rho: rho,
auth_path, r: r,
}); auth_path: auth_path
}
);
} }
let mut outputs = vec![]; let mut outputs = vec![];
@@ -405,7 +425,13 @@ 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(JSOutput { value, a_pk, r }); outputs.push(
JSOutput {
value: value,
a_pk: a_pk,
r: r
}
);
} }
let vpub_old = Some(test_vector.read_u64::<LittleEndian>().unwrap()); let vpub_old = Some(test_vector.read_u64::<LittleEndian>().unwrap());
@@ -421,13 +447,13 @@ fn test_sprout_constraints() {
let mac2 = get_u256(&mut test_vector); let mac2 = get_u256(&mut test_vector);
let js = JoinSplit { let js = JoinSplit {
vpub_old, vpub_old: vpub_old,
vpub_new, vpub_new: vpub_new,
h_sig, h_sig: h_sig,
phi, phi: phi,
inputs, inputs: inputs,
outputs, outputs: outputs,
rt, rt: rt
}; };
js.synthesize(&mut cs).unwrap(); js.synthesize(&mut cs).unwrap();
@@ -438,10 +464,7 @@ 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!( assert_eq!(cs.hash(), "1a228d3c6377130d1778c7885811dc8b8864049cb5af8aff7e6cd46c5bc4b84c");
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());
@@ -452,14 +475,10 @@ 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 expected_inputs.write_u64::<LittleEndian>(vpub_old.unwrap()).unwrap();
.write_u64::<LittleEndian>(vpub_old.unwrap()) expected_inputs.write_u64::<LittleEndian>(vpub_new.unwrap()).unwrap();
.unwrap();
expected_inputs
.write_u64::<LittleEndian>(vpub_new.unwrap())
.unwrap();
use bellman::gadgets::multipack; use circuit::multipack;
let expected_inputs = multipack::bytes_to_bits(&expected_inputs); let expected_inputs = multipack::bytes_to_bits(&expected_inputs);
let expected_inputs = multipack::compute_multipacking::<Bls12>(&expected_inputs); let expected_inputs = multipack::compute_multipacking::<Bls12>(&expected_inputs);

View File

@@ -1,46 +1,54 @@
use bellman::gadgets::boolean::Boolean; use pairing::{Engine};
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine; use circuit::boolean::{Boolean};
use super::commitment::note_comm;
use super::prfs::*;
use super::*; use super::*;
use super::prfs::*;
use super::commitment::note_comm;
pub struct OutputNote { pub struct OutputNote {
pub cm: Vec<Boolean>, pub cm: Vec<Boolean>
} }
impl OutputNote { impl OutputNote {
pub fn compute<E, CS>( pub fn compute<'a, E, CS>(
mut cs: CS, mut cs: CS,
a_pk: Option<PayingKey>, a_pk: Option<PayingKey>,
value: &NoteValue, value: &NoteValue,
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 where E: Engine, CS: ConstraintSystem<E>,
E: Engine,
CS: ConstraintSystem<E>,
{ {
let rho = prf_rho(cs.namespace(|| "rho"), phi, h_sig, nonce)?; let rho = prf_rho(
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(cs.namespace(|| "r"), r.as_ref().map(|r| &r.0[..]))?; let r = witness_u256(
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 { cm }) Ok(OutputNote {
cm: cm
})
} }
} }

View File

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

@@ -1,8 +1,17 @@
//! Helpers for testing circuit implementations. use pairing::{
Engine,
Field,
PrimeField,
PrimeFieldRepr
};
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; use bellman::{
LinearCombination,
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; SynthesisError,
ConstraintSystem,
Variable,
Index
};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
@@ -11,27 +20,27 @@ use byteorder::{BigEndian, ByteOrder};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use blake2s_simd::{Params as Blake2sParams, State as Blake2sState}; use blake2_rfc::blake2s::Blake2s;
#[derive(Debug)] #[derive(Debug)]
enum NamedObject { enum NamedObject {
Constraint(usize), Constraint(usize),
Var(Variable), Var(Variable),
Namespace, Namespace
} }
/// Constraint system for testing purposes. /// Constraint system for testing purposes.
pub struct TestConstraintSystem<E: ScalarEngine> { pub struct TestConstraintSystem<E: Engine> {
named_objects: HashMap<String, NamedObject>, named_objects: HashMap<String, NamedObject>,
current_namespace: Vec<String>, current_namespace: Vec<String>,
constraints: Vec<( constraints: Vec<(
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)]
@@ -43,7 +52,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
} }
} }
} }
@@ -58,17 +67,20 @@ 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: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> { fn proc_lc<E: Engine>(
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_with(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
@@ -86,7 +98,11 @@ fn proc_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVari
map map
} }
fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { fn hash_lc<E: Engine>(
terms: &[(Variable, E::Fr)],
h: &mut Blake2s
)
{
let map = proc_lc::<E>(terms); let map = proc_lc::<E>(terms);
let mut buf = [0u8; 9 + 32]; let mut buf = [0u8; 9 + 32];
@@ -98,7 +114,7 @@ fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
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);
@@ -111,17 +127,18 @@ fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
} }
} }
fn eval_lc<E: ScalarEngine>( 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);
@@ -131,20 +148,17 @@ fn eval_lc<E: ScalarEngine>(
acc acc
} }
impl<E: ScalarEngine> 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( map.insert("ONE".into(), NamedObject::Var(TestConstraintSystem::<E>::one()));
"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![]
} }
} }
@@ -157,9 +171,9 @@ impl<E: ScalarEngine> TestConstraintSystem<E> {
tmp tmp
}; };
let powers_of_two = (0..E::Fr::NUM_BITS) let powers_of_two = (0..E::Fr::NUM_BITS).map(|i| {
.map(|i| E::Fr::from_str("2").unwrap().pow(&[u64::from(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();
@@ -186,7 +200,7 @@ impl<E: ScalarEngine> 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();
} }
@@ -216,7 +230,7 @@ impl<E: ScalarEngine> TestConstraintSystem<E> {
} }
pub fn hash(&self) -> String { pub fn hash(&self) -> String {
let mut h = Blake2sParams::new().hash_length(32).to_state(); let mut h = Blake2s::new(32);
{ {
let mut buf = [0u8; 24]; let mut buf = [0u8; 24];
@@ -249,52 +263,57 @@ impl<E: ScalarEngine> 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)) => match v.get_unchecked() { Some(&NamedObject::Var(ref v)) => {
Index::Input(index) => self.inputs[index].0 = to, match v.get_unchecked() {
Index::Aux(index) => self.aux[index].0 = to, Index::Input(index) => self.inputs[index].0 = to,
}, Index::Aux(index) => self.aux[index].0 = to
Some(e) => panic!( }
"tried to set path `{}` to value, but `{:?}` already exists there.", }
path, e Some(e) => panic!("tried to set path `{}` to value, but `{:?}` already exists there.", 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
} }
} }
true return true;
} }
pub fn num_inputs(&self) -> usize { pub fn num_inputs(&self) -> usize {
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);
@@ -302,17 +321,17 @@ impl<E: ScalarEngine> 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)) => match v.get_unchecked() { Some(&NamedObject::Var(ref v)) => {
Index::Input(index) => self.inputs[index].0, match v.get_unchecked() {
Index::Aux(index) => self.aux[index].0, Index::Input(index) => self.inputs[index].0,
}, Index::Aux(index) => self.aux[index].0
Some(e) => panic!( }
"tried to get value of path `{}`, but `{:?}` exists there (not a variable)", }
path, e Some(e) => panic!("tried to get value of path `{}`, but `{:?}` exists there (not a variable)", path, e),
), _ => panic!("no variable exists at path: {}", path)
_ => panic!("no variable exists at path: {}", path),
} }
} }
@@ -333,7 +352,8 @@ 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 += "/";
} }
@@ -345,14 +365,15 @@ fn compute_path(ns: &[String], this: String) -> String {
name name
} }
impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> { impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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());
@@ -363,11 +384,12 @@ impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var) Ok(var)
} }
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc_input<F, A, AR>(
where &mut self,
F: FnOnce() -> Result<E::Fr, SynthesisError>, annotation: A,
A: FnOnce() -> AR, f: F
AR: Into<String>, ) -> Result<Variable, SynthesisError>
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());
@@ -378,13 +400,17 @@ impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var) Ok(var)
} }
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC) fn enforce<A, AR, LA, LB, LC>(
where &mut self,
A: FnOnce() -> AR, annotation: A,
AR: Into<String>, a: LA,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>, b: LB,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>, c: LC
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();
@@ -398,9 +424,7 @@ impl<E: ScalarEngine> 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 where NR: Into<String>, N: FnOnce() -> NR
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());
@@ -408,43 +432,47 @@ impl<E: ScalarEngine> 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
} }
} }
#[test] #[test]
fn test_cs() { fn test_cs() {
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use pairing::PrimeField;
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 let a = cs.namespace(|| "a").alloc(|| "var", || Ok(Fr::from_str("10").unwrap())).unwrap();
.namespace(|| "a") let b = cs.namespace(|| "b").alloc(|| "var", || Ok(Fr::from_str("4").unwrap())).unwrap();
.alloc(|| "var", || Ok(Fr::from_str("10").unwrap())) let c = cs.alloc(|| "product", || Ok(Fr::from_str("40").unwrap())).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(|| "mult", |lc| lc + a, |lc| lc + b, |lc| lc + c); cs.enforce(
|| "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(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b); cs.enforce(
|| "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,13 +1,19 @@
//! Circuit representation of a [`u32`], with helpers for the [`sha256`] use pairing::{
//! gadgets. Engine,
//! Field,
//! [`sha256`]: crate::gadgets::sha256 PrimeField
};
use ff::{Field, PrimeField, ScalarEngine}; use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination
};
use crate::{ConstraintSystem, LinearCombination, SynthesisError}; use super::boolean::{
Boolean,
use super::boolean::{AllocatedBit, Boolean}; AllocatedBit
};
use super::multieq::MultiEq; use super::multieq::MultiEq;
@@ -17,12 +23,13 @@ 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;
@@ -37,16 +44,18 @@ impl UInt32 {
} }
UInt32 { UInt32 {
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>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError> pub fn alloc<E, CS>(
where mut cs: CS,
E: ScalarEngine, value: Option<u32>
CS: ConstraintSystem<E>, ) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
let values = match value { let values = match value {
Some(mut val) => { Some(mut val) => {
@@ -58,28 +67,28 @@ impl UInt32 {
} }
v v
} },
None => vec![None; 32], None => vec![None; 32]
}; };
let bits = values let bits = values.into_iter()
.into_iter() .enumerate()
.enumerate() .map(|(i, v)| {
.map(|(i, v)| { Ok(Boolean::from(AllocatedBit::alloc(
Ok(Boolean::from(AllocatedBit::alloc( cs.namespace(|| format!("allocated bit {}", i)),
cs.namespace(|| format!("allocated bit {}", i)), v
v, )?))
)?)) })
}) .collect::<Result<Vec<_>, SynthesisError>>()?;
.collect::<Result<Vec<_>, SynthesisError>>()?;
Ok(UInt32 { bits, value }) Ok(UInt32 {
bits: bits,
value: value
})
} }
pub fn into_bits_be(self) -> Vec<Boolean> { pub fn into_bits_be(&self) -> Vec<Boolean> {
let mut ret = self.bits; self.bits.iter().rev().cloned().collect()
ret.reverse();
ret
} }
pub fn from_bits_be(bits: &[Boolean]) -> Self { pub fn from_bits_be(bits: &[Boolean]) -> Self {
@@ -90,30 +99,28 @@ 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) => { Some(true) => { value.as_mut().map(|v| *v |= 1); },
value.as_mut().map(|v| *v |= 1); Some(false) => {},
} None => { value = None; }
Some(false) => {}
None => {
value = None;
}
} }
} }
UInt32 { UInt32 {
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 self.bits.clone()
} }
/// 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();
@@ -122,50 +129,48 @@ impl UInt32 {
for b in new_bits.iter().rev() { for b in new_bits.iter().rev() {
value.as_mut().map(|v| *v <<= 1); value.as_mut().map(|v| *v <<= 1);
match *b { match b {
Boolean::Constant(b) => { &Boolean::Constant(b) => {
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,
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 let new_bits = self.bits.iter()
.bits .skip(by)
.iter() .chain(self.bits.iter())
.skip(by) .take(32)
.chain(self.bits.iter()) .cloned()
.take(32) .collect();
.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))
} }
} }
@@ -174,18 +179,17 @@ impl UInt32 {
let fill = Boolean::constant(false); let fill = Boolean::constant(false);
let new_bits = self let new_bits = self.bits
.bits .iter() // The bits are least significant first
.iter() // The bits are least significant first .skip(by) // Skip the bits that will be lost during the shift
.skip(by) // Skip the bits that will be lost during the shift .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros .take(32) // Only 32 bits needed!
.take(32) // Only 32 bits needed! .cloned()
.cloned() .collect();
.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)
} }
} }
@@ -195,99 +199,121 @@ 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 where E: Engine,
E: ScalarEngine, CS: ConstraintSystem<E>,
CS: ConstraintSystem<E>, F: Fn(u32, u32, u32) -> u32,
F: Fn(u32, u32, u32) -> u32, U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>
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(tri_fn(a, b, c)), (Some(a), Some(b), Some(c)) => {
_ => None, Some(tri_fn(a, b, c))
},
_ => None
}; };
let bits = a let bits = a.bits.iter()
.bits .zip(b.bits.iter())
.iter() .zip(c.bits.iter())
.zip(b.bits.iter()) .enumerate()
.zip(c.bits.iter()) .map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
.enumerate() .collect::<Result<_, _>>()?;
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
.collect::<Result<_, _>>()?;
Ok(UInt32 { Ok(UInt32 {
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>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError> pub fn sha256_maj<E, CS>(
where cs: CS,
E: ScalarEngine, a: &Self,
CS: ConstraintSystem<E>, b: &Self,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
Self::triop( Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ (a & c) ^ (b & c),
cs, |cs, i, a, b, c| {
a, Boolean::sha256_maj(
b, cs.namespace(|| format!("maj {}", i)),
c, a,
|a, b, c| (a & b) ^ (a & c) ^ (b & c), b,
|cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c), 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>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError> pub fn sha256_ch<E, CS>(
where cs: CS,
E: ScalarEngine, a: &Self,
CS: ConstraintSystem<E>, b: &Self,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
Self::triop( Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ ((!a) & c),
cs, |cs, i, a, b, c| {
a, Boolean::sha256_ch(
b, cs.namespace(|| format!("ch {}", i)),
c, a,
|a, b, c| (a & b) ^ ((!a) & c), b,
|cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c), c
)
}
) )
} }
/// XOR this `UInt32` with another `UInt32` /// XOR this `UInt32` with another `UInt32`
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError> pub fn xor<E, CS>(
where &self,
E: ScalarEngine, mut cs: CS,
CS: ConstraintSystem<E>, other: &Self
) -> 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 ^ b), (Some(a), Some(b)) => {
_ => None, Some(a ^ b)
},
_ => None
}; };
let bits = self let bits = self.bits.iter()
.bits .zip(other.bits.iter())
.iter() .enumerate()
.zip(other.bits.iter()) .map(|(i, (a, b))| {
.enumerate() Boolean::xor(
.map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b)) cs.namespace(|| format!("xor of bit {}", i)),
.collect::<Result<_, _>>()?; a,
b
)
})
.collect::<Result<_, _>>()?;
Ok(UInt32 { Ok(UInt32 {
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>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError> pub fn addmany<E, CS, M>(
where mut cs: M,
E: ScalarEngine, operands: &[Self]
CS: ConstraintSystem<E>, ) -> Result<Self, SynthesisError>
M: ConstraintSystem<E, Root = MultiEq<E, CS>>, where E: Engine,
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
@@ -297,7 +323,7 @@ impl UInt32 {
// Compute the maximum value of the sum so we allocate enough bits for // Compute the maximum value of the sum so we allocate enough bits for
// the result // the result
let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value())); let mut max_value = (operands.len() as u64) * (u32::max_value() as u64);
// Keep track of the resulting value // Keep track of the resulting value
let mut result_value = Some(0u64); let mut result_value = Some(0u64);
@@ -313,8 +339,8 @@ impl UInt32 {
// Accumulate the value // Accumulate the value
match op.value { match op.value {
Some(val) => { Some(val) => {
result_value.as_mut().map(|v| *v += u64::from(val)); 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
@@ -358,7 +384,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
@@ -379,53 +405,48 @@ 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 super::UInt32; use rand::{XorShiftRng, SeedableRng, Rng};
use crate::gadgets::boolean::Boolean; use ::circuit::boolean::{Boolean};
use crate::gadgets::multieq::MultiEq; use super::{UInt32};
use crate::gadgets::test::*; use pairing::bls12_381::{Bls12};
use crate::ConstraintSystem; use pairing::{Field};
use ff::Field; use ::circuit::test::*;
use pairing::bls12_381::Bls12; use bellman::{ConstraintSystem};
use rand_core::{RngCore, SeedableRng}; use circuit::multieq::MultiEq;
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([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let v = (0..32) let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::<Vec<_>>();
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits_be(&v); let b = UInt32::from_bits_be(&v);
for (i, bit) in b.bits.iter().enumerate() { for (i, bit) in b.bits.iter().enumerate() {
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!()
} }
} }
} }
@@ -433,34 +454,30 @@ 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([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let v = (0..32) let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::<Vec<_>>();
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits(&v); let b = UInt32::from_bits(&v);
for (i, bit) in b.bits.iter().enumerate() { for (i, bit) in b.bits.iter().enumerate() {
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!()
} }
} }
} }
@@ -468,17 +485,14 @@ mod test {
#[test] #[test]
fn test_uint32_xor() { fn test_uint32_xor() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let mut expected = a ^ b ^ c; let mut expected = a ^ b ^ c;
@@ -494,14 +508,14 @@ mod test {
assert!(r.value == Some(expected)); assert!(r.value == Some(expected));
for b in r.bits.iter() { for b in r.bits.iter() {
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));
} }
} }
@@ -513,17 +527,14 @@ 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([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let a_bit = UInt32::constant(a); let a_bit = UInt32::constant(a);
let b_bit = UInt32::constant(b); let b_bit = UInt32::constant(b);
@@ -533,18 +544,17 @@ mod test {
let r = { let r = {
let mut cs = MultiEq::new(&mut cs); let mut cs = MultiEq::new(&mut cs);
let r = let r = UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
r r
}; };
assert!(r.value == Some(expected)); assert!(r.value == Some(expected));
for b in r.bits.iter() { for b in r.bits.iter() {
match *b { match b {
Boolean::Is(_) => panic!(), &Boolean::Is(_) => panic!(),
Boolean::Not(_) => panic!(), &Boolean::Not(_) => panic!(),
Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
} }
@@ -556,18 +566,15 @@ mod test {
#[test] #[test]
fn test_uint32_addmany() { fn test_uint32_addmany() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let d = rng.next_u32(); let d: u32 = rng.gen();
let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d);
@@ -579,7 +586,8 @@ mod test {
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
let r = { let r = {
let mut cs = MultiEq::new(&mut cs); let mut cs = MultiEq::new(&mut cs);
UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap() let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap();
r
}; };
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -587,14 +595,16 @@ mod test {
assert!(r.value == Some(expected)); assert!(r.value == Some(expected));
for b in r.bits.iter() { for b in r.bits.iter() {
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;
@@ -613,12 +623,9 @@ mod test {
#[test] #[test]
fn test_uint32_rotr() { fn test_uint32_rotr() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let mut num = rng.next_u32(); let mut num = rng.gen();
let a = UInt32::constant(num); let a = UInt32::constant(num);
@@ -630,11 +637,11 @@ mod test {
let mut tmp = num; let mut tmp = num;
for b in &b.bits { for b in &b.bits {
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;
@@ -646,18 +653,15 @@ mod test {
#[test] #[test]
fn test_uint32_shr() { fn test_uint32_shr() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..50 { for _ in 0..50 {
for i in 0..60 { for i in 0..60 {
let num = rng.next_u32(); let num = rng.gen();
let a = UInt32::constant(num).shr(i); let a = UInt32::constant(num).shr(i);
let b = UInt32::constant(num.wrapping_shr(i as u32)); let b = UInt32::constant(num >> i);
assert_eq!(a.value.unwrap(), num.wrapping_shr(i as u32)); assert_eq!(a.value.unwrap(), num >> i);
assert_eq!(a.bits.len(), b.bits.len()); assert_eq!(a.bits.len(), b.bits.len());
for (a, b) in a.bits.iter().zip(b.bits.iter()) { for (a, b) in a.bits.iter().zip(b.bits.iter()) {
@@ -669,17 +673,14 @@ 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([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let mut expected = (a & b) ^ (a & c) ^ (b & c); let mut expected = (a & b) ^ (a & c) ^ (b & c);
@@ -697,10 +698,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));
} }
@@ -713,17 +714,14 @@ 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([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = rng.next_u32(); let a: u32 = rng.gen();
let b = rng.next_u32(); let b: u32 = rng.gen();
let c = rng.next_u32(); let c: u32 = rng.gen();
let mut expected = (a & b) ^ ((!a) & c); let mut expected = (a & b) ^ ((!a) & c);
@@ -741,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

@@ -1,34 +1,40 @@
//! Various constants used by the Zcash primitives.
/// First 64 bytes of the BLAKE2s input during group hash. /// First 64 bytes of the BLAKE2s input during group hash.
/// 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: &[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: &[u8; 8] = b"Zcashivk"; pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8]
= b"Zcashivk";
/// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho) /// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho)
pub const PRF_NF_PERSONALIZATION: &[u8; 8] = b"Zcash_nf"; pub const PRF_NF_PERSONALIZATION: &'static [u8; 8]
= 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: &[u8; 8] = b"Zcash_PH"; pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8]
= 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: &[u8; 8] = b"Zcash_gd"; pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8]
= 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: &[u8; 8] = b"Zcash_G_"; pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8]
= 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: &[u8; 8] = b"Zcash_H_"; pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8]
= 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: &[u8; 8] = b"Zcash_cv"; pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8]
= 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: &[u8; 8] = b"Zcash_J_"; pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8]
= b"Zcash_J_";

View File

@@ -0,0 +1,46 @@
use jubjub::{
JubjubEngine,
PrimeOrder,
edwards
};
use pairing::{
PrimeField
};
use blake2_rfc::blake2s::Blake2s;
use constants;
/// Produces a random point in the Jubjub curve.
/// The point is guaranteed to be prime order
/// and not the identity.
pub fn group_hash<E: JubjubEngine>(
tag: &[u8],
personalization: &[u8],
params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>>
{
assert_eq!(personalization.len(), 8);
// Check to see that scalar field is 255 bits
assert!(E::Fr::NUM_BITS == 255);
let mut h = Blake2s::with_params(32, &[], &[], personalization);
h.update(constants::GH_FIRST_BLOCK);
h.update(tag);
let h = h.finalize().as_ref().to_vec();
assert!(h.len() == 32);
match edwards::Point::<E, _>::read(&h[..], params) {
Ok(p) => {
let p = p.mul_by_cofactor(params);
if p != edwards::Point::zero() {
Some(p)
} else {
None
}
},
Err(_) => None
}
}

View File

@@ -1,56 +1,65 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use pairing::{
Field,
SqrtField,
PrimeField,
PrimeFieldRepr,
BitIterator
};
use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; use super::{
JubjubEngine,
JubjubParams,
Unknown,
PrimeOrder,
montgomery
};
use rand_core::RngCore; use rand::{
Rng
};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::io::{self, Read, Write}; use std::io::{
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.
// //
// 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
#[derive(Debug)]
pub struct Point<E: JubjubEngine, Subgroup> { pub struct Point<E: JubjubEngine, Subgroup> {
x: E::Fr, x: E::Fr,
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, PrimeOrder>> for Point<E, Unknown>
fn from(p: &Point<E, Unknown>) -> Point<E, Unknown> { {
p.clone() fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
} {
}
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for 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, Subgroup> Clone for Point<E, Subgroup>
fn from(p: &Point<E, PrimeOrder>) -> Point<E, Unknown> { {
convert_subgroup(p)
}
}
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
convert_subgroup(self) convert_subgroup(self)
} }
@@ -81,7 +90,11 @@ 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>(reader: R, params: &E::Params) -> io::Result<Self> { pub fn read<R: Read>(
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)?;
@@ -89,16 +102,22 @@ 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) => Self::get_for_y(y, x_sign, params) Ok(y) => {
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "not on curve")), match Self::get_for_y(y, x_sign, params) {
Err(_) => Err(io::Error::new( Some(p) => Ok(p),
io::ErrorKind::InvalidInput, None => {
"y is not in field", Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve"))
)), }
}
},
Err(_) => {
Err(io::Error::new(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.
@@ -130,34 +149,37 @@ impl<E: JubjubEngine> Point<E, Unknown> {
t.mul_assign(&y); t.mul_assign(&y);
Some(Point { Some(Point {
x, x: x,
y, y: y,
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: Rng>(rng: &mut R, params: &E::Params) -> Self
{
loop { loop {
let y = E::Fr::random(rng); let y: E::Fr = rng.gen();
let sign = rng.next_u32() % 2 != 0;
if let Some(p) = Self::get_for_y(y, sign, params) { if let Some(p) = Self::get_for_y(y, rng.gen(), params) {
return p; return p;
} }
} }
@@ -165,8 +187,12 @@ 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>(&self, writer: W) -> io::Result<()> { pub fn write<W: Write>(
let (x, y) = self.to_xy(); &self,
writer: W
) -> io::Result<()>
{
let (x, y) = self.into_xy();
assert_eq!(E::Fr::NUM_BITS, 255); assert_eq!(E::Fr::NUM_BITS, 255);
@@ -180,12 +206,16 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
} }
/// Convert from a Montgomery point /// Convert from a Montgomery point
pub fn from_montgomery(m: &montgomery::Point<E, Subgroup>, params: &E::Params) -> Self { pub fn from_montgomery(
match m.to_xy() { m: &montgomery::Point<E, Subgroup>,
params: &E::Params
) -> Self
{
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
@@ -218,7 +248,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
@@ -275,9 +305,9 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
Point { Point {
x: u, x: u,
y: v, y: v,
t, t: t,
z, z: z,
_marker: PhantomData, _marker: PhantomData
} }
} }
} }
@@ -300,12 +330,12 @@ 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
} }
} }
/// Convert to affine coordinates pub fn into_xy(&self) -> (E::Fr, E::Fr)
pub fn to_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;
@@ -392,12 +422,13 @@ 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
@@ -411,7 +442,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
b.mul_assign(&other.y); b.mul_assign(&other.y);
// C = d * t1 * t2 // C = d * t1 * t2
let mut c = *params.edwards_d(); let mut c = params.edwards_d().clone();
c.mul_assign(&self.t); c.mul_assign(&self.t);
c.mul_assign(&other.t); c.mul_assign(&other.t);
@@ -464,12 +495,17 @@ 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>>(&self, scalar: S, params: &E::Params) -> Self { pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(
&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,6 +1,3 @@
//! The [Jubjub] curve for efficient elliptic curve operations in circuits built
//! over [BLS12-381].
//!
//! Jubjub is a twisted Edwards curve defined over the BLS12-381 scalar //! Jubjub is a twisted Edwards curve defined over the BLS12-381 scalar
//! field, Fr. It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with //! field, Fr. It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with
//! `d = -(10240/10241)`. It is birationally equivalent to a Montgomery //! `d = -(10240/10241)`. It is birationally equivalent to a Montgomery
@@ -19,18 +16,22 @@
//! It is a complete twisted Edwards curve, so the equivalence with //! It is a complete twisted Edwards curve, so the equivalence with
//! the Montgomery curve forms a group isomorphism, allowing points //! the Montgomery curve forms a group isomorphism, allowing points
//! to be freely converted between the two forms. //! to be freely converted between the two forms.
//!
//! [Jubjub]: https://zips.z.cash/protocol/protocol.pdf#jubjub
//! [BLS12-381]: pairing::bls12_381
use ff::{Field, PrimeField, SqrtField}; use pairing::{
use pairing::Engine; Engine,
Field,
PrimeField,
SqrtField
};
use crate::group_hash::group_hash; use group_hash::group_hash;
use crate::constants; use constants;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{
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;
@@ -46,12 +47,10 @@ pub mod fs;
pub mod tests; pub mod tests;
/// Point of unknown order. /// Point of unknown order.
#[derive(Debug)] pub enum Unknown { }
pub enum Unknown {}
/// Point of prime order. /// Point of prime order.
#[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.
@@ -83,7 +82,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 {
@@ -128,7 +127,7 @@ pub trait JubjubParams<E: JubjubEngine>: Sized {
fn generator(&self, base: FixedGenerators) -> &edwards::Point<E, PrimeOrder>; fn generator(&self, base: FixedGenerators) -> &edwards::Point<E, PrimeOrder>;
/// Returns a window table [0, 1, ..., 8] for different magnitudes of some /// Returns a window table [0, 1, ..., 8] for different magnitudes of some
/// fixed generator. /// fixed generator.
fn circuit_generators(&self, _: FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>]; fn circuit_generators(&self, FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>];
/// Returns the window size for exponentiation of Pedersen hash generators /// Returns the window size for exponentiation of Pedersen hash generators
/// outside the circuit /// outside the circuit
fn pedersen_hash_exp_window_size() -> u32; fn pedersen_hash_exp_window_size() -> u32;
@@ -154,18 +153,10 @@ pub struct JubjubBls12 {
} }
impl JubjubParams<Bls12> for JubjubBls12 { impl JubjubParams<Bls12> for JubjubBls12 {
fn edwards_d(&self) -> &Fr { fn edwards_d(&self) -> &Fr { &self.edwards_d }
&self.edwards_d fn montgomery_a(&self) -> &Fr { &self.montgomery_a }
} fn montgomery_2a(&self) -> &Fr { &self.montgomery_2a }
fn montgomery_a(&self) -> &Fr { fn scale(&self) -> &Fr { &self.scale }
&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
} }
@@ -181,10 +172,12 @@ 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 {
@@ -200,19 +193,13 @@ impl JubjubBls12 {
let mut tmp_params = JubjubBls12 { let mut tmp_params = JubjubBls12 {
// d = -(10240/10241) // d = -(10240/10241)
edwards_d: Fr::from_str( edwards_d: Fr::from_str("19257038036680949359750312669786877991949435402254120286184196891950884077233").unwrap(),
"19257038036680949359750312669786877991949435402254120286184196891950884077233",
)
.unwrap(),
// A = 40962 // A = 40962
montgomery_a, montgomery_a: montgomery_a,
// 2A = 2.A // 2A = 2.A
montgomery_2a, montgomery_2a: montgomery_2a,
// scaling factor = sqrt(4 / (a - d)) // scaling factor = sqrt(4 / (a - d))
scale: Fr::from_str( scale: Fr::from_str("17814886934372412843466061268024708274627479829237077604635722030778476050649").unwrap(),
"17814886934372412843466061268024708274627479829237077604635722030778476050649",
)
.unwrap(),
// We'll initialize these below // We'll initialize these below
pedersen_hash_generators: vec![], pedersen_hash_generators: vec![],
@@ -222,29 +209,65 @@ impl JubjubBls12 {
fixed_base_circuit_generators: vec![], fixed_base_circuit_generators: vec![],
}; };
fn find_group_hash<E: JubjubEngine>(
m: &[u8],
personalization: &[u8; 8],
params: &E::Params
) -> edwards::Point<E, PrimeOrder>
{
let mut tag = m.to_vec();
let i = tag.len();
tag.push(0u8);
loop {
let gh = group_hash(
&tag,
personalization,
params
);
// We don't want to overflow and start reusing generators
assert!(tag[i] != u8::max_value());
tag[i] += 1;
if let Some(gh) = gh {
break gh;
}
}
}
// Create the bases for the Pedersen hashes // Create the bases for the Pedersen hashes
{ {
let mut pedersen_hash_generators = vec![]; let mut pedersen_hash_generators = vec![];
for m in 0..6 { for m in 0..5 {
use byteorder::{LittleEndian, WriteBytesExt}; use byteorder::{WriteBytesExt, LittleEndian};
let mut segment_number = [0u8; 4]; let mut segment_number = [0u8; 4];
(&mut segment_number[0..4]) (&mut segment_number[0..4]).write_u32::<LittleEndian>(m).unwrap();
.write_u32::<LittleEndian>(m)
.unwrap();
pedersen_hash_generators.push(JubjubBls12::find_group_hash( pedersen_hash_generators.push(
&segment_number, find_group_hash(
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &segment_number,
&tmp_params, constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
)); &tmp_params
)
);
}
// Check for duplicates, far worse than spec inconsistencies!
for (i, p1) in pedersen_hash_generators.iter().enumerate() {
if p1 == &edwards::Point::zero() {
panic!("Neutral element!");
}
for p2 in pedersen_hash_generators.iter().skip(i+1) {
if p1 == p2 {
panic!("Duplicate generator!");
}
}
} }
JubjubBls12::check_consistency_of_pedersen_hash_generators(
&tmp_params,
&pedersen_hash_generators,
);
tmp_params.pedersen_hash_generators = pedersen_hash_generators; tmp_params.pedersen_hash_generators = pedersen_hash_generators;
} }
@@ -286,50 +309,25 @@ 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 = let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize];
vec![edwards::Point::zero(); FixedGenerators::Max as usize];
fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] =
JubjubBls12::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] =
JubjubBls12::find_group_hash( find_group_hash(b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params);
b"r",
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NullifierPosition as usize] = fixed_base_generators[FixedGenerators::NullifierPosition as usize] =
JubjubBls12::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] =
JubjubBls12::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] =
JubjubBls12::find_group_hash( find_group_hash(b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
b"r",
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] =
JubjubBls12::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() {
@@ -337,7 +335,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!");
} }
@@ -353,7 +351,7 @@ impl JubjubBls12 {
let mut pedersen_circuit_generators = vec![]; let mut pedersen_circuit_generators = vec![];
// Process each segment // Process each segment
for gen in tmp_params.pedersen_hash_generators.iter().cloned() { for mut gen in tmp_params.pedersen_hash_generators.iter().cloned() {
let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params); let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params);
let mut windows = vec![]; let mut windows = vec![];
for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() { for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() {
@@ -363,7 +361,7 @@ impl JubjubBls12 {
// coeffs = g, g*2, g*3, g*4 // coeffs = g, g*2, g*3, g*4
for _ in 0..4 { for _ in 0..4 {
coeffs.push(g.to_xy().expect("cannot produce O")); coeffs.push(g.into_xy().expect("cannot produce O"));
g = g.add(&gen, &tmp_params); g = g.add(&gen, &tmp_params);
} }
windows.push(coeffs); windows.push(coeffs);
@@ -390,7 +388,7 @@ impl JubjubBls12 {
let mut coeffs = vec![(Fr::zero(), Fr::one())]; let mut coeffs = vec![(Fr::zero(), Fr::one())];
let mut g = gen.clone(); let mut g = gen.clone();
for _ in 0..7 { for _ in 0..7 {
coeffs.push(g.to_xy()); coeffs.push(g.into_xy());
g = g.add(&gen, &tmp_params); g = g.add(&gen, &tmp_params);
} }
windows.push(coeffs); windows.push(coeffs);
@@ -406,71 +404,10 @@ impl JubjubBls12 {
tmp_params tmp_params
} }
fn find_group_hash<E: JubjubEngine>(
m: &[u8],
personalization: &[u8; 8],
params: &E::Params,
) -> edwards::Point<E, PrimeOrder> {
let mut tag = m.to_vec();
let i = tag.len();
tag.push(0u8);
loop {
let gh = group_hash(&tag, personalization, params);
// We don't want to overflow and start reusing generators
assert!(tag[i] != u8::max_value());
tag[i] += 1;
if let Some(gh) = gh {
break gh;
}
}
}
/// Check for simple relations between the generators, that make finding collisions easy;
/// far worse than spec inconsistencies!
fn check_consistency_of_pedersen_hash_generators<E: JubjubEngine>(
tmp_params: &E::Params,
pedersen_hash_generators: &[edwards::Point<E, PrimeOrder>],
) {
for (i, p1) in pedersen_hash_generators.iter().enumerate() {
if p1 == &edwards::Point::zero() {
panic!("Neutral element!");
}
for p2 in pedersen_hash_generators.iter().skip(i + 1) {
if p1 == p2 {
panic!("Duplicate generator!");
}
if p1 == &p2.negate() {
panic!("Inverse generator!");
}
}
// check for a generator being the sum of any other two
for (j, p2) in pedersen_hash_generators.iter().enumerate() {
if j == i {
continue;
}
for (k, p3) in pedersen_hash_generators.iter().enumerate() {
if k == j || k == i {
continue;
}
let sum = &p2.add(&p3, &tmp_params);
if sum == p1 {
panic!("Linear relation between generators!");
}
}
}
}
}
} }
#[test] #[test]
fn test_jubjub_bls12() { fn test_jubjub_bls12() {
use hex_literal::hex;
let params = JubjubBls12::new(); let params = JubjubBls12::new();
tests::test_suite::<Bls12>(&params); tests::test_suite::<Bls12>(&params);
@@ -478,14 +415,10 @@ 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( Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
"22440861827555040311190986994816762244378363690614952020532787748720529117853",
)
.unwrap(),
false, false,
&params, &params
) ).unwrap();
.unwrap();
assert!(p == q); assert!(p == q);
@@ -493,46 +426,10 @@ 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( Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
"22440861827555040311190986994816762244378363690614952020532787748720529117853",
)
.unwrap(),
true, true,
&params, &params
) ).unwrap();
.unwrap();
assert!(p == q); assert!(p == q);
} }
#[test]
#[should_panic(expected = "Linear relation between generators!")]
fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() {
let params = JubjubBls12::new();
let mut pedersen_hash_generators: Vec<edwards::Point<Bls12, PrimeOrder>> = vec![];
use byteorder::{LittleEndian, WriteBytesExt};
for m in 0..5 {
let mut segment_number = [0u8; 4];
(&mut segment_number[0..4])
.write_u32::<LittleEndian>(m)
.unwrap();
let p = JubjubBls12::find_group_hash(
&segment_number,
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&params,
);
pedersen_hash_generators.push(p);
}
let p1 = pedersen_hash_generators[0].clone();
let p2 = pedersen_hash_generators[1].clone();
//test for linear relation
pedersen_hash_generators.push(p1.add(&p2, &params));
JubjubBls12::check_consistency_of_pedersen_hash_generators(&params, &pedersen_hash_generators);
}

View File

@@ -1,8 +1,22 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use pairing::{
Field,
SqrtField,
PrimeField,
PrimeFieldRepr,
BitIterator
};
use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; use super::{
JubjubEngine,
JubjubParams,
Unknown,
PrimeOrder,
edwards
};
use rand_core::RngCore; use rand::{
Rng
};
use std::marker::PhantomData; use std::marker::PhantomData;
@@ -11,25 +25,29 @@ 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)
} }
@@ -40,13 +58,16 @@ 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) => self.x == other.x && self.y == other.y, (false, false) => {
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;
@@ -64,32 +85,38 @@ impl<E: JubjubEngine> Point<E, Unknown> {
y.negate(); y.negate();
} }
Some(Point { return Some(Point {
x, x: x,
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: Rng>(rng: &mut R, params: &E::Params) -> Self
{
loop { loop {
let x = E::Fr::random(rng); let x: E::Fr = rng.gen();
let sign = rng.next_u32() % 2 != 0;
if let Some(p) = Self::get_for_x(x, sign, params) { match Self::get_for_x(x, rng.gen(), params) {
return p; Some(p) => {
return p
},
None => {}
} }
} }
} }
@@ -97,8 +124,12 @@ 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(e: &edwards::Point<E, Subgroup>, params: &E::Params) -> Self { pub fn from_edwards(
let (x, y) = e.to_xy(); e: &edwards::Point<E, Subgroup>,
params: &E::Params
) -> Self
{
let (x, y) = e.into_xy();
if y == E::Fr::one() { if y == E::Fr::one() {
// The only solution for y = 1 is x = 0. (0, 1) is // The only solution for y = 1 is x = 0. (0, 1) is
@@ -125,7 +156,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.
@@ -152,7 +183,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: u, x: u,
y: v, y: v,
infinity: false, infinity: false,
_marker: PhantomData, _marker: PhantomData
} }
} }
} }
@@ -173,11 +204,12 @@ 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 to_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 {
@@ -213,7 +245,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
let mut delta = E::Fr::one(); let mut delta = E::Fr::one();
{ {
let mut tmp = *params.montgomery_a(); let mut tmp = params.montgomery_a().clone();
tmp.mul_assign(&self.x); tmp.mul_assign(&self.x);
tmp.double(); tmp.double();
delta.add_assign(&tmp); delta.add_assign(&tmp);
@@ -247,12 +279,13 @@ 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
@@ -275,10 +308,7 @@ 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( delta.mul_assign(&tmp.inverse().expect("self.x != other.x, so this must be nonzero"));
&tmp.inverse()
.expect("self.x != other.x, so this must be nonzero"),
);
} }
let mut x3 = delta; let mut x3 = delta;
@@ -297,7 +327,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
x: x3, x: x3,
y: y3, y: y3,
infinity: false, infinity: false,
_marker: PhantomData, _marker: PhantomData
} }
} }
} }
@@ -305,7 +335,12 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
} }
#[must_use] #[must_use]
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(&self, scalar: S, params: &E::Params) -> Self { pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(
&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,9 +1,20 @@
use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; use super::{
JubjubEngine,
JubjubParams,
PrimeOrder,
montgomery,
edwards
};
use ff::{Field, LegendreSymbol, PrimeField, PrimeFieldRepr, SqrtField}; use pairing::{
Field,
PrimeField,
PrimeFieldRepr,
SqrtField,
LegendreSymbol
};
use rand_core::{RngCore, SeedableRng}; use rand::{XorShiftRng, SeedableRng, Rand};
use rand_xorshift::XorShiftRng;
pub fn test_suite<E: JubjubEngine>(params: &E::Params) { pub fn test_suite<E: JubjubEngine>(params: &E::Params) {
test_back_and_forth::<E>(params); test_back_and_forth::<E>(params);
@@ -18,7 +29,12 @@ 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>>(x: E::Fr, y: E::Fr, params: &P) -> bool { fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(
x: E::Fr,
y: E::Fr,
params: &P
) -> bool
{
let mut lhs = y; let mut lhs = y;
lhs.square(); lhs.square();
@@ -39,8 +55,9 @@ fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(x: E::Fr, y: E::Fr, par
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();
@@ -61,10 +78,7 @@ fn is_on_twisted_edwards_curve<E: JubjubEngine, P: JubjubParams<E>>(
} }
fn test_loworder<E: JubjubEngine>(params: &E::Params) { fn test_loworder<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let inf = montgomery::Point::zero(); let inf = montgomery::Point::zero();
// try to find a point of order 8 // try to find a point of order 8
@@ -95,18 +109,15 @@ fn test_loworder<E: JubjubEngine>(params: &E::Params) {
fn test_mul_associativity<E: JubjubEngine>(params: &E::Params) { fn test_mul_associativity<E: JubjubEngine>(params: &E::Params) {
use self::edwards::Point; use self::edwards::Point;
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
// Pick a random point and multiply it by the cofactor // Pick a random point and multiply it by the cofactor
let base = Point::<E, _>::rand(rng, params).mul_by_cofactor(params); let base = Point::<E, _>::rand(rng, params).mul_by_cofactor(params);
let mut a = E::Fs::random(rng); let mut a = E::Fs::rand(rng);
let b = E::Fs::random(rng); let b = E::Fs::rand(rng);
let c = E::Fs::random(rng); let c = E::Fs::rand(rng);
let res1 = base.mul(a, params).mul(b, params).mul(c, params); let res1 = base.mul(a, params).mul(b, params).mul(c, params);
let res2 = base.mul(b, params).mul(c, params).mul(a, params); let res2 = base.mul(b, params).mul(c, params).mul(a, params);
@@ -119,28 +130,23 @@ fn test_mul_associativity<E: JubjubEngine>(params: &E::Params) {
assert!(res2 == res3); assert!(res2 == res3);
assert!(res3 == res4); assert!(res3 == res4);
let (x, y) = res1.to_xy(); let (x, y) = res1.into_xy();
assert!(is_on_twisted_edwards_curve(x, y, params)); assert!(is_on_twisted_edwards_curve(x, y, params));
let (x, y) = res2.to_xy(); let (x, y) = res2.into_xy();
assert!(is_on_twisted_edwards_curve(x, y, params)); assert!(is_on_twisted_edwards_curve(x, y, params));
let (x, y) = res3.to_xy(); let (x, y) = res3.into_xy();
assert!(is_on_twisted_edwards_curve(x, y, params)); assert!(is_on_twisted_edwards_curve(x, y, params));
} }
} }
fn test_order<E: JubjubEngine>(params: &E::Params) { fn test_order<E: JubjubEngine>(params: &E::Params) {
use self::edwards::Point; use self::edwards::Point;
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
// The neutral element is in the prime order subgroup. // The neutral element is in the prime order subgroup.
assert!(Point::<E, PrimeOrder>::zero() assert!(Point::<E, PrimeOrder>::zero().as_prime_order(params).is_some());
.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
@@ -164,10 +170,7 @@ fn test_order<E: JubjubEngine>(params: &E::Params) {
} }
fn test_addition_associativity<E: JubjubEngine>(params: &E::Params) { fn test_addition_associativity<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
use self::montgomery::Point; use self::montgomery::Point;
@@ -191,10 +194,7 @@ fn test_addition_associativity<E: JubjubEngine>(params: &E::Params) {
} }
fn test_identities<E: JubjubEngine>(params: &E::Params) { fn test_identities<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
{ {
use self::edwards::Point; use self::edwards::Point;
@@ -228,28 +228,26 @@ fn test_identities<E: JubjubEngine>(params: &E::Params) {
} }
fn test_get_for<E: JubjubEngine>(params: &E::Params) { fn test_get_for<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let y = E::Fr::random(rng); let y = E::Fr::rand(rng);
let sign = rng.next_u32() % 2 == 1; let sign = bool::rand(rng);
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.to_xy().0.into_repr().is_odd() == sign); assert!(p.into_xy().0.into_repr().is_odd() == sign);
p = p.negate(); p = p.negate();
assert!(edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap() == p); assert!(
edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap()
==
p
);
} }
} }
} }
fn test_read_write<E: JubjubEngine>(params: &E::Params) { fn test_read_write<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let e = edwards::Point::<E, _>::rand(rng, params); let e = edwards::Point::<E, _>::rand(rng, params);
@@ -264,35 +262,29 @@ fn test_read_write<E: JubjubEngine>(params: &E::Params) {
} }
fn test_rand<E: JubjubEngine>(params: &E::Params) { fn test_rand<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let p = montgomery::Point::<E, _>::rand(rng, params); let p = montgomery::Point::<E, _>::rand(rng, params);
let e = edwards::Point::<E, _>::rand(rng, params); let e = edwards::Point::<E, _>::rand(rng, params);
{ {
let (x, y) = p.to_xy().unwrap(); let (x, y) = p.into_xy().unwrap();
assert!(is_on_mont_curve(x, y, params)); assert!(is_on_mont_curve(x, y, params));
} }
{ {
let (x, y) = e.to_xy(); let (x, y) = e.into_xy();
assert!(is_on_twisted_edwards_curve(x, y, params)); assert!(is_on_twisted_edwards_curve(x, y, params));
} }
} }
} }
fn test_back_and_forth<E: JubjubEngine>(params: &E::Params) { fn test_back_and_forth<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x5d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let s = E::Fs::random(rng); let s = E::Fs::rand(rng);
let edwards_p1 = edwards::Point::<E, _>::rand(rng, params); let edwards_p1 = edwards::Point::<E, _>::rand(rng, params);
let mont_p1 = montgomery::Point::from_edwards(&edwards_p1, params); let mont_p1 = montgomery::Point::from_edwards(&edwards_p1, params);
let mont_p2 = montgomery::Point::<E, _>::rand(rng, params); let mont_p2 = montgomery::Point::<E, _>::rand(rng, params);
@@ -301,9 +293,13 @@ 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!(montgomery::Point::from_edwards(&edwards, params) == mont); assert!(
montgomery::Point::from_edwards(&edwards, params) == mont
);
assert!(edwards::Point::from_montgomery(&mont, params) == edwards); assert!(
edwards::Point::from_montgomery(&mont, params) == edwards
);
} }
} }
@@ -387,7 +383,8 @@ 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();

Some files were not shown because too many files have changed in this diff Show More