Merge pull request #3 from gilardh/master

paper librustzcash sync
This commit is contained in:
Duke Leto
2020-03-14 08:09:30 -07:00
committed by GitHub
174 changed files with 22122 additions and 12729 deletions

120
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,120 @@
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

@@ -1,8 +1,19 @@
language: rust
rust:
- 1.32.0
- 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

982
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,11 +5,10 @@ members = [
"group",
"librustzcash",
"pairing",
"sapling-crypto",
"zcash_client_backend",
"zcash_history",
"zcash_primitives",
"zcash_proofs",
"zcash_wallet",
"zip32",
]
[profile.release]

View File

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

View File

@@ -1,12 +1,23 @@
# bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) #
This is a research project being built for [Zcash](https://z.cash/).
`bellman` is a crate for building zk-SNARK circuits. It provides circuit traits
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
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* 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.

View File

@@ -1,21 +1,20 @@
//! This module contains an `EvaluationDomain` abstraction for
//! performing various kinds of polynomial arithmetic on top of
//! the scalar field.
//! This module contains an [`EvaluationDomain`] abstraction for performing
//! various kinds of polynomial arithmetic on top of the scalar field.
//!
//! In pairing-based SNARKs like Groth16, we need to calculate
//! a quotient polynomial over a target polynomial with roots
//! at distinct points associated with each constraint of the
//! constraint system. In order to be efficient, we choose these
//! roots to be the powers of a 2^n root of unity in the field.
//! This allows us to perform polynomial operations in O(n)
//! by performing an O(n log n) FFT over such a domain.
//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient
//! polynomial over a target polynomial with roots at distinct points associated
//! with each constraint of the constraint system. In order to be efficient, we
//! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the
//! field. 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 group::CurveProjective;
use super::{
SynthesisError
};
use super::SynthesisError;
use super::multicore::Worker;
@@ -25,24 +24,27 @@ pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
omega: E::Fr,
omegainv: E::Fr,
geninv: E::Fr,
minv: E::Fr
minv: E::Fr,
}
impl<E: ScalarEngine, G: Group<E>> AsRef<[G]> for EvaluationDomain<E, G> {
fn as_ref(&self) -> &[G] {
&self.coeffs
}
}
impl<E: ScalarEngine, G: Group<E>> AsMut<[G]> for EvaluationDomain<E, G> {
fn as_mut(&mut self) -> &mut [G] {
&mut self.coeffs
}
}
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
pub fn as_ref(&self) -> &[G] {
&self.coeffs
}
pub fn as_mut(&mut self) -> &mut [G] {
&mut self.coeffs
}
pub fn into_coeffs(self) -> Vec<G> {
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
let mut m = 1;
let mut exp = 0;
@@ -53,7 +55,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
// The pairing-friendly curve may not be able to support
// large enough (radix2) evaluation domains.
if exp >= E::Fr::S {
return Err(SynthesisError::PolynomialDegreeTooLarge)
return Err(SynthesisError::PolynomialDegreeTooLarge);
}
}
@@ -67,29 +69,30 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
coeffs.resize(m, G::group_zero());
Ok(EvaluationDomain {
coeffs: coeffs,
exp: exp,
omega: omega,
coeffs,
exp,
omega,
omegainv: omega.inverse().unwrap(),
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap()
minv: E::Fr::from_str(&format!("{}", m))
.unwrap()
.inverse()
.unwrap(),
})
}
pub fn fft(&mut self, worker: &Worker)
{
pub fn fft(&mut self, worker: &Worker) {
best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
}
pub fn ifft(&mut self, worker: &Worker)
{
pub fn ifft(&mut self, worker: &Worker) {
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
worker.scope(self.coeffs.len(), |scope, chunk| {
let minv = self.minv;
for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || {
scope.spawn(move |_scope| {
for v in v {
v.group_mul_assign(&minv);
}
@@ -98,11 +101,10 @@ 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| {
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
scope.spawn(move || {
scope.spawn(move |_scope| {
let mut u = g.pow(&[(i * chunk) as u64]);
for v in v.iter_mut() {
v.group_mul_assign(&u);
@@ -113,14 +115,12 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
});
}
pub fn coset_fft(&mut self, worker: &Worker)
{
pub fn coset_fft(&mut self, worker: &Worker) {
self.distribute_powers(worker, E::Fr::multiplicative_generator());
self.fft(worker);
}
pub fn icoset_fft(&mut self, worker: &Worker)
{
pub fn icoset_fft(&mut self, worker: &Worker) {
let geninv = self.geninv;
self.ifft(worker);
@@ -139,13 +139,15 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
/// The target polynomial is the zero polynomial in our
/// evaluation domain, so we must perform division over
/// a coset.
pub fn divide_by_z_on_coset(&mut self, worker: &Worker)
{
let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap();
pub fn divide_by_z_on_coset(&mut self, worker: &Worker) {
let i = self
.z(&E::Fr::multiplicative_generator())
.inverse()
.unwrap();
worker.scope(self.coeffs.len(), |scope, chunk| {
for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || {
scope.spawn(move |_scope| {
for v in v {
v.group_mul_assign(&i);
}
@@ -159,8 +161,12 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
scope.spawn(move || {
for (a, b) in self
.coeffs
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move |_scope| {
for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_mul_assign(&b.0);
}
@@ -174,8 +180,12 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
scope.spawn(move || {
for (a, b) in self
.coeffs
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move |_scope| {
for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_sub_assign(&b);
}
@@ -200,7 +210,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> {
fn clone(&self) -> Point<G> {
@@ -231,7 +241,7 @@ impl<E: ScalarEngine> PartialEq for Scalar<E> {
}
}
impl<E: ScalarEngine> Copy for Scalar<E> { }
impl<E: ScalarEngine> Copy for Scalar<E> {}
impl<E: ScalarEngine> Clone for Scalar<E> {
fn clone(&self) -> Scalar<E> {
@@ -254,8 +264,7 @@ impl<E: ScalarEngine> Group<E> for Scalar<E> {
}
}
fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32)
{
fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) {
let log_cpus = worker.log_num_cpus();
if log_n <= log_cpus {
@@ -265,8 +274,7 @@ fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &
}
}
fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32)
{
fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32) {
fn bitreverse(mut n: u32, l: u32) -> u32 {
let mut r = 0;
for _ in 0..l {
@@ -288,22 +296,22 @@ fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u
let mut m = 1;
for _ in 0..log_n {
let w_m = omega.pow(&[(n / (2*m)) as u64]);
let w_m = omega.pow(&[u64::from(n / (2 * m))]);
let mut k = 0;
while k < n {
let mut w = E::Fr::one();
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);
let mut tmp = a[(k+j) as usize];
let mut tmp = a[(k + j) as usize];
tmp.group_sub_assign(&t);
a[(k+j+m) as usize] = tmp;
a[(k+j) as usize].group_add_assign(&t);
a[(k + j + m) as usize] = tmp;
a[(k + j) as usize].group_add_assign(&t);
w.mul_assign(&w_m);
}
k += 2*m;
k += 2 * m;
}
m *= 2;
@@ -315,9 +323,8 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
worker: &Worker,
omega: &E::Fr,
log_n: u32,
log_cpus: u32
)
{
log_cpus: u32,
) {
assert!(log_n >= log_cpus);
let num_cpus = 1 << log_cpus;
@@ -329,18 +336,18 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
let a = &*a;
for (j, tmp) in tmp.iter_mut().enumerate() {
scope.spawn(move || {
scope.spawn(move |_scope| {
// Shuffle into a sub-FFT
let omega_j = omega.pow(&[j as u64]);
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
let mut elt = E::Fr::one();
for i in 0..(1 << log_new_n) {
for (i, tmp) in tmp.iter_mut().enumerate() {
for s in 0..num_cpus {
let idx = (i + (s << log_new_n)) % (1 << log_n);
let mut t = a[idx];
t.group_mul_assign(&elt);
tmp[i].group_add_assign(&t);
tmp.group_add_assign(&t);
elt.mul_assign(&omega_step);
}
elt.mul_assign(&omega_j);
@@ -357,7 +364,7 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
let tmp = &tmp;
for (idx, a) in a.chunks_mut(chunk).enumerate() {
scope.spawn(move || {
scope.spawn(move |_scope| {
let mut idx = idx * chunk;
let mask = (1 << log_cpus) - 1;
for a in a {
@@ -375,16 +382,19 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
#[test]
fn polynomial_arith() {
use pairing::bls12_381::Bls12;
use rand::{self, Rand};
use rand_core::RngCore;
fn test_mul<E: ScalarEngine, R: rand::Rng>(rng: &mut R)
{
fn test_mul<E: ScalarEngine, R: RngCore>(rng: &mut R) {
let worker = Worker::new();
for coeffs_a in 0..70 {
for coeffs_b in 0..70 {
let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
let mut a: Vec<_> = (0..coeffs_a)
.map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect();
let mut b: Vec<_> = (0..coeffs_b)
.map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect();
// naive evaluation
let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b];
@@ -423,10 +433,9 @@ fn polynomial_arith() {
#[test]
fn fft_composition() {
use pairing::bls12_381::Bls12;
use rand;
use rand_core::RngCore;
fn test_comp<E: ScalarEngine, R: rand::Rng>(rng: &mut R)
{
fn test_comp<E: ScalarEngine, R: RngCore>(rng: &mut R) {
let worker = Worker::new();
for coeffs in 0..10 {
@@ -434,7 +443,7 @@ fn fft_composition() {
let mut v = vec![];
for _ in 0..coeffs {
v.push(Scalar::<E>(rng.gen()));
v.push(Scalar::<E>(E::Fr::random(rng)));
}
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
@@ -462,22 +471,23 @@ fn fft_composition() {
#[test]
fn parallel_fft_consistency() {
use pairing::bls12_381::Bls12;
use rand::{self, Rand};
use rand_core::RngCore;
use std::cmp::min;
fn test_consistency<E: ScalarEngine, R: rand::Rng>(rng: &mut R)
{
fn test_consistency<E: ScalarEngine, R: RngCore>(rng: &mut R) {
let worker = Worker::new();
for _ in 0..5 {
for log_d in 0..10 {
let d = 1 << log_d;
let v1 = (0..d).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect::<Vec<_>>();
let v1 = (0..d)
.map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect::<Vec<_>>();
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
let mut 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);
serial_fft(&mut v2.coeffs, &v2.omega, log_d);

View File

@@ -1,23 +1,17 @@
#[cfg(test)]
//! Self-contained sub-circuit implementations for various primitives.
pub mod test;
pub mod boolean;
pub mod multieq;
pub mod uint32;
pub mod blake2s;
pub mod num;
pub mod boolean;
pub mod lookup;
pub mod ecc;
pub mod pedersen_hash;
pub mod multieq;
pub mod multipack;
pub mod num;
pub mod sha256;
pub mod uint32;
pub mod sapling;
pub mod sprout;
use bellman::{
SynthesisError
};
use crate::SynthesisError;
// TODO: This should probably be removed and we
// should use existing helper methods on `Option`
@@ -25,7 +19,7 @@ use bellman::{
/// This basically is just an extension to `Option`
/// which allows for a convenient mapping to an
/// error on `None`.
trait Assignment<T> {
pub trait Assignment<T> {
fn get(&self) -> Result<&T, SynthesisError>;
}
@@ -33,7 +27,7 @@ impl<T> Assignment<T> for Option<T> {
fn get(&self) -> Result<&T, SynthesisError> {
match *self {
Some(ref v) => Ok(v),
None => Err(SynthesisError::AssignmentMissing)
None => Err(SynthesisError::AssignmentMissing),
}
}
}

View File

@@ -0,0 +1,697 @@
//! 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,23 +1,16 @@
use ff::Field;
use pairing::Engine;
//! Window table lookup gadgets.
use ff::{Field, ScalarEngine};
use super::*;
use super::num::{
AllocatedNum,
Num
};
use super::boolean::Boolean;
use bellman::{
ConstraintSystem
};
use super::num::{AllocatedNum, Num};
use super::*;
use crate::ConstraintSystem;
// Synthesize the constants for each base pattern.
fn synth<'a, E: Engine, I>(
window_size: usize,
constants: I,
assignment: &mut [E::Fr]
)
where I: IntoIterator<Item=&'a E::Fr>
fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
where
I: IntoIterator<Item = &'a E::Fr>,
{
assert_eq!(assignment.len(), 1 << window_size);
@@ -36,19 +29,23 @@ fn synth<'a, E: Engine, I>(
/// Performs a 3-bit window table lookup. `bits` is in
/// little-endian order.
pub fn lookup3_xy<E: Engine, CS>(
pub fn lookup3_xy<E: ScalarEngine, CS>(
mut cs: CS,
bits: &[Boolean],
coords: &[(E::Fr, E::Fr)]
coords: &[(E::Fr, E::Fr)],
) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError>
where CS: ConstraintSystem<E>
where
CS: ConstraintSystem<E>,
{
assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 8);
// Calculate the index into `coords`
let i =
match (bits[0].get_value(), bits[1].get_value(), bits[2].get_value()) {
let i = match (
bits[0].get_value(),
bits[1].get_value(),
bits[2].get_value(),
) {
(Some(a_value), Some(b_value), Some(c_value)) => {
let mut tmp = 0;
if a_value {
@@ -61,25 +58,15 @@ pub fn lookup3_xy<E: Engine, CS>(
tmp += 4;
}
Some(tmp)
},
_ => None
}
_ => None,
};
// 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
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
let mut x_coeffs = [E::Fr::zero(); 8];
@@ -93,30 +80,38 @@ pub fn lookup3_xy<E: Engine, CS>(
cs.enforce(
|| "x-coordinate lookup",
|lc| lc + (x_coeffs[0b001], one)
|lc| {
lc + (x_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, x_coeffs[0b011])
+ &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 + res_x.get_variable()
|lc| {
lc + res_x.get_variable()
- (x_coeffs[0b000], one)
- &bits[1].lc::<E>(one, x_coeffs[0b010])
- &bits[2].lc::<E>(one, x_coeffs[0b100])
- &precomp.lc::<E>(one, x_coeffs[0b110]),
- &precomp.lc::<E>(one, x_coeffs[0b110])
},
);
cs.enforce(
|| "y-coordinate lookup",
|lc| lc + (y_coeffs[0b001], one)
|lc| {
lc + (y_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, y_coeffs[0b011])
+ &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 + res_y.get_variable()
|lc| {
lc + res_y.get_variable()
- (y_coeffs[0b000], one)
- &bits[1].lc::<E>(one, y_coeffs[0b010])
- &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))
@@ -124,19 +119,19 @@ pub fn lookup3_xy<E: Engine, CS>(
/// Performs a 3-bit window table lookup, where
/// one of the bits is a sign bit.
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
pub fn lookup3_xy_with_conditional_negation<E: ScalarEngine, CS>(
mut cs: CS,
bits: &[Boolean],
coords: &[(E::Fr, E::Fr)]
coords: &[(E::Fr, E::Fr)],
) -> Result<(Num<E>, Num<E>), SynthesisError>
where CS: ConstraintSystem<E>
where
CS: ConstraintSystem<E>,
{
assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 4);
// 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)) => {
let mut tmp = 0;
if a_value {
@@ -146,22 +141,19 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
tmp += 2;
}
Some(tmp)
},
_ => None
}
_ => None,
};
// Allocate the y-coordinate resulting from the lookup
// and conditional negation
let y = AllocatedNum::alloc(
cs.namespace(|| "y"),
|| {
let mut tmp = coords[*i.get()?].1;
if *bits[2].get_value().get()? {
tmp.negate();
}
Ok(tmp)
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
let mut tmp = coords[*i.get()?].1;
if *bits[2].get_value().get()? {
tmp.negate();
}
)?;
Ok(tmp)
})?;
let one = CS::one();
@@ -174,21 +166,21 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?;
let x = Num::zero()
.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[1], x_coeffs[0b10])
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
.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[1], x_coeffs[0b10])
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
let y_lc = precomp.lc::<E>(one, y_coeffs[0b11]) +
&bits[1].lc::<E>(one, y_coeffs[0b10]) +
&bits[0].lc::<E>(one, y_coeffs[0b01]) +
(y_coeffs[0b00], one);
let y_lc = precomp.lc::<E>(one, y_coeffs[0b11])
+ &bits[1].lc::<E>(one, y_coeffs[0b10])
+ &bits[0].lc::<E>(one, y_coeffs[0b01])
+ (y_coeffs[0b00], one);
cs.enforce(
|| "y-coordinate lookup",
|lc| lc + &y_lc + &y_lc,
|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()))
@@ -196,46 +188,52 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
#[cfg(test)]
mod test {
use rand::{SeedableRng, Rand, Rng, XorShiftRng};
use super::*;
use ::circuit::test::*;
use ::circuit::boolean::{Boolean, AllocatedBit};
use crate::gadgets::boolean::{AllocatedBit, Boolean};
use crate::gadgets::test::*;
use pairing::bls12_381::{Bls12, Fr};
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
#[test]
fn test_lookup3_xy() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0656]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.gen();
let a = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let a_val = rng.next_u32() % 2 != 0;
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
let b_val = rng.gen();
let b = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let b_val = rng.next_u32() % 2 != 0;
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
let c_val = rng.gen();
let c = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let c_val = rng.next_u32() % 2 != 0;
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..8).map(|_| (rng.gen(), rng.gen())).collect();
let points: Vec<(Fr, Fr)> = (0..8)
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
.collect();
let res = lookup3_xy(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied());
let mut index = 0;
if a_val { index += 1 }
if b_val { index += 2 }
if c_val { index += 4 }
if a_val {
index += 1
}
if b_val {
index += 2
}
if c_val {
index += 4
}
assert_eq!(res.0.get_value().unwrap(), points[index].0);
assert_eq!(res.1.get_value().unwrap(), points[index].1);
@@ -244,53 +242,63 @@ mod test {
#[test]
fn test_lookup3_xy_with_conditional_negation() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.gen();
let a = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let a_val = rng.next_u32() % 2 != 0;
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
let b_val = rng.gen();
let b = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let b_val = rng.next_u32() % 2 != 0;
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
let c_val = rng.gen();
let c = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let c_val = rng.next_u32() % 2 != 0;
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..4).map(|_| (rng.gen(), rng.gen())).collect();
let points: Vec<(Fr, Fr)> = (0..4)
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
.collect();
let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied());
let mut index = 0;
if a_val { index += 1 }
if b_val { index += 2 }
if a_val {
index += 1
}
if b_val {
index += 2
}
assert_eq!(res.0.get_value().unwrap(), points[index].0);
let mut tmp = points[index].1;
if c_val { tmp.negate() }
if c_val {
tmp.negate()
}
assert_eq!(res.1.get_value().unwrap(), tmp);
}
}
#[test]
fn test_synth() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let window_size = 4;
let mut assignment = vec![Fr::zero(); 1 << window_size];
let constants: Vec<_> = (0..(1 << window_size)).map(|_| Fr::rand(&mut rng)).collect();
let constants: Vec<_> = (0..(1 << window_size))
.map(|_| Fr::random(&mut rng))
.collect();
synth::<Bls12, _>(window_size, &constants, &mut assignment);

View File

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

View File

@@ -0,0 +1,111 @@
//! 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,78 +1,62 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr};
use pairing::Engine;
//! Gadgets representing numbers in the scalar field of the underlying curve.
use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use super::{
Assignment
};
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
use super::boolean::{
self,
Boolean,
AllocatedBit
};
use super::Assignment;
pub struct AllocatedNum<E: Engine> {
use super::boolean::{self, AllocatedBit, Boolean};
pub struct AllocatedNum<E: ScalarEngine> {
value: Option<E::Fr>,
variable: Variable
variable: Variable,
}
impl<E: Engine> Clone for AllocatedNum<E> {
impl<E: ScalarEngine> Clone for AllocatedNum<E> {
fn clone(&self) -> Self {
AllocatedNum {
value: self.value,
variable: self.variable
variable: self.variable,
}
}
}
impl<E: Engine> AllocatedNum<E> {
pub fn alloc<CS, F>(
mut cs: CS,
value: F,
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>,
F: FnOnce() -> Result<E::Fr, SynthesisError>
impl<E: ScalarEngine> AllocatedNum<E> {
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
where
CS: ConstraintSystem<E>,
F: FnOnce() -> Result<E::Fr, SynthesisError>,
{
let mut new_value = None;
let var = cs.alloc(|| "num", || {
let tmp = value()?;
let var = cs.alloc(
|| "num",
|| {
let tmp = value()?;
new_value = Some(tmp);
new_value = Some(tmp);
Ok(tmp)
})?;
Ok(tmp)
},
)?;
Ok(AllocatedNum {
value: new_value,
variable: var
variable: var,
})
}
pub fn inputize<CS>(
&self,
mut cs: CS
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
pub fn inputize<CS>(&self, 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(
|| "enforce input is correct",
|lc| lc + input,
|lc| lc + CS::one(),
|lc| lc + self.variable
|lc| lc + self.variable,
);
Ok(())
@@ -83,20 +67,19 @@ impl<E: Engine> AllocatedNum<E> {
/// order, requiring that the representation
/// strictly exists "in the field" (i.e., a
/// congruency is not allowed.)
pub fn into_bits_le_strict<CS>(
&self,
mut cs: CS
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
where
CS: ConstraintSystem<E>,
{
pub fn kary_and<E, CS>(
mut cs: CS,
v: &[AllocatedBit]
v: &[AllocatedBit],
) -> Result<AllocatedBit, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{
assert!(v.len() > 0);
assert!(!v.is_empty());
// Let's keep this simple for now and just AND them all
// manually
@@ -109,7 +92,7 @@ impl<E: Engine> AllocatedNum<E> {
cur = Some(AllocatedBit::and(
cs.namespace(|| format!("and {}", i)),
cur.as_ref().unwrap(),
v
v,
)?);
}
}
@@ -145,15 +128,12 @@ impl<E: Engine> AllocatedNum<E> {
if b {
// This is part of a run of ones. Let's just
// 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.
current_run.push(a_bit.clone());
result.push(a_bit);
} else {
if current_run.len() > 0 {
if !current_run.is_empty() {
// This is the start of a run of zeros, but we need
// to k-ary AND against `last_run` first.
@@ -162,7 +142,7 @@ impl<E: Engine> AllocatedNum<E> {
}
last_run = Some(kary_and(
cs.namespace(|| format!("run ending at {}", i)),
&current_run
&current_run,
)?);
current_run.truncate(0);
}
@@ -175,7 +155,7 @@ impl<E: Engine> AllocatedNum<E> {
let a_bit = AllocatedBit::alloc_conditionally(
cs.namespace(|| format!("bit {}", i)),
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);
}
@@ -201,30 +181,20 @@ impl<E: Engine> AllocatedNum<E> {
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
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect())
Ok(result.into_iter().map(Boolean::from).rev().collect())
}
/// Convert the allocated number into its little-endian representation.
/// Note that this does not strongly enforce that the commitment is
/// "in the field."
pub fn into_bits_le<CS>(
&self,
mut cs: CS
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
pub fn to_bits_le<CS>(&self, 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 coeff = E::Fr::one();
@@ -237,94 +207,91 @@ impl<E: Engine> AllocatedNum<E> {
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(|b| Boolean::from(b)).collect())
Ok(bits.into_iter().map(Boolean::from).collect())
}
pub fn mul<CS>(
&self,
mut cs: CS,
other: &Self
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
where
CS: ConstraintSystem<E>,
{
let mut value = None;
let var = cs.alloc(|| "product num", || {
let mut tmp = *self.value.get()?;
tmp.mul_assign(other.value.get()?);
let var = cs.alloc(
|| "product num",
|| {
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
cs.enforce(
|| "multiplication constraint",
|lc| lc + self.variable,
|lc| lc + other.variable,
|lc| lc + var
|lc| lc + var,
);
Ok(AllocatedNum {
value: value,
variable: var
value,
variable: var,
})
}
pub fn square<CS>(
&self,
mut cs: CS
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
pub fn square<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError>
where
CS: ConstraintSystem<E>,
{
let mut value = None;
let var = cs.alloc(|| "squared num", || {
let mut tmp = *self.value.get()?;
tmp.square();
let var = cs.alloc(
|| "squared num",
|| {
let mut tmp = *self.value.get()?;
tmp.square();
value = Some(tmp);
value = Some(tmp);
Ok(tmp)
})?;
Ok(tmp)
},
)?;
// Constrain: a * a = aa
cs.enforce(
|| "squaring constraint",
|lc| lc + self.variable,
|lc| lc + self.variable,
|lc| lc + var
|lc| lc + var,
);
Ok(AllocatedNum {
value: value,
variable: var
value,
variable: var,
})
}
pub fn assert_nonzero<CS>(
&self,
mut cs: CS
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
pub fn assert_nonzero<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
where
CS: ConstraintSystem<E>,
{
let inv = cs.alloc(|| "ephemeral inverse", || {
let tmp = *self.value.get()?;
let inv = cs.alloc(
|| "ephemeral inverse",
|| {
let tmp = *self.value.get()?;
if tmp.is_zero() {
Err(SynthesisError::DivisionByZero)
} else {
Ok(tmp.inverse().unwrap())
}
})?;
if tmp.is_zero() {
Err(SynthesisError::DivisionByZero)
} else {
Ok(tmp.inverse().unwrap())
}
},
)?;
// Constrain a * inv = 1, which is only valid
// iff a has a multiplicative inverse, untrue
@@ -333,7 +300,7 @@ impl<E: Engine> AllocatedNum<E> {
|| "nonzero assertion constraint",
|lc| lc + self.variable,
|lc| lc + inv,
|lc| lc + CS::one()
|lc| lc + CS::one(),
);
Ok(())
@@ -346,44 +313,39 @@ impl<E: Engine> AllocatedNum<E> {
mut cs: CS,
a: &Self,
b: &Self,
condition: &Boolean
condition: &Boolean,
) -> Result<(Self, Self), SynthesisError>
where CS: ConstraintSystem<E>
where
CS: ConstraintSystem<E>,
{
let c = Self::alloc(
cs.namespace(|| "conditional reversal result 1"),
|| {
if *condition.get_value().get()? {
Ok(*b.value.get()?)
} else {
Ok(*a.value.get()?)
}
let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || {
if *condition.get_value().get()? {
Ok(*b.value.get()?)
} else {
Ok(*a.value.get()?)
}
)?;
})?;
cs.enforce(
|| "first conditional reversal",
|lc| lc + a.variable - b.variable,
|_| 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"),
|| {
if *condition.get_value().get()? {
Ok(*a.value.get()?)
} else {
Ok(*b.value.get()?)
}
let d = Self::alloc(cs.namespace(|| "conditional reversal result 2"), || {
if *condition.get_value().get()? {
Ok(*a.value.get()?)
} else {
Ok(*b.value.get()?)
}
)?;
})?;
cs.enforce(
|| "second conditional reversal",
|lc| lc + b.variable - a.variable,
|_| condition.lc(CS::one(), E::Fr::one()),
|lc| lc + b.variable - d.variable
|lc| lc + b.variable - d.variable,
);
Ok((c, d))
@@ -398,25 +360,25 @@ impl<E: Engine> AllocatedNum<E> {
}
}
pub struct Num<E: Engine> {
pub struct Num<E: ScalarEngine> {
value: Option<E::Fr>,
lc: LinearCombination<E>
lc: LinearCombination<E>,
}
impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
impl<E: ScalarEngine> From<AllocatedNum<E>> for Num<E> {
fn from(num: AllocatedNum<E>) -> Num<E> {
Num {
value: num.value,
lc: LinearCombination::<E>::zero() + num.variable
lc: LinearCombination::<E>::zero() + num.variable,
}
}
}
impl<E: Engine> Num<E> {
impl<E: ScalarEngine> Num<E> {
pub fn zero() -> Self {
Num {
value: Some(E::Fr::zero()),
lc: LinearCombination::zero()
lc: LinearCombination::zero(),
}
}
@@ -428,13 +390,7 @@ impl<E: Engine> Num<E> {
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()) {
(Some(mut curval), Some(bval)) => {
if bval {
@@ -442,25 +398,27 @@ impl<E: Engine> Num<E> {
}
Some(curval)
},
_ => None
}
_ => None,
};
Num {
value: newval,
lc: self.lc + &bit.lc(one, coeff)
lc: self.lc + &bit.lc(one, coeff),
}
}
}
#[cfg(test)]
mod test {
use rand::{SeedableRng, Rand, Rng, XorShiftRng};
use bellman::{ConstraintSystem};
use crate::ConstraintSystem;
use ff::{BitIterator, Field, PrimeField};
use pairing::bls12_381::{Bls12, Fr};
use ::circuit::test::*;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use super::{AllocatedNum, Boolean};
use crate::gadgets::test::*;
#[test]
fn test_allocated_num() {
@@ -489,8 +447,10 @@ mod test {
fn test_num_multiplication() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let n = 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 n =
AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap();
let n2 =
AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap();
let n3 = n.mul(&mut cs, &n2).unwrap();
assert!(cs.is_satisfied());
@@ -502,12 +462,15 @@ mod test {
#[test]
fn test_num_conditional_reversal() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
{
let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
let condition = Boolean::constant(false);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
@@ -520,8 +483,8 @@ mod test {
{
let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
let condition = Boolean::constant(true);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
@@ -560,7 +523,7 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
n.into_bits_le_strict(&mut cs).unwrap();
n.to_bits_le_strict(&mut cs).unwrap();
assert!(cs.is_satisfied());
@@ -568,28 +531,37 @@ mod test {
cs.set("bit 254/boolean", Fr::one());
// this makes the conditional boolean constraint fail
assert_eq!(cs.which_is_unsatisfied().unwrap(), "bit 254/boolean constraint");
assert_eq!(
cs.which_is_unsatisfied().unwrap(),
"bit 254/boolean constraint"
);
}
#[test]
fn test_into_bits() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for i in 0..200 {
let r = Fr::rand(&mut rng);
let r = Fr::random(&mut rng);
let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
let bits = if i % 2 == 0 {
n.into_bits_le(&mut cs).unwrap()
n.to_bits_le(&mut cs).unwrap()
} else {
n.into_bits_le_strict(&mut cs).unwrap()
n.to_bits_le_strict(&mut cs).unwrap()
};
assert!(cs.is_satisfied());
for (b, a) in BitIterator::new(r.into_repr()).skip(1).zip(bits.iter().rev()) {
for (b, a) in BitIterator::new(r.into_repr())
.skip(1)
.zip(bits.iter().rev())
{
if let &Boolean::Is(ref a) = a {
assert_eq!(b, a.get_value().unwrap());
} else {
@@ -597,7 +569,7 @@ mod test {
}
}
cs.set("num", Fr::rand(&mut rng));
cs.set("num", Fr::random(&mut rng));
assert!(!cs.is_satisfied());
cs.set("num", r);
assert!(cs.is_satisfied());

View File

@@ -1,9 +1,15 @@
use super::uint32::UInt32;
use super::multieq::MultiEq;
use super::boolean::Boolean;
use bellman::{ConstraintSystem, SynthesisError};
use pairing::Engine;
//! 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 crate::{ConstraintSystem, SynthesisError};
use ff::ScalarEngine;
#[allow(clippy::unreadable_literal)]
const ROUND_CONSTANTS: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
@@ -12,37 +18,36 @@ const ROUND_CONSTANTS: [u32; 64] = [
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
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] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];
pub fn sha256_block_no_padding<E, CS>(
mut cs: CS,
input: &[Boolean]
input: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{
assert_eq!(input.len(), 512);
Ok(sha256_compression_function(
&mut cs,
&input,
&get_sha256_iv()
)?
.into_iter()
.flat_map(|e| e.into_bits_be())
.collect())
Ok(
sha256_compression_function(&mut cs, &input, &get_sha256_iv())?
.into_iter()
.flat_map(|e| e.into_bits_be())
.collect(),
)
}
pub fn sha256<E, CS>(
mut cs: CS,
input: &[Boolean]
) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{
assert!(input.len() % 8 == 0);
@@ -62,16 +67,10 @@ pub fn sha256<E, CS>(
let mut cur = get_sha256_iv();
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> {
@@ -81,16 +80,19 @@ fn get_sha256_iv() -> Vec<UInt32> {
fn sha256_compression_function<E, CS>(
cs: CS,
input: &[Boolean],
current_hash_value: &[UInt32]
current_hash_value: &[UInt32],
) -> Result<Vec<UInt32>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{
assert_eq!(input.len(), 512);
assert_eq!(current_hash_value.len(), 8);
let mut w = input.chunks(32)
.map(|e| UInt32::from_bits_be(e))
.collect::<Vec<_>>();
let mut w = input
.chunks(32)
.map(|e| UInt32::from_bits_be(e))
.collect::<Vec<_>>();
// We can save some constraints by combining some of
// the constraints in different u32 additions
@@ -100,30 +102,18 @@ fn sha256_compression_function<E, CS>(
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)
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(
cs.namespace(|| "second xor for s0"),
&w[i-15].shr(3)
)?;
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(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)
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(
cs.namespace(|| "second xor for s1"),
&w[i-2].shr(10)
)?;
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(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?;
let tmp = UInt32::addmany(
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
@@ -134,29 +124,21 @@ fn sha256_compression_function<E, CS>(
enum Maybe {
Deferred(Vec<UInt32>),
Concrete(UInt32)
Concrete(UInt32),
}
impl Maybe {
fn compute<E, CS, M>(
self,
cs: M,
others: &[UInt32]
) -> Result<UInt32, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root=MultiEq<E, CS>>
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
{
Ok(match self {
Maybe::Concrete(ref v) => {
return Ok(v.clone())
},
Maybe::Concrete(ref v) => return Ok(v.clone()),
Maybe::Deferred(mut v) => {
v.extend(others.into_iter().cloned());
UInt32::addmany(
cs,
&v
)?
v.extend(others.iter().cloned());
UInt32::addmany(cs, &v)?
}
})
}
@@ -177,22 +159,11 @@ fn sha256_compression_function<E, CS>(
// S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?;
let mut s1 = new_e.rotr(6);
s1 = s1.xor(
cs.namespace(|| "first xor for s1"),
&new_e.rotr(11)
)?;
s1 = s1.xor(
cs.namespace(|| "second xor for s1"),
&new_e.rotr(25)
)?;
s1 = s1.xor(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)
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]
let temp1 = vec![
@@ -200,28 +171,17 @@ fn sha256_compression_function<E, CS>(
s1,
ch,
UInt32::constant(ROUND_CONSTANTS[i]),
w[i].clone()
w[i].clone(),
];
// S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?;
let mut s0 = new_a.rotr(2);
s0 = s0.xor(
cs.namespace(|| "first xor for s0"),
&new_a.rotr(13)
)?;
s0 = s0.xor(
cs.namespace(|| "second xor for s0"),
&new_a.rotr(22)
)?;
s0 = s0.xor(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)
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
let temp2 = vec![s0, maj];
@@ -244,7 +204,13 @@ fn sha256_compression_function<E, CS>(
d = c;
c = b;
b = new_a;
a = Maybe::Deferred(temp1.iter().cloned().chain(temp2.iter().cloned()).collect::<Vec<_>>());
a = Maybe::Deferred(
temp1
.iter()
.cloned()
.chain(temp2.iter().cloned())
.collect::<Vec<_>>(),
);
}
/*
@@ -261,42 +227,42 @@ fn sha256_compression_function<E, CS>(
let h0 = a.compute(
cs.namespace(|| "deferred h0 computation"),
&[current_hash_value[0].clone()]
&[current_hash_value[0].clone()],
)?;
let h1 = UInt32::addmany(
cs.namespace(|| "new h1"),
&[current_hash_value[1].clone(), b]
&[current_hash_value[1].clone(), b],
)?;
let h2 = UInt32::addmany(
cs.namespace(|| "new h2"),
&[current_hash_value[2].clone(), c]
&[current_hash_value[2].clone(), c],
)?;
let h3 = UInt32::addmany(
cs.namespace(|| "new h3"),
&[current_hash_value[3].clone(), d]
&[current_hash_value[3].clone(), d],
)?;
let h4 = e.compute(
cs.namespace(|| "deferred h4 computation"),
&[current_hash_value[4].clone()]
&[current_hash_value[4].clone()],
)?;
let h5 = UInt32::addmany(
cs.namespace(|| "new h5"),
&[current_hash_value[5].clone(), f]
&[current_hash_value[5].clone(), f],
)?;
let h6 = UInt32::addmany(
cs.namespace(|| "new h6"),
&[current_hash_value[6].clone(), g]
&[current_hash_value[6].clone(), g],
)?;
let h7 = UInt32::addmany(
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])
@@ -305,10 +271,12 @@ fn sha256_compression_function<E, CS>(
#[cfg(test)]
mod test {
use super::*;
use circuit::boolean::AllocatedBit;
use crate::gadgets::boolean::AllocatedBit;
use crate::gadgets::test::TestConstraintSystem;
use hex_literal::hex;
use pairing::bls12_381::Bls12;
use circuit::test::TestConstraintSystem;
use rand::{XorShiftRng, SeedableRng, Rng};
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
#[test]
fn test_blank_hash() {
@@ -317,11 +285,7 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect();
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();
assert!(cs.is_satisfied());
@@ -330,7 +294,7 @@ mod test {
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
let mut out = out_bits.into_iter();
for b in expected.into_iter() {
for b in expected.iter() {
for i in (0..8).rev() {
let c = out.next().unwrap().get_value().unwrap();
@@ -341,25 +305,27 @@ mod test {
#[test]
fn test_full_block() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let iv = get_sha256_iv();
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits: Vec<_> = (0..512).map(|i| {
Boolean::from(
AllocatedBit::alloc(
cs.namespace(|| format!("input bit {}", i)),
Some(rng.gen())
).unwrap()
)
}).collect();
let input_bits: Vec<_> = (0..512)
.map(|i| {
Boolean::from(
AllocatedBit::alloc(
cs.namespace(|| format!("input bit {}", i)),
Some(rng.next_u32() % 2 != 0),
)
.unwrap(),
)
})
.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_eq!(cs.num_constraints() - 512, 25840);
@@ -367,18 +333,18 @@ mod test {
#[test]
fn test_against_vectors() {
use crypto::sha2::Sha256;
use crypto::digest::Digest;
use sha2::{Digest, Sha256};
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
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 data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
h.input(&data);
let mut hash_result = [0u8; 32];
h.result(&mut hash_result[..]);
let hash_result = h.result();
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
@@ -387,7 +353,11 @@ mod test {
for bit_i in (0..8).rev() {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into());
input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
}
}
@@ -395,17 +365,19 @@ mod test {
assert!(cs.is_satisfied());
let mut s = hash_result.as_ref().iter()
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
let mut s = hash_result
.as_ref()
.iter()
.flat_map(|&byte| (0..8).rev().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,13 +1,8 @@
use ff::{Field, PrimeField, PrimeFieldRepr};
use pairing::Engine;
//! Helpers for testing circuit implementations.
use bellman::{
LinearCombination,
SynthesisError,
ConstraintSystem,
Variable,
Index
};
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
use std::collections::HashMap;
use std::fmt::Write;
@@ -16,27 +11,27 @@ use byteorder::{BigEndian, ByteOrder};
use std::cmp::Ordering;
use std::collections::BTreeMap;
use blake2_rfc::blake2s::Blake2s;
use blake2s_simd::{Params as Blake2sParams, State as Blake2sState};
#[derive(Debug)]
enum NamedObject {
Constraint(usize),
Var(Variable),
Namespace
Namespace,
}
/// Constraint system for testing purposes.
pub struct TestConstraintSystem<E: Engine> {
pub struct TestConstraintSystem<E: ScalarEngine> {
named_objects: HashMap<String, NamedObject>,
current_namespace: Vec<String>,
constraints: Vec<(
LinearCombination<E>,
LinearCombination<E>,
LinearCombination<E>,
String
String,
)>,
inputs: Vec<(E::Fr, String)>,
aux: Vec<(E::Fr, String)>
aux: Vec<(E::Fr, String)>,
}
#[derive(Clone, Copy)]
@@ -48,7 +43,7 @@ impl PartialEq for OrderedVariable {
match (self.0.get_unchecked(), other.0.get_unchecked()) {
(Index::Input(ref a), Index::Input(ref b)) => a == b,
(Index::Aux(ref a), Index::Aux(ref b)) => a == b,
_ => false
_ => false,
}
}
}
@@ -63,20 +58,17 @@ impl Ord for OrderedVariable {
(Index::Input(ref a), Index::Input(ref b)) => a.cmp(b),
(Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b),
(Index::Input(_), Index::Aux(_)) => Ordering::Less,
(Index::Aux(_), Index::Input(_)) => Ordering::Greater
(Index::Aux(_), Index::Input(_)) => Ordering::Greater,
}
}
}
fn proc_lc<E: Engine>(
terms: &[(Variable, E::Fr)],
) -> BTreeMap<OrderedVariable, E::Fr>
{
fn proc_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
let mut map = BTreeMap::new();
for &(var, coeff) in terms {
map.entry(OrderedVariable(var))
.or_insert(E::Fr::zero())
.add_assign(&coeff);
.or_insert_with(E::Fr::zero)
.add_assign(&coeff);
}
// Remove terms that have a zero coefficient to normalize
@@ -94,11 +86,7 @@ fn proc_lc<E: Engine>(
map
}
fn hash_lc<E: Engine>(
terms: &[(Variable, E::Fr)],
h: &mut Blake2s
)
{
fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
let map = proc_lc::<E>(terms);
let mut buf = [0u8; 9 + 32];
@@ -110,7 +98,7 @@ fn hash_lc<E: Engine>(
Index::Input(i) => {
buf[0] = b'I';
BigEndian::write_u64(&mut buf[1..9], i as u64);
},
}
Index::Aux(i) => {
buf[0] = b'A';
BigEndian::write_u64(&mut buf[1..9], i as u64);
@@ -123,18 +111,17 @@ fn hash_lc<E: Engine>(
}
}
fn eval_lc<E: Engine>(
fn eval_lc<E: ScalarEngine>(
terms: &[(Variable, E::Fr)],
inputs: &[(E::Fr, String)],
aux: &[(E::Fr, String)]
) -> E::Fr
{
aux: &[(E::Fr, String)],
) -> E::Fr {
let mut acc = E::Fr::zero();
for &(var, ref coeff) in terms {
let mut tmp = match var.get_unchecked() {
Index::Input(index) => inputs[index].0,
Index::Aux(index) => aux[index].0
Index::Aux(index) => aux[index].0,
};
tmp.mul_assign(&coeff);
@@ -144,17 +131,20 @@ fn eval_lc<E: Engine>(
acc
}
impl<E: Engine> TestConstraintSystem<E> {
impl<E: ScalarEngine> TestConstraintSystem<E> {
pub fn new() -> TestConstraintSystem<E> {
let mut map = HashMap::new();
map.insert("ONE".into(), NamedObject::Var(TestConstraintSystem::<E>::one()));
map.insert(
"ONE".into(),
NamedObject::Var(TestConstraintSystem::<E>::one()),
);
TestConstraintSystem {
named_objects: map,
current_namespace: vec![],
constraints: vec![],
inputs: vec![(E::Fr::one(), "ONE".into())],
aux: vec![]
aux: vec![],
}
}
@@ -167,9 +157,9 @@ impl<E: Engine> TestConstraintSystem<E> {
tmp
};
let powers_of_two = (0..E::Fr::NUM_BITS).map(|i| {
E::Fr::from_str("2").unwrap().pow(&[i as u64])
}).collect::<Vec<_>>();
let powers_of_two = (0..E::Fr::NUM_BITS)
.map(|i| E::Fr::from_str("2").unwrap().pow(&[u64::from(i)]))
.collect::<Vec<_>>();
let pp = |s: &mut String, lc: &LinearCombination<E>| {
write!(s, "(").unwrap();
@@ -196,7 +186,7 @@ impl<E: Engine> TestConstraintSystem<E> {
match var.0.get_unchecked() {
Index::Input(i) => {
write!(s, "`{}`", &self.inputs[i].1).unwrap();
},
}
Index::Aux(i) => {
write!(s, "`{}`", &self.aux[i].1).unwrap();
}
@@ -226,7 +216,7 @@ impl<E: Engine> TestConstraintSystem<E> {
}
pub fn hash(&self) -> String {
let mut h = Blake2s::new(32);
let mut h = Blake2sParams::new().hash_length(32).to_state();
{
let mut buf = [0u8; 24];
@@ -259,57 +249,52 @@ impl<E: Engine> TestConstraintSystem<E> {
a.mul_assign(&b);
if a != c {
return Some(&*path)
return Some(&*path);
}
}
None
}
pub fn is_satisfied(&self) -> bool
{
pub fn is_satisfied(&self) -> bool {
self.which_is_unsatisfied().is_none()
}
pub fn num_constraints(&self) -> usize
{
pub fn num_constraints(&self) -> usize {
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) {
Some(&NamedObject::Var(ref v)) => {
match v.get_unchecked() {
Index::Input(index) => self.inputs[index].0 = to,
Index::Aux(index) => self.aux[index].0 = to
}
}
Some(e) => panic!("tried to set path `{}` to value, but `{:?}` already exists there.", path, e),
_ => panic!("no variable exists at path: {}", path)
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
Index::Input(index) => self.inputs[index].0 = to,
Index::Aux(index) => self.aux[index].0 = to,
},
Some(e) => panic!(
"tried to set path `{}` to value, but `{:?}` already exists there.",
path, e
),
_ => panic!("no variable exists at path: {}", path),
}
}
pub fn verify(&self, expected: &[E::Fr]) -> bool
{
pub fn verify(&self, expected: &[E::Fr]) -> bool {
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 {
return false
return false;
}
}
return true;
true
}
pub fn num_inputs(&self) -> usize {
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();
assert_eq!(path, name);
@@ -317,17 +302,17 @@ impl<E: Engine> TestConstraintSystem<E> {
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) {
Some(&NamedObject::Var(ref v)) => {
match v.get_unchecked() {
Index::Input(index) => self.inputs[index].0,
Index::Aux(index) => self.aux[index].0
}
}
Some(e) => panic!("tried to get value of path `{}`, but `{:?}` exists there (not a variable)", path, e),
_ => panic!("no variable exists at path: {}", path)
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
Index::Input(index) => self.inputs[index].0,
Index::Aux(index) => self.aux[index].0,
},
Some(e) => panic!(
"tried to get value of path `{}`, but `{:?}` exists there (not a variable)",
path, e
),
_ => panic!("no variable exists at path: {}", path),
}
}
@@ -348,8 +333,7 @@ fn compute_path(ns: &[String], this: String) -> String {
let mut name = String::new();
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 {
name += "/";
}
@@ -361,15 +345,14 @@ fn compute_path(ns: &[String], this: String) -> String {
name
}
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
type Root = Self;
fn alloc<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
let index = self.aux.len();
let path = compute_path(&self.current_namespace, annotation().into());
@@ -380,12 +363,11 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var)
}
fn alloc_input<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
let index = self.inputs.len();
let path = compute_path(&self.current_namespace, annotation().into());
@@ -396,17 +378,13 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var)
}
fn enforce<A, AR, LA, LB, LC>(
&mut self,
annotation: A,
a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<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 index = self.constraints.len();
@@ -420,7 +398,9 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
}
fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR
where
NR: Into<String>,
N: FnOnce() -> NR,
{
let name = name_fn().into();
let path = compute_path(&self.current_namespace, name.clone());
@@ -428,13 +408,11 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
self.current_namespace.push(name);
}
fn pop_namespace(&mut self)
{
fn pop_namespace(&mut self) {
assert!(self.current_namespace.pop().is_some());
}
fn get_root(&mut self) -> &mut Self::Root
{
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
@@ -447,28 +425,26 @@ fn test_cs() {
let mut cs = TestConstraintSystem::<Bls12>::new();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 0);
let a = cs.namespace(|| "a").alloc(|| "var", || Ok(Fr::from_str("10").unwrap())).unwrap();
let b = cs.namespace(|| "b").alloc(|| "var", || Ok(Fr::from_str("4").unwrap())).unwrap();
let c = cs.alloc(|| "product", || Ok(Fr::from_str("40").unwrap())).unwrap();
let a = cs
.namespace(|| "a")
.alloc(|| "var", || Ok(Fr::from_str("10").unwrap()))
.unwrap();
let b = cs
.namespace(|| "b")
.alloc(|| "var", || Ok(Fr::from_str("4").unwrap()))
.unwrap();
let c = cs
.alloc(|| "product", || Ok(Fr::from_str("40").unwrap()))
.unwrap();
cs.enforce(
|| "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_eq!(cs.num_constraints(), 1);
cs.set("a/var", Fr::from_str("4").unwrap());
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.which_is_unsatisfied() == Some("mult"));

View File

@@ -1,16 +1,13 @@
use ff::{Field, PrimeField};
use pairing::Engine;
//! Circuit representation of a [`u32`], with helpers for the [`sha256`]
//! gadgets.
//!
//! [`sha256`]: crate::gadgets::sha256
use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination
};
use ff::{Field, PrimeField, ScalarEngine};
use super::boolean::{
Boolean,
AllocatedBit
};
use crate::{ConstraintSystem, LinearCombination, SynthesisError};
use super::boolean::{AllocatedBit, Boolean};
use super::multieq::MultiEq;
@@ -20,13 +17,12 @@ use super::multieq::MultiEq;
pub struct UInt32 {
// Least significant bit first
bits: Vec<Boolean>,
value: Option<u32>
value: Option<u32>,
}
impl UInt32 {
/// 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 tmp = value;
@@ -41,18 +37,16 @@ impl UInt32 {
}
UInt32 {
bits: bits,
value: Some(value)
bits,
value: Some(value),
}
}
/// Allocate a `UInt32` in the constraint system
pub fn alloc<E, CS>(
mut cs: CS,
value: Option<u32>
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{
let values = match value {
Some(mut val) => {
@@ -64,28 +58,28 @@ impl UInt32 {
}
v
},
None => vec![None; 32]
}
None => vec![None; 32],
};
let bits = values.into_iter()
.enumerate()
.map(|(i, v)| {
Ok(Boolean::from(AllocatedBit::alloc(
cs.namespace(|| format!("allocated bit {}", i)),
v
)?))
})
.collect::<Result<Vec<_>, SynthesisError>>()?;
let bits = values
.into_iter()
.enumerate()
.map(|(i, v)| {
Ok(Boolean::from(AllocatedBit::alloc(
cs.namespace(|| format!("allocated bit {}", i)),
v,
)?))
})
.collect::<Result<Vec<_>, SynthesisError>>()?;
Ok(UInt32 {
bits: bits,
value: value
})
Ok(UInt32 { bits, value })
}
pub fn into_bits_be(&self) -> Vec<Boolean> {
self.bits.iter().rev().cloned().collect()
pub fn into_bits_be(self) -> Vec<Boolean> {
let mut ret = self.bits;
ret.reverse();
ret
}
pub fn from_bits_be(bits: &[Boolean]) -> Self {
@@ -96,28 +90,30 @@ impl UInt32 {
value.as_mut().map(|v| *v <<= 1);
match b.get_value() {
Some(true) => { value.as_mut().map(|v| *v |= 1); },
Some(false) => {},
None => { value = None; }
Some(true) => {
value.as_mut().map(|v| *v |= 1);
}
Some(false) => {}
None => {
value = None;
}
}
}
UInt32 {
value: value,
bits: bits.iter().rev().cloned().collect()
value,
bits: bits.iter().rev().cloned().collect(),
}
}
/// Turns this `UInt32` into its little-endian byte order representation.
pub fn into_bits(&self) -> Vec<Boolean> {
self.bits.clone()
pub fn into_bits(self) -> Vec<Boolean> {
self.bits
}
/// Converts a little-endian byte order representation of bits into a
/// `UInt32`.
pub fn from_bits(bits: &[Boolean]) -> Self
{
pub fn from_bits(bits: &[Boolean]) -> Self {
assert_eq!(bits.len(), 32);
let new_bits = bits.to_vec();
@@ -126,48 +122,50 @@ impl UInt32 {
for b in new_bits.iter().rev() {
value.as_mut().map(|v| *v <<= 1);
match b {
&Boolean::Constant(b) => {
match *b {
Boolean::Constant(b) => {
if b {
value.as_mut().map(|v| *v |= 1);
}
},
&Boolean::Is(ref b) => {
match b.get_value() {
Some(true) => { value.as_mut().map(|v| *v |= 1); },
Some(false) => {},
None => { value = None }
}
},
&Boolean::Not(ref b) => {
match b.get_value() {
Some(false) => { value.as_mut().map(|v| *v |= 1); },
Some(true) => {},
None => { value = None }
}
}
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 {
value: value,
bits: new_bits
value,
bits: new_bits,
}
}
pub fn rotr(&self, by: usize) -> Self {
let by = by % 32;
let new_bits = self.bits.iter()
.skip(by)
.chain(self.bits.iter())
.take(32)
.cloned()
.collect();
let new_bits = self
.bits
.iter()
.skip(by)
.chain(self.bits.iter())
.take(32)
.cloned()
.collect();
UInt32 {
bits: new_bits,
value: self.value.map(|v| v.rotate_right(by as u32))
value: self.value.map(|v| v.rotate_right(by as u32)),
}
}
@@ -176,17 +174,18 @@ impl UInt32 {
let fill = Boolean::constant(false);
let new_bits = self.bits
.iter() // The bits are least significant first
.skip(by) // Skip the bits that will be lost during the shift
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
.take(32) // Only 32 bits needed!
.cloned()
.collect();
let new_bits = self
.bits
.iter() // The bits are least significant first
.skip(by) // Skip the bits that will be lost during the shift
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
.take(32) // Only 32 bits needed!
.cloned()
.collect();
UInt32 {
bits: new_bits,
value: self.value.map(|v| v >> by as u32)
value: self.value.map(|v| v >> by as u32),
}
}
@@ -196,121 +195,99 @@ impl UInt32 {
b: &Self,
c: &Self,
tri_fn: F,
circuit_fn: U
circuit_fn: U,
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>,
F: Fn(u32, u32, u32) -> u32,
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
F: Fn(u32, u32, u32) -> u32,
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
{
let new_value = match (a.value, b.value, c.value) {
(Some(a), Some(b), Some(c)) => {
Some(tri_fn(a, b, c))
},
_ => None
(Some(a), Some(b), Some(c)) => Some(tri_fn(a, b, c)),
_ => None,
};
let bits = a.bits.iter()
.zip(b.bits.iter())
.zip(c.bits.iter())
.enumerate()
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
.collect::<Result<_, _>>()?;
let bits = a
.bits
.iter()
.zip(b.bits.iter())
.zip(c.bits.iter())
.enumerate()
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
.collect::<Result<_, _>>()?;
Ok(UInt32 {
bits: bits,
value: new_value
bits,
value: new_value,
})
}
/// Compute the `maj` value (a and b) xor (a and c) xor (b and c)
/// during SHA256.
pub fn sha256_maj<E, CS>(
cs: CS,
a: &Self,
b: &Self,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ (a & c) ^ (b & c),
|cs, i, a, b, c| {
Boolean::sha256_maj(
cs.namespace(|| format!("maj {}", i)),
a,
b,
c
)
}
Self::triop(
cs,
a,
b,
c,
|a, b, c| (a & b) ^ (a & c) ^ (b & c),
|cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c),
)
}
/// Compute the `ch` value `(a and b) xor ((not a) and c)`
/// during SHA256.
pub fn sha256_ch<E, CS>(
cs: CS,
a: &Self,
b: &Self,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ ((!a) & c),
|cs, i, a, b, c| {
Boolean::sha256_ch(
cs.namespace(|| format!("ch {}", i)),
a,
b,
c
)
}
Self::triop(
cs,
a,
b,
c,
|a, b, c| (a & b) ^ ((!a) & c),
|cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c),
)
}
/// XOR this `UInt32` with another `UInt32`
pub fn xor<E, CS>(
&self,
mut cs: CS,
other: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{
let new_value = match (self.value, other.value) {
(Some(a), Some(b)) => {
Some(a ^ b)
},
_ => None
(Some(a), Some(b)) => Some(a ^ b),
_ => None,
};
let bits = self.bits.iter()
.zip(other.bits.iter())
.enumerate()
.map(|(i, (a, b))| {
Boolean::xor(
cs.namespace(|| format!("xor of bit {}", i)),
a,
b
)
})
.collect::<Result<_, _>>()?;
let bits = self
.bits
.iter()
.zip(other.bits.iter())
.enumerate()
.map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b))
.collect::<Result<_, _>>()?;
Ok(UInt32 {
bits: bits,
value: new_value
bits,
value: new_value,
})
}
/// Perform modular addition of several `UInt32` objects.
pub fn addmany<E, CS, M>(
mut cs: M,
operands: &[Self]
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root=MultiEq<E, CS>>
pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
{
// Make some arbitrary bounds for ourselves to avoid overflows
// in the scalar field
@@ -320,7 +297,7 @@ impl UInt32 {
// Compute the maximum value of the sum so we allocate enough bits for
// the result
let mut max_value = (operands.len() as u64) * (u32::max_value() as u64);
let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value()));
// Keep track of the resulting value
let mut result_value = Some(0u64);
@@ -336,8 +313,8 @@ impl UInt32 {
// Accumulate the value
match op.value {
Some(val) => {
result_value.as_mut().map(|v| *v += val as u64);
},
result_value.as_mut().map(|v| *v += u64::from(val));
}
None => {
// If any of our operands have unknown value, we won't
// know the value of the result
@@ -381,7 +358,7 @@ impl UInt32 {
// Allocate the bit
let b = AllocatedBit::alloc(
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
@@ -402,48 +379,53 @@ impl UInt32 {
Ok(UInt32 {
bits: result_bits,
value: modular_value
value: modular_value,
})
}
}
#[cfg(test)]
mod test {
use rand::{XorShiftRng, SeedableRng, Rng};
use ::circuit::boolean::{Boolean};
use super::{UInt32};
use super::UInt32;
use crate::gadgets::boolean::Boolean;
use crate::gadgets::multieq::MultiEq;
use crate::gadgets::test::*;
use crate::ConstraintSystem;
use ff::Field;
use pairing::bls12_381::{Bls12};
use ::circuit::test::*;
use bellman::{ConstraintSystem};
use circuit::multieq::MultiEq;
use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
#[test]
fn test_uint32_from_bits_be() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 {
let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::<Vec<_>>();
let v = (0..32)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits_be(&v);
for (i, bit) in b.bits.iter().enumerate() {
match bit {
&Boolean::Constant(bit) => {
match *bit {
Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
},
_ => unreachable!()
}
_ => unreachable!(),
}
}
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 {
(&Boolean::Constant(true), &Boolean::Constant(true)) => {},
(&Boolean::Constant(false), &Boolean::Constant(false)) => {},
_ => unreachable!()
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}
_ => unreachable!(),
}
}
}
@@ -451,30 +433,34 @@ mod test {
#[test]
fn test_uint32_from_bits() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 {
let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::<Vec<_>>();
let v = (0..32)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits(&v);
for (i, bit) in b.bits.iter().enumerate() {
match bit {
&Boolean::Constant(bit) => {
match *bit {
Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
},
_ => unreachable!()
}
_ => unreachable!(),
}
}
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 {
(&Boolean::Constant(true), &Boolean::Constant(true)) => {},
(&Boolean::Constant(false), &Boolean::Constant(false)) => {},
_ => unreachable!()
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}
_ => unreachable!(),
}
}
}
@@ -482,14 +468,17 @@ mod test {
#[test]
fn test_uint32_xor() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen();
let b: u32 = rng.gen();
let c: u32 = rng.gen();
let a = rng.next_u32();
let b = rng.next_u32();
let c = rng.next_u32();
let mut expected = a ^ b ^ c;
@@ -505,14 +494,14 @@ mod test {
assert!(r.value == Some(expected));
for b in r.bits.iter() {
match b {
&Boolean::Is(ref b) => {
match *b {
Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Not(ref b) => {
}
Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Constant(b) => {
}
Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}
}
@@ -524,14 +513,17 @@ mod test {
#[test]
fn test_uint32_addmany_constants() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen();
let b: u32 = rng.gen();
let c: u32 = rng.gen();
let a = rng.next_u32();
let b = rng.next_u32();
let c = rng.next_u32();
let a_bit = UInt32::constant(a);
let b_bit = UInt32::constant(b);
@@ -541,17 +533,18 @@ mod test {
let r = {
let mut cs = MultiEq::new(&mut cs);
let r = UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
let r =
UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
r
};
assert!(r.value == Some(expected));
for b in r.bits.iter() {
match b {
&Boolean::Is(_) => panic!(),
&Boolean::Not(_) => panic!(),
&Boolean::Constant(b) => {
match *b {
Boolean::Is(_) => panic!(),
Boolean::Not(_) => panic!(),
Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}
}
@@ -563,15 +556,18 @@ mod test {
#[test]
fn test_uint32_addmany() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen();
let b: u32 = rng.gen();
let c: u32 = rng.gen();
let d: u32 = rng.gen();
let a = rng.next_u32();
let b = rng.next_u32();
let c = rng.next_u32();
let d = rng.next_u32();
let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d);
@@ -583,8 +579,7 @@ mod test {
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
let r = {
let mut cs = MultiEq::new(&mut cs);
let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap();
r
UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap()
};
assert!(cs.is_satisfied());
@@ -592,16 +587,14 @@ mod test {
assert!(r.value == Some(expected));
for b in r.bits.iter() {
match b {
&Boolean::Is(ref b) => {
match *b {
Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Constant(_) => {
unreachable!()
}
Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}
Boolean::Constant(_) => unreachable!(),
}
expected >>= 1;
@@ -620,9 +613,12 @@ mod test {
#[test]
fn test_uint32_rotr() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let mut num = rng.gen();
let mut num = rng.next_u32();
let a = UInt32::constant(num);
@@ -634,11 +630,11 @@ mod test {
let mut tmp = num;
for b in &b.bits {
match b {
&Boolean::Constant(b) => {
match *b {
Boolean::Constant(b) => {
assert_eq!(b, tmp & 1 == 1);
},
_ => unreachable!()
}
_ => unreachable!(),
}
tmp >>= 1;
@@ -650,11 +646,14 @@ mod test {
#[test]
fn test_uint32_shr() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..50 {
for i in 0..60 {
let num = rng.gen();
let num = rng.next_u32();
let a = UInt32::constant(num).shr(i);
let b = UInt32::constant(num.wrapping_shr(i as u32));
@@ -670,14 +669,17 @@ mod test {
#[test]
fn test_uint32_sha256_maj() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen();
let b: u32 = rng.gen();
let c: u32 = rng.gen();
let a = rng.next_u32();
let b = rng.next_u32();
let c = rng.next_u32();
let mut expected = (a & b) ^ (a & c) ^ (b & c);
@@ -695,10 +697,10 @@ mod test {
match b {
&Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1));
},
}
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
}
&Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}
@@ -711,14 +713,17 @@ mod test {
#[test]
fn test_uint32_sha256_ch() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen();
let b: u32 = rng.gen();
let c: u32 = rng.gen();
let a = rng.next_u32();
let b = rng.next_u32();
let c = rng.next_u32();
let mut expected = (a & b) ^ ((!a) & c);
@@ -736,10 +741,10 @@ mod test {
match b {
&Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1));
},
}
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
}
&Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1));
}

View File

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

View File

@@ -1,17 +1,16 @@
//! The [Groth16] proving system.
//!
//! [Groth16]: https://eprint.iacr.org/2016/260
use group::{CurveAffine, EncodedPoint};
use pairing::{
Engine,
PairingCurveAffine,
};
use pairing::{Engine, PairingCurveAffine};
use ::{
SynthesisError
};
use crate::SynthesisError;
use multiexp::SourceBuilder;
use crate::multiexp::SourceBuilder;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{self, Read, Write};
use std::sync::Arc;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
#[cfg(test)]
mod tests;
@@ -28,23 +27,17 @@ pub use self::verifier::*;
pub struct Proof<E: Engine> {
pub a: E::G1Affine,
pub b: E::G2Affine,
pub c: E::G1Affine
pub c: E::G1Affine,
}
impl<E: Engine> PartialEq for Proof<E> {
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> {
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.b.into_compressed().as_ref())?;
writer.write_all(self.c.into_compressed().as_ref())?;
@@ -52,48 +45,56 @@ impl<E: Engine> Proof<E> {
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 g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty();
reader.read_exact(g1_repr.as_mut())?;
let a = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| {
if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
})?;
}
})?;
reader.read_exact(g2_repr.as_mut())?;
let b = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| {
if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
})?;
}
})?;
reader.read_exact(g1_repr.as_mut())?;
let c = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| {
if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
})?;
}
})?;
Ok(Proof {
a: a,
b: b,
c: c
})
Ok(Proof { a, b, c })
}
}
@@ -122,27 +123,23 @@ pub struct VerifyingKey<E: Engine> {
// 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
// at infinity.
pub ic: Vec<E::G1Affine>
pub ic: Vec<E::G1Affine>,
}
impl<E: Engine> PartialEq for VerifyingKey<E> {
fn eq(&self, other: &Self) -> bool {
self.alpha_g1 == other.alpha_g1 &&
self.beta_g1 == other.beta_g1 &&
self.beta_g2 == other.beta_g2 &&
self.gamma_g2 == other.gamma_g2 &&
self.delta_g1 == other.delta_g1 &&
self.delta_g2 == other.delta_g2 &&
self.ic == other.ic
self.alpha_g1 == other.alpha_g1
&& self.beta_g1 == other.beta_g1
&& self.beta_g2 == other.beta_g2
&& self.gamma_g2 == other.gamma_g2
&& self.delta_g1 == other.delta_g1
&& self.delta_g2 == other.delta_g2
&& self.ic == other.ic
}
}
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.beta_g1.into_uncompressed().as_ref())?;
writer.write_all(self.beta_g2.into_uncompressed().as_ref())?;
@@ -157,30 +154,39 @@ impl<E: Engine> VerifyingKey<E> {
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 g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(g1_repr.as_mut())?;
let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let alpha_g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?;
let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let beta_g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let beta_g2 = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let gamma_g2 = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?;
let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let delta_g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let delta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let delta_g2 = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let ic_len = reader.read_u32::<BigEndian>()? as usize;
@@ -189,25 +195,30 @@ impl<E: Engine> VerifyingKey<E> {
for _ in 0..ic_len {
reader.read_exact(g1_repr.as_mut())?;
let g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| {
if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
}
})?;
ic.push(g1);
}
Ok(VerifyingKey {
alpha_g1: alpha_g1,
beta_g1: beta_g1,
beta_g2: beta_g2,
gamma_g2: gamma_g2,
delta_g1: delta_g1,
delta_g2: delta_g2,
ic: ic
alpha_g1,
beta_g1,
beta_g2,
gamma_g2,
delta_g1,
delta_g2,
ic,
})
}
}
@@ -234,26 +245,22 @@ pub struct Parameters<E: Engine> {
// G1 and G2 for C/B queries, respectively. Never contains points at
// infinity for the same reason as the "A" polynomials.
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> {
fn eq(&self, other: &Self) -> bool {
self.vk == other.vk &&
self.h == other.h &&
self.l == other.l &&
self.a == other.a &&
self.b_g1 == other.b_g1 &&
self.b_g2 == other.b_g2
self.vk == other.vk
&& self.h == other.h
&& self.l == other.l
&& self.a == other.a
&& self.b_g1 == other.b_g1
&& self.b_g2 == other.b_g2
}
}
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)?;
writer.write_u32::<BigEndian>(self.h.len() as u32)?;
@@ -284,27 +291,26 @@ impl<E: Engine> Parameters<E> {
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 mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(repr.as_mut())?;
if checked {
repr
.into_affine()
repr.into_affine()
} else {
repr
.into_affine_unchecked()
repr.into_affine_unchecked()
}
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
.and_then(|e| {
if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
}
})
};
@@ -313,17 +319,20 @@ impl<E: Engine> Parameters<E> {
reader.read_exact(repr.as_mut())?;
if checked {
repr
.into_affine()
repr.into_affine()
} else {
repr
.into_affine_unchecked()
repr.into_affine_unchecked()
}
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
.and_then(|e| {
if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
}
})
};
@@ -371,12 +380,12 @@ impl<E: Engine> Parameters<E> {
}
Ok(Parameters {
vk: vk,
vk,
h: Arc::new(h),
l: Arc::new(l),
a: Arc::new(a),
b_g1: Arc::new(b_g1),
b_g2: Arc::new(b_g2)
b_g2: Arc::new(b_g2),
})
}
}
@@ -389,39 +398,30 @@ pub struct PreparedVerifyingKey<E: Engine> {
/// -delta in G2
neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
/// Copy of IC from `VerifiyingKey`.
ic: Vec<E::G1Affine>
ic: Vec<E::G1Affine>,
}
pub trait ParameterSource<E: Engine> {
type G1Builder: SourceBuilder<E::G1Affine>;
type G2Builder: SourceBuilder<E::G2Affine>;
fn get_vk(
&mut self,
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_vk(&mut self, 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(
&mut self,
num_inputs: usize,
num_aux: usize
num_aux: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g1(
&mut self,
num_inputs: usize,
num_aux: usize
num_aux: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g2(
&mut self,
num_inputs: usize,
num_aux: usize
num_aux: usize,
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
}
@@ -429,54 +429,39 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
type G1Builder = (Arc<Vec<E::G1Affine>>, 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())
}
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))
}
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))
}
fn get_a(
&mut self,
num_inputs: usize,
_: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
{
_: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
}
fn get_b_g1(
&mut self,
num_inputs: usize,
_: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
{
_: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
}
fn get_b_g2(
&mut self,
num_inputs: usize,
_: usize
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>
{
_: usize,
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> {
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
}
}
@@ -484,41 +469,38 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
#[cfg(test)]
mod test_with_bls12_381 {
use super::*;
use {Circuit, SynthesisError, ConstraintSystem};
use crate::{Circuit, ConstraintSystem, SynthesisError};
use ff::Field;
use rand::{Rand, thread_rng};
use pairing::bls12_381::{Bls12, Fr};
use rand::thread_rng;
#[test]
fn serialization() {
struct MySillyCircuit<E: Engine> {
a: Option<E::Fr>,
b: Option<E::Fr>
b: Option<E::Fr>,
}
impl<E: Engine> Circuit<E> for MySillyCircuit<E> {
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
cs: &mut CS,
) -> Result<(), SynthesisError> {
let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
let c = cs.alloc_input(|| "c", || {
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
let c = cs.alloc_input(
|| "c",
|| {
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
a.mul_assign(&b);
Ok(a)
})?;
a.mul_assign(&b);
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(())
}
@@ -526,10 +508,9 @@ mod test_with_bls12_381 {
let rng = &mut thread_rng();
let params = generate_random_parameters::<Bls12, _, _>(
MySillyCircuit { a: None, b: None },
rng
).unwrap();
let params =
generate_random_parameters::<Bls12, _, _>(MySillyCircuit { a: None, b: None }, rng)
.unwrap();
{
let mut v = vec![];
@@ -547,19 +528,20 @@ mod test_with_bls12_381 {
let pvk = prepare_verifying_key::<Bls12>(&params.vk);
for _ in 0..100 {
let a = Fr::rand(rng);
let b = Fr::rand(rng);
let a = Fr::random(rng);
let b = Fr::random(rng);
let mut c = a;
c.mul_assign(&b);
let proof = create_random_proof(
MySillyCircuit {
a: Some(a),
b: Some(b)
b: Some(b),
},
&params,
rng
).unwrap();
rng,
)
.unwrap();
let mut v = vec![];
proof.write(&mut v).unwrap();

View File

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

View File

@@ -1,12 +1,13 @@
use ff::{
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError,
PrimeFieldRepr, ScalarEngine, SqrtField};
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine,
SqrtField,
};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use pairing::{Engine, PairingCurveAffine};
use rand_core::RngCore;
use std::cmp::Ordering;
use std::fmt;
use rand::{Rand, Rng};
use std::num::Wrapping;
const MODULUS_R: Wrapping<u32> = Wrapping(64513);
@@ -15,18 +16,16 @@ const MODULUS_R: Wrapping<u32> = Wrapping(64513);
pub struct Fr(Wrapping<u32>);
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)
}
}
impl Rand for Fr {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fr(Wrapping(rng.gen()) % MODULUS_R)
}
}
impl Field for Fr {
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fr(Wrapping(rng.next_u32()) % MODULUS_R)
}
fn zero() -> Self {
Fr(Wrapping(0))
}
@@ -82,9 +81,13 @@ impl SqrtField for Fr {
fn legendre(&self) -> LegendreSymbol {
// s = self^((r - 1) // 2)
let s = self.pow([32256]);
if s == <Fr as Field>::zero() { LegendreSymbol::Zero }
else if s == <Fr as Field>::one() { LegendreSymbol::QuadraticResidue }
else { LegendreSymbol::QuadraticNonResidue }
if s == <Fr as Field>::zero() {
LegendreSymbol::Zero
} else if s == <Fr as Field>::one() {
LegendreSymbol::QuadraticResidue
} else {
LegendreSymbol::QuadraticNonResidue
}
}
fn sqrt(&self) -> Option<Self> {
@@ -102,7 +105,7 @@ impl SqrtField for Fr {
let mut m = Fr::S;
while t != <Fr as Field>::one() {
let mut i = 1;
let mut i = 1;
{
let mut t2i = t;
t2i.square();
@@ -145,14 +148,8 @@ impl PartialOrd for FrRepr {
}
}
impl Rand for FrRepr {
fn rand<R: Rng>(rng: &mut R) -> Self {
FrRepr([rng.gen()])
}
}
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])
}
}
@@ -271,10 +268,13 @@ impl Engine for DummyEngine {
type Fqk = Fr;
fn miller_loop<'a, I>(i: I) -> Self::Fqk
where I: IntoIterator<Item=&'a (
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared
)>
where
I: IntoIterator<
Item = &'a (
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared,
),
>,
{
let mut acc = <Fr as Field>::zero();
@@ -288,8 +288,7 @@ impl Engine for DummyEngine {
}
/// 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)
}
}
@@ -300,6 +299,10 @@ impl CurveProjective for Fr {
type Scalar = Fr;
type Engine = DummyEngine;
fn random<R: RngCore>(rng: &mut R) -> Self {
<Fr as Field>::random(rng)
}
fn zero() -> Self {
<Fr as Field>::zero()
}
@@ -312,9 +315,7 @@ impl CurveProjective for Fr {
<Fr as Field>::is_zero(self)
}
fn batch_normalization(_: &mut [Self]) {
}
fn batch_normalization(_: &mut [Self]) {}
fn is_normalized(&self) -> bool {
true
@@ -336,8 +337,7 @@ impl CurveProjective for Fr {
<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();
<Fr as Field>::mul_assign(self, &tmp);
@@ -419,8 +419,7 @@ impl CurveAffine for Fr {
<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 tmp = Fr::from_repr(other.into()).unwrap();

View File

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

View File

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

View File

@@ -1,33 +1,154 @@
extern crate ff;
extern crate group;
#[cfg(feature = "pairing")]
extern crate pairing;
extern crate rand;
//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
//! traits and and primitive structures, as well as basic gadget implementations
//! such as booleans and number abstractions.
//!
//! # Example circuit
//!
//! Say we want to write a circuit that proves we know the preimage to some hash
//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a
//! 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.
extern crate futures;
extern crate bit_vec;
extern crate byteorder;
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
#[cfg(feature = "multicore")]
extern crate crossbeam;
#[cfg(feature = "multicore")]
extern crate futures_cpupool;
#[cfg(feature = "multicore")]
extern crate num_cpus;
pub mod multicore;
mod multiexp;
pub mod domain;
pub mod gadgets;
#[cfg(feature = "groth16")]
pub mod groth16;
pub mod multicore;
mod multiexp;
use ff::{Field, ScalarEngine};
use std::ops::{Add, Sub};
use std::fmt;
use std::error::Error;
use std::fmt;
use std::io;
use std::marker::PhantomData;
use std::ops::{Add, Sub};
/// Computations are expressed in terms of arithmetic circuits, in particular
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a
@@ -35,10 +156,7 @@ use std::marker::PhantomData;
/// CRS generation and during proving.
pub trait Circuit<E: ScalarEngine> {
/// 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.
@@ -64,7 +182,7 @@ impl Variable {
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Index {
Input(usize),
Aux(usize)
Aux(usize),
}
/// This represents a linear combination of some variables, with coefficients
@@ -97,6 +215,7 @@ impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
coeff.negate();
@@ -191,7 +310,7 @@ pub enum SynthesisError {
/// During verification, our verifying key was malformed.
MalformedVerifyingKey,
/// During CRS generation, we observed an unconstrained auxiliary variable
UnconstrainedVariable
UnconstrainedVariable,
}
impl From<io::Error> for SynthesisError {
@@ -203,21 +322,23 @@ impl From<io::Error> for SynthesisError {
impl Error for SynthesisError {
fn description(&self) -> &str {
match *self {
SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed",
SynthesisError::AssignmentMissing => {
"an assignment for a variable could not be computed"
}
SynthesisError::DivisionByZero => "division by zero",
SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
SynthesisError::IoError(_) => "encountered an I/O error",
SynthesisError::MalformedVerifyingKey => "malformed verifying key",
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained"
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained",
}
}
}
impl fmt::Display for SynthesisError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
if let &SynthesisError::IoError(ref e) = self {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
if let SynthesisError::IoError(ref e) = *self {
write!(f, "I/O error: ")?;
e.fmt(f)
} else {
@@ -242,40 +363,36 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// 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
/// namespace.
fn alloc<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> 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
/// determine the assignment of the variable.
fn alloc_input<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> 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
/// 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
)
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 enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<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
/// for downstream use; use `namespace` instead.
fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR;
where
NR: Into<String>,
N: FnOnce() -> NR;
/// Exit out of the existing namespace. Not intended for
/// downstream use; use `namespace` instead.
@@ -286,11 +403,10 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
fn get_root(&mut self) -> &mut Self::Root;
/// Begin a namespace for this constraint system.
fn namespace<'a, NR, N>(
&'a mut self,
name_fn: N
) -> Namespace<'a, E, Self::Root>
where NR: Into<String>, N: FnOnce() -> NR
fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root>
where
NR: Into<String>,
N: FnOnce() -> NR,
{
self.get_root().push_namespace(name_fn);
@@ -300,7 +416,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
/// a namespace context) and, when dropped, pops out of the namespace context.
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>);
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E>>(&'a mut CS, PhantomData<E>);
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
type Root = CS::Root;
@@ -309,37 +425,31 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
CS::one()
}
fn alloc<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
self.0.alloc(annotation, f)
}
fn alloc_input<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
self.0.alloc_input(annotation, f)
}
fn enforce<A, AR, LA, LB, LC>(
&mut self,
annotation: A,
a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
{
self.0.enforce(annotation, a, b, c)
}
@@ -349,18 +459,18 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
// never a root constraint system.
fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR
where
NR: Into<String>,
N: FnOnce() -> NR,
{
panic!("only the root's push_namespace should be called");
}
fn pop_namespace(&mut self)
{
fn pop_namespace(&mut self) {
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()
}
}
@@ -380,54 +490,48 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs
CS::one()
}
fn alloc<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
(**self).alloc(annotation, f)
}
fn alloc_input<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
(**self).alloc_input(annotation, f)
}
fn enforce<A, AR, LA, LB, LC>(
&mut self,
annotation: A,
a: LA,
b: LB,
c: LC
)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
{
(**self).enforce(annotation, a, b, c)
}
fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR
where
NR: Into<String>,
N: FnOnce() -> NR,
{
(**self).push_namespace(name_fn)
}
fn pop_namespace(&mut self)
{
fn pop_namespace(&mut self) {
(**self).pop_namespace()
}
fn get_root(&mut self) -> &mut Self::Root
{
fn get_root(&mut self) -> &mut Self::Root {
(**self).get_root()
}
}

View File

@@ -1,20 +1,21 @@
//! This is an interface for dealing with the kinds of
//! parallel computations involved in bellman. It's
//! currently just a thin wrapper around CpuPool and
//! crossbeam but may be extended in the future to
//! allow for various parallelism strategies.
//! An interface for dealing with the kinds of parallel computations involved in
//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and
//! [`crossbeam`] but may be extended in the future to allow for various
//! parallelism strategies.
//!
//! [`CpuPool`]: futures_cpupool::CpuPool
#[cfg(feature = "multicore")]
mod implementation {
use num_cpus;
use crossbeam::{self, thread::Scope};
use futures::{Future, IntoFuture, Poll};
use futures_cpupool::{CpuPool, CpuFuture};
use crossbeam::{self, Scope};
use futures_cpupool::{CpuFuture, CpuPool};
use num_cpus;
#[derive(Clone)]
pub struct Worker {
cpus: usize,
pool: CpuPool
pool: CpuPool,
}
impl Worker {
@@ -23,8 +24,8 @@ mod implementation {
// CPUs configured.
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
Worker {
cpus: cpus,
pool: CpuPool::new(cpus)
cpus,
pool: CpuPool::new(cpus),
}
}
@@ -36,26 +37,22 @@ mod implementation {
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
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)
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
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
@@ -63,22 +60,21 @@ mod implementation {
elements / self.cpus
};
crossbeam::scope(|scope| {
f(scope, chunk_size)
})
// 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> {
future: CpuFuture<T, E>
future: CpuFuture<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>
{
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.future.poll()
}
}
@@ -88,7 +84,7 @@ mod implementation {
let mut pow = 0;
while (1 << (pow+1)) <= num {
while (1 << (pow + 1)) <= num {
pow += 1;
}
@@ -159,8 +155,8 @@ mod implementation {
pub struct DummyScope;
impl DummyScope {
pub fn spawn<F: FnOnce()>(&self, f: F) {
f();
pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
f(self);
}
}
}

View File

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

View File

@@ -1,37 +1,22 @@
extern crate bellman;
extern crate ff;
extern crate pairing;
extern crate rand;
// For randomness (during paramgen and proof generation)
use rand::{thread_rng, Rng};
use rand::thread_rng;
// For benchmarking
use std::time::{Duration, Instant};
// Bring in some tools for using pairing-friendly curves
use ff::Field;
use ff::{Field, ScalarEngine};
use pairing::Engine;
// 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.
use bellman::{
Circuit,
ConstraintSystem,
SynthesisError
};
use bellman::{Circuit, ConstraintSystem, SynthesisError};
// We're going to use the Groth16 proving system.
use bellman::groth16::{
Proof,
generate_random_parameters,
prepare_verifying_key,
create_random_proof,
verify_proof,
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof,
};
const MIMC_ROUNDS: usize = 322;
@@ -49,12 +34,7 @@ const MIMC_ROUNDS: usize = 322;
/// 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);
for i in 0..MIMC_ROUNDS {
@@ -76,80 +56,81 @@ fn mimc<E: Engine>(
struct MiMCDemo<'a, E: Engine> {
xl: Option<E::Fr>,
xr: Option<E::Fr>,
constants: &'a [E::Fr]
constants: &'a [E::Fr],
}
/// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to
/// synthesize the constraint system.
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);
// Allocate the first component of the preimage.
let mut xl_value = self.xl;
let mut xl = cs.alloc(|| "preimage xl", || {
xl_value.ok_or(SynthesisError::AssignmentMissing)
})?;
let mut xl = cs.alloc(
|| "preimage xl",
|| xl_value.ok_or(SynthesisError::AssignmentMissing),
)?;
// Allocate the second component of the preimage.
let mut xr_value = self.xr;
let mut xr = cs.alloc(|| "preimage xr", || {
xr_value.ok_or(SynthesisError::AssignmentMissing)
})?;
let mut xr = cs.alloc(
|| "preimage xr",
|| xr_value.ok_or(SynthesisError::AssignmentMissing),
)?;
for i in 0..MIMC_ROUNDS {
// xL, xR := xR + (xL + Ci)^3, xL
let cs = &mut cs.namespace(|| format!("round {}", i));
// tmp = (xL + Ci)^2
let mut tmp_value = xl_value.map(|mut e| {
let tmp_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.square();
e
});
let mut tmp = cs.alloc(|| "tmp", || {
tmp_value.ok_or(SynthesisError::AssignmentMissing)
})?;
let tmp = cs.alloc(
|| "tmp",
|| tmp_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "tmp = (xL + Ci)^2",
|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 + tmp * (xL + Ci)
// new_xL - xR = tmp * (xL + Ci)
let mut new_xl_value = xl_value.map(|mut e| {
let new_xl_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.mul_assign(&tmp_value.unwrap());
e.add_assign(&xr_value.unwrap());
e
});
let mut new_xl = if i == (MIMC_ROUNDS-1) {
let new_xl = if i == (MIMC_ROUNDS - 1) {
// This is the last round, xL is our image and so
// we allocate a public input.
cs.alloc_input(|| "image", || {
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
})?
cs.alloc_input(
|| "image",
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
} else {
cs.alloc(|| "new_xl", || {
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
})?
cs.alloc(
|| "new_xl",
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
};
cs.enforce(
|| "new_xL = xR + (xL + Ci)^3",
|lc| lc + tmp,
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + new_xl - xr
|lc| lc + new_xl - xr,
);
// xR = xL
@@ -172,7 +153,9 @@ fn test_mimc() {
let rng = &mut thread_rng();
// Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
let constants = (0..MIMC_ROUNDS)
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng))
.collect::<Vec<_>>();
println!("Creating parameters...");
@@ -181,7 +164,7 @@ fn test_mimc() {
let c = MiMCDemo::<Bls12> {
xl: None,
xr: None,
constants: &constants
constants: &constants,
};
generate_random_parameters(c, rng).unwrap()
@@ -203,8 +186,8 @@ fn test_mimc() {
for _ in 0..SAMPLES {
// Generate a random preimage and compute the image
let xl = rng.gen();
let xr = rng.gen();
let xl = <Bls12 as ScalarEngine>::Fr::random(rng);
let xr = <Bls12 as ScalarEngine>::Fr::random(rng);
let image = mimc::<Bls12>(xl, xr, &constants);
proof_vec.truncate(0);
@@ -216,7 +199,7 @@ fn test_mimc() {
let c = MiMCDemo {
xl: Some(xl),
xr: Some(xr),
constants: &constants
constants: &constants,
};
// Create a groth16 proof with our parameters.
@@ -230,20 +213,16 @@ fn test_mimc() {
let start = Instant::now();
let proof = Proof::read(&proof_vec[..]).unwrap();
// Check the proof
assert!(verify_proof(
&pvk,
&proof,
&[image]
).unwrap());
assert!(verify_proof(&pvk, &proof, &[image]).unwrap());
total_verifying += start.elapsed();
}
let proving_avg = total_proving / SAMPLES;
let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64
+ (proving_avg.as_secs() as f64);
let proving_avg =
proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64);
let verifying_avg = total_verifying / SAMPLES;
let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64
+ (verifying_avg.as_secs() as f64);
let verifying_avg =
verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64);
println!("Average proving time: {:?} seconds", proving_avg);
println!("Average verifying time: {:?} seconds", verifying_avg);

View File

@@ -1,18 +1,23 @@
[package]
name = "ff"
version = "0.4.0"
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"
rand = "0.4"
ff_derive = { version = "0.3.0", path = "ff_derive", optional = true }
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

@@ -12,14 +12,18 @@ Add the `ff` crate to your `Cargo.toml`:
```toml
[dependencies]
ff = "0.4"
ff = "0.5"
```
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. See the **[documentation](https://docs.rs/ff/0.4.0/ff/)** for more.
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.
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:
@@ -41,13 +45,16 @@ extern crate ff;
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.
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)
* 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.

View File

@@ -1,12 +1,13 @@
[package]
name = "ff_derive"
version = "0.3.0"
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
@@ -15,6 +16,9 @@ proc-macro = true
num-bigint = "0.2"
num-traits = "0.2"
num-integer = "0.1"
proc-macro2 = "0.4"
quote = "0.6"
syn = "0.14"
proc-macro2 = "1"
quote = "1"
syn = "1"
[badges]
maintenance = { status = "passively-maintained" }

View File

@@ -2,17 +2,11 @@
extern crate proc_macro;
extern crate proc_macro2;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate num_bigint;
extern crate num_integer;
extern crate num_traits;
use num_bigint::BigUint;
use num_integer::Integer;
use num_traits::{One, ToPrimitive, Zero};
use quote::quote;
use quote::TokenStreamExt;
use std::str::FromStr;
@@ -52,13 +46,8 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut gen = proc_macro2::TokenStream::new();
let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt(
&ast.ident,
&repr_ident,
modulus,
limbs,
generator,
);
let (constants_impl, sqrt_impl) =
prime_field_constants_and_sqrt(&ast.ident, &repr_ident, modulus, limbs, generator);
gen.extend(constants_impl);
gen.extend(prime_field_repr_impl(&repr_ident, limbs));
@@ -96,10 +85,10 @@ fn fetch_wrapped_ident(body: &syn::Data) -> Option<syn::Ident> {
/// Fetch an attribute string from the derived struct.
fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> {
for attr in attrs {
if let Some(meta) = attr.interpret_meta() {
if let Ok(meta) = attr.parse_meta() {
match meta {
syn::Meta::NameValue(nv) => {
if nv.ident.to_string() == name {
if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) {
match nv.lit {
syn::Lit::Str(ref s) => return Some(s.value()),
_ => {
@@ -127,27 +116,20 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
impl ::std::fmt::Debug for #repr
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
try!(write!(f, "0x"));
write!(f, "0x")?;
for i in self.0.iter().rev() {
try!(write!(f, "{:016x}", *i));
write!(f, "{:016x}", *i)?;
}
Ok(())
}
}
impl ::rand::Rand for #repr {
#[inline(always)]
fn rand<R: ::rand::Rng>(rng: &mut R) -> Self {
#repr(rng.gen())
}
}
impl ::std::fmt::Display for #repr {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
try!(write!(f, "0x"));
write!(f, "0x")?;
for i in self.0.iter().rev() {
try!(write!(f, "{:016x}", *i));
write!(f, "{:016x}", *i)?;
}
Ok(())
@@ -366,7 +348,8 @@ fn biguint_num_bits(mut v: BigUint) -> u32 {
fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
let mut ret = BigUint::one();
for i in exp.to_bytes_be()
for i in exp
.to_bytes_be()
.into_iter()
.flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd()))
{
@@ -387,11 +370,13 @@ fn test_exp() {
&BigUint::from_str("5489673498567349856734895").unwrap(),
&BigUint::from_str(
"52435875175126190479447740508185965837690552500527637822603658699938581184513"
).unwrap()
)
.unwrap()
),
BigUint::from_str(
"4371221214068404307866768905142520595925044802278091865033317963560480051536"
).unwrap()
)
.unwrap()
);
}
@@ -430,7 +415,7 @@ fn prime_field_constants_and_sqrt(
let mod_minus_1_over_2 =
biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs);
let legendre_impl = quote!{
let legendre_impl = quote! {
fn legendre(&self) -> ::ff::LegendreSymbol {
// s = self^((modulus - 1) // 2)
let s = self.pow(#mod_minus_1_over_2);
@@ -452,7 +437,7 @@ fn prime_field_constants_and_sqrt(
// Compute -R as (m - r)
let rneg = biguint_to_u64_vec(&modulus - &r, limbs);
quote!{
quote! {
impl ::ff::SqrtField for #name {
#legendre_impl
@@ -479,7 +464,7 @@ fn prime_field_constants_and_sqrt(
let t_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs);
let t = biguint_to_u64_vec(t.clone(), limbs);
quote!{
quote! {
impl ::ff::SqrtField for #name {
#legendre_impl
@@ -526,7 +511,7 @@ fn prime_field_constants_and_sqrt(
}
}
} else {
quote!{}
quote! {}
};
// Compute R^2 mod m
@@ -543,36 +528,39 @@ fn prime_field_constants_and_sqrt(
}
inv = inv.wrapping_neg();
(quote! {
/// This is the modulus m of the prime field
const MODULUS: #repr = #repr([#(#modulus,)*]);
(
quote! {
/// This is the modulus m of the prime field
const MODULUS: #repr = #repr([#(#modulus,)*]);
/// The number of bits needed to represent the modulus.
const MODULUS_BITS: u32 = #modulus_num_bits;
/// The number of bits needed to represent the modulus.
const MODULUS_BITS: u32 = #modulus_num_bits;
/// The number of bits that must be shaved from the beginning of
/// the representation when randomly sampling.
const REPR_SHAVE_BITS: u32 = #repr_shave_bits;
/// The number of bits that must be shaved from the beginning of
/// the representation when randomly sampling.
const REPR_SHAVE_BITS: u32 = #repr_shave_bits;
/// 2^{limbs*64} mod m
const R: #repr = #repr(#r);
/// 2^{limbs*64} mod m
const R: #repr = #repr(#r);
/// 2^{limbs*64*2} mod m
const R2: #repr = #repr(#r2);
/// 2^{limbs*64*2} mod m
const R2: #repr = #repr(#r2);
/// -(m^{-1} mod m) mod m
const INV: u64 = #inv;
/// -(m^{-1} mod m) mod m
const INV: u64 = #inv;
/// Multiplicative generator of `MODULUS` - 1 order, also quadratic
/// nonresidue.
const GENERATOR: #repr = #repr(#generator);
/// Multiplicative generator of `MODULUS` - 1 order, also quadratic
/// nonresidue.
const GENERATOR: #repr = #repr(#generator);
/// 2^s * t = MODULUS - 1 with t odd
const S: u32 = #s;
/// 2^s * t = MODULUS - 1 with t odd
const S: u32 = #s;
/// 2^s root of unity computed by GENERATOR^t
const ROOT_OF_UNITY: #repr = #repr(#root_of_unity);
}, sqrt_impl)
/// 2^s root of unity computed by GENERATOR^t
const ROOT_OF_UNITY: #repr = #repr(#root_of_unity);
},
sqrt_impl,
)
}
/// Implement PrimeField for the derived type.
@@ -592,9 +580,9 @@ fn prime_field_impl(
mont_paramlist.append_separated(
(0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| {
if i != 0 {
quote!{mut #x: u64}
quote! {mut #x: u64}
} else {
quote!{#x: u64}
quote! {#x: u64}
}
}),
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
@@ -607,7 +595,7 @@ fn prime_field_impl(
for i in 0..limbs {
{
let temp = get_temp(i);
gen.extend(quote!{
gen.extend(quote! {
let k = #temp.wrapping_mul(INV);
let mut carry = 0;
::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry);
@@ -616,7 +604,7 @@ fn prime_field_impl(
for j in 1..limbs {
let temp = get_temp(i + j);
gen.extend(quote!{
gen.extend(quote! {
#temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry);
});
}
@@ -624,17 +612,17 @@ fn prime_field_impl(
let temp = get_temp(i + limbs);
if i == 0 {
gen.extend(quote!{
gen.extend(quote! {
#temp = ::ff::adc(#temp, 0, &mut carry);
});
} else {
gen.extend(quote!{
gen.extend(quote! {
#temp = ::ff::adc(#temp, carry2, &mut carry);
});
}
if i != (limbs - 1) {
gen.extend(quote!{
gen.extend(quote! {
let carry2 = carry;
});
}
@@ -643,7 +631,7 @@ fn prime_field_impl(
for i in 0..limbs {
let temp = get_temp(limbs + i);
gen.extend(quote!{
gen.extend(quote! {
(self.0).0[#i] = #temp;
});
}
@@ -655,14 +643,14 @@ fn prime_field_impl(
let mut gen = proc_macro2::TokenStream::new();
for i in 0..(limbs - 1) {
gen.extend(quote!{
gen.extend(quote! {
let mut carry = 0;
});
for j in (i + 1)..limbs {
let temp = get_temp(i + j);
if i == 0 {
gen.extend(quote!{
gen.extend(quote! {
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry);
});
} else {
@@ -674,7 +662,7 @@ fn prime_field_impl(
let temp = get_temp(i + limbs);
gen.extend(quote!{
gen.extend(quote! {
let #temp = carry;
});
}
@@ -684,21 +672,21 @@ fn prime_field_impl(
let temp1 = get_temp(limbs * 2 - i - 1);
if i == 1 {
gen.extend(quote!{
gen.extend(quote! {
let #temp0 = #temp1 >> 63;
});
} else if i == (limbs * 2 - 1) {
gen.extend(quote!{
gen.extend(quote! {
let #temp0 = #temp0 << 1;
});
} else {
gen.extend(quote!{
gen.extend(quote! {
let #temp0 = (#temp0 << 1) | (#temp1 >> 63);
});
}
}
gen.extend(quote!{
gen.extend(quote! {
let mut carry = 0;
});
@@ -706,7 +694,7 @@ fn prime_field_impl(
let temp0 = get_temp(i * 2);
let temp1 = get_temp(i * 2 + 1);
if i == 0 {
gen.extend(quote!{
gen.extend(quote! {
let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry);
});
} else {
@@ -715,7 +703,7 @@ fn prime_field_impl(
});
}
gen.extend(quote!{
gen.extend(quote! {
let #temp1 = ::ff::adc(#temp1, 0, &mut carry);
});
}
@@ -726,7 +714,7 @@ fn prime_field_impl(
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
);
gen.extend(quote!{
gen.extend(quote! {
self.mont_reduce(#mont_calling);
});
@@ -741,7 +729,7 @@ fn prime_field_impl(
let mut gen = proc_macro2::TokenStream::new();
for i in 0..limbs {
gen.extend(quote!{
gen.extend(quote! {
let mut carry = 0;
});
@@ -749,7 +737,7 @@ fn prime_field_impl(
let temp = get_temp(i + j);
if i == 0 {
gen.extend(quote!{
gen.extend(quote! {
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry);
});
} else {
@@ -761,7 +749,7 @@ fn prime_field_impl(
let temp = get_temp(i + limbs);
gen.extend(quote!{
gen.extend(quote! {
let #temp = carry;
});
}
@@ -772,29 +760,29 @@ fn prime_field_impl(
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
);
gen.extend(quote!{
gen.extend(quote! {
self.mont_reduce(#mont_calling);
});
gen
}
let squaring_impl = sqr_impl(quote!{self}, limbs);
let multiply_impl = mul_impl(quote!{self}, quote!{other}, limbs);
let squaring_impl = sqr_impl(quote! {self}, limbs);
let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs);
let montgomery_impl = mont_impl(limbs);
// (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ...
let mut into_repr_params = proc_macro2::TokenStream::new();
into_repr_params.append_separated(
(0..limbs)
.map(|i| quote!{ (self.0).0[#i] })
.chain((0..limbs).map(|_| quote!{0})),
.map(|i| quote! { (self.0).0[#i] })
.chain((0..limbs).map(|_| quote! {0})),
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
);
let top_limb_index = limbs - 1;
quote!{
quote! {
impl ::std::marker::Copy for #name { }
impl ::std::clone::Clone for #name {
@@ -839,22 +827,6 @@ fn prime_field_impl(
}
}
impl ::rand::Rand for #name {
/// Computes a uniformly random element using rejection sampling.
fn rand<R: ::rand::Rng>(rng: &mut R) -> Self {
loop {
let mut tmp = #name(#repr::rand(rng));
// Mask away the unused bits at the beginning.
tmp.0.as_mut()[#top_limb_index] &= 0xffffffffffffffff >> REPR_SHAVE_BITS;
if tmp.is_valid() {
return tmp
}
}
}
}
impl From<#name> for #repr {
fn from(e: #name) -> #repr {
e.into_repr()
@@ -904,6 +876,26 @@ fn prime_field_impl(
}
impl ::ff::Field for #name {
/// Computes a uniformly random element using rejection sampling.
fn random<R: ::rand_core::RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
loop {
let mut tmp = {
let mut repr = [0u64; #limbs];
for i in 0..#limbs {
repr[i] = rng.next_u64();
}
#name(#repr(repr))
};
// Mask away the unused most-significant bits.
tmp.0.as_mut()[#top_limb_index] &= 0xffffffffffffffff >> REPR_SHAVE_BITS;
if tmp.is_valid() {
return tmp
}
}
}
#[inline]
fn zero() -> Self {
#name(#repr::from(0))

View File

@@ -1,23 +1,24 @@
//! 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)]
extern crate byteorder;
extern crate rand;
#[cfg(feature = "derive")]
#[macro_use]
extern crate ff_derive;
#[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 + rand::Rand
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;
@@ -100,7 +101,6 @@ pub trait PrimeFieldRepr:
+ fmt::Debug
+ fmt::Display
+ 'static
+ rand::Rand
+ AsRef<[u64]>
+ AsMut<[u64]>
+ From<u64>
@@ -207,7 +207,7 @@ impl Error for PrimeFieldDecodingError {
}
impl fmt::Display for PrimeFieldDecodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
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)
@@ -263,7 +263,7 @@ pub trait PrimeField: Field {
}
/// Convert this prime field element into a biginteger representation.
fn from_repr(Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
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.

View File

@@ -1,17 +1,23 @@
[package]
name = "group"
version = "0.1.0"
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 = { path = "../ff" }
rand = "0.4"
ff = { version = "0.5.0", path = "../ff" }
rand = "0.7"
rand_xorshift = "0.2"
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -1,10 +1,13 @@
# group [![Crates.io](https://img.shields.io/crates/v/group.svg)](https://crates.io/crates/group) #
`group` is a crate for working with groups over elliptic curves.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* 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.

View File

@@ -1,7 +1,8 @@
extern crate ff;
extern crate rand;
// 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;
@@ -13,23 +14,16 @@ 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
+ rand::Rand
+ 'static
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;
@@ -185,7 +179,7 @@ impl Error for GroupDecodingError {
}
impl fmt::Display for GroupDecodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self {
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
write!(f, "{} decoding error: {}", description, err)

View File

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

View File

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

View File

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

View File

@@ -112,8 +112,7 @@ extern "C" {
bool librustzcash_sapling_output_proof(
void *ctx,
const unsigned char *esk,
const unsigned char *diversifier,
const unsigned char *pk_d,
const unsigned char *payment_address,
const unsigned char *rcm,
const uint64_t value,
unsigned char *cv,
@@ -308,6 +307,33 @@ extern "C" {
unsigned char *j_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_

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

View File

@@ -1,13 +1,13 @@
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12;
use sapling_crypto::{
use zcash_primitives::{
jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams},
primitives::{Diversifier, ProofGenerationKey},
};
use super::JUBJUB;
use {
use crate::{
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
};
@@ -28,6 +28,8 @@ fn key_components() {
note_v: u64,
note_r: [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
@@ -87,6 +89,12 @@ fn key_components() {
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
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 {
sk: [
@@ -143,6 +151,12 @@ fn key_components() {
0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72,
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 {
sk: [
@@ -199,6 +213,12 @@ fn key_components() {
0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f,
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 {
sk: [
@@ -255,6 +275,12 @@ fn key_components() {
0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa,
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 {
sk: [
@@ -311,6 +337,12 @@ fn key_components() {
0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b,
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 {
sk: [
@@ -367,6 +399,12 @@ fn key_components() {
0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69,
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 {
sk: [
@@ -423,6 +461,12 @@ fn key_components() {
0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b,
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 {
sk: [
@@ -479,6 +523,12 @@ fn key_components() {
0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36,
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 {
sk: [
@@ -535,6 +585,12 @@ fn key_components() {
0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07,
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 {
sk: [
@@ -591,6 +647,12 @@ fn key_components() {
0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63,
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,
],
},
];
@@ -616,7 +678,7 @@ fn key_components() {
}
let pgk = ProofGenerationKey { ak, nsk };
let fvk = pgk.into_viewing_key(&JUBJUB);
let fvk = pgk.to_viewing_key(&JUBJUB);
{
let mut vec = Vec::new();
fvk.nk.write(&mut vec).unwrap();
@@ -642,10 +704,10 @@ fn key_components() {
let diversifier = Diversifier(tv.default_d);
assert!(librustzcash_check_diversifier(&tv.default_d));
let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap();
let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
{
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);
}
{
@@ -663,5 +725,7 @@ fn key_components() {
note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap();
assert_eq!(&vec, &tv.note_cm);
}
assert_eq!(note.nf(&fvk, tv.note_pos, &JUBJUB), tv.note_nf);
}
}

View File

@@ -0,0 +1,225 @@
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,9 +1,10 @@
use sapling_crypto::jubjub::{FixedGenerators, JubjubParams};
use zcash_primitives::jubjub::{FixedGenerators, JubjubParams};
use super::JUBJUB;
mod key_agreement;
mod key_components;
mod mmr;
mod notes;
mod signatures;

View File

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

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

@@ -1,6 +1,16 @@
# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) #
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.
`pairing` is a crate for using pairing-friendly elliptic curves.
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/)
@@ -8,13 +18,15 @@ Bring the `pairing` crate into your project just as you normally would.
## Security Warnings
This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks.
This library does not make any guarantees about constant-time operations, memory
access patterns, or resistance to side-channel attacks.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* 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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,8 @@
extern crate ff;
extern crate group;
extern crate pairing;
extern crate rand;
extern crate rand_core;
extern crate rand_xorshift;
extern crate test;
mod bls12_381;

View File

@@ -14,12 +14,11 @@ macro_rules! curve_impl {
pub struct $affine {
pub(crate) x: $basefield,
pub(crate) y: $basefield,
pub(crate) infinity: bool
pub(crate) infinity: bool,
}
impl ::std::fmt::Display for $affine
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
impl ::std::fmt::Display for $affine {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
if self.infinity {
write!(f, "{}(Infinity)", $name)
} else {
@@ -30,14 +29,13 @@ macro_rules! curve_impl {
#[derive(Copy, Clone, Debug, Eq)]
pub struct $projective {
pub(crate) x: $basefield,
pub(crate) y: $basefield,
pub(crate) z: $basefield
pub(crate) x: $basefield,
pub(crate) y: $basefield,
pub(crate) z: $basefield,
}
impl ::std::fmt::Display for $projective
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
impl ::std::fmt::Display for $projective {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{}", self.into_affine())
}
}
@@ -89,7 +87,9 @@ macro_rules! curve_impl {
let mut res = $projective::zero();
for i in bits {
res.double();
if i { res.add_assign_mixed(self) }
if i {
res.add_assign_mixed(self)
}
}
res
}
@@ -112,12 +112,8 @@ macro_rules! curve_impl {
$affine {
x: x,
y: if (y < negy) ^ greatest {
y
} else {
negy
},
infinity: false
y: if (y < negy) ^ greatest { y } else { negy },
infinity: false,
}
})
}
@@ -156,7 +152,7 @@ macro_rules! curve_impl {
$affine {
x: $basefield::zero(),
y: $basefield::one(),
infinity: true
infinity: true,
}
}
@@ -182,7 +178,6 @@ macro_rules! curve_impl {
fn into_projective(&self) -> $projective {
(*self).into()
}
}
impl PairingCurveAffine for $affine {
@@ -197,14 +192,18 @@ macro_rules! curve_impl {
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
self.perform_pairing(other)
}
}
impl Rand for $projective {
fn rand<R: Rng>(rng: &mut R) -> Self {
impl CurveProjective for $projective {
type Engine = Bls12;
type Scalar = $scalarfield;
type Base = $basefield;
type Affine = $affine;
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
loop {
let x = rng.gen();
let greatest = rng.gen();
let x = $basefield::random(rng);
let greatest = rng.next_u32() % 2 != 0;
if let Some(p) = $affine::get_point_from_x(x, greatest) {
let p = p.scale_by_cofactor();
@@ -215,13 +214,6 @@ 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
// Z = 0.
@@ -229,7 +221,7 @@ macro_rules! curve_impl {
$projective {
x: $basefield::zero(),
y: $basefield::one(),
z: $basefield::zero()
z: $basefield::zero(),
}
}
@@ -247,8 +239,7 @@ macro_rules! curve_impl {
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
// Genelle, Prouff and Quisquater
// Section 3.2
@@ -256,9 +247,10 @@ macro_rules! curve_impl {
// First pass: compute [a, ab, abc, ...]
let mut prod = Vec::with_capacity(v.len());
let mut tmp = $basefield::one();
for g in v.iter_mut()
// Ignore normalized elements
.filter(|g| !g.is_normalized())
for g in v
.iter_mut()
// Ignore normalized elements
.filter(|g| !g.is_normalized())
{
tmp.mul_assign(&g.z);
prod.push(tmp);
@@ -268,13 +260,19 @@ macro_rules! curve_impl {
tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero.
// Second pass: iterate backwards to compute inverses
for (g, s) in v.iter_mut()
// Backwards
.rev()
// Ignore normalized elements
.filter(|g| !g.is_normalized())
// Backwards, skip last element, fill in one for last term.
.zip(prod.into_iter().rev().skip(1).chain(Some($basefield::one())))
for (g, s) in v
.iter_mut()
// Backwards
.rev()
// Ignore normalized elements
.filter(|g| !g.is_normalized())
// Backwards, skip last element, fill in one for last term.
.zip(
prod.into_iter()
.rev()
.skip(1)
.chain(Some($basefield::one())),
)
{
// tmp := tmp * g.z; g.z := tmp * s = 1/z
let mut newtmp = tmp;
@@ -285,9 +283,7 @@ macro_rules! curve_impl {
}
// 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
z.square(); // 1/z^2
g.x.mul_assign(&z); // x/z^2
@@ -540,8 +536,7 @@ macro_rules! curve_impl {
let mut found_one = false;
for i in BitIterator::new(other.into())
{
for i in BitIterator::new(other.into()) {
if found_one {
res.double();
} else {
@@ -579,7 +574,7 @@ macro_rules! curve_impl {
$projective {
x: p.x,
y: p.y,
z: $basefield::one()
z: $basefield::one(),
}
}
}
@@ -596,7 +591,7 @@ macro_rules! curve_impl {
$affine {
x: p.x,
y: p.y,
infinity: false
infinity: false,
}
} else {
// Z is nonzero, so it must have an inverse in a field.
@@ -616,22 +611,22 @@ macro_rules! curve_impl {
$affine {
x: x,
y: y,
infinity: false
infinity: false,
}
}
}
}
}
};
}
pub mod g1 {
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
use super::g2::G2Affine;
use crate::{Engine, PairingCurveAffine};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand::{Rand, Rng};
use rand_core::RngCore;
use std::fmt;
use {Engine, PairingCurveAffine};
curve_impl!(
"G1",
@@ -661,7 +656,7 @@ pub mod g1 {
}
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)
}
}
@@ -771,7 +766,7 @@ pub mod g1 {
}
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)
}
}
@@ -939,7 +934,7 @@ pub mod g1 {
#[test]
fn g1_generator() {
use SqrtField;
use crate::SqrtField;
let mut x = Fq::zero();
let mut i = 0;
@@ -957,7 +952,7 @@ pub mod g1 {
let negyrepr = negy.into_repr();
let p = G1Affine {
x: x,
x,
y: if yrepr < negyrepr { y } else { negy },
infinity: false,
};
@@ -992,7 +987,8 @@ pub mod g1 {
0x9fe83b1b4a5d648d,
0xf583cc5a508f6a40,
0xc3ad2aefde0bb13,
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0x60aa6f9552f03aae,
0xecd01d5181300d35,
@@ -1000,7 +996,8 @@ pub mod g1 {
0xe760f57922998c9d,
0x953703f5795a39e5,
0xfe3ae0922df702c,
])).unwrap(),
]))
.unwrap(),
infinity: false,
};
assert!(!p.is_on_curve());
@@ -1017,7 +1014,8 @@ pub mod g1 {
0xea034ee2928b30a8,
0xbd8833dc7c79a7f7,
0xe45c9f0c0438675,
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0x3b450eb1ab7b5dad,
0xa65cb81e975e8675,
@@ -1025,7 +1023,8 @@ pub mod g1 {
0x753ddf21a2601d20,
0x532d0b640bd3ff8b,
0x118d2c543f031102,
])).unwrap(),
]))
.unwrap(),
infinity: false,
};
assert!(!p.is_on_curve());
@@ -1043,7 +1042,8 @@ pub mod g1 {
0xf35de9ce0d6b4e84,
0x265bddd23d1dec54,
0x12a8778088458308,
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0x8a22defa0d526256,
0xc57ca55456fcb9ae,
@@ -1051,7 +1051,8 @@ pub mod g1 {
0x921beef89d4f29df,
0x5b6fda44ad85fa78,
0xed74ab9f302cbe0,
])).unwrap(),
]))
.unwrap(),
infinity: false,
};
assert!(p.is_on_curve());
@@ -1069,7 +1070,8 @@ pub mod g1 {
0x485e77d50a5df10d,
0x4c6fcac4b55fd479,
0x86ed4d9906fb064,
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0xd25ee6461538c65,
0x9f3bbb2ecd3719b9,
@@ -1077,7 +1079,8 @@ pub mod g1 {
0xcefca68333c35288,
0x570c8005f8573fa6,
0x152ca696fe034442,
])).unwrap(),
]))
.unwrap(),
z: Fq::one(),
};
@@ -1089,7 +1092,8 @@ pub mod g1 {
0x5f44314ec5e3fb03,
0x24e8538737c6e675,
0x8abd623a594fba8,
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0x6b0528f088bb7044,
0x2fdeb5c82917ff9e,
@@ -1097,7 +1101,8 @@ pub mod g1 {
0xd65104c6f95a872a,
0x1f2998a5a9c61253,
0xe74846154a9e44,
])).unwrap(),
]))
.unwrap(),
z: Fq::one(),
});
@@ -1113,7 +1118,8 @@ pub mod g1 {
0xc4f9a52a428e23bb,
0xd178b28dd4f407ef,
0x17fb8905e9183c69
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0xd0de9d65292b7710,
0xf6a05f2bcf1d9ca7,
@@ -1121,7 +1127,8 @@ pub mod g1 {
0xeec8d1a5b7466c58,
0x4bc362649dce6376,
0x430cbdc5455b00a
])).unwrap(),
]))
.unwrap(),
infinity: false,
}
);
@@ -1137,7 +1144,8 @@ pub mod g1 {
0x485e77d50a5df10d,
0x4c6fcac4b55fd479,
0x86ed4d9906fb064,
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0xd25ee6461538c65,
0x9f3bbb2ecd3719b9,
@@ -1145,7 +1153,8 @@ pub mod g1 {
0xcefca68333c35288,
0x570c8005f8573fa6,
0x152ca696fe034442,
])).unwrap(),
]))
.unwrap(),
z: Fq::one(),
};
@@ -1163,7 +1172,8 @@ pub mod g1 {
0x4b914c16687dcde0,
0x66c8baf177d20533,
0xaf960cff3d83833
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0x3f0675695f5177a8,
0x2b6d82ae178a1ba0,
@@ -1171,7 +1181,8 @@ pub mod g1 {
0x1771a65b60572f4e,
0x8b547c1313b27555,
0x135075589a687b1e
])).unwrap(),
]))
.unwrap(),
infinity: false,
}
);
@@ -1194,7 +1205,8 @@ pub mod g1 {
0x71ffa8021531705,
0x7418d484386d267,
0xd5108d8ff1fbd6,
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0xa776ccbfe9981766,
0x255632964ff40f4a,
@@ -1202,7 +1214,8 @@ pub mod g1 {
0x520f74773e74c8c3,
0x484c8fc982008f0,
0xee2c3d922008cc6,
])).unwrap(),
]))
.unwrap(),
infinity: false,
};
@@ -1214,7 +1227,8 @@ pub mod g1 {
0xc6e05201e5f83991,
0xf7c75910816f207c,
0x18d4043e78103106,
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0xa776ccbfe9981766,
0x255632964ff40f4a,
@@ -1222,7 +1236,8 @@ pub mod g1 {
0x520f74773e74c8c3,
0x484c8fc982008f0,
0xee2c3d922008cc6,
])).unwrap(),
]))
.unwrap(),
infinity: false,
};
@@ -1237,7 +1252,8 @@ pub mod g1 {
0x9676ff02ec39c227,
0x4c12c15d7e55b9f3,
0x57fd1e317db9bd,
])).unwrap(),
]))
.unwrap(),
y: Fq::from_repr(FqRepr([
0x1288334016679345,
0xf955cd68615ff0b5,
@@ -1245,7 +1261,8 @@ pub mod g1 {
0x1267d70db51049fb,
0x4696deb9ab2ba3e7,
0xb1e4e11177f59d4,
])).unwrap(),
]))
.unwrap(),
infinity: false,
};
@@ -1274,11 +1291,11 @@ pub mod g1 {
pub mod g2 {
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
use super::g1::G1Affine;
use crate::{Engine, PairingCurveAffine};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand::{Rand, Rng};
use rand_core::RngCore;
use std::fmt;
use {Engine, PairingCurveAffine};
curve_impl!(
"G2",
@@ -1308,7 +1325,7 @@ pub mod g2 {
}
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)
}
}
@@ -1434,7 +1451,7 @@ pub mod g2 {
}
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)
}
}
@@ -1623,7 +1640,7 @@ pub mod g2 {
#[test]
fn g2_generator() {
use SqrtField;
use crate::SqrtField;
let mut x = Fq2::zero();
let mut i = 0;
@@ -1639,7 +1656,7 @@ pub mod g2 {
negy.negate();
let p = G2Affine {
x: x,
x,
y: if y < negy { y } else { negy },
infinity: false,
};
@@ -1675,7 +1692,8 @@ pub mod g2 {
0x7a17a004747e3dbe,
0xcc65406a7c2e5a73,
0x10b8c03d64db4d0c,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xd30e70fe2f029778,
0xda30772df0f5212e,
@@ -1683,7 +1701,8 @@ pub mod g2 {
0xfb777e5b9b568608,
0x789bac1fec71a2b9,
0x1342f02e2da54405,
])).unwrap(),
]))
.unwrap(),
},
y: Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -1693,7 +1712,8 @@ pub mod g2 {
0x663015d9410eb608,
0x78e82a79d829a544,
0x40a00545bb3c1e,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x4709802348e79377,
0xb5ac4dc9204bcfbd,
@@ -1701,7 +1721,8 @@ pub mod g2 {
0x15008b1dc399e8df,
0x68128fd0548a3829,
0x16a613db5c873aaa,
])).unwrap(),
]))
.unwrap(),
},
infinity: false,
};
@@ -1720,7 +1741,8 @@ pub mod g2 {
0x41abba710d6c692c,
0xffcc4b2b62ce8484,
0x6993ec01b8934ed,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xb94e92d5f874e26,
0x44516408bc115d95,
@@ -1728,7 +1750,8 @@ pub mod g2 {
0xa5a0c2b7131f3555,
0x83800965822367e7,
0x10cf1d3ad8d90bfa,
])).unwrap(),
]))
.unwrap(),
},
y: Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -1738,7 +1761,8 @@ pub mod g2 {
0x5a9171720e73eb51,
0x38eb4fd8d658adb7,
0xb649051bbc1164d,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x9225814253d7df75,
0xc196c2513477f887,
@@ -1746,7 +1770,8 @@ pub mod g2 {
0x55f2b8efad953e04,
0x7379345eda55265e,
0x377f2e6208fd4cb,
])).unwrap(),
]))
.unwrap(),
},
infinity: false,
};
@@ -1766,7 +1791,8 @@ pub mod g2 {
0x2199bc19c48c393d,
0x4a151b732a6075bf,
0x17762a3b9108c4a7,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x26f461e944bbd3d1,
0x298f3189a9cf6ed6,
@@ -1774,7 +1800,8 @@ pub mod g2 {
0x7e147f3f9e6e241,
0x72a9b63583963fff,
0x158b0083c000462,
])).unwrap(),
]))
.unwrap(),
},
y: Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -1784,7 +1811,8 @@ pub mod g2 {
0x68cad19430706b4d,
0x3ccfb97b924dcea8,
0x1660f93434588f8d,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xaaed3985b6dcb9c7,
0xc1e985d6d898d9f4,
@@ -1792,7 +1820,8 @@ pub mod g2 {
0x3940a2dbb914b529,
0xbeb88137cf34f3e7,
0x1699ee577c61b694,
])).unwrap(),
]))
.unwrap(),
},
infinity: false,
};
@@ -1812,7 +1841,8 @@ pub mod g2 {
0x72556c999f3707ac,
0x4617f2e6774e9711,
0x100b2fe5bffe030b,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x7a33555977ec608,
0xe23039d1fe9c0881,
@@ -1820,7 +1850,8 @@ pub mod g2 {
0x4637c4f417667e2e,
0x93ebe7c3e41f6acc,
0xde884f89a9a371b,
])).unwrap(),
]))
.unwrap(),
},
y: Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -1830,7 +1861,8 @@ pub mod g2 {
0x25fd427b4122f231,
0xd83112aace35cae,
0x191b2432407cbb7f,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xf68ae82fe97662f5,
0xe986057068b50b7d,
@@ -1838,7 +1870,8 @@ pub mod g2 {
0x9eaa6d19de569196,
0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b,
])).unwrap(),
]))
.unwrap(),
},
z: Fq2::one(),
};
@@ -1852,7 +1885,8 @@ pub mod g2 {
0x8e73a96b329ad190,
0x27c546f75ee1f3ab,
0xa33d27add5e7e82,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x93b1ebcd54870dfe,
0xf1578300e1342e11,
@@ -1860,7 +1894,8 @@ pub mod g2 {
0x2089faf462438296,
0x828e5848cd48ea66,
0x141ecbac1deb038b,
])).unwrap(),
]))
.unwrap(),
},
y: Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -1870,7 +1905,8 @@ pub mod g2 {
0x2767032fc37cc31d,
0xd5ee2aba84fd10fe,
0x16576ccd3dd0a4e8,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x4da9b6f6a96d1dd2,
0x9657f7da77f1650e,
@@ -1878,7 +1914,8 @@ pub mod g2 {
0x31898db63f87363a,
0xabab040ddbd097cc,
0x11ad236b9ba02990,
])).unwrap(),
]))
.unwrap(),
},
z: Fq2::one(),
});
@@ -1896,7 +1933,8 @@ pub mod g2 {
0xf1273e6406eef9cc,
0xababd760ff05cb92,
0xd7c20456617e89
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xd1a50b8572cbd2b8,
0x238f0ac6119d07df,
@@ -1904,7 +1942,8 @@ pub mod g2 {
0x8b203284c51edf6b,
0xc8a0b730bbb21f5e,
0x1a3b59d29a31274
])).unwrap(),
]))
.unwrap(),
},
y: Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -1914,7 +1953,8 @@ pub mod g2 {
0x64528ab3863633dc,
0x159384333d7cba97,
0x4cb84741f3cafe8
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x242af0dc3640e1a4,
0xe90a73ad65c66919,
@@ -1922,7 +1962,8 @@ pub mod g2 {
0x38528f92b689644d,
0xb6884deec59fb21f,
0x3c075d3ec52ba90
])).unwrap(),
]))
.unwrap(),
},
infinity: false,
}
@@ -1940,7 +1981,8 @@ pub mod g2 {
0x72556c999f3707ac,
0x4617f2e6774e9711,
0x100b2fe5bffe030b,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x7a33555977ec608,
0xe23039d1fe9c0881,
@@ -1948,7 +1990,8 @@ pub mod g2 {
0x4637c4f417667e2e,
0x93ebe7c3e41f6acc,
0xde884f89a9a371b,
])).unwrap(),
]))
.unwrap(),
},
y: Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -1958,7 +2001,8 @@ pub mod g2 {
0x25fd427b4122f231,
0xd83112aace35cae,
0x191b2432407cbb7f,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xf68ae82fe97662f5,
0xe986057068b50b7d,
@@ -1966,7 +2010,8 @@ pub mod g2 {
0x9eaa6d19de569196,
0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b,
])).unwrap(),
]))
.unwrap(),
},
z: Fq2::one(),
};
@@ -1986,7 +2031,8 @@ pub mod g2 {
0xbcedcfce1e52d986,
0x9755d4a3926e9862,
0x18bab73760fd8024
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x4e7c5e0a2ae5b99e,
0x96e582a27f028961,
@@ -1994,7 +2040,8 @@ pub mod g2 {
0xeb0cf5e610ef4fe7,
0x7b4c2bae8db6e70b,
0xf136e43909fca0
])).unwrap(),
]))
.unwrap(),
},
y: Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -2004,7 +2051,8 @@ pub mod g2 {
0xa5a2a51f7fde787b,
0x8b92866bc6384188,
0x81a53fe531d64ef
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x4c5d607666239b34,
0xeddb5f48304d14b3,
@@ -2012,7 +2060,8 @@ pub mod g2 {
0xb271f52f12ead742,
0x244e6c2015c83348,
0x19e2deae6eb9b441
])).unwrap(),
]))
.unwrap(),
},
infinity: false,
}

View File

@@ -1173,7 +1173,9 @@ fn test_neg_one() {
}
#[cfg(test)]
use rand::{Rand, SeedableRng, XorShiftRng};
use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test]
fn test_fq_repr_ordering() {
@@ -1396,7 +1398,10 @@ fn test_fq_repr_num_bits() {
#[test]
fn test_fq_repr_sub_noborrow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FqRepr([
0x827a4a08041ebd9,
@@ -1426,7 +1431,7 @@ fn test_fq_repr_sub_noborrow() {
);
for _ in 0..1000 {
let mut a = FqRepr::rand(&mut rng);
let mut a = Fq::random(&mut rng).into_repr();
a.0[5] >>= 30;
let mut b = a;
for _ in 0..10 {
@@ -1483,7 +1488,10 @@ fn test_fq_repr_sub_noborrow() {
#[test]
fn test_fq_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FqRepr([
0x827a4a08041ebd9,
@@ -1514,9 +1522,9 @@ fn test_fq_repr_add_nocarry() {
// Test for the associativity of addition.
for _ in 0..1000 {
let mut a = FqRepr::rand(&mut rng);
let mut b = FqRepr::rand(&mut rng);
let mut c = FqRepr::rand(&mut rng);
let mut a = Fq::random(&mut rng).into_repr();
let mut b = Fq::random(&mut rng).into_repr();
let mut c = Fq::random(&mut rng).into_repr();
// Unset the first few bits, so that overflow won't occur.
a.0[5] >>= 3;
@@ -1574,31 +1582,32 @@ fn test_fq_is_valid() {
a.0.sub_noborrow(&FqRepr::from(1));
assert!(a.is_valid());
assert!(Fq(FqRepr::from(0)).is_valid());
assert!(
Fq(FqRepr([
0xdf4671abd14dab3e,
0xe2dc0c9f534fbd33,
0x31ca6c880cc444a6,
0x257a67e70ef33359,
0xf9b29e493f899b36,
0x17c8be1800b9f059
])).is_valid()
);
assert!(
!Fq(FqRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff
])).is_valid()
);
assert!(Fq(FqRepr([
0xdf4671abd14dab3e,
0xe2dc0c9f534fbd33,
0x31ca6c880cc444a6,
0x257a67e70ef33359,
0xf9b29e493f899b36,
0x17c8be1800b9f059
]))
.is_valid());
assert!(!Fq(FqRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff
]))
.is_valid());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
let a = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
assert!(a.is_valid());
}
}
@@ -1708,13 +1717,16 @@ fn test_fq_add_assign() {
// Test associativity
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Generate a, b, c and ensure (a + b) + c == a + (b + c).
let a = Fq::rand(&mut rng);
let b = Fq::rand(&mut rng);
let c = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
let b = Fq::random(&mut rng);
let c = Fq::random(&mut rng);
let mut tmp1 = a;
tmp1.add_assign(&b);
@@ -1818,12 +1830,15 @@ fn test_fq_sub_assign() {
);
}
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Ensure that (a - b) + (b - a) = 0.
let a = Fq::rand(&mut rng);
let b = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
let b = Fq::random(&mut rng);
let mut tmp1 = a;
tmp1.sub_assign(&b);
@@ -1865,13 +1880,16 @@ fn test_fq_mul_assign() {
]))
);
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 {
// Ensure that (a * b) * c = a * (b * c)
let a = Fq::rand(&mut rng);
let b = Fq::rand(&mut rng);
let c = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
let b = Fq::random(&mut rng);
let c = Fq::random(&mut rng);
let mut tmp1 = a;
tmp1.mul_assign(&b);
@@ -1887,10 +1905,10 @@ fn test_fq_mul_assign() {
for _ in 0..1000000 {
// Ensure that r * (a + b + c) = r*a + r*b + r*c
let r = Fq::rand(&mut rng);
let mut a = Fq::rand(&mut rng);
let mut b = Fq::rand(&mut rng);
let mut c = Fq::rand(&mut rng);
let r = Fq::random(&mut rng);
let mut a = Fq::random(&mut rng);
let mut b = Fq::random(&mut rng);
let mut c = Fq::random(&mut rng);
let mut tmp1 = a;
tmp1.add_assign(&b);
@@ -1929,14 +1947,18 @@ fn test_fq_squaring() {
0xdc05c659b4e15b27,
0x79361e5a802c6a23,
0x24bcbe5d51b9a6f
])).unwrap()
]))
.unwrap()
);
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 {
// Ensure that (a * a) = a^2
let a = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
let mut tmp = a;
tmp.square();
@@ -1952,13 +1974,16 @@ fn test_fq_squaring() {
fn test_fq_inverse() {
assert!(Fq::zero().inverse().is_none());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let one = Fq::one();
for _ in 0..1000 {
// Ensure that a * a^-1 = 1
let mut a = Fq::rand(&mut rng);
let mut a = Fq::random(&mut rng);
let ainv = a.inverse().unwrap();
a.mul_assign(&ainv);
assert_eq!(a, one);
@@ -1967,11 +1992,14 @@ fn test_fq_inverse() {
#[test]
fn test_fq_double() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Ensure doubling a is equivalent to adding a to itself.
let mut a = Fq::rand(&mut rng);
let mut a = Fq::random(&mut rng);
let mut b = a;
b.add_assign(&a);
a.double();
@@ -1988,11 +2016,14 @@ fn test_fq_negate() {
assert!(a.is_zero());
}
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Ensure (a - (-a)) = 0.
let mut a = Fq::rand(&mut rng);
let mut a = Fq::random(&mut rng);
let mut b = a;
b.negate();
a.add_assign(&b);
@@ -2003,12 +2034,15 @@ fn test_fq_negate() {
#[test]
fn test_fq_pow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for i in 0..1000 {
// Exponentiate by various small numbers and ensure it consists with repeated
// multiplication.
let a = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
let target = a.pow(&[i]);
let mut c = Fq::one();
for _ in 0..i {
@@ -2019,7 +2053,7 @@ fn test_fq_pow() {
for _ in 0..1000 {
// Exponentiating by the modulus should have no effect in a prime field.
let a = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
assert_eq!(a, a.pow(Fq::char()));
}
@@ -2029,13 +2063,16 @@ fn test_fq_pow() {
fn test_fq_sqrt() {
use ff::SqrtField;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero());
for _ in 0..1000 {
// Ensure sqrt(a^2) = a or -a
let a = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
let mut nega = a;
nega.negate();
let mut b = a;
@@ -2048,7 +2085,7 @@ fn test_fq_sqrt() {
for _ in 0..1000 {
// Ensure sqrt(a)^2 = a for random a
let a = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
if let Some(mut tmp) = a.sqrt() {
tmp.square();
@@ -2061,16 +2098,15 @@ fn test_fq_sqrt() {
#[test]
fn test_fq_from_into_repr() {
// q + 1 should not be in the field
assert!(
Fq::from_repr(FqRepr([
0xb9feffffffffaaac,
0x1eabfffeb153ffff,
0x6730d2a0f6b0f624,
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a
])).is_err()
);
assert!(Fq::from_repr(FqRepr([
0xb9feffffffffaaac,
0x1eabfffeb153ffff,
0x6730d2a0f6b0f624,
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a
]))
.is_err());
// q should not be in the field
assert!(Fq::from_repr(Fq::char()).is_err());
@@ -2108,11 +2144,14 @@ fn test_fq_from_into_repr() {
// Zero should be in the field.
assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Try to turn Fq elements into representations and back again, and compare.
let a = Fq::rand(&mut rng);
let a = Fq::random(&mut rng);
let a_repr = a.into_repr();
let b_repr = FqRepr::from(a);
assert_eq!(a_repr, b_repr);
@@ -2186,10 +2225,10 @@ fn test_fq_root_of_unity() {
#[test]
fn fq_field_tests() {
::tests::field::random_field_tests::<Fq>();
::tests::field::random_sqrt_tests::<Fq>();
::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
::tests::field::from_str_tests::<Fq>();
crate::tests::field::random_field_tests::<Fq>();
crate::tests::field::random_sqrt_tests::<Fq>();
crate::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
crate::tests::field::from_str_tests::<Fq>();
}
#[test]
@@ -2205,7 +2244,7 @@ fn test_fq_ordering() {
#[test]
fn fq_repr_tests() {
::tests::repr::random_repr_tests::<FqRepr>();
crate::tests::repr::random_repr_tests::<Fq>();
}
#[test]

View File

@@ -2,7 +2,7 @@ use super::fq::FROBENIUS_COEFF_FQ12_C1;
use super::fq2::Fq2;
use super::fq6::Fq6;
use ff::Field;
use rand::{Rand, Rng};
use rand_core::RngCore;
/// An element of Fq12, represented by c0 + c1 * w.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -12,20 +12,11 @@ pub struct 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)
}
}
impl Rand for Fq12 {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fq12 {
c0: rng.gen(),
c1: rng.gen(),
}
}
}
impl Fq12 {
pub fn conjugate(&mut self) {
self.c1.negate();
@@ -49,6 +40,13 @@ impl 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 {
Fq12 {
c0: Fq6::zero(),
@@ -149,24 +147,29 @@ impl Field for Fq12 {
}
#[cfg(test)]
use rand::{SeedableRng, XorShiftRng};
use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test]
fn test_fq12_mul_by_014() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
let c0 = Fq2::rand(&mut rng);
let c1 = Fq2::rand(&mut rng);
let c5 = Fq2::rand(&mut rng);
let mut a = Fq12::rand(&mut rng);
let c0 = Fq2::random(&mut rng);
let c1 = Fq2::random(&mut rng);
let c5 = Fq2::random(&mut rng);
let mut a = Fq12::random(&mut rng);
let mut b = a;
a.mul_by_014(&c0, &c1, &c5);
b.mul_assign(&Fq12 {
c0: Fq6 {
c0: c0,
c1: c1,
c0,
c1,
c2: Fq2::zero(),
},
c1: Fq6 {
@@ -184,6 +187,6 @@ fn test_fq12_mul_by_014() {
fn fq12_field_tests() {
use ff::PrimeField;
::tests::field::random_field_tests::<Fq12>();
::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
crate::tests::field::random_field_tests::<Fq12>();
crate::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
}

View File

@@ -1,6 +1,6 @@
use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE};
use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE};
use ff::{Field, SqrtField};
use rand::{Rand, Rng};
use rand_core::RngCore;
use std::cmp::Ordering;
@@ -12,7 +12,7 @@ pub struct 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)
}
}
@@ -56,16 +56,14 @@ impl Fq2 {
}
}
impl Rand for Fq2 {
fn rand<R: Rng>(rng: &mut R) -> Self {
impl Field for Fq2 {
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fq2 {
c0: rng.gen(),
c1: rng.gen(),
c0: Fq::random(rng),
c1: Fq::random(rng),
}
}
}
impl Field for Fq2 {
fn zero() -> Self {
Fq2 {
c0: Fq::zero(),
@@ -263,12 +261,11 @@ fn test_fq2_basics() {
);
assert!(Fq2::zero().is_zero());
assert!(!Fq2::one().is_zero());
assert!(
!Fq2 {
c0: Fq::zero(),
c1: Fq::one(),
}.is_zero()
);
assert!(!Fq2 {
c0: Fq::zero(),
c1: Fq::one(),
}
.is_zero());
}
#[test]
@@ -311,7 +308,8 @@ fn test_fq2_squaring() {
0xf7f295a94e58ae7c,
0x41b76dcc1c3fbe5e,
0x7080c5fa1d8e042,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x38f473b3c870a4ab,
0x6ad3291177c8c7e5,
@@ -319,7 +317,8 @@ fn test_fq2_squaring() {
0xbfb99020604137a0,
0xfc58a7b7be815407,
0x10d1615e75250a21,
])).unwrap(),
]))
.unwrap(),
};
a.square();
assert_eq!(
@@ -332,7 +331,8 @@ fn test_fq2_squaring() {
0xcb674157618da176,
0x4cf17b5893c3d327,
0x7eac81369c43361
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xc1579cf58e980cf8,
0xa23eb7e12dd54d98,
@@ -340,7 +340,8 @@ fn test_fq2_squaring() {
0x38d0d7275a9689e1,
0x739c983042779a65,
0x1542a61c8a8db994
])).unwrap(),
]))
.unwrap(),
}
);
}
@@ -358,7 +359,8 @@ fn test_fq2_mul() {
0x9ee53e7e84d7532e,
0x1c202d8ed97afb45,
0x51d3f9253e2516f,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf,
0x143c215d8176b319,
@@ -366,7 +368,8 @@ fn test_fq2_mul() {
0x9533e4a9a5158be,
0x7a5e1ecb676d65f9,
0x180c3ee46656b008,
])).unwrap(),
]))
.unwrap(),
};
a.mul_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -376,7 +379,8 @@ fn test_fq2_mul() {
0xcd460f9f0c23e430,
0x6c9110292bfa409,
0x2c93a72eb8af83e,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x4b1c3f936d8992d4,
0x1d2a72916dba4c8a,
@@ -384,7 +388,8 @@ fn test_fq2_mul() {
0x57a06d3135a752ae,
0x634cd3c6c565096d,
0x19e17334d4e93558,
])).unwrap(),
]))
.unwrap(),
});
assert_eq!(
a,
@@ -396,7 +401,8 @@ fn test_fq2_mul() {
0x5511fe4d84ee5f78,
0x5310a202d92f9963,
0x1751afbe166e5399
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x84af0e1bd630117a,
0x6c63cd4da2c2aa7,
@@ -404,7 +410,8 @@ fn test_fq2_mul() {
0xc975106579c275ee,
0x33a9ac82ce4c5083,
0x1ef1a36c201589d
])).unwrap(),
]))
.unwrap(),
}
);
}
@@ -424,7 +431,8 @@ fn test_fq2_inverse() {
0x9ee53e7e84d7532e,
0x1c202d8ed97afb45,
0x51d3f9253e2516f,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf,
0x143c215d8176b319,
@@ -432,7 +440,8 @@ fn test_fq2_inverse() {
0x9533e4a9a5158be,
0x7a5e1ecb676d65f9,
0x180c3ee46656b008,
])).unwrap(),
]))
.unwrap(),
};
let a = a.inverse().unwrap();
assert_eq!(
@@ -445,7 +454,8 @@ fn test_fq2_inverse() {
0xdfba703293941c30,
0xa6c3d8f9586f2636,
0x1351ef01941b70c4
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x8c39fd76a8312cb4,
0x15d7b6b95defbff0,
@@ -453,7 +463,8 @@ fn test_fq2_inverse() {
0xcbf651a0f367afb2,
0xdf4e54f0d3ef15a6,
0x103bdf241afb0019
])).unwrap(),
]))
.unwrap(),
}
);
}
@@ -471,7 +482,8 @@ fn test_fq2_addition() {
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
@@ -479,7 +491,8 @@ fn test_fq2_addition() {
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
]))
.unwrap(),
};
a.add_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -489,7 +502,8 @@ fn test_fq2_addition() {
0x3b88899a42a6318f,
0x986a4a62fa82a49d,
0x13ce433fa26027f5,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9,
0xa1379b6facf6e596,
@@ -497,7 +511,8 @@ fn test_fq2_addition() {
0x2236f55246d0d44d,
0x4c8c1800eb104566,
0x11d6e20e986c2085,
])).unwrap(),
]))
.unwrap(),
});
assert_eq!(
a,
@@ -509,7 +524,8 @@ fn test_fq2_addition() {
0xf4ef57d604b6bca2,
0x65309427b3d5d090,
0x14c715d5553f01d2
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xfdb032e7d9079a94,
0x35a2809d15468d83,
@@ -517,7 +533,8 @@ fn test_fq2_addition() {
0xd62fa51334f560fa,
0x9ad265eb46e01984,
0x1303f3465112c8bc
])).unwrap(),
]))
.unwrap(),
}
);
}
@@ -535,7 +552,8 @@ fn test_fq2_subtraction() {
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
@@ -543,7 +561,8 @@ fn test_fq2_subtraction() {
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
]))
.unwrap(),
};
a.sub_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([
@@ -553,7 +572,8 @@ fn test_fq2_subtraction() {
0x3b88899a42a6318f,
0x986a4a62fa82a49d,
0x13ce433fa26027f5,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9,
0xa1379b6facf6e596,
@@ -561,7 +581,8 @@ fn test_fq2_subtraction() {
0x2236f55246d0d44d,
0x4c8c1800eb104566,
0x11d6e20e986c2085,
])).unwrap(),
]))
.unwrap(),
});
assert_eq!(
a,
@@ -573,7 +594,8 @@ fn test_fq2_subtraction() {
0xe255902672ef6c43,
0x7f77a718021c342d,
0x72ba14049fe9881
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xeb4abaf7c255d1cd,
0x11df49bc6cacc256,
@@ -581,7 +603,8 @@ fn test_fq2_subtraction() {
0xf63905f39ad8cb1f,
0x4cd5dd9fb40b3b8f,
0x957411359ba6e4c
])).unwrap(),
]))
.unwrap(),
}
);
}
@@ -599,7 +622,8 @@ fn test_fq2_negation() {
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
@@ -607,7 +631,8 @@ fn test_fq2_negation() {
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
]))
.unwrap(),
};
a.negate();
assert_eq!(
@@ -620,7 +645,8 @@ fn test_fq2_negation() {
0xab107d49317487ab,
0x7e555df189f880e3,
0x19083f5486a10cbd
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x228109103250c9d0,
0x8a411ad149045812,
@@ -628,7 +654,8 @@ fn test_fq2_negation() {
0xb07e9bc405608611,
0xfcd559cbe77bd8b8,
0x18d400b280d93e62
])).unwrap(),
]))
.unwrap(),
}
);
}
@@ -646,7 +673,8 @@ fn test_fq2_doubling() {
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
@@ -654,7 +682,8 @@ fn test_fq2_doubling() {
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
]))
.unwrap(),
};
a.double();
assert_eq!(
@@ -667,7 +696,8 @@ fn test_fq2_doubling() {
0x72cd9c7784211627,
0x998c938972a657e7,
0x1f1a52b65bdb3b9
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x2efbeddf9b5dc1b6,
0x28d5ca5ad09f4fdb,
@@ -675,7 +705,8 @@ fn test_fq2_doubling() {
0x67f15f81dc49195b,
0x9c8c9bd4b79fa83d,
0x25a226f714d506e
])).unwrap(),
]))
.unwrap(),
}
);
}
@@ -693,7 +724,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
@@ -701,7 +733,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
]))
.unwrap(),
};
a.frobenius_map(0);
assert_eq!(
@@ -714,7 +747,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
@@ -722,7 +756,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837
])).unwrap(),
]))
.unwrap(),
}
);
a.frobenius_map(1);
@@ -736,7 +771,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x228109103250c9d0,
0x8a411ad149045812,
@@ -744,7 +780,8 @@ fn test_fq2_frobenius_map() {
0xb07e9bc405608611,
0xfcd559cbe77bd8b8,
0x18d400b280d93e62
])).unwrap(),
]))
.unwrap(),
}
);
a.frobenius_map(1);
@@ -758,7 +795,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
@@ -766,7 +804,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837
])).unwrap(),
]))
.unwrap(),
}
);
a.frobenius_map(2);
@@ -780,7 +819,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
@@ -788,7 +828,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837
])).unwrap(),
]))
.unwrap(),
}
);
}
@@ -807,7 +848,8 @@ fn test_fq2_sqrt() {
0xdb4a116b5bf74aa1,
0x1e58b2159dfe10e2,
0x7ca7da1f13606ac
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xfa8de88b7516d2c3,
0x371a75ed14f41629,
@@ -815,9 +857,11 @@ fn test_fq2_sqrt() {
0x212611bca4e99121,
0x8ee5394d77afb3d,
0xec92336650e49d5
])).unwrap(),
}.sqrt()
]))
.unwrap(),
}
.sqrt()
.unwrap(),
Fq2 {
c0: Fq::from_repr(FqRepr([
0x40b299b2704258c5,
@@ -826,7 +870,8 @@ fn test_fq2_sqrt() {
0x8d7f1f723d02c1d3,
0x881b3e01b611c070,
0x10f6963bbad2ebc5
])).unwrap(),
]))
.unwrap(),
c1: Fq::from_repr(FqRepr([
0xc099534fc209e752,
0x7670594665676447,
@@ -834,7 +879,8 @@ fn test_fq2_sqrt() {
0x6b852aeaf2afcb1b,
0xa4c93b08105d71a9,
0x8d7cfff94216330
])).unwrap(),
]))
.unwrap(),
}
);
@@ -847,10 +893,12 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a
])).unwrap(),
c1: Fq::zero(),
}.sqrt()
]))
.unwrap(),
c1: Fq::zero(),
}
.sqrt()
.unwrap(),
Fq2 {
c0: Fq::zero(),
c1: Fq::from_repr(FqRepr([
@@ -860,7 +908,8 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a
])).unwrap(),
]))
.unwrap(),
}
);
}
@@ -879,11 +928,16 @@ fn test_fq2_legendre() {
}
#[cfg(test)]
use rand::{SeedableRng, XorShiftRng};
use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test]
fn test_fq2_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let nqr = Fq2 {
c0: Fq::one(),
@@ -891,7 +945,7 @@ fn test_fq2_mul_nonresidue() {
};
for _ in 0..1000 {
let mut a = Fq2::rand(&mut rng);
let mut a = Fq2::random(&mut rng);
let mut b = a;
a.mul_by_nonresidue();
b.mul_assign(&nqr);
@@ -904,7 +958,7 @@ fn test_fq2_mul_nonresidue() {
fn fq2_field_tests() {
use ff::PrimeField;
::tests::field::random_field_tests::<Fq2>();
::tests::field::random_sqrt_tests::<Fq2>();
::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
crate::tests::field::random_field_tests::<Fq2>();
crate::tests::field::random_sqrt_tests::<Fq2>();
crate::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::fq2::Fq2;
use ff::Field;
use rand::{Rand, Rng};
use rand_core::RngCore;
/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2).
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -12,21 +12,11 @@ pub struct 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)
}
}
impl Rand for Fq6 {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fq6 {
c0: rng.gen(),
c1: rng.gen(),
c2: rng.gen(),
}
}
}
impl Fq6 {
/// Multiply by quadratic nonresidue v.
pub fn mul_by_nonresidue(&mut self) {
@@ -110,6 +100,14 @@ impl 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 {
Fq6 {
c0: Fq2::zero(),
@@ -302,11 +300,16 @@ impl Field for Fq6 {
}
#[cfg(test)]
use rand::{SeedableRng, XorShiftRng};
use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test]
fn test_fq6_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let nqr = Fq6 {
c0: Fq2::zero(),
@@ -315,7 +318,7 @@ fn test_fq6_mul_nonresidue() {
};
for _ in 0..1000 {
let mut a = Fq6::rand(&mut rng);
let mut a = Fq6::random(&mut rng);
let mut b = a;
a.mul_by_nonresidue();
b.mul_assign(&nqr);
@@ -326,17 +329,20 @@ fn test_fq6_mul_nonresidue() {
#[test]
fn test_fq6_mul_by_1() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
let c1 = Fq2::rand(&mut rng);
let mut a = Fq6::rand(&mut rng);
let c1 = Fq2::random(&mut rng);
let mut a = Fq6::random(&mut rng);
let mut b = a;
a.mul_by_1(&c1);
b.mul_assign(&Fq6 {
c0: Fq2::zero(),
c1: c1,
c1,
c2: Fq2::zero(),
});
@@ -346,18 +352,21 @@ fn test_fq6_mul_by_1() {
#[test]
fn test_fq6_mul_by_01() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
let c0 = Fq2::rand(&mut rng);
let c1 = Fq2::rand(&mut rng);
let mut a = Fq6::rand(&mut rng);
let c0 = Fq2::random(&mut rng);
let c1 = Fq2::random(&mut rng);
let mut a = Fq6::random(&mut rng);
let mut b = a;
a.mul_by_01(&c0, &c1);
b.mul_assign(&Fq6 {
c0: c0,
c1: c1,
c0,
c1,
c2: Fq2::zero(),
});
@@ -369,6 +378,6 @@ fn test_fq6_mul_by_01() {
fn fq6_field_tests() {
use ff::PrimeField;
::tests::field::random_field_tests::<Fq6>();
::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
crate::tests::field::random_field_tests::<Fq6>();
crate::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
}

View File

@@ -6,7 +6,9 @@ use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr};
pub struct Fr(FrRepr);
#[cfg(test)]
use rand::{Rand, SeedableRng, XorShiftRng};
use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test]
fn test_fr_repr_ordering() {
@@ -197,7 +199,10 @@ fn test_fr_repr_num_bits() {
#[test]
fn test_fr_repr_sub_noborrow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FrRepr([
0x8e62a7e85264e2c3,
@@ -221,7 +226,7 @@ fn test_fr_repr_sub_noborrow() {
);
for _ in 0..1000 {
let mut a = FrRepr::rand(&mut rng);
let mut a = Fr::random(&mut rng).into_repr();
a.0[3] >>= 30;
let mut b = a;
for _ in 0..10 {
@@ -296,7 +301,10 @@ fn test_fr_legendre() {
#[test]
fn test_fr_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FrRepr([
0xd64f669809cbc6a4,
@@ -322,9 +330,9 @@ fn test_fr_repr_add_nocarry() {
// Test for the associativity of addition.
for _ in 0..1000 {
let mut a = FrRepr::rand(&mut rng);
let mut b = FrRepr::rand(&mut rng);
let mut c = FrRepr::rand(&mut rng);
let mut a = Fr::random(&mut rng).into_repr();
let mut b = Fr::random(&mut rng).into_repr();
let mut c = Fr::random(&mut rng).into_repr();
// Unset the first few bits, so that overflow won't occur.
a.0[3] >>= 3;
@@ -380,27 +388,28 @@ fn test_fr_is_valid() {
a.0.sub_noborrow(&FrRepr::from(1));
assert!(a.is_valid());
assert!(Fr(FrRepr::from(0)).is_valid());
assert!(
Fr(FrRepr([
0xffffffff00000000,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48
])).is_valid()
);
assert!(
!Fr(FrRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff
])).is_valid()
);
assert!(Fr(FrRepr([
0xffffffff00000000,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48
]))
.is_valid());
assert!(!Fr(FrRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff
]))
.is_valid());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
let a = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
assert!(a.is_valid());
}
}
@@ -492,13 +501,16 @@ fn test_fr_add_assign() {
// Test associativity
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Generate a, b, c and ensure (a + b) + c == a + (b + c).
let a = Fr::rand(&mut rng);
let b = Fr::rand(&mut rng);
let c = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
let b = Fr::random(&mut rng);
let c = Fr::random(&mut rng);
let mut tmp1 = a;
tmp1.add_assign(&b);
@@ -586,12 +598,15 @@ fn test_fr_sub_assign() {
);
}
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Ensure that (a - b) + (b - a) = 0.
let a = Fr::rand(&mut rng);
let b = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
let b = Fr::random(&mut rng);
let mut tmp1 = a;
tmp1.sub_assign(&b);
@@ -627,13 +642,16 @@ fn test_fr_mul_assign() {
]))
);
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 {
// Ensure that (a * b) * c = a * (b * c)
let a = Fr::rand(&mut rng);
let b = Fr::rand(&mut rng);
let c = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
let b = Fr::random(&mut rng);
let c = Fr::random(&mut rng);
let mut tmp1 = a;
tmp1.mul_assign(&b);
@@ -649,10 +667,10 @@ fn test_fr_mul_assign() {
for _ in 0..1000000 {
// Ensure that r * (a + b + c) = r*a + r*b + r*c
let r = Fr::rand(&mut rng);
let mut a = Fr::rand(&mut rng);
let mut b = Fr::rand(&mut rng);
let mut c = Fr::rand(&mut rng);
let r = Fr::random(&mut rng);
let mut a = Fr::random(&mut rng);
let mut b = Fr::random(&mut rng);
let mut c = Fr::random(&mut rng);
let mut tmp1 = a;
tmp1.add_assign(&b);
@@ -687,14 +705,18 @@ fn test_fr_squaring() {
0xb79a310579e76ec2,
0xac1da8d0a9af4e5f,
0x13f629c49bf23e97
])).unwrap()
]))
.unwrap()
);
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 {
// Ensure that (a * a) = a^2
let a = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
let mut tmp = a;
tmp.square();
@@ -710,13 +732,16 @@ fn test_fr_squaring() {
fn test_fr_inverse() {
assert!(Fr::zero().inverse().is_none());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let one = Fr::one();
for _ in 0..1000 {
// Ensure that a * a^-1 = 1
let mut a = Fr::rand(&mut rng);
let mut a = Fr::random(&mut rng);
let ainv = a.inverse().unwrap();
a.mul_assign(&ainv);
assert_eq!(a, one);
@@ -725,11 +750,14 @@ fn test_fr_inverse() {
#[test]
fn test_fr_double() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Ensure doubling a is equivalent to adding a to itself.
let mut a = Fr::rand(&mut rng);
let mut a = Fr::random(&mut rng);
let mut b = a;
b.add_assign(&a);
a.double();
@@ -746,11 +774,14 @@ fn test_fr_negate() {
assert!(a.is_zero());
}
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Ensure (a - (-a)) = 0.
let mut a = Fr::rand(&mut rng);
let mut a = Fr::random(&mut rng);
let mut b = a;
b.negate();
a.add_assign(&b);
@@ -761,12 +792,15 @@ fn test_fr_negate() {
#[test]
fn test_fr_pow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for i in 0..1000 {
// Exponentiate by various small numbers and ensure it consists with repeated
// multiplication.
let a = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
let target = a.pow(&[i]);
let mut c = Fr::one();
for _ in 0..i {
@@ -777,7 +811,7 @@ fn test_fr_pow() {
for _ in 0..1000 {
// Exponentiating by the modulus should have no effect in a prime field.
let a = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
assert_eq!(a, a.pow(Fr::char()));
}
@@ -787,13 +821,16 @@ fn test_fr_pow() {
fn test_fr_sqrt() {
use ff::SqrtField;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero());
for _ in 0..1000 {
// Ensure sqrt(a^2) = a or -a
let a = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
let mut nega = a;
nega.negate();
let mut b = a;
@@ -806,7 +843,7 @@ fn test_fr_sqrt() {
for _ in 0..1000 {
// Ensure sqrt(a)^2 = a for random a
let a = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
if let Some(mut tmp) = a.sqrt() {
tmp.square();
@@ -819,14 +856,13 @@ fn test_fr_sqrt() {
#[test]
fn test_fr_from_into_repr() {
// r + 1 should not be in the field
assert!(
Fr::from_repr(FrRepr([
0xffffffff00000002,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48
])).is_err()
);
assert!(Fr::from_repr(FrRepr([
0xffffffff00000002,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48
]))
.is_err());
// r should not be in the field
assert!(Fr::from_repr(Fr::char()).is_err());
@@ -858,11 +894,14 @@ fn test_fr_from_into_repr() {
// Zero should be in the field.
assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 {
// Try to turn Fr elements into representations and back again, and compare.
let a = Fr::rand(&mut rng);
let a = Fr::random(&mut rng);
let a_repr = a.into_repr();
let b_repr = FrRepr::from(a);
assert_eq!(a_repr, b_repr);
@@ -926,7 +965,8 @@ fn test_fr_display() {
0x185ec8eb3f5b5aee,
0x684499ffe4b9dd99,
0x7c9bba7afb68faa
])).unwrap()
]))
.unwrap()
),
"Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string()
);
@@ -938,7 +978,8 @@ fn test_fr_display() {
0xb0ad10817df79b6a,
0xd034a80a2b74132b,
0x41cf9a1336f50719
])).unwrap()
]))
.unwrap()
),
"Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string()
);
@@ -974,13 +1015,13 @@ fn test_fr_root_of_unity() {
#[test]
fn fr_field_tests() {
::tests::field::random_field_tests::<Fr>();
::tests::field::random_sqrt_tests::<Fr>();
::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
::tests::field::from_str_tests::<Fr>();
crate::tests::field::random_field_tests::<Fr>();
crate::tests::field::random_sqrt_tests::<Fr>();
crate::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
crate::tests::field::from_str_tests::<Fr>();
}
#[test]
fn fr_repr_tests() {
::tests::repr::random_repr_tests::<FrRepr>();
crate::tests::repr::random_repr_tests::<Fr>();
}

View File

@@ -1,3 +1,6 @@
//! An implementation of the BLS12-381 pairing-friendly elliptic curve
//! construction.
mod ec;
mod fq;
mod fq12;
@@ -9,8 +12,8 @@ mod fr;
mod tests;
pub use self::ec::{
G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2, G2Affine, G2Compressed, G2Prepared,
G2Uncompressed,
G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared,
G2Uncompressed, G1, G2,
};
pub use self::fq::{Fq, FqRepr};
pub use self::fq12::Fq12;
@@ -366,5 +369,5 @@ impl G2Prepared {
#[test]
fn bls12_engine_tests() {
::tests::engine::engine_tests::<Bls12>();
crate::tests::engine::engine_tests::<Bls12>();
}

View File

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

View File

@@ -1,21 +1,20 @@
//! A library for working with pairing-friendly curves.
// `clippy` is a code linting tool for improving code quality by catching
// common mistakes or strange code patterns. If the `cargo-clippy` feature
// is provided, all compiler warnings are prohibited.
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
#![cfg_attr(feature = "cargo-clippy", allow(inline_always))]
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))]
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
#![cfg_attr(feature = "cargo-clippy", allow(write_literal))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))]
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
// Force public structures to implement Debug
#![deny(missing_debug_implementations)]
extern crate byteorder;
extern crate ff;
extern crate group;
extern crate rand;
#[cfg(test)]
pub mod tests;
@@ -34,8 +33,7 @@ pub trait Engine: ScalarEngine {
Base = Self::Fq,
Scalar = Self::Fr,
Affine = Self::G1Affine,
>
+ From<Self::G1Affine>;
> + From<Self::G1Affine>;
/// The affine representation of an element in G1.
type G1Affine: PairingCurveAffine<
@@ -45,8 +43,7 @@ pub trait Engine: ScalarEngine {
Projective = Self::G1,
Pair = Self::G2Affine,
PairingResult = Self::Fqk,
>
+ From<Self::G1>;
> + From<Self::G1>;
/// The projective representation of an element in G2.
type G2: CurveProjective<
@@ -54,8 +51,7 @@ pub trait Engine: ScalarEngine {
Base = Self::Fqe,
Scalar = Self::Fr,
Affine = Self::G2Affine,
>
+ From<Self::G2Affine>;
> + From<Self::G2Affine>;
/// The affine representation of an element in G2.
type G2Affine: PairingCurveAffine<
@@ -65,8 +61,7 @@ pub trait Engine: ScalarEngine {
Projective = Self::G2,
Pair = Self::G1Affine,
PairingResult = Self::Fqk,
>
+ From<Self::G2>;
> + From<Self::G2>;
/// The base field that hosts G1.
type Fq: PrimeField + SqrtField;
@@ -88,7 +83,7 @@ pub trait Engine: ScalarEngine {
>;
/// 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)`.
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
@@ -97,8 +92,9 @@ pub trait Engine: ScalarEngine {
G2: Into<Self::G2Affine>,
{
Self::final_exponentiation(&Self::miller_loop(
[(&(p.into().prepare()), &(q.into().prepare()))].into_iter(),
)).unwrap()
[(&(p.into().prepare()), &(q.into().prepare()))].iter(),
))
.unwrap()
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +0,0 @@
Copyrights in the "sapling-crypto" library are retained by their contributors. No
copyright assignment is required to contribute to the "sapling-crypto" library.
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)
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
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,28 +0,0 @@
[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" }
ff = { path = "../ff" }
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"

View File

@@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -1,23 +0,0 @@
# 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

@@ -1,23 +0,0 @@
#![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

@@ -1,102 +0,0 @@
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

@@ -1,438 +0,0 @@
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,114 +0,0 @@
use ff::{Field, PrimeField};
use pairing::Engine;
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,194 +0,0 @@
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 ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr};
#[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,46 +0,0 @@
use jubjub::{
JubjubEngine,
PrimeOrder,
edwards
};
use ff::{
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,23 +0,0 @@
extern crate pairing;
extern crate bellman;
extern crate blake2_rfc;
extern crate digest;
extern crate ff;
extern crate rand;
extern crate byteorder;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[cfg(test)]
extern crate crypto;
pub mod jubjub;
pub mod group_hash;
pub mod circuit;
pub mod pedersen_hash;
pub mod primitives;
pub mod constants;
pub mod redjubjub;
pub mod util;

View File

@@ -1,271 +0,0 @@
use ff::{Field, PrimeField, PrimeFieldRepr};
use constants;
use group_hash::group_hash;
use pedersen_hash::{
pedersen_hash,
Personalization
};
use byteorder::{
LittleEndian,
WriteBytesExt
};
use jubjub::{
JubjubEngine,
JubjubParams,
edwards,
PrimeOrder,
FixedGenerators
};
use blake2_rfc::blake2s::Blake2s;
#[derive(Clone)]
pub struct ValueCommitment<E: JubjubEngine> {
pub value: u64,
pub randomness: E::Fs
}
impl<E: JubjubEngine> ValueCommitment<E> {
pub fn cm(
&self,
params: &E::Params
) -> edwards::Point<E, PrimeOrder>
{
params.generator(FixedGenerators::ValueCommitmentValue)
.mul(self.value, params)
.add(
&params.generator(FixedGenerators::ValueCommitmentRandomness)
.mul(self.randomness, params),
params
)
}
}
#[derive(Clone)]
pub struct ProofGenerationKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>,
pub nsk: E::Fs
}
impl<E: JubjubEngine> ProofGenerationKey<E> {
pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey<E> {
ViewingKey {
ak: self.ak.clone(),
nk: params.generator(FixedGenerators::ProofGenerationKey)
.mul(self.nsk, params)
}
}
}
#[derive(Debug)]
pub struct ViewingKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>,
pub nk: edwards::Point<E, PrimeOrder>
}
impl<E: JubjubEngine> ViewingKey<E> {
pub fn rk(
&self,
ar: E::Fs,
params: &E::Params
) -> edwards::Point<E, PrimeOrder> {
self.ak.add(
&params.generator(FixedGenerators::SpendingKeyGenerator)
.mul(ar, params),
params
)
}
pub fn ivk(&self) -> E::Fs {
let mut preimage = [0; 64];
self.ak.write(&mut preimage[0..32]).unwrap();
self.nk.write(&mut preimage[32..64]).unwrap();
let mut h = Blake2s::with_params(32, &[], &[], constants::CRH_IVK_PERSONALIZATION);
h.update(&preimage);
let mut h = h.finalize().as_ref().to_vec();
// Drop the most significant five bits, so it can be interpreted as a scalar.
h[31] &= 0b0000_0111;
let mut e = <E::Fs as PrimeField>::Repr::default();
e.read_le(&h[..]).unwrap();
E::Fs::from_repr(e).expect("should be a valid scalar")
}
pub fn into_payment_address(
&self,
diversifier: Diversifier,
params: &E::Params
) -> Option<PaymentAddress<E>>
{
diversifier.g_d(params).map(|g_d| {
let pk_d = g_d.mul(self.ivk(), params);
PaymentAddress {
pk_d: pk_d,
diversifier: diversifier
}
})
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Diversifier(pub [u8; 11]);
impl Diversifier {
pub fn g_d<E: JubjubEngine>(
&self,
params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>>
{
group_hash::<E>(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION, params)
}
}
#[derive(Clone, Debug)]
pub struct PaymentAddress<E: JubjubEngine> {
pub pk_d: edwards::Point<E, PrimeOrder>,
pub diversifier: Diversifier
}
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
fn eq(&self, other: &Self) -> bool {
self.pk_d == other.pk_d && self.diversifier == other.diversifier
}
}
impl<E: JubjubEngine> PaymentAddress<E> {
pub fn g_d(
&self,
params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>>
{
self.diversifier.g_d(params)
}
pub fn create_note(
&self,
value: u64,
randomness: E::Fs,
params: &E::Params
) -> Option<Note<E>>
{
self.g_d(params).map(|g_d| {
Note {
value: value,
r: randomness,
g_d: g_d,
pk_d: self.pk_d.clone()
}
})
}
}
#[derive(Clone, Debug)]
pub struct Note<E: JubjubEngine> {
/// The value of the note
pub value: u64,
/// The diversified base of the address, GH(d)
pub g_d: edwards::Point<E, PrimeOrder>,
/// The public key of the address, g_d^ivk
pub pk_d: edwards::Point<E, PrimeOrder>,
/// The commitment randomness
pub r: E::Fs
}
impl<E: JubjubEngine> PartialEq for Note<E> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
&& self.g_d == other.g_d
&& self.pk_d == other.pk_d
&& self.r == other.r
}
}
impl<E: JubjubEngine> Note<E> {
pub fn uncommitted() -> E::Fr {
// The smallest u-coordinate that is not on the curve
// is one.
// TODO: This should be relocated to JubjubEngine as
// it's specific to the curve we're using, not all
// twisted edwards curves.
E::Fr::one()
}
/// Computes the note commitment, returning the full point.
fn cm_full_point(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder>
{
// Calculate the note contents, as bytes
let mut note_contents = vec![];
// Writing the value in little endian
(&mut note_contents).write_u64::<LittleEndian>(self.value).unwrap();
// Write g_d
self.g_d.write(&mut note_contents).unwrap();
// Write pk_d
self.pk_d.write(&mut note_contents).unwrap();
assert_eq!(note_contents.len(), 32 + 32 + 8);
// Compute the Pedersen hash of the note contents
let hash_of_contents = pedersen_hash(
Personalization::NoteCommitment,
note_contents.into_iter()
.flat_map(|byte| {
(0..8).map(move |i| ((byte >> i) & 1) == 1)
}),
params
);
// Compute final commitment
params.generator(FixedGenerators::NoteCommitmentRandomness)
.mul(self.r, params)
.add(&hash_of_contents, params)
}
/// Computes the nullifier given the viewing key and
/// note position
pub fn nf(
&self,
viewing_key: &ViewingKey<E>,
position: u64,
params: &E::Params
) -> Vec<u8>
{
// Compute rho = cm + position.G
let rho = self
.cm_full_point(params)
.add(
&params.generator(FixedGenerators::NullifierPosition)
.mul(position, params),
params
);
// Compute nf = BLAKE2s(nk | rho)
let mut nf_preimage = [0u8; 64];
viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap();
rho.write(&mut nf_preimage[32..64]).unwrap();
let mut h = Blake2s::with_params(32, &[], &[], constants::PRF_NF_PERSONALIZATION);
h.update(&nf_preimage);
h.finalize().as_ref().to_vec()
}
/// Computes the note commitment
pub fn cm(&self, params: &E::Params) -> E::Fr
{
// The commitment is in the prime order subgroup, so mapping the
// commitment to the x-coordinate is an injective encoding.
self.cm_full_point(params).into_xy().0
}
}

2
zcash_client_backend/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# Protobufs
src/proto/

View File

@@ -0,0 +1,31 @@
[package]
name = "zcash_client_backend"
description = "APIs for creating shielded Zcash light clients"
version = "0.1.0"
authors = [
"Jack Grigg <jack@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"
[dependencies]
bech32 = "0.7"
ff = { version = "0.5.0", path = "../ff" }
hex = "0.3"
pairing = { version = "0.15.0", path = "../pairing" }
protobuf = "2"
subtle = "2"
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
[build-dependencies]
protobuf-codegen-pure = "2"
[dev-dependencies]
rand_core = "0.5"
rand_xorshift = "0.2"
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -1,12 +1,14 @@
# zcash_wallet
# zcash_client_backend
This library contains Rust structs and traits for creating shielded Zcash wallets.
This library contains Rust structs and traits for creating shielded Zcash light
clients.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* 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.

View File

@@ -0,0 +1,11 @@
use protobuf_codegen_pure;
fn main() {
protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
out_dir: "src/proto",
input: &["proto/compact_formats.proto"],
includes: &["proto"],
customize: Default::default(),
})
.expect("protoc");
}

View File

@@ -0,0 +1,48 @@
syntax = "proto3";
package cash.z.wallet.sdk.rpc;
option go_package = "walletrpc";
// Remember that proto3 fields are all optional. A field that is not present will be set to its zero value.
// bytes fields of hashes are in canonical little-endian format.
// CompactBlock is a packaging of ONLY the data from a block that's needed to:
// 1. Detect a payment to your shielded Sapling address
// 2. Detect a spend of your shielded Sapling notes
// 3. Update your witnesses to generate new Sapling spend proofs.
message CompactBlock {
uint32 protoVersion = 1; // the version of this wire format, for storage
uint64 height = 2; // the height of this block
bytes hash = 3;
bytes prevHash = 4;
uint32 time = 5;
bytes header = 6; // (hash, prevHash, and time) OR (full header)
repeated CompactTx vtx = 7; // compact transactions from this block
}
message CompactTx {
// Index and hash will allow the receiver to call out to chain
// explorers or other data structures to retrieve more information
// about this transaction.
uint64 index = 1;
bytes hash = 2;
// The transaction fee: present if server can provide. In the case of a
// stateless server and a transaction with transparent inputs, this will be
// unset because the calculation requires reference to prior transactions.
// in a pure-Sapling context, the fee will be calculable as:
// valueBalance + (sum(vPubNew) - sum(vPubOld) - sum(tOut))
uint32 fee = 3;
repeated CompactSpend spends = 4;
repeated CompactOutput outputs = 5;
}
message CompactSpend {
bytes nf = 1;
}
message CompactOutput {
bytes cmu = 1;
bytes epk = 2;
bytes ciphertext = 3;
}

View File

@@ -0,0 +1,5 @@
//! Zcash global and per-network constants.
pub mod mainnet;
pub mod regtest;
pub mod testnet;

View File

@@ -0,0 +1,30 @@
//! Constants for the Zcash main network.
/// The mainnet coin type for ZEC, as defined by [SLIP 44].
///
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
pub const COIN_TYPE: u32 = 133;
/// The HRP for a Bech32-encoded mainnet [`ExtendedSpendingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-main";
/// The HRP for a Bech32-encoded mainnet [`ExtendedFullViewingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews";
/// The HRP for a Bech32-encoded mainnet [`PaymentAddress`].
///
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
///
/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs";

View File

@@ -0,0 +1,45 @@
//! # Regtest constants
//!
//! `regtest` is a `zcashd`-specific environment used for local testing. They mostly reuse
//! the testnet constants.
//! These constants are defined in [the `zcashd` codebase].
//! [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L482-L496
/// The regtest cointype reuses the testnet cointype
pub const COIN_TYPE: u32 = 1;
/// The HRP for a Bech32-encoded regtest [`ExtendedSpendingKey`].
///
/// It is defined in [the `zcashd` codebase].
///
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L496
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-regtest";
/// The HRP for a Bech32-encoded regtest [`ExtendedFullViewingKey`].
///
/// It is defined in [the `zcashd` codebase].
///
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L494
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewregtestsapling";
/// The HRP for a Bech32-encoded regtest [`PaymentAddress`].
///
/// It is defined in [the `zcashd` codebase].
///
/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L493
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zregtestsapling";
/// The prefix for a Base58Check-encoded regtest [`TransparentAddress::PublicKey`].
/// Same as the testnet prefix.
///
/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey
pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25];
/// The prefix for a Base58Check-encoded regtest [`TransparentAddress::Script`].
/// Same as the testnet prefix.
///
/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script
pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xba];

View File

@@ -0,0 +1,30 @@
//! Constants for the Zcash test network.
/// The testnet coin type for ZEC, as defined by [SLIP 44].
///
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
pub const COIN_TYPE: u32 = 1;
/// The HRP for a Bech32-encoded testnet [`ExtendedSpendingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-test";
/// The HRP for a Bech32-encoded testnet [`ExtendedFullViewingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling";
/// The HRP for a Bech32-encoded testnet [`PaymentAddress`].
///
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
///
/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling";

View File

@@ -0,0 +1,76 @@
use pairing::bls12_381::Bls12;
use zcash_primitives::{
note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo},
primitives::{Note, PaymentAddress},
transaction::Transaction,
zip32::ExtendedFullViewingKey,
JUBJUB,
};
/// A decrypted shielded output.
pub struct DecryptedOutput {
/// The index of the output within [`shielded_outputs`].
///
/// [`shielded_outputs`]: zcash_primitives::transaction::TransactionData
pub index: usize,
/// The note within the output.
pub note: Note<Bls12>,
/// The address the note was sent to.
pub to: PaymentAddress<Bls12>,
/// The memo included with the note.
pub memo: Memo,
/// True if this output was recovered using an [`OutgoingViewingKey`], meaning that
/// this is a logical output of the transaction.
///
/// [`OutgoingViewingKey`]: zcash_primitives::keys::OutgoingViewingKey
pub outgoing: bool,
}
/// Scans a [`Transaction`] for any information that can be decrypted by the set of
/// [`ExtendedFullViewingKey`]s.
pub fn decrypt_transaction(
tx: &Transaction,
extfvks: &[ExtendedFullViewingKey],
) -> Vec<DecryptedOutput> {
let mut decrypted = vec![];
// Cache IncomingViewingKey calculation
let vks: Vec<_> = extfvks
.iter()
.map(|extfvk| (extfvk.fvk.vk.ivk(), extfvk.fvk.ovk))
.collect();
for (index, output) in tx.shielded_outputs.iter().enumerate() {
let epk = match output.ephemeral_key.as_prime_order(&JUBJUB) {
Some(p) => p,
None => continue,
};
for (ivk, ovk) in &vks {
let ((note, to, memo), outgoing) =
match try_sapling_note_decryption(ivk, &epk, &output.cmu, &output.enc_ciphertext) {
Some(ret) => (ret, false),
None => match try_sapling_output_recovery(
ovk,
&output.cv,
&output.cmu,
&epk,
&output.enc_ciphertext,
&output.out_ciphertext,
) {
Some(ret) => (ret, true),
None => continue,
},
};
decrypted.push(DecryptedOutput {
index,
note,
to,
memo,
outgoing,
})
}
}
decrypted
}

View File

@@ -0,0 +1,347 @@
//! Encoding and decoding functions for Zcash key and address structs.
//!
//! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`]
//! module.
//!
//! [`constants`]: crate::constants
use bech32::{self, Error, FromBase32, ToBase32};
use pairing::bls12_381::Bls12;
use std::io::{self, Write};
use zcash_primitives::{
primitives::PaymentAddress,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
fn bech32_encode<F>(hrp: &str, write: F) -> String
where
F: Fn(&mut dyn Write) -> io::Result<()>,
{
let mut data: Vec<u8> = vec![];
write(&mut data).expect("Should be able to write to a Vec");
bech32::encode(hrp, data.to_base32()).expect("hrp is invalid")
}
fn bech32_decode<T, F>(hrp: &str, s: &str, read: F) -> Result<Option<T>, Error>
where
F: Fn(Vec<u8>) -> Option<T>,
{
let (decoded_hrp, data) = bech32::decode(s)?;
if decoded_hrp == hrp {
Vec::<u8>::from_base32(&data).map(|data| read(data))
} else {
Ok(None)
}
}
/// Writes an [`ExtendedSpendingKey`] as a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{
/// constants::testnet::{COIN_TYPE, HRP_SAPLING_EXTENDED_SPENDING_KEY},
/// encoding::encode_extended_spending_key,
/// keys::spending_key,
/// };
///
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
/// let encoded = encode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY, &extsk);
/// ```
pub fn encode_extended_spending_key(hrp: &str, extsk: &ExtendedSpendingKey) -> String {
bech32_encode(hrp, |w| extsk.write(w))
}
/// Decodes an [`ExtendedSpendingKey`] from a Bech32-encoded string.
pub fn decode_extended_spending_key(
hrp: &str,
s: &str,
) -> Result<Option<ExtendedSpendingKey>, Error> {
bech32_decode(hrp, s, |data| ExtendedSpendingKey::read(&data[..]).ok())
}
/// Writes an [`ExtendedFullViewingKey`] as a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{
/// constants::testnet::{COIN_TYPE, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY},
/// encoding::encode_extended_full_viewing_key,
/// keys::spending_key,
/// };
/// use zcash_primitives::zip32::ExtendedFullViewingKey;
///
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
/// let extfvk = ExtendedFullViewingKey::from(&extsk);
/// let encoded = encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk);
/// ```
pub fn encode_extended_full_viewing_key(hrp: &str, extfvk: &ExtendedFullViewingKey) -> String {
bech32_encode(hrp, |w| extfvk.write(w))
}
/// Decodes an [`ExtendedFullViewingKey`] from a Bech32-encoded string.
pub fn decode_extended_full_viewing_key(
hrp: &str,
s: &str,
) -> Result<Option<ExtendedFullViewingKey>, Error> {
bech32_decode(hrp, s, |data| ExtendedFullViewingKey::read(&data[..]).ok())
}
/// Writes a [`PaymentAddress`] as a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use pairing::bls12_381::Bls12;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
/// encoding::encode_payment_address,
/// };
/// use zcash_primitives::{
/// jubjub::edwards,
/// primitives::{Diversifier, PaymentAddress},
/// JUBJUB,
/// };
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
/// 0xbc, 0xe5,
/// ]);
///
/// let pa = PaymentAddress::from_parts(
/// Diversifier([0u8; 11]),
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// )
/// .unwrap();
///
/// assert_eq!(
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe",
/// );
/// ```
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
bech32_encode(hrp, |w| w.write_all(&addr.to_bytes()))
}
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use pairing::bls12_381::Bls12;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
/// encoding::decode_payment_address,
/// };
/// use zcash_primitives::{
/// jubjub::edwards,
/// primitives::{Diversifier, PaymentAddress},
/// JUBJUB,
/// };
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
/// 0xbc, 0xe5,
/// ]);
///
/// let pa = PaymentAddress::from_parts(
/// Diversifier([0u8; 11]),
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// )
/// .unwrap();
///
/// assert_eq!(
/// decode_payment_address(
/// HRP_SAPLING_PAYMENT_ADDRESS,
/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe",
/// ),
/// Ok(Some(pa)),
/// );
/// ```
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
bech32_decode(hrp, s, |data| {
if data.len() != 43 {
return None;
}
let mut bytes = [0; 43];
bytes.copy_from_slice(&data);
PaymentAddress::<Bls12>::from_bytes(&bytes, &JUBJUB)
})
}
#[cfg(test)]
mod tests {
use pairing::bls12_381::Bls12;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use zcash_primitives::JUBJUB;
use zcash_primitives::{
jubjub::edwards,
primitives::{Diversifier, PaymentAddress},
zip32::ExtendedSpendingKey,
};
use super::{
decode_extended_full_viewing_key, decode_extended_spending_key, decode_payment_address,
encode_extended_full_viewing_key, encode_extended_spending_key, encode_payment_address,
};
use crate::constants;
#[test]
fn extended_spending_key() {
let extsk = ExtendedSpendingKey::master(&[0; 32][..]);
let encoded_main = "secret-extended-key-main1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs87qvlj";
let encoded_test = "secret-extended-key-test1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsvzyw8j";
assert_eq!(
encode_extended_spending_key(
constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
&extsk
),
encoded_main
);
assert_eq!(
decode_extended_spending_key(
constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
encoded_main
)
.unwrap(),
Some(extsk.clone())
);
assert_eq!(
encode_extended_spending_key(
constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
&extsk
),
encoded_test
);
assert_eq!(
decode_extended_spending_key(
constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
encoded_test
)
.unwrap(),
Some(extsk)
);
}
#[test]
fn extended_full_viewing_key() {
let extfvk = (&ExtendedSpendingKey::master(&[0; 32][..])).into();
let encoded_main = "zxviews1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsxmansf";
let encoded_test = "zxviewtestsapling1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs8evfkz";
assert_eq!(
encode_extended_full_viewing_key(
constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
&extfvk
),
encoded_main
);
assert_eq!(
decode_extended_full_viewing_key(
constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
encoded_main
)
.unwrap(),
Some(extfvk.clone())
);
assert_eq!(
encode_extended_full_viewing_key(
constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
&extfvk
),
encoded_test
);
assert_eq!(
decode_extended_full_viewing_key(
constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
encoded_test
)
.unwrap(),
Some(extfvk)
);
}
#[test]
fn payment_address() {
let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let addr = PaymentAddress::from_parts(
Diversifier([0u8; 11]),
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
)
.unwrap();
let encoded_main =
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
let encoded_test =
"ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe";
assert_eq!(
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
encoded_main
);
assert_eq!(
decode_payment_address(
constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
encoded_main
)
.unwrap(),
Some(addr.clone())
);
assert_eq!(
encode_payment_address(constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
encoded_test
);
assert_eq!(
decode_payment_address(
constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
encoded_test
)
.unwrap(),
Some(addr)
);
}
#[test]
fn invalid_diversifier() {
let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let addr = PaymentAddress::from_parts(
Diversifier([1u8; 11]),
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
)
.unwrap();
let encoded_main =
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr);
assert_eq!(
decode_payment_address(
constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
&encoded_main
)
.unwrap(),
None
);
}
}

View File

@@ -0,0 +1,43 @@
//! Helper functions for managing light client key material.
use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey};
/// Derives the ZIP 32 [`ExtendedSpendingKey`] for a given coin type and account from the
/// given seed.
///
/// # Panics
///
/// Panics if `seed` is shorter than 32 bytes.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{constants::testnet::COIN_TYPE, keys::spending_key};
///
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
/// ```
pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendingKey {
if seed.len() < 32 {
panic!("ZIP 32 seeds MUST be at least 32 bytes");
}
ExtendedSpendingKey::from_path(
&ExtendedSpendingKey::master(&seed),
&[
ChildIndex::Hardened(32),
ChildIndex::Hardened(coin_type),
ChildIndex::Hardened(account),
],
)
}
#[cfg(test)]
mod tests {
use super::spending_key;
#[test]
#[should_panic]
fn spending_key_panics_on_short_seed() {
let _ = spending_key(&[0; 31][..], 0, 0);
}
}

View File

@@ -0,0 +1,17 @@
//! *A crate for implementing Zcash light clients.*
//!
//! `zcash_client_backend` contains Rust structs and traits for creating shielded Zcash
//! light clients.
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
pub mod constants;
mod decrypt;
pub mod encoding;
pub mod keys;
pub mod proto;
pub mod wallet;
pub mod welding_rig;
pub use decrypt::{decrypt_transaction, DecryptedOutput};

View File

@@ -0,0 +1,83 @@
//! Generated code for handling light client protobuf structs.
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use zcash_primitives::{
block::{BlockHash, BlockHeader},
jubjub::{edwards, PrimeOrder},
JUBJUB,
};
pub mod compact_formats;
impl compact_formats::CompactBlock {
/// Returns the [`BlockHash`] for this block.
///
/// # Panics
///
/// This function will panic if [`CompactBlock.header`] is not set and
/// [`CompactBlock.hash`] is not exactly 32 bytes.
///
/// [`CompactBlock.header`]: #structfield.header
/// [`CompactBlock.hash`]: #structfield.hash
pub fn hash(&self) -> BlockHash {
if let Some(header) = self.header() {
header.hash()
} else {
BlockHash::from_slice(&self.hash)
}
}
/// Returns the [`BlockHash`] for this block's parent.
///
/// # Panics
///
/// This function will panic if [`CompactBlock.header`] is not set and
/// [`CompactBlock.prevHash`] is not exactly 32 bytes.
///
/// [`CompactBlock.header`]: #structfield.header
/// [`CompactBlock.prevHash`]: #structfield.prevHash
pub fn prev_hash(&self) -> BlockHash {
if let Some(header) = self.header() {
header.prev_block
} else {
BlockHash::from_slice(&self.prevHash)
}
}
/// Returns the [`BlockHeader`] for this block if present.
///
/// A convenience method that parses [`CompactBlock.header`] if present.
///
/// [`CompactBlock.header`]: #structfield.header
pub fn header(&self) -> Option<BlockHeader> {
if self.header.is_empty() {
None
} else {
BlockHeader::read(&self.header[..]).ok()
}
}
}
impl compact_formats::CompactOutput {
/// Returns the note commitment for this output.
///
/// A convenience method that parses [`CompactOutput.cmu`].
///
/// [`CompactOutput.cmu`]: #structfield.cmu
pub fn cmu(&self) -> Result<Fr, ()> {
let mut repr = FrRepr::default();
repr.read_le(&self.cmu[..]).map_err(|_| ())?;
Fr::from_repr(repr).map_err(|_| ())
}
/// Returns the ephemeral public key for this output.
///
/// A convenience method that parses [`CompactOutput.epk`].
///
/// [`CompactOutput.epk`]: #structfield.epk
pub fn epk(&self) -> Result<edwards::Point<Bls12, PrimeOrder>, ()> {
let p = edwards::Point::<Bls12, _>::read(&self.epk[..], &JUBJUB).map_err(|_| ())?;
p.as_prime_order(&JUBJUB).ok_or(())
}
}

View File

@@ -0,0 +1,46 @@
//! Structs representing transaction data scanned from the block chain by a wallet or
//! light client.
use pairing::bls12_381::{Bls12, Fr};
use zcash_primitives::{
jubjub::{edwards, PrimeOrder},
merkle_tree::IncrementalWitness,
primitives::{Note, PaymentAddress},
sapling::Node,
transaction::TxId,
};
/// A subset of a [`Transaction`] relevant to wallets and light clients.
///
/// [`Transaction`]: zcash_primitives::transaction::Transaction
pub struct WalletTx {
pub txid: TxId,
pub index: usize,
pub num_spends: usize,
pub num_outputs: usize,
pub shielded_spends: Vec<WalletShieldedSpend>,
pub shielded_outputs: Vec<WalletShieldedOutput>,
}
/// A subset of a [`SpendDescription`] relevant to wallets and light clients.
///
/// [`SpendDescription`]: zcash_primitives::transaction::components::SpendDescription
pub struct WalletShieldedSpend {
pub index: usize,
pub nf: Vec<u8>,
pub account: usize,
}
/// A subset of an [`OutputDescription`] relevant to wallets and light clients.
///
/// [`OutputDescription`]: zcash_primitives::transaction::components::OutputDescription
pub struct WalletShieldedOutput {
pub index: usize,
pub cmu: Fr,
pub epk: edwards::Point<Bls12, PrimeOrder>,
pub account: usize,
pub note: Note<Bls12>,
pub to: PaymentAddress<Bls12>,
pub is_change: bool,
pub witness: IncrementalWitness<Node>,
}

View File

@@ -0,0 +1,397 @@
//! Tools for scanning a compact representation of the Zcash block chain.
use ff::PrimeField;
use std::collections::HashSet;
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_primitives::{
jubjub::fs::Fs,
merkle_tree::{CommitmentTree, IncrementalWitness},
note_encryption::try_sapling_compact_note_decryption,
sapling::Node,
transaction::TxId,
zip32::ExtendedFullViewingKey,
};
use crate::proto::compact_formats::{CompactBlock, CompactOutput};
use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx};
/// Scans a [`CompactOutput`] with a set of [`ExtendedFullViewingKey`]s.
///
/// Returns a [`WalletShieldedOutput`] and corresponding [`IncrementalWitness`] if this
/// output belongs to any of the given [`ExtendedFullViewingKey`]s.
///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented
/// with this output's commitment.
fn scan_output(
(index, output): (usize, CompactOutput),
ivks: &[Fs],
spent_from_accounts: &HashSet<usize>,
tree: &mut CommitmentTree<Node>,
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
block_witnesses: &mut [&mut IncrementalWitness<Node>],
new_witnesses: &mut [&mut IncrementalWitness<Node>],
) -> Option<WalletShieldedOutput> {
let cmu = output.cmu().ok()?;
let epk = output.epk().ok()?;
let ct = output.ciphertext;
// Increment tree and witnesses
let node = Node::new(cmu.into_repr());
for witness in existing_witnesses {
witness.append(node).unwrap();
}
for witness in block_witnesses {
witness.append(node).unwrap();
}
for witness in new_witnesses {
witness.append(node).unwrap();
}
tree.append(node).unwrap();
for (account, ivk) in ivks.iter().enumerate() {
let (note, to) = match try_sapling_compact_note_decryption(ivk, &epk, &cmu, &ct) {
Some(ret) => ret,
None => continue,
};
// A note is marked as "change" if the account that received it
// also spent notes in the same transaction. This will catch,
// for instance:
// - Change created by spending fractions of notes.
// - Notes created by consolidation transactions.
// - Notes sent from one account to itself.
let is_change = spent_from_accounts.contains(&account);
return Some(WalletShieldedOutput {
index,
cmu,
epk,
account,
note,
to,
is_change,
witness: IncrementalWitness::from_tree(tree),
});
}
None
}
/// Scans a [`CompactBlock`] with a set of [`ExtendedFullViewingKey`]s.
///
/// Returns a vector of [`WalletTx`]s belonging to any of the given
/// [`ExtendedFullViewingKey`]s, and the corresponding new [`IncrementalWitness`]es.
///
/// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are
/// incremented appropriately.
pub fn scan_block(
block: CompactBlock,
extfvks: &[ExtendedFullViewingKey],
nullifiers: &[(&[u8], usize)],
tree: &mut CommitmentTree<Node>,
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
) -> Vec<WalletTx> {
let mut wtxs: Vec<WalletTx> = vec![];
let ivks: Vec<_> = extfvks.iter().map(|extfvk| extfvk.fvk.vk.ivk()).collect();
for tx in block.vtx.into_iter() {
let num_spends = tx.spends.len();
let num_outputs = tx.outputs.len();
// Check for spent notes
// The only step that is not constant-time is the filter() at the end.
let shielded_spends: Vec<_> = tx
.spends
.into_iter()
.enumerate()
.map(|(index, spend)| {
// Find the first tracked nullifier that matches this spend, and produce
// a WalletShieldedSpend if there is a match, in constant time.
nullifiers
.iter()
.map(|&(nf, account)| CtOption::new(account as u64, nf.ct_eq(&spend.nf[..])))
.fold(CtOption::new(0, 0.into()), |first, next| {
CtOption::conditional_select(&next, &first, first.is_some())
})
.map(|account| WalletShieldedSpend {
index,
nf: spend.nf,
account: account as usize,
})
})
.filter(|spend| spend.is_some().into())
.map(|spend| spend.unwrap())
.collect();
// Collect the set of accounts that were spent from in this transaction
let spent_from_accounts: HashSet<_> =
shielded_spends.iter().map(|spend| spend.account).collect();
// Check for incoming notes while incrementing tree and witnesses
let mut shielded_outputs: Vec<WalletShieldedOutput> = vec![];
{
// Grab mutable references to new witnesses from previous transactions
// in this block so that we can update them. Scoped so we don't hold
// mutable references to wtxs for too long.
let mut block_witnesses: Vec<_> = wtxs
.iter_mut()
.flat_map(|tx| {
tx.shielded_outputs
.iter_mut()
.map(|output| &mut output.witness)
})
.collect();
for to_scan in tx.outputs.into_iter().enumerate() {
// Grab mutable references to new witnesses from previous outputs
// in this transaction so that we can update them. Scoped so we
// don't hold mutable references to shielded_outputs for too long.
let mut new_witnesses: Vec<_> = shielded_outputs
.iter_mut()
.map(|output| &mut output.witness)
.collect();
if let Some(output) = scan_output(
to_scan,
&ivks,
&spent_from_accounts,
tree,
existing_witnesses,
&mut block_witnesses,
&mut new_witnesses,
) {
shielded_outputs.push(output);
}
}
}
if !(shielded_spends.is_empty() && shielded_outputs.is_empty()) {
let mut txid = TxId([0u8; 32]);
txid.0.copy_from_slice(&tx.hash);
wtxs.push(WalletTx {
txid,
index: tx.index as usize,
num_spends,
num_outputs,
shielded_spends,
shielded_outputs,
});
}
}
wtxs
}
#[cfg(test)]
mod tests {
use ff::{Field, PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr};
use rand_core::{OsRng, RngCore};
use zcash_primitives::{
jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform},
merkle_tree::CommitmentTree,
note_encryption::{Memo, SaplingNoteEncryption},
primitives::Note,
transaction::components::Amount,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
use super::scan_block;
use crate::proto::compact_formats::{CompactBlock, CompactOutput, CompactSpend, CompactTx};
fn random_compact_tx<R: RngCore>(rng: &mut R) -> CompactTx {
let fake_nf = {
let mut nf = vec![0; 32];
rng.fill_bytes(&mut nf);
nf
};
let fake_cmu = {
let fake_cmu = Fr::random(rng);
let mut bytes = vec![];
fake_cmu.into_repr().write_le(&mut bytes).unwrap();
bytes
};
let fake_epk = {
let mut buffer = vec![0; 64];
rng.fill_bytes(&mut buffer);
let fake_esk = Fs::to_uniform(&buffer[..]);
let fake_epk = JUBJUB
.generator(FixedGenerators::SpendingKeyGenerator)
.mul(fake_esk, &JUBJUB);
let mut bytes = vec![];
fake_epk.write(&mut bytes).unwrap();
bytes
};
let mut cspend = CompactSpend::new();
cspend.set_nf(fake_nf);
let mut cout = CompactOutput::new();
cout.set_cmu(fake_cmu);
cout.set_epk(fake_epk);
cout.set_ciphertext(vec![0; 52]);
let mut ctx = CompactTx::new();
let mut txid = vec![0; 32];
rng.fill_bytes(&mut txid);
ctx.set_hash(txid);
ctx.spends.push(cspend);
ctx.outputs.push(cout);
ctx
}
/// Create a fake CompactBlock at the given height, with a transaction containing a
/// single spend of the given nullifier and a single output paying the given address.
/// Returns the CompactBlock.
fn fake_compact_block(
height: i32,
nf: [u8; 32],
extfvk: ExtendedFullViewingKey,
value: Amount,
tx_after: bool,
) -> CompactBlock {
let to = extfvk.default_address().unwrap().1;
// Create a fake Note for the account
let mut rng = OsRng;
let note = Note {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
r: Fs::random(&mut rng),
};
let encryptor = SaplingNoteEncryption::new(
extfvk.fvk.ovk,
note.clone(),
to.clone(),
Memo::default(),
&mut rng,
);
let mut cmu = vec![];
note.cm(&JUBJUB).into_repr().write_le(&mut cmu).unwrap();
let mut epk = vec![];
encryptor.epk().write(&mut epk).unwrap();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
// Create a fake CompactBlock containing the note
let mut cb = CompactBlock::new();
cb.set_height(height as u64);
// Add a random Sapling tx before ours
{
let mut tx = random_compact_tx(&mut rng);
tx.index = cb.vtx.len() as u64;
cb.vtx.push(tx);
}
let mut cspend = CompactSpend::new();
cspend.set_nf(nf.to_vec());
let mut cout = CompactOutput::new();
cout.set_cmu(cmu);
cout.set_epk(epk);
cout.set_ciphertext(enc_ciphertext[..52].to_vec());
let mut ctx = CompactTx::new();
let mut txid = vec![0; 32];
rng.fill_bytes(&mut txid);
ctx.set_hash(txid);
ctx.spends.push(cspend);
ctx.outputs.push(cout);
ctx.index = cb.vtx.len() as u64;
cb.vtx.push(ctx);
// Optionally add another random Sapling tx after ours
if tx_after {
let mut tx = random_compact_tx(&mut rng);
tx.index = cb.vtx.len() as u64;
cb.vtx.push(tx);
}
cb
}
#[test]
fn scan_block_with_my_tx() {
let extsk = ExtendedSpendingKey::master(&[]);
let extfvk = ExtendedFullViewingKey::from(&extsk);
let cb = fake_compact_block(
1,
[0; 32],
extfvk.clone(),
Amount::from_u64(5).unwrap(),
false,
);
assert_eq!(cb.vtx.len(), 2);
let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []);
assert_eq!(txs.len(), 1);
let tx = &txs[0];
assert_eq!(tx.index, 1);
assert_eq!(tx.num_spends, 1);
assert_eq!(tx.num_outputs, 1);
assert_eq!(tx.shielded_spends.len(), 0);
assert_eq!(tx.shielded_outputs.len(), 1);
assert_eq!(tx.shielded_outputs[0].index, 0);
assert_eq!(tx.shielded_outputs[0].account, 0);
assert_eq!(tx.shielded_outputs[0].note.value, 5);
// Check that the witness root matches
assert_eq!(tx.shielded_outputs[0].witness.root(), tree.root());
}
#[test]
fn scan_block_with_txs_after_my_tx() {
let extsk = ExtendedSpendingKey::master(&[]);
let extfvk = ExtendedFullViewingKey::from(&extsk);
let cb = fake_compact_block(
1,
[0; 32],
extfvk.clone(),
Amount::from_u64(5).unwrap(),
true,
);
assert_eq!(cb.vtx.len(), 3);
let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []);
assert_eq!(txs.len(), 1);
let tx = &txs[0];
assert_eq!(tx.index, 1);
assert_eq!(tx.num_spends, 1);
assert_eq!(tx.num_outputs, 1);
assert_eq!(tx.shielded_spends.len(), 0);
assert_eq!(tx.shielded_outputs.len(), 1);
assert_eq!(tx.shielded_outputs[0].index, 0);
assert_eq!(tx.shielded_outputs[0].account, 0);
assert_eq!(tx.shielded_outputs[0].note.value, 5);
// Check that the witness root matches
assert_eq!(tx.shielded_outputs[0].witness.root(), tree.root());
}
#[test]
fn scan_block_with_my_spend() {
let extsk = ExtendedSpendingKey::master(&[]);
let extfvk = ExtendedFullViewingKey::from(&extsk);
let nf = [7; 32];
let account = 12;
let cb = fake_compact_block(1, nf, extfvk, Amount::from_u64(5).unwrap(), false);
assert_eq!(cb.vtx.len(), 2);
let mut tree = CommitmentTree::new();
let txs = scan_block(cb, &[], &[(&nf, account)], &mut tree, &mut []);
assert_eq!(txs.len(), 1);
let tx = &txs[0];
assert_eq!(tx.index, 1);
assert_eq!(tx.num_spends, 1);
assert_eq!(tx.num_outputs, 1);
assert_eq!(tx.shielded_spends.len(), 1);
assert_eq!(tx.shielded_outputs.len(), 0);
assert_eq!(tx.shielded_spends[0].index, 0);
assert_eq!(tx.shielded_spends[0].nf, nf);
assert_eq!(tx.shielded_spends[0].account, account);
}
}

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