120
.github/workflows/ci.yml
vendored
Normal file
120
.github/workflows/ci.yml
vendored
Normal 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
|
||||||
13
.travis.yml
13
.travis.yml
@@ -1,8 +1,19 @@
|
|||||||
language: rust
|
language: rust
|
||||||
rust:
|
rust:
|
||||||
- 1.32.0
|
- 1.37.0
|
||||||
|
|
||||||
cache: cargo
|
cache: cargo
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- rustup component add rustfmt
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
- cargo build --verbose --release --all
|
||||||
|
- cargo fmt --all -- --check
|
||||||
- cargo test --verbose --release --all
|
- 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
982
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -5,11 +5,10 @@ members = [
|
|||||||
"group",
|
"group",
|
||||||
"librustzcash",
|
"librustzcash",
|
||||||
"pairing",
|
"pairing",
|
||||||
"sapling-crypto",
|
"zcash_client_backend",
|
||||||
|
"zcash_history",
|
||||||
"zcash_primitives",
|
"zcash_primitives",
|
||||||
"zcash_proofs",
|
"zcash_proofs",
|
||||||
"zcash_wallet",
|
|
||||||
"zip32",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|||||||
@@ -1,25 +1,33 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||||
description = "zk-SNARK library"
|
description = "zk-SNARK library"
|
||||||
documentation = "https://github.com/ebfull/bellman"
|
readme = "README.md"
|
||||||
homepage = "https://github.com/ebfull/bellman"
|
homepage = "https://github.com/ebfull/bellman"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
name = "bellman"
|
name = "bellman"
|
||||||
repository = "https://github.com/ebfull/bellman"
|
repository = "https://github.com/ebfull/bellman"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.4"
|
|
||||||
bit-vec = "0.4.4"
|
bit-vec = "0.4.4"
|
||||||
ff = { path = "../ff" }
|
blake2s_simd = "0.5"
|
||||||
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
futures-cpupool = { version = "0.1", optional = true }
|
futures-cpupool = { version = "0.1", optional = true }
|
||||||
group = { path = "../group" }
|
group = { version = "0.2.0", path = "../group" }
|
||||||
num_cpus = { version = "1", optional = true }
|
num_cpus = { version = "1", optional = true }
|
||||||
crossbeam = { version = "0.3", optional = true }
|
crossbeam = { version = "0.7", optional = true }
|
||||||
pairing = { path = "../pairing", optional = true }
|
pairing = { version = "0.15.0", path = "../pairing", optional = true }
|
||||||
|
rand_core = "0.5"
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex-literal = "0.2"
|
||||||
|
rand = "0.7"
|
||||||
|
rand_xorshift = "0.2"
|
||||||
|
sha2 = "0.8"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
groth16 = ["pairing"]
|
groth16 = ["pairing"]
|
||||||
multicore = ["futures-cpupool", "crossbeam", "num_cpus"]
|
multicore = ["futures-cpupool", "crossbeam", "num_cpus"]
|
||||||
@@ -29,3 +37,6 @@ default = ["groth16", "multicore"]
|
|||||||
name = "mimc"
|
name = "mimc"
|
||||||
path = "tests/mimc.rs"
|
path = "tests/mimc.rs"
|
||||||
required-features = ["groth16"]
|
required-features = ["groth16"]
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
# bellman [](https://crates.io/crates/bellman) #
|
# bellman [](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
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
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)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
//! This module contains an `EvaluationDomain` abstraction for
|
//! This module contains an [`EvaluationDomain`] abstraction for performing
|
||||||
//! performing various kinds of polynomial arithmetic on top of
|
//! various kinds of polynomial arithmetic on top of the scalar field.
|
||||||
//! the scalar field.
|
|
||||||
//!
|
//!
|
||||||
//! In pairing-based SNARKs like Groth16, we need to calculate
|
//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient
|
||||||
//! a quotient polynomial over a target polynomial with roots
|
//! polynomial over a target polynomial with roots at distinct points associated
|
||||||
//! at distinct points associated with each constraint of the
|
//! with each constraint of the constraint system. In order to be efficient, we
|
||||||
//! constraint system. In order to be efficient, we choose these
|
//! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the
|
||||||
//! roots to be the powers of a 2^n root of unity in the field.
|
//! field. This allows us to perform polynomial operations in O(n) by performing
|
||||||
//! This allows us to perform polynomial operations in O(n)
|
//! an O(n log n) FFT over such a domain.
|
||||||
//! 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 ff::{Field, PrimeField, ScalarEngine};
|
||||||
use group::CurveProjective;
|
use group::CurveProjective;
|
||||||
|
|
||||||
use super::{
|
use super::SynthesisError;
|
||||||
SynthesisError
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::multicore::Worker;
|
use super::multicore::Worker;
|
||||||
|
|
||||||
@@ -25,24 +24,27 @@ pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
|
|||||||
omega: E::Fr,
|
omega: E::Fr,
|
||||||
omegainv: E::Fr,
|
omegainv: E::Fr,
|
||||||
geninv: E::Fr,
|
geninv: E::Fr,
|
||||||
minv: E::Fr
|
minv: E::Fr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine, G: Group<E>> 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> {
|
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> {
|
pub fn into_coeffs(self) -> Vec<G> {
|
||||||
self.coeffs
|
self.coeffs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError>
|
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError> {
|
||||||
{
|
|
||||||
// Compute the size of our evaluation domain
|
// Compute the size of our evaluation domain
|
||||||
let mut m = 1;
|
let mut m = 1;
|
||||||
let mut exp = 0;
|
let mut exp = 0;
|
||||||
@@ -53,7 +55,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
// The pairing-friendly curve may not be able to support
|
// The pairing-friendly curve may not be able to support
|
||||||
// large enough (radix2) evaluation domains.
|
// large enough (radix2) evaluation domains.
|
||||||
if exp >= E::Fr::S {
|
if exp >= E::Fr::S {
|
||||||
return Err(SynthesisError::PolynomialDegreeTooLarge)
|
return Err(SynthesisError::PolynomialDegreeTooLarge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,29 +69,30 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
coeffs.resize(m, G::group_zero());
|
coeffs.resize(m, G::group_zero());
|
||||||
|
|
||||||
Ok(EvaluationDomain {
|
Ok(EvaluationDomain {
|
||||||
coeffs: coeffs,
|
coeffs,
|
||||||
exp: exp,
|
exp,
|
||||||
omega: omega,
|
omega,
|
||||||
omegainv: omega.inverse().unwrap(),
|
omegainv: omega.inverse().unwrap(),
|
||||||
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
|
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
|
||||||
minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap()
|
minv: E::Fr::from_str(&format!("{}", m))
|
||||||
|
.unwrap()
|
||||||
|
.inverse()
|
||||||
|
.unwrap(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fft(&mut self, worker: &Worker)
|
pub fn fft(&mut self, worker: &Worker) {
|
||||||
{
|
|
||||||
best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
|
best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ifft(&mut self, worker: &Worker)
|
pub fn ifft(&mut self, worker: &Worker) {
|
||||||
{
|
|
||||||
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
|
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
|
||||||
|
|
||||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
let minv = self.minv;
|
let minv = self.minv;
|
||||||
|
|
||||||
for v in self.coeffs.chunks_mut(chunk) {
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
for v in v {
|
for v in v {
|
||||||
v.group_mul_assign(&minv);
|
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| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
let mut u = g.pow(&[(i * chunk) as u64]);
|
let mut u = g.pow(&[(i * chunk) as u64]);
|
||||||
for v in v.iter_mut() {
|
for v in v.iter_mut() {
|
||||||
v.group_mul_assign(&u);
|
v.group_mul_assign(&u);
|
||||||
@@ -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.distribute_powers(worker, E::Fr::multiplicative_generator());
|
||||||
self.fft(worker);
|
self.fft(worker);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn icoset_fft(&mut self, worker: &Worker)
|
pub fn icoset_fft(&mut self, worker: &Worker) {
|
||||||
{
|
|
||||||
let geninv = self.geninv;
|
let geninv = self.geninv;
|
||||||
|
|
||||||
self.ifft(worker);
|
self.ifft(worker);
|
||||||
@@ -139,13 +139,15 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
/// The target polynomial is the zero polynomial in our
|
/// The target polynomial is the zero polynomial in our
|
||||||
/// evaluation domain, so we must perform division over
|
/// evaluation domain, so we must perform division over
|
||||||
/// a coset.
|
/// a coset.
|
||||||
pub fn divide_by_z_on_coset(&mut self, worker: &Worker)
|
pub fn divide_by_z_on_coset(&mut self, worker: &Worker) {
|
||||||
{
|
let i = self
|
||||||
let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap();
|
.z(&E::Fr::multiplicative_generator())
|
||||||
|
.inverse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for v in self.coeffs.chunks_mut(chunk) {
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
for v in v {
|
for v in v {
|
||||||
v.group_mul_assign(&i);
|
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());
|
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||||
|
|
||||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
|
for (a, b) in self
|
||||||
scope.spawn(move || {
|
.coeffs
|
||||||
|
.chunks_mut(chunk)
|
||||||
|
.zip(other.coeffs.chunks(chunk))
|
||||||
|
{
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
a.group_mul_assign(&b.0);
|
a.group_mul_assign(&b.0);
|
||||||
}
|
}
|
||||||
@@ -174,8 +180,12 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
assert_eq!(self.coeffs.len(), other.coeffs.len());
|
||||||
|
|
||||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
|
for (a, b) in self
|
||||||
scope.spawn(move || {
|
.coeffs
|
||||||
|
.chunks_mut(chunk)
|
||||||
|
.zip(other.coeffs.chunks(chunk))
|
||||||
|
{
|
||||||
|
scope.spawn(move |_scope| {
|
||||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
a.group_sub_assign(&b);
|
a.group_sub_assign(&b);
|
||||||
}
|
}
|
||||||
@@ -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> {
|
impl<G: CurveProjective> Clone for Point<G> {
|
||||||
fn clone(&self) -> 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> {
|
impl<E: ScalarEngine> Clone for Scalar<E> {
|
||||||
fn clone(&self) -> 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();
|
let log_cpus = worker.log_num_cpus();
|
||||||
|
|
||||||
if log_n <= log_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 {
|
fn bitreverse(mut n: u32, l: u32) -> u32 {
|
||||||
let mut r = 0;
|
let mut r = 0;
|
||||||
for _ in 0..l {
|
for _ in 0..l {
|
||||||
@@ -288,22 +296,22 @@ fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u
|
|||||||
|
|
||||||
let mut m = 1;
|
let mut m = 1;
|
||||||
for _ in 0..log_n {
|
for _ in 0..log_n {
|
||||||
let w_m = omega.pow(&[(n / (2*m)) as u64]);
|
let w_m = omega.pow(&[u64::from(n / (2 * m))]);
|
||||||
|
|
||||||
let mut k = 0;
|
let mut k = 0;
|
||||||
while k < n {
|
while k < n {
|
||||||
let mut w = E::Fr::one();
|
let mut w = E::Fr::one();
|
||||||
for j in 0..m {
|
for j in 0..m {
|
||||||
let mut t = a[(k+j+m) as usize];
|
let mut t = a[(k + j + m) as usize];
|
||||||
t.group_mul_assign(&w);
|
t.group_mul_assign(&w);
|
||||||
let mut tmp = a[(k+j) as usize];
|
let mut tmp = a[(k + j) as usize];
|
||||||
tmp.group_sub_assign(&t);
|
tmp.group_sub_assign(&t);
|
||||||
a[(k+j+m) as usize] = tmp;
|
a[(k + j + m) as usize] = tmp;
|
||||||
a[(k+j) as usize].group_add_assign(&t);
|
a[(k + j) as usize].group_add_assign(&t);
|
||||||
w.mul_assign(&w_m);
|
w.mul_assign(&w_m);
|
||||||
}
|
}
|
||||||
|
|
||||||
k += 2*m;
|
k += 2 * m;
|
||||||
}
|
}
|
||||||
|
|
||||||
m *= 2;
|
m *= 2;
|
||||||
@@ -315,9 +323,8 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
|||||||
worker: &Worker,
|
worker: &Worker,
|
||||||
omega: &E::Fr,
|
omega: &E::Fr,
|
||||||
log_n: u32,
|
log_n: u32,
|
||||||
log_cpus: u32
|
log_cpus: u32,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
assert!(log_n >= log_cpus);
|
assert!(log_n >= log_cpus);
|
||||||
|
|
||||||
let num_cpus = 1 << log_cpus;
|
let num_cpus = 1 << log_cpus;
|
||||||
@@ -329,18 +336,18 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
|||||||
let a = &*a;
|
let a = &*a;
|
||||||
|
|
||||||
for (j, tmp) in tmp.iter_mut().enumerate() {
|
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
// Shuffle into a sub-FFT
|
// Shuffle into a sub-FFT
|
||||||
let omega_j = omega.pow(&[j as u64]);
|
let omega_j = omega.pow(&[j as u64]);
|
||||||
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
|
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
|
||||||
|
|
||||||
let mut elt = E::Fr::one();
|
let mut elt = E::Fr::one();
|
||||||
for i in 0..(1 << log_new_n) {
|
for (i, tmp) in tmp.iter_mut().enumerate() {
|
||||||
for s in 0..num_cpus {
|
for s in 0..num_cpus {
|
||||||
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
||||||
let mut t = a[idx];
|
let mut t = a[idx];
|
||||||
t.group_mul_assign(&elt);
|
t.group_mul_assign(&elt);
|
||||||
tmp[i].group_add_assign(&t);
|
tmp.group_add_assign(&t);
|
||||||
elt.mul_assign(&omega_step);
|
elt.mul_assign(&omega_step);
|
||||||
}
|
}
|
||||||
elt.mul_assign(&omega_j);
|
elt.mul_assign(&omega_j);
|
||||||
@@ -357,7 +364,7 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
|||||||
let tmp = &tmp;
|
let tmp = &tmp;
|
||||||
|
|
||||||
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
let mut idx = idx * chunk;
|
let mut idx = idx * chunk;
|
||||||
let mask = (1 << log_cpus) - 1;
|
let mask = (1 << log_cpus) - 1;
|
||||||
for a in a {
|
for a in a {
|
||||||
@@ -375,16 +382,19 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
|||||||
#[test]
|
#[test]
|
||||||
fn polynomial_arith() {
|
fn polynomial_arith() {
|
||||||
use pairing::bls12_381::Bls12;
|
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();
|
let worker = Worker::new();
|
||||||
|
|
||||||
for coeffs_a in 0..70 {
|
for coeffs_a in 0..70 {
|
||||||
for coeffs_b in 0..70 {
|
for coeffs_b in 0..70 {
|
||||||
let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
|
let mut a: Vec<_> = (0..coeffs_a)
|
||||||
let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
|
.map(|_| Scalar::<E>(E::Fr::random(rng)))
|
||||||
|
.collect();
|
||||||
|
let mut b: Vec<_> = (0..coeffs_b)
|
||||||
|
.map(|_| Scalar::<E>(E::Fr::random(rng)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
// naive evaluation
|
// naive evaluation
|
||||||
let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b];
|
let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b];
|
||||||
@@ -423,10 +433,9 @@ fn polynomial_arith() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn fft_composition() {
|
fn fft_composition() {
|
||||||
use pairing::bls12_381::Bls12;
|
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();
|
let worker = Worker::new();
|
||||||
|
|
||||||
for coeffs in 0..10 {
|
for coeffs in 0..10 {
|
||||||
@@ -434,7 +443,7 @@ fn fft_composition() {
|
|||||||
|
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
for _ in 0..coeffs {
|
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();
|
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
|
||||||
@@ -462,22 +471,23 @@ fn fft_composition() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parallel_fft_consistency() {
|
fn parallel_fft_consistency() {
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use rand::{self, Rand};
|
use rand_core::RngCore;
|
||||||
use std::cmp::min;
|
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();
|
let worker = Worker::new();
|
||||||
|
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
for log_d in 0..10 {
|
for log_d in 0..10 {
|
||||||
let d = 1 << log_d;
|
let d = 1 << log_d;
|
||||||
|
|
||||||
let v1 = (0..d).map(|_| Scalar::<E>(E::Fr::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 v1 = EvaluationDomain::from_coeffs(v1).unwrap();
|
||||||
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
|
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
|
||||||
|
|
||||||
for log_cpus in log_d..min(log_d+1, 3) {
|
for log_cpus in log_d..min(log_d + 1, 3) {
|
||||||
parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus);
|
parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus);
|
||||||
serial_fft(&mut v2.coeffs, &v2.omega, log_d);
|
serial_fft(&mut v2.coeffs, &v2.omega, log_d);
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,17 @@
|
|||||||
#[cfg(test)]
|
//! Self-contained sub-circuit implementations for various primitives.
|
||||||
|
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
pub mod boolean;
|
|
||||||
pub mod multieq;
|
|
||||||
pub mod uint32;
|
|
||||||
pub mod blake2s;
|
pub mod blake2s;
|
||||||
pub mod num;
|
pub mod boolean;
|
||||||
pub mod lookup;
|
pub mod lookup;
|
||||||
pub mod ecc;
|
pub mod multieq;
|
||||||
pub mod pedersen_hash;
|
|
||||||
pub mod multipack;
|
pub mod multipack;
|
||||||
|
pub mod num;
|
||||||
pub mod sha256;
|
pub mod sha256;
|
||||||
|
pub mod uint32;
|
||||||
|
|
||||||
pub mod sapling;
|
use crate::SynthesisError;
|
||||||
pub mod sprout;
|
|
||||||
|
|
||||||
use bellman::{
|
|
||||||
SynthesisError
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: This should probably be removed and we
|
// TODO: This should probably be removed and we
|
||||||
// should use existing helper methods on `Option`
|
// should use existing helper methods on `Option`
|
||||||
@@ -25,7 +19,7 @@ use bellman::{
|
|||||||
/// This basically is just an extension to `Option`
|
/// This basically is just an extension to `Option`
|
||||||
/// which allows for a convenient mapping to an
|
/// which allows for a convenient mapping to an
|
||||||
/// error on `None`.
|
/// error on `None`.
|
||||||
trait Assignment<T> {
|
pub trait Assignment<T> {
|
||||||
fn get(&self) -> Result<&T, SynthesisError>;
|
fn get(&self) -> Result<&T, SynthesisError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +27,7 @@ impl<T> Assignment<T> for Option<T> {
|
|||||||
fn get(&self) -> Result<&T, SynthesisError> {
|
fn get(&self) -> Result<&T, SynthesisError> {
|
||||||
match *self {
|
match *self {
|
||||||
Some(ref v) => Ok(v),
|
Some(ref v) => Ok(v),
|
||||||
None => Err(SynthesisError::AssignmentMissing)
|
None => Err(SynthesisError::AssignmentMissing),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
697
bellman/src/gadgets/blake2s.rs
Normal file
697
bellman/src/gadgets/blake2s.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,16 @@
|
|||||||
use ff::Field;
|
//! Window table lookup gadgets.
|
||||||
use pairing::Engine;
|
|
||||||
|
use ff::{Field, ScalarEngine};
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use super::num::{
|
|
||||||
AllocatedNum,
|
|
||||||
Num
|
|
||||||
};
|
|
||||||
use super::boolean::Boolean;
|
use super::boolean::Boolean;
|
||||||
use bellman::{
|
use super::num::{AllocatedNum, Num};
|
||||||
ConstraintSystem
|
use super::*;
|
||||||
};
|
use crate::ConstraintSystem;
|
||||||
|
|
||||||
// Synthesize the constants for each base pattern.
|
// Synthesize the constants for each base pattern.
|
||||||
fn synth<'a, E: Engine, I>(
|
fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
|
||||||
window_size: usize,
|
where
|
||||||
constants: I,
|
I: IntoIterator<Item = &'a E::Fr>,
|
||||||
assignment: &mut [E::Fr]
|
|
||||||
)
|
|
||||||
where I: IntoIterator<Item=&'a E::Fr>
|
|
||||||
{
|
{
|
||||||
assert_eq!(assignment.len(), 1 << window_size);
|
assert_eq!(assignment.len(), 1 << window_size);
|
||||||
|
|
||||||
@@ -36,19 +29,23 @@ fn synth<'a, E: Engine, I>(
|
|||||||
|
|
||||||
/// Performs a 3-bit window table lookup. `bits` is in
|
/// Performs a 3-bit window table lookup. `bits` is in
|
||||||
/// little-endian order.
|
/// little-endian order.
|
||||||
pub fn lookup3_xy<E: Engine, CS>(
|
pub fn lookup3_xy<E: ScalarEngine, CS>(
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
bits: &[Boolean],
|
bits: &[Boolean],
|
||||||
coords: &[(E::Fr, E::Fr)]
|
coords: &[(E::Fr, E::Fr)],
|
||||||
) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError>
|
) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError>
|
||||||
where CS: ConstraintSystem<E>
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
assert_eq!(bits.len(), 3);
|
assert_eq!(bits.len(), 3);
|
||||||
assert_eq!(coords.len(), 8);
|
assert_eq!(coords.len(), 8);
|
||||||
|
|
||||||
// Calculate the index into `coords`
|
// Calculate the index into `coords`
|
||||||
let i =
|
let i = match (
|
||||||
match (bits[0].get_value(), bits[1].get_value(), bits[2].get_value()) {
|
bits[0].get_value(),
|
||||||
|
bits[1].get_value(),
|
||||||
|
bits[2].get_value(),
|
||||||
|
) {
|
||||||
(Some(a_value), Some(b_value), Some(c_value)) => {
|
(Some(a_value), Some(b_value), Some(c_value)) => {
|
||||||
let mut tmp = 0;
|
let mut tmp = 0;
|
||||||
if a_value {
|
if a_value {
|
||||||
@@ -61,25 +58,15 @@ pub fn lookup3_xy<E: Engine, CS>(
|
|||||||
tmp += 4;
|
tmp += 4;
|
||||||
}
|
}
|
||||||
Some(tmp)
|
Some(tmp)
|
||||||
},
|
}
|
||||||
_ => None
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Allocate the x-coordinate resulting from the lookup
|
// Allocate the x-coordinate resulting from the lookup
|
||||||
let res_x = AllocatedNum::alloc(
|
let res_x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(coords[*i.get()?].0))?;
|
||||||
cs.namespace(|| "x"),
|
|
||||||
|| {
|
|
||||||
Ok(coords[*i.get()?].0)
|
|
||||||
}
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Allocate the y-coordinate resulting from the lookup
|
// Allocate the y-coordinate resulting from the lookup
|
||||||
let res_y = AllocatedNum::alloc(
|
let res_y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords[*i.get()?].1))?;
|
||||||
cs.namespace(|| "y"),
|
|
||||||
|| {
|
|
||||||
Ok(coords[*i.get()?].1)
|
|
||||||
}
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Compute the coefficients for the lookup constraints
|
// Compute the coefficients for the lookup constraints
|
||||||
let mut x_coeffs = [E::Fr::zero(); 8];
|
let mut x_coeffs = [E::Fr::zero(); 8];
|
||||||
@@ -93,30 +80,38 @@ pub fn lookup3_xy<E: Engine, CS>(
|
|||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "x-coordinate lookup",
|
|| "x-coordinate lookup",
|
||||||
|lc| lc + (x_coeffs[0b001], one)
|
|lc| {
|
||||||
|
lc + (x_coeffs[0b001], one)
|
||||||
+ &bits[1].lc::<E>(one, x_coeffs[0b011])
|
+ &bits[1].lc::<E>(one, x_coeffs[0b011])
|
||||||
+ &bits[2].lc::<E>(one, x_coeffs[0b101])
|
+ &bits[2].lc::<E>(one, x_coeffs[0b101])
|
||||||
+ &precomp.lc::<E>(one, x_coeffs[0b111]),
|
+ &precomp.lc::<E>(one, x_coeffs[0b111])
|
||||||
|
},
|
||||||
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|
||||||
|lc| lc + res_x.get_variable()
|
|lc| {
|
||||||
|
lc + res_x.get_variable()
|
||||||
- (x_coeffs[0b000], one)
|
- (x_coeffs[0b000], one)
|
||||||
- &bits[1].lc::<E>(one, x_coeffs[0b010])
|
- &bits[1].lc::<E>(one, x_coeffs[0b010])
|
||||||
- &bits[2].lc::<E>(one, x_coeffs[0b100])
|
- &bits[2].lc::<E>(one, x_coeffs[0b100])
|
||||||
- &precomp.lc::<E>(one, x_coeffs[0b110]),
|
- &precomp.lc::<E>(one, x_coeffs[0b110])
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "y-coordinate lookup",
|
|| "y-coordinate lookup",
|
||||||
|lc| lc + (y_coeffs[0b001], one)
|
|lc| {
|
||||||
|
lc + (y_coeffs[0b001], one)
|
||||||
+ &bits[1].lc::<E>(one, y_coeffs[0b011])
|
+ &bits[1].lc::<E>(one, y_coeffs[0b011])
|
||||||
+ &bits[2].lc::<E>(one, y_coeffs[0b101])
|
+ &bits[2].lc::<E>(one, y_coeffs[0b101])
|
||||||
+ &precomp.lc::<E>(one, y_coeffs[0b111]),
|
+ &precomp.lc::<E>(one, y_coeffs[0b111])
|
||||||
|
},
|
||||||
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|
||||||
|lc| lc + res_y.get_variable()
|
|lc| {
|
||||||
|
lc + res_y.get_variable()
|
||||||
- (y_coeffs[0b000], one)
|
- (y_coeffs[0b000], one)
|
||||||
- &bits[1].lc::<E>(one, y_coeffs[0b010])
|
- &bits[1].lc::<E>(one, y_coeffs[0b010])
|
||||||
- &bits[2].lc::<E>(one, y_coeffs[0b100])
|
- &bits[2].lc::<E>(one, y_coeffs[0b100])
|
||||||
- &precomp.lc::<E>(one, y_coeffs[0b110]),
|
- &precomp.lc::<E>(one, y_coeffs[0b110])
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok((res_x, res_y))
|
Ok((res_x, res_y))
|
||||||
@@ -124,19 +119,19 @@ pub fn lookup3_xy<E: Engine, CS>(
|
|||||||
|
|
||||||
/// Performs a 3-bit window table lookup, where
|
/// Performs a 3-bit window table lookup, where
|
||||||
/// one of the bits is a sign bit.
|
/// one of the bits is a sign bit.
|
||||||
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
|
pub fn lookup3_xy_with_conditional_negation<E: ScalarEngine, CS>(
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
bits: &[Boolean],
|
bits: &[Boolean],
|
||||||
coords: &[(E::Fr, E::Fr)]
|
coords: &[(E::Fr, E::Fr)],
|
||||||
) -> Result<(Num<E>, Num<E>), SynthesisError>
|
) -> Result<(Num<E>, Num<E>), SynthesisError>
|
||||||
where CS: ConstraintSystem<E>
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
assert_eq!(bits.len(), 3);
|
assert_eq!(bits.len(), 3);
|
||||||
assert_eq!(coords.len(), 4);
|
assert_eq!(coords.len(), 4);
|
||||||
|
|
||||||
// Calculate the index into `coords`
|
// Calculate the index into `coords`
|
||||||
let i =
|
let i = match (bits[0].get_value(), bits[1].get_value()) {
|
||||||
match (bits[0].get_value(), bits[1].get_value()) {
|
|
||||||
(Some(a_value), Some(b_value)) => {
|
(Some(a_value), Some(b_value)) => {
|
||||||
let mut tmp = 0;
|
let mut tmp = 0;
|
||||||
if a_value {
|
if a_value {
|
||||||
@@ -146,22 +141,19 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
|
|||||||
tmp += 2;
|
tmp += 2;
|
||||||
}
|
}
|
||||||
Some(tmp)
|
Some(tmp)
|
||||||
},
|
}
|
||||||
_ => None
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Allocate the y-coordinate resulting from the lookup
|
// Allocate the y-coordinate resulting from the lookup
|
||||||
// and conditional negation
|
// and conditional negation
|
||||||
let y = AllocatedNum::alloc(
|
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
|
||||||
cs.namespace(|| "y"),
|
let mut tmp = coords[*i.get()?].1;
|
||||||
|| {
|
if *bits[2].get_value().get()? {
|
||||||
let mut tmp = coords[*i.get()?].1;
|
tmp.negate();
|
||||||
if *bits[2].get_value().get()? {
|
|
||||||
tmp.negate();
|
|
||||||
}
|
|
||||||
Ok(tmp)
|
|
||||||
}
|
}
|
||||||
)?;
|
Ok(tmp)
|
||||||
|
})?;
|
||||||
|
|
||||||
let one = CS::one();
|
let one = CS::one();
|
||||||
|
|
||||||
@@ -174,21 +166,21 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
|
|||||||
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?;
|
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?;
|
||||||
|
|
||||||
let x = Num::zero()
|
let x = Num::zero()
|
||||||
.add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00])
|
.add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00])
|
||||||
.add_bool_with_coeff(one, &bits[0], x_coeffs[0b01])
|
.add_bool_with_coeff(one, &bits[0], x_coeffs[0b01])
|
||||||
.add_bool_with_coeff(one, &bits[1], x_coeffs[0b10])
|
.add_bool_with_coeff(one, &bits[1], x_coeffs[0b10])
|
||||||
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
|
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
|
||||||
|
|
||||||
let y_lc = precomp.lc::<E>(one, y_coeffs[0b11]) +
|
let y_lc = precomp.lc::<E>(one, y_coeffs[0b11])
|
||||||
&bits[1].lc::<E>(one, y_coeffs[0b10]) +
|
+ &bits[1].lc::<E>(one, y_coeffs[0b10])
|
||||||
&bits[0].lc::<E>(one, y_coeffs[0b01]) +
|
+ &bits[0].lc::<E>(one, y_coeffs[0b01])
|
||||||
(y_coeffs[0b00], one);
|
+ (y_coeffs[0b00], one);
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "y-coordinate lookup",
|
|| "y-coordinate lookup",
|
||||||
|lc| lc + &y_lc + &y_lc,
|
|lc| lc + &y_lc + &y_lc,
|
||||||
|lc| lc + &bits[2].lc::<E>(one, E::Fr::one()),
|
|lc| lc + &bits[2].lc::<E>(one, E::Fr::one()),
|
||||||
|lc| lc + &y_lc - y.get_variable()
|
|lc| lc + &y_lc - y.get_variable(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok((x, y.into()))
|
Ok((x, y.into()))
|
||||||
@@ -196,46 +188,52 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use rand::{SeedableRng, Rand, Rng, XorShiftRng};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use ::circuit::test::*;
|
use crate::gadgets::boolean::{AllocatedBit, Boolean};
|
||||||
use ::circuit::boolean::{Boolean, AllocatedBit};
|
use crate::gadgets::test::*;
|
||||||
use pairing::bls12_381::{Bls12, Fr};
|
use pairing::bls12_381::{Bls12, Fr};
|
||||||
|
use rand_core::{RngCore, SeedableRng};
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lookup3_xy() {
|
fn test_lookup3_xy() {
|
||||||
let mut rng = XorShiftRng::from_seed([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 {
|
for _ in 0..100 {
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let a_val = rng.gen();
|
let a_val = rng.next_u32() % 2 != 0;
|
||||||
let a = Boolean::from(
|
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
|
||||||
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let b_val = rng.gen();
|
let b_val = rng.next_u32() % 2 != 0;
|
||||||
let b = Boolean::from(
|
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
|
||||||
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let c_val = rng.gen();
|
let c_val = rng.next_u32() % 2 != 0;
|
||||||
let c = Boolean::from(
|
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
|
||||||
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let bits = vec![a, b, c];
|
let bits = vec![a, b, c];
|
||||||
|
|
||||||
let points: Vec<(Fr, Fr)> = (0..8).map(|_| (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();
|
let res = lookup3_xy(&mut cs, &bits, &points).unwrap();
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
if a_val { index += 1 }
|
if a_val {
|
||||||
if b_val { index += 2 }
|
index += 1
|
||||||
if c_val { index += 4 }
|
}
|
||||||
|
if b_val {
|
||||||
|
index += 2
|
||||||
|
}
|
||||||
|
if c_val {
|
||||||
|
index += 4
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(res.0.get_value().unwrap(), points[index].0);
|
assert_eq!(res.0.get_value().unwrap(), points[index].0);
|
||||||
assert_eq!(res.1.get_value().unwrap(), points[index].1);
|
assert_eq!(res.1.get_value().unwrap(), points[index].1);
|
||||||
@@ -244,53 +242,63 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lookup3_xy_with_conditional_negation() {
|
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 {
|
for _ in 0..100 {
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let a_val = rng.gen();
|
let a_val = rng.next_u32() % 2 != 0;
|
||||||
let a = Boolean::from(
|
let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
|
||||||
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let b_val = rng.gen();
|
let b_val = rng.next_u32() % 2 != 0;
|
||||||
let b = Boolean::from(
|
let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
|
||||||
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let c_val = rng.gen();
|
let c_val = rng.next_u32() % 2 != 0;
|
||||||
let c = Boolean::from(
|
let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
|
||||||
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let bits = vec![a, b, c];
|
let bits = vec![a, b, c];
|
||||||
|
|
||||||
let points: Vec<(Fr, Fr)> = (0..4).map(|_| (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();
|
let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap();
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
if a_val { index += 1 }
|
if a_val {
|
||||||
if b_val { index += 2 }
|
index += 1
|
||||||
|
}
|
||||||
|
if b_val {
|
||||||
|
index += 2
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(res.0.get_value().unwrap(), points[index].0);
|
assert_eq!(res.0.get_value().unwrap(), points[index].0);
|
||||||
let mut tmp = points[index].1;
|
let mut tmp = points[index].1;
|
||||||
if c_val { tmp.negate() }
|
if c_val {
|
||||||
|
tmp.negate()
|
||||||
|
}
|
||||||
assert_eq!(res.1.get_value().unwrap(), tmp);
|
assert_eq!(res.1.get_value().unwrap(), tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_synth() {
|
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 window_size = 4;
|
||||||
|
|
||||||
let mut assignment = vec![Fr::zero(); 1 << window_size];
|
let mut assignment = vec![Fr::zero(); 1 << window_size];
|
||||||
let constants: Vec<_> = (0..(1 << window_size)).map(|_| Fr::rand(&mut rng)).collect();
|
let constants: Vec<_> = (0..(1 << window_size))
|
||||||
|
.map(|_| Fr::random(&mut rng))
|
||||||
|
.collect();
|
||||||
|
|
||||||
synth::<Bls12, _>(window_size, &constants, &mut assignment);
|
synth::<Bls12, _>(window_size, &constants, &mut assignment);
|
||||||
|
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
use ff::{Field, PrimeField};
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
use pairing::Engine;
|
|
||||||
|
|
||||||
use bellman::{
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||||
SynthesisError,
|
|
||||||
ConstraintSystem,
|
|
||||||
LinearCombination,
|
|
||||||
Variable
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>>{
|
pub struct MultiEq<E: ScalarEngine, CS: ConstraintSystem<E>> {
|
||||||
cs: CS,
|
cs: CS,
|
||||||
ops: usize,
|
ops: usize,
|
||||||
bits_used: usize,
|
bits_used: usize,
|
||||||
@@ -16,19 +10,18 @@ pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>>{
|
|||||||
rhs: LinearCombination<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 {
|
pub fn new(cs: CS) -> Self {
|
||||||
MultiEq {
|
MultiEq {
|
||||||
cs: cs,
|
cs,
|
||||||
ops: 0,
|
ops: 0,
|
||||||
bits_used: 0,
|
bits_used: 0,
|
||||||
lhs: LinearCombination::zero(),
|
lhs: LinearCombination::zero(),
|
||||||
rhs: LinearCombination::zero()
|
rhs: LinearCombination::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accumulate(&mut self)
|
fn accumulate(&mut self) {
|
||||||
{
|
|
||||||
let ops = self.ops;
|
let ops = self.ops;
|
||||||
let lhs = self.lhs.clone();
|
let lhs = self.lhs.clone();
|
||||||
let rhs = self.rhs.clone();
|
let rhs = self.rhs.clone();
|
||||||
@@ -36,7 +29,7 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
|||||||
|| format!("multieq {}", ops),
|
|| format!("multieq {}", ops),
|
||||||
|_| lhs,
|
|_| lhs,
|
||||||
|lc| lc + CS::one(),
|
|lc| lc + CS::one(),
|
||||||
|_| rhs
|
|_| rhs,
|
||||||
);
|
);
|
||||||
self.lhs = LinearCombination::zero();
|
self.lhs = LinearCombination::zero();
|
||||||
self.rhs = LinearCombination::zero();
|
self.rhs = LinearCombination::zero();
|
||||||
@@ -48,9 +41,8 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
num_bits: usize,
|
num_bits: usize,
|
||||||
lhs: &LinearCombination<E>,
|
lhs: &LinearCombination<E>,
|
||||||
rhs: &LinearCombination<E>
|
rhs: &LinearCombination<E>,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
// Check if we will exceed the capacity
|
// Check if we will exceed the capacity
|
||||||
if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) {
|
if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) {
|
||||||
self.accumulate();
|
self.accumulate();
|
||||||
@@ -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) {
|
fn drop(&mut self) {
|
||||||
if self.bits_used > 0 {
|
if self.bits_used > 0 {
|
||||||
self.accumulate();
|
self.accumulate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS>
|
impl<E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> {
|
||||||
{
|
|
||||||
type Root = Self;
|
type Root = Self;
|
||||||
|
|
||||||
fn one() -> Variable {
|
fn one() -> Variable {
|
||||||
CS::one()
|
CS::one()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc<F, A, AR>(
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
self.cs.alloc(annotation, f)
|
self.cs.alloc(annotation, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_input<F, A, AR>(
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
self.cs.alloc_input(annotation, f)
|
self.cs.alloc_input(annotation, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce<A, AR, LA, LB, LC>(
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
A: FnOnce() -> AR,
|
||||||
a: LA,
|
AR: Into<String>,
|
||||||
b: LB,
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
c: LC
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
)
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
where A: FnOnce() -> AR, AR: Into<String>,
|
|
||||||
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
|
||||||
{
|
{
|
||||||
self.cs.enforce(annotation, a, b, c)
|
self.cs.enforce(annotation, a, b, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
{
|
{
|
||||||
self.cs.get_root().push_namespace(name_fn)
|
self.cs.get_root().push_namespace(name_fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
fn pop_namespace(&mut self) {
|
||||||
{
|
|
||||||
self.cs.get_root().pop_namespace()
|
self.cs.get_root().pop_namespace()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_root(&mut self) -> &mut Self::Root
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
{
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
111
bellman/src/gadgets/multipack.rs
Normal file
111
bellman/src/gadgets/multipack.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,78 +1,62 @@
|
|||||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr};
|
//! Gadgets representing numbers in the scalar field of the underlying curve.
|
||||||
use pairing::Engine;
|
|
||||||
|
|
||||||
use bellman::{
|
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||||
SynthesisError,
|
|
||||||
ConstraintSystem,
|
|
||||||
LinearCombination,
|
|
||||||
Variable
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||||
Assignment
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::boolean::{
|
use super::Assignment;
|
||||||
self,
|
|
||||||
Boolean,
|
|
||||||
AllocatedBit
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct AllocatedNum<E: Engine> {
|
use super::boolean::{self, AllocatedBit, Boolean};
|
||||||
|
|
||||||
|
pub struct AllocatedNum<E: ScalarEngine> {
|
||||||
value: Option<E::Fr>,
|
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 {
|
fn clone(&self) -> Self {
|
||||||
AllocatedNum {
|
AllocatedNum {
|
||||||
value: self.value,
|
value: self.value,
|
||||||
variable: self.variable
|
variable: self.variable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> AllocatedNum<E> {
|
impl<E: ScalarEngine> AllocatedNum<E> {
|
||||||
pub fn alloc<CS, F>(
|
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
|
||||||
mut cs: CS,
|
where
|
||||||
value: F,
|
CS: ConstraintSystem<E>,
|
||||||
) -> Result<Self, SynthesisError>
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
where CS: ConstraintSystem<E>,
|
|
||||||
F: FnOnce() -> Result<E::Fr, SynthesisError>
|
|
||||||
{
|
{
|
||||||
let mut new_value = None;
|
let mut new_value = None;
|
||||||
let var = cs.alloc(|| "num", || {
|
let var = cs.alloc(
|
||||||
let tmp = value()?;
|
|| "num",
|
||||||
|
|| {
|
||||||
|
let tmp = value()?;
|
||||||
|
|
||||||
new_value = Some(tmp);
|
new_value = Some(tmp);
|
||||||
|
|
||||||
Ok(tmp)
|
Ok(tmp)
|
||||||
})?;
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(AllocatedNum {
|
Ok(AllocatedNum {
|
||||||
value: new_value,
|
value: new_value,
|
||||||
variable: var
|
variable: var,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inputize<CS>(
|
pub fn inputize<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
|
||||||
&self,
|
where
|
||||||
mut cs: CS
|
CS: ConstraintSystem<E>,
|
||||||
) -> Result<(), SynthesisError>
|
|
||||||
where CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
let input = cs.alloc_input(
|
let input = cs.alloc_input(|| "input variable", || Ok(*self.value.get()?))?;
|
||||||
|| "input variable",
|
|
||||||
|| {
|
|
||||||
Ok(*self.value.get()?)
|
|
||||||
}
|
|
||||||
)?;
|
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "enforce input is correct",
|
|| "enforce input is correct",
|
||||||
|lc| lc + input,
|
|lc| lc + input,
|
||||||
|lc| lc + CS::one(),
|
|lc| lc + CS::one(),
|
||||||
|lc| lc + self.variable
|
|lc| lc + self.variable,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -83,20 +67,19 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
/// order, requiring that the representation
|
/// order, requiring that the representation
|
||||||
/// strictly exists "in the field" (i.e., a
|
/// strictly exists "in the field" (i.e., a
|
||||||
/// congruency is not allowed.)
|
/// congruency is not allowed.)
|
||||||
pub fn into_bits_le_strict<CS>(
|
pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
&self,
|
where
|
||||||
mut cs: CS
|
CS: ConstraintSystem<E>,
|
||||||
) -> Result<Vec<Boolean>, SynthesisError>
|
|
||||||
where CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
pub fn kary_and<E, CS>(
|
pub fn kary_and<E, CS>(
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
v: &[AllocatedBit]
|
v: &[AllocatedBit],
|
||||||
) -> Result<AllocatedBit, SynthesisError>
|
) -> Result<AllocatedBit, SynthesisError>
|
||||||
where E: Engine,
|
where
|
||||||
CS: ConstraintSystem<E>
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
assert!(v.len() > 0);
|
assert!(!v.is_empty());
|
||||||
|
|
||||||
// Let's keep this simple for now and just AND them all
|
// Let's keep this simple for now and just AND them all
|
||||||
// manually
|
// manually
|
||||||
@@ -109,7 +92,7 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
cur = Some(AllocatedBit::and(
|
cur = Some(AllocatedBit::and(
|
||||||
cs.namespace(|| format!("and {}", i)),
|
cs.namespace(|| format!("and {}", i)),
|
||||||
cur.as_ref().unwrap(),
|
cur.as_ref().unwrap(),
|
||||||
v
|
v,
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,15 +128,12 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
if b {
|
if b {
|
||||||
// This is part of a run of ones. Let's just
|
// This is part of a run of ones. Let's just
|
||||||
// allocate the boolean with the expected value.
|
// allocate the boolean with the expected value.
|
||||||
let a_bit = AllocatedBit::alloc(
|
let a_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), a_bit)?;
|
||||||
cs.namespace(|| format!("bit {}", i)),
|
|
||||||
a_bit
|
|
||||||
)?;
|
|
||||||
// ... and add it to the current run of ones.
|
// ... and add it to the current run of ones.
|
||||||
current_run.push(a_bit.clone());
|
current_run.push(a_bit.clone());
|
||||||
result.push(a_bit);
|
result.push(a_bit);
|
||||||
} else {
|
} else {
|
||||||
if current_run.len() > 0 {
|
if !current_run.is_empty() {
|
||||||
// This is the start of a run of zeros, but we need
|
// This is the start of a run of zeros, but we need
|
||||||
// to k-ary AND against `last_run` first.
|
// to k-ary AND against `last_run` first.
|
||||||
|
|
||||||
@@ -162,7 +142,7 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
}
|
}
|
||||||
last_run = Some(kary_and(
|
last_run = Some(kary_and(
|
||||||
cs.namespace(|| format!("run ending at {}", i)),
|
cs.namespace(|| format!("run ending at {}", i)),
|
||||||
¤t_run
|
¤t_run,
|
||||||
)?);
|
)?);
|
||||||
current_run.truncate(0);
|
current_run.truncate(0);
|
||||||
}
|
}
|
||||||
@@ -175,7 +155,7 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
let a_bit = AllocatedBit::alloc_conditionally(
|
let a_bit = AllocatedBit::alloc_conditionally(
|
||||||
cs.namespace(|| format!("bit {}", i)),
|
cs.namespace(|| format!("bit {}", i)),
|
||||||
a_bit,
|
a_bit,
|
||||||
&last_run.as_ref().expect("char always starts with a one")
|
&last_run.as_ref().expect("char always starts with a one"),
|
||||||
)?;
|
)?;
|
||||||
result.push(a_bit);
|
result.push(a_bit);
|
||||||
}
|
}
|
||||||
@@ -201,30 +181,20 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
|
|
||||||
lc = lc - self.variable;
|
lc = lc - self.variable;
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||||
|| "unpacking constraint",
|
|
||||||
|lc| lc,
|
|
||||||
|lc| lc,
|
|
||||||
|_| lc
|
|
||||||
);
|
|
||||||
|
|
||||||
// Convert into booleans, and reverse for little-endian bit order
|
// Convert into booleans, and reverse for little-endian bit order
|
||||||
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect())
|
Ok(result.into_iter().map(Boolean::from).rev().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the allocated number into its little-endian representation.
|
/// Convert the allocated number into its little-endian representation.
|
||||||
/// Note that this does not strongly enforce that the commitment is
|
/// Note that this does not strongly enforce that the commitment is
|
||||||
/// "in the field."
|
/// "in the field."
|
||||||
pub fn into_bits_le<CS>(
|
pub fn to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
&self,
|
where
|
||||||
mut cs: CS
|
CS: ConstraintSystem<E>,
|
||||||
) -> Result<Vec<Boolean>, SynthesisError>
|
|
||||||
where CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
let bits = boolean::field_into_allocated_bits_le(
|
let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?;
|
||||||
&mut cs,
|
|
||||||
self.value
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut lc = LinearCombination::zero();
|
let mut lc = LinearCombination::zero();
|
||||||
let mut coeff = E::Fr::one();
|
let mut coeff = E::Fr::one();
|
||||||
@@ -237,94 +207,91 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
|
|
||||||
lc = lc - self.variable;
|
lc = lc - self.variable;
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||||
|| "unpacking constraint",
|
|
||||||
|lc| lc,
|
|
||||||
|lc| lc,
|
|
||||||
|_| lc
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(bits.into_iter().map(|b| Boolean::from(b)).collect())
|
Ok(bits.into_iter().map(Boolean::from).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mul<CS>(
|
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||||
&self,
|
where
|
||||||
mut cs: CS,
|
CS: ConstraintSystem<E>,
|
||||||
other: &Self
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
let mut value = None;
|
let mut value = None;
|
||||||
|
|
||||||
let var = cs.alloc(|| "product num", || {
|
let var = cs.alloc(
|
||||||
let mut tmp = *self.value.get()?;
|
|| "product num",
|
||||||
tmp.mul_assign(other.value.get()?);
|
|| {
|
||||||
|
let mut tmp = *self.value.get()?;
|
||||||
|
tmp.mul_assign(other.value.get()?);
|
||||||
|
|
||||||
value = Some(tmp);
|
value = Some(tmp);
|
||||||
|
|
||||||
Ok(tmp)
|
Ok(tmp)
|
||||||
})?;
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
// Constrain: a * b = ab
|
// Constrain: a * b = ab
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "multiplication constraint",
|
|| "multiplication constraint",
|
||||||
|lc| lc + self.variable,
|
|lc| lc + self.variable,
|
||||||
|lc| lc + other.variable,
|
|lc| lc + other.variable,
|
||||||
|lc| lc + var
|
|lc| lc + var,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(AllocatedNum {
|
Ok(AllocatedNum {
|
||||||
value: value,
|
value,
|
||||||
variable: var
|
variable: var,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn square<CS>(
|
pub fn square<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError>
|
||||||
&self,
|
where
|
||||||
mut cs: CS
|
CS: ConstraintSystem<E>,
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
let mut value = None;
|
let mut value = None;
|
||||||
|
|
||||||
let var = cs.alloc(|| "squared num", || {
|
let var = cs.alloc(
|
||||||
let mut tmp = *self.value.get()?;
|
|| "squared num",
|
||||||
tmp.square();
|
|| {
|
||||||
|
let mut tmp = *self.value.get()?;
|
||||||
|
tmp.square();
|
||||||
|
|
||||||
value = Some(tmp);
|
value = Some(tmp);
|
||||||
|
|
||||||
Ok(tmp)
|
Ok(tmp)
|
||||||
})?;
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
// Constrain: a * a = aa
|
// Constrain: a * a = aa
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "squaring constraint",
|
|| "squaring constraint",
|
||||||
|lc| lc + self.variable,
|
|lc| lc + self.variable,
|
||||||
|lc| lc + self.variable,
|
|lc| lc + self.variable,
|
||||||
|lc| lc + var
|
|lc| lc + var,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(AllocatedNum {
|
Ok(AllocatedNum {
|
||||||
value: value,
|
value,
|
||||||
variable: var
|
variable: var,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_nonzero<CS>(
|
pub fn assert_nonzero<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
|
||||||
&self,
|
where
|
||||||
mut cs: CS
|
CS: ConstraintSystem<E>,
|
||||||
) -> Result<(), SynthesisError>
|
|
||||||
where CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
let inv = cs.alloc(|| "ephemeral inverse", || {
|
let inv = cs.alloc(
|
||||||
let tmp = *self.value.get()?;
|
|| "ephemeral inverse",
|
||||||
|
|| {
|
||||||
if tmp.is_zero() {
|
let tmp = *self.value.get()?;
|
||||||
Err(SynthesisError::DivisionByZero)
|
|
||||||
} else {
|
if tmp.is_zero() {
|
||||||
Ok(tmp.inverse().unwrap())
|
Err(SynthesisError::DivisionByZero)
|
||||||
}
|
} else {
|
||||||
})?;
|
Ok(tmp.inverse().unwrap())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
// Constrain a * inv = 1, which is only valid
|
// Constrain a * inv = 1, which is only valid
|
||||||
// iff a has a multiplicative inverse, untrue
|
// iff a has a multiplicative inverse, untrue
|
||||||
@@ -333,7 +300,7 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
|| "nonzero assertion constraint",
|
|| "nonzero assertion constraint",
|
||||||
|lc| lc + self.variable,
|
|lc| lc + self.variable,
|
||||||
|lc| lc + inv,
|
|lc| lc + inv,
|
||||||
|lc| lc + CS::one()
|
|lc| lc + CS::one(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -346,44 +313,39 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
a: &Self,
|
a: &Self,
|
||||||
b: &Self,
|
b: &Self,
|
||||||
condition: &Boolean
|
condition: &Boolean,
|
||||||
) -> Result<(Self, Self), SynthesisError>
|
) -> Result<(Self, Self), SynthesisError>
|
||||||
where CS: ConstraintSystem<E>
|
where
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let c = Self::alloc(
|
let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || {
|
||||||
cs.namespace(|| "conditional reversal result 1"),
|
if *condition.get_value().get()? {
|
||||||
|| {
|
Ok(*b.value.get()?)
|
||||||
if *condition.get_value().get()? {
|
} else {
|
||||||
Ok(*b.value.get()?)
|
Ok(*a.value.get()?)
|
||||||
} else {
|
|
||||||
Ok(*a.value.get()?)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)?;
|
})?;
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "first conditional reversal",
|
|| "first conditional reversal",
|
||||||
|lc| lc + a.variable - b.variable,
|
|lc| lc + a.variable - b.variable,
|
||||||
|_| condition.lc(CS::one(), E::Fr::one()),
|
|_| condition.lc(CS::one(), E::Fr::one()),
|
||||||
|lc| lc + a.variable - c.variable
|
|lc| lc + a.variable - c.variable,
|
||||||
);
|
);
|
||||||
|
|
||||||
let d = Self::alloc(
|
let d = Self::alloc(cs.namespace(|| "conditional reversal result 2"), || {
|
||||||
cs.namespace(|| "conditional reversal result 2"),
|
if *condition.get_value().get()? {
|
||||||
|| {
|
Ok(*a.value.get()?)
|
||||||
if *condition.get_value().get()? {
|
} else {
|
||||||
Ok(*a.value.get()?)
|
Ok(*b.value.get()?)
|
||||||
} else {
|
|
||||||
Ok(*b.value.get()?)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)?;
|
})?;
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "second conditional reversal",
|
|| "second conditional reversal",
|
||||||
|lc| lc + b.variable - a.variable,
|
|lc| lc + b.variable - a.variable,
|
||||||
|_| condition.lc(CS::one(), E::Fr::one()),
|
|_| condition.lc(CS::one(), E::Fr::one()),
|
||||||
|lc| lc + b.variable - d.variable
|
|lc| lc + b.variable - d.variable,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok((c, d))
|
Ok((c, d))
|
||||||
@@ -398,25 +360,25 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Num<E: Engine> {
|
pub struct Num<E: ScalarEngine> {
|
||||||
value: Option<E::Fr>,
|
value: Option<E::Fr>,
|
||||||
lc: LinearCombination<E>
|
lc: LinearCombination<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
|
impl<E: ScalarEngine> From<AllocatedNum<E>> for Num<E> {
|
||||||
fn from(num: AllocatedNum<E>) -> Num<E> {
|
fn from(num: AllocatedNum<E>) -> Num<E> {
|
||||||
Num {
|
Num {
|
||||||
value: num.value,
|
value: num.value,
|
||||||
lc: LinearCombination::<E>::zero() + num.variable
|
lc: LinearCombination::<E>::zero() + num.variable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Num<E> {
|
impl<E: ScalarEngine> Num<E> {
|
||||||
pub fn zero() -> Self {
|
pub fn zero() -> Self {
|
||||||
Num {
|
Num {
|
||||||
value: Some(E::Fr::zero()),
|
value: Some(E::Fr::zero()),
|
||||||
lc: LinearCombination::zero()
|
lc: LinearCombination::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,13 +390,7 @@ impl<E: Engine> Num<E> {
|
|||||||
LinearCombination::zero() + (coeff, &self.lc)
|
LinearCombination::zero() + (coeff, &self.lc)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_bool_with_coeff(
|
pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: E::Fr) -> Self {
|
||||||
self,
|
|
||||||
one: Variable,
|
|
||||||
bit: &Boolean,
|
|
||||||
coeff: E::Fr
|
|
||||||
) -> Self
|
|
||||||
{
|
|
||||||
let newval = match (self.value, bit.get_value()) {
|
let newval = match (self.value, bit.get_value()) {
|
||||||
(Some(mut curval), Some(bval)) => {
|
(Some(mut curval), Some(bval)) => {
|
||||||
if bval {
|
if bval {
|
||||||
@@ -442,25 +398,27 @@ impl<E: Engine> Num<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Some(curval)
|
Some(curval)
|
||||||
},
|
}
|
||||||
_ => None
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Num {
|
Num {
|
||||||
value: newval,
|
value: newval,
|
||||||
lc: self.lc + &bit.lc(one, coeff)
|
lc: self.lc + &bit.lc(one, coeff),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use rand::{SeedableRng, Rand, Rng, XorShiftRng};
|
use crate::ConstraintSystem;
|
||||||
use bellman::{ConstraintSystem};
|
|
||||||
use ff::{BitIterator, Field, PrimeField};
|
use ff::{BitIterator, Field, PrimeField};
|
||||||
use pairing::bls12_381::{Bls12, Fr};
|
use pairing::bls12_381::{Bls12, Fr};
|
||||||
use ::circuit::test::*;
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use super::{AllocatedNum, Boolean};
|
use super::{AllocatedNum, Boolean};
|
||||||
|
use crate::gadgets::test::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_allocated_num() {
|
fn test_allocated_num() {
|
||||||
@@ -489,8 +447,10 @@ mod test {
|
|||||||
fn test_num_multiplication() {
|
fn test_num_multiplication() {
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap();
|
let n =
|
||||||
let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap();
|
AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap();
|
||||||
|
let n2 =
|
||||||
|
AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap();
|
||||||
let n3 = n.mul(&mut cs, &n2).unwrap();
|
let n3 = n.mul(&mut cs, &n2).unwrap();
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
@@ -502,12 +462,15 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_num_conditional_reversal() {
|
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 mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || 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(rng.gen())).unwrap();
|
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
|
||||||
let condition = Boolean::constant(false);
|
let condition = Boolean::constant(false);
|
||||||
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
|
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
|
||||||
|
|
||||||
@@ -520,8 +483,8 @@ mod test {
|
|||||||
{
|
{
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || 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(rng.gen())).unwrap();
|
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
|
||||||
let condition = Boolean::constant(true);
|
let condition = Boolean::constant(true);
|
||||||
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
|
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
|
||||||
|
|
||||||
@@ -560,7 +523,7 @@ mod test {
|
|||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
|
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
|
||||||
n.into_bits_le_strict(&mut cs).unwrap();
|
n.to_bits_le_strict(&mut cs).unwrap();
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
@@ -568,28 +531,37 @@ mod test {
|
|||||||
cs.set("bit 254/boolean", Fr::one());
|
cs.set("bit 254/boolean", Fr::one());
|
||||||
|
|
||||||
// this makes the conditional boolean constraint fail
|
// this makes the conditional boolean constraint fail
|
||||||
assert_eq!(cs.which_is_unsatisfied().unwrap(), "bit 254/boolean constraint");
|
assert_eq!(
|
||||||
|
cs.which_is_unsatisfied().unwrap(),
|
||||||
|
"bit 254/boolean constraint"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_into_bits() {
|
fn test_into_bits() {
|
||||||
let mut rng = XorShiftRng::from_seed([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 {
|
for i in 0..200 {
|
||||||
let r = Fr::rand(&mut rng);
|
let r = Fr::random(&mut rng);
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
|
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
|
||||||
|
|
||||||
let bits = if i % 2 == 0 {
|
let bits = if i % 2 == 0 {
|
||||||
n.into_bits_le(&mut cs).unwrap()
|
n.to_bits_le(&mut cs).unwrap()
|
||||||
} else {
|
} else {
|
||||||
n.into_bits_le_strict(&mut cs).unwrap()
|
n.to_bits_le_strict(&mut cs).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
for (b, a) in BitIterator::new(r.into_repr()).skip(1).zip(bits.iter().rev()) {
|
for (b, a) in BitIterator::new(r.into_repr())
|
||||||
|
.skip(1)
|
||||||
|
.zip(bits.iter().rev())
|
||||||
|
{
|
||||||
if let &Boolean::Is(ref a) = a {
|
if let &Boolean::Is(ref a) = a {
|
||||||
assert_eq!(b, a.get_value().unwrap());
|
assert_eq!(b, a.get_value().unwrap());
|
||||||
} else {
|
} else {
|
||||||
@@ -597,7 +569,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs.set("num", Fr::rand(&mut rng));
|
cs.set("num", Fr::random(&mut rng));
|
||||||
assert!(!cs.is_satisfied());
|
assert!(!cs.is_satisfied());
|
||||||
cs.set("num", r);
|
cs.set("num", r);
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
use super::uint32::UInt32;
|
//! Circuits for the [SHA-256] hash function and its internal compression
|
||||||
use super::multieq::MultiEq;
|
//! function.
|
||||||
use super::boolean::Boolean;
|
//!
|
||||||
use bellman::{ConstraintSystem, SynthesisError};
|
//! [SHA-256]: https://tools.ietf.org/html/rfc6234
|
||||||
use pairing::Engine;
|
|
||||||
|
|
||||||
|
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] = [
|
const ROUND_CONSTANTS: [u32; 64] = [
|
||||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
@@ -12,37 +18,36 @@ const ROUND_CONSTANTS: [u32; 64] = [
|
|||||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[allow(clippy::unreadable_literal)]
|
||||||
const IV: [u32; 8] = [
|
const IV: [u32; 8] = [
|
||||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn sha256_block_no_padding<E, CS>(
|
pub fn sha256_block_no_padding<E, CS>(
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
input: &[Boolean]
|
input: &[Boolean],
|
||||||
) -> Result<Vec<Boolean>, SynthesisError>
|
) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
where E: Engine, CS: ConstraintSystem<E>
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
assert_eq!(input.len(), 512);
|
assert_eq!(input.len(), 512);
|
||||||
|
|
||||||
Ok(sha256_compression_function(
|
Ok(
|
||||||
&mut cs,
|
sha256_compression_function(&mut cs, &input, &get_sha256_iv())?
|
||||||
&input,
|
.into_iter()
|
||||||
&get_sha256_iv()
|
.flat_map(|e| e.into_bits_be())
|
||||||
)?
|
.collect(),
|
||||||
.into_iter()
|
)
|
||||||
.flat_map(|e| e.into_bits_be())
|
|
||||||
.collect())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha256<E, CS>(
|
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
mut cs: CS,
|
where
|
||||||
input: &[Boolean]
|
E: ScalarEngine,
|
||||||
) -> Result<Vec<Boolean>, SynthesisError>
|
CS: ConstraintSystem<E>,
|
||||||
where E: Engine, CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
assert!(input.len() % 8 == 0);
|
assert!(input.len() % 8 == 0);
|
||||||
|
|
||||||
@@ -62,16 +67,10 @@ pub fn sha256<E, CS>(
|
|||||||
|
|
||||||
let mut cur = get_sha256_iv();
|
let mut cur = get_sha256_iv();
|
||||||
for (i, block) in padded.chunks(512).enumerate() {
|
for (i, block) in padded.chunks(512).enumerate() {
|
||||||
cur = sha256_compression_function(
|
cur = sha256_compression_function(cs.namespace(|| format!("block {}", i)), block, &cur)?;
|
||||||
cs.namespace(|| format!("block {}", i)),
|
|
||||||
block,
|
|
||||||
&cur
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(cur.into_iter()
|
Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect())
|
||||||
.flat_map(|e| e.into_bits_be())
|
|
||||||
.collect())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sha256_iv() -> Vec<UInt32> {
|
fn get_sha256_iv() -> Vec<UInt32> {
|
||||||
@@ -81,16 +80,19 @@ fn get_sha256_iv() -> Vec<UInt32> {
|
|||||||
fn sha256_compression_function<E, CS>(
|
fn sha256_compression_function<E, CS>(
|
||||||
cs: CS,
|
cs: CS,
|
||||||
input: &[Boolean],
|
input: &[Boolean],
|
||||||
current_hash_value: &[UInt32]
|
current_hash_value: &[UInt32],
|
||||||
) -> Result<Vec<UInt32>, SynthesisError>
|
) -> Result<Vec<UInt32>, SynthesisError>
|
||||||
where E: Engine, CS: ConstraintSystem<E>
|
where
|
||||||
|
E: ScalarEngine,
|
||||||
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
assert_eq!(input.len(), 512);
|
assert_eq!(input.len(), 512);
|
||||||
assert_eq!(current_hash_value.len(), 8);
|
assert_eq!(current_hash_value.len(), 8);
|
||||||
|
|
||||||
let mut w = input.chunks(32)
|
let mut w = input
|
||||||
.map(|e| UInt32::from_bits_be(e))
|
.chunks(32)
|
||||||
.collect::<Vec<_>>();
|
.map(|e| UInt32::from_bits_be(e))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// We can save some constraints by combining some of
|
// We can save some constraints by combining some of
|
||||||
// the constraints in different u32 additions
|
// the constraints in different u32 additions
|
||||||
@@ -100,30 +102,18 @@ fn sha256_compression_function<E, CS>(
|
|||||||
let cs = &mut cs.namespace(|| format!("w extension {}", i));
|
let cs = &mut cs.namespace(|| format!("w extension {}", i));
|
||||||
|
|
||||||
// s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
|
// s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
|
||||||
let mut s0 = w[i-15].rotr(7);
|
let mut s0 = w[i - 15].rotr(7);
|
||||||
s0 = s0.xor(
|
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?;
|
||||||
cs.namespace(|| "first xor for s0"),
|
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?;
|
||||||
&w[i-15].rotr(18)
|
|
||||||
)?;
|
|
||||||
s0 = s0.xor(
|
|
||||||
cs.namespace(|| "second xor for s0"),
|
|
||||||
&w[i-15].shr(3)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
|
// s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
|
||||||
let mut s1 = w[i-2].rotr(17);
|
let mut s1 = w[i - 2].rotr(17);
|
||||||
s1 = s1.xor(
|
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?;
|
||||||
cs.namespace(|| "first xor for s1"),
|
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?;
|
||||||
&w[i-2].rotr(19)
|
|
||||||
)?;
|
|
||||||
s1 = s1.xor(
|
|
||||||
cs.namespace(|| "second xor for s1"),
|
|
||||||
&w[i-2].shr(10)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let tmp = UInt32::addmany(
|
let tmp = UInt32::addmany(
|
||||||
cs.namespace(|| "computation of w[i]"),
|
cs.namespace(|| "computation of w[i]"),
|
||||||
&[w[i-16].clone(), s0, w[i-7].clone(), s1]
|
&[w[i - 16].clone(), s0, w[i - 7].clone(), s1],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// w[i] := w[i-16] + s0 + w[i-7] + s1
|
// w[i] := w[i-16] + s0 + w[i-7] + s1
|
||||||
@@ -134,29 +124,21 @@ fn sha256_compression_function<E, CS>(
|
|||||||
|
|
||||||
enum Maybe {
|
enum Maybe {
|
||||||
Deferred(Vec<UInt32>),
|
Deferred(Vec<UInt32>),
|
||||||
Concrete(UInt32)
|
Concrete(UInt32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Maybe {
|
impl Maybe {
|
||||||
fn compute<E, CS, M>(
|
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
|
||||||
self,
|
where
|
||||||
cs: M,
|
E: ScalarEngine,
|
||||||
others: &[UInt32]
|
CS: ConstraintSystem<E>,
|
||||||
) -> Result<UInt32, SynthesisError>
|
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||||
where E: Engine,
|
|
||||||
CS: ConstraintSystem<E>,
|
|
||||||
M: ConstraintSystem<E, Root=MultiEq<E, CS>>
|
|
||||||
{
|
{
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Maybe::Concrete(ref v) => {
|
Maybe::Concrete(ref v) => return Ok(v.clone()),
|
||||||
return Ok(v.clone())
|
|
||||||
},
|
|
||||||
Maybe::Deferred(mut v) => {
|
Maybe::Deferred(mut v) => {
|
||||||
v.extend(others.into_iter().cloned());
|
v.extend(others.iter().cloned());
|
||||||
UInt32::addmany(
|
UInt32::addmany(cs, &v)?
|
||||||
cs,
|
|
||||||
&v
|
|
||||||
)?
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -177,22 +159,11 @@ fn sha256_compression_function<E, CS>(
|
|||||||
// S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
|
// S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
|
||||||
let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?;
|
let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?;
|
||||||
let mut s1 = new_e.rotr(6);
|
let mut s1 = new_e.rotr(6);
|
||||||
s1 = s1.xor(
|
s1 = s1.xor(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?;
|
||||||
cs.namespace(|| "first xor for s1"),
|
s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?;
|
||||||
&new_e.rotr(11)
|
|
||||||
)?;
|
|
||||||
s1 = s1.xor(
|
|
||||||
cs.namespace(|| "second xor for s1"),
|
|
||||||
&new_e.rotr(25)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// ch := (e and f) xor ((not e) and g)
|
// ch := (e and f) xor ((not e) and g)
|
||||||
let ch = UInt32::sha256_ch(
|
let ch = UInt32::sha256_ch(cs.namespace(|| "ch"), &new_e, &f, &g)?;
|
||||||
cs.namespace(|| "ch"),
|
|
||||||
&new_e,
|
|
||||||
&f,
|
|
||||||
&g
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// temp1 := h + S1 + ch + k[i] + w[i]
|
// temp1 := h + S1 + ch + k[i] + w[i]
|
||||||
let temp1 = vec![
|
let temp1 = vec![
|
||||||
@@ -200,28 +171,17 @@ fn sha256_compression_function<E, CS>(
|
|||||||
s1,
|
s1,
|
||||||
ch,
|
ch,
|
||||||
UInt32::constant(ROUND_CONSTANTS[i]),
|
UInt32::constant(ROUND_CONSTANTS[i]),
|
||||||
w[i].clone()
|
w[i].clone(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
|
// S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
|
||||||
let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?;
|
let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?;
|
||||||
let mut s0 = new_a.rotr(2);
|
let mut s0 = new_a.rotr(2);
|
||||||
s0 = s0.xor(
|
s0 = s0.xor(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?;
|
||||||
cs.namespace(|| "first xor for s0"),
|
s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?;
|
||||||
&new_a.rotr(13)
|
|
||||||
)?;
|
|
||||||
s0 = s0.xor(
|
|
||||||
cs.namespace(|| "second xor for s0"),
|
|
||||||
&new_a.rotr(22)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// maj := (a and b) xor (a and c) xor (b and c)
|
// maj := (a and b) xor (a and c) xor (b and c)
|
||||||
let maj = UInt32::sha256_maj(
|
let maj = UInt32::sha256_maj(cs.namespace(|| "maj"), &new_a, &b, &c)?;
|
||||||
cs.namespace(|| "maj"),
|
|
||||||
&new_a,
|
|
||||||
&b,
|
|
||||||
&c
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// temp2 := S0 + maj
|
// temp2 := S0 + maj
|
||||||
let temp2 = vec![s0, maj];
|
let temp2 = vec![s0, maj];
|
||||||
@@ -244,7 +204,13 @@ fn sha256_compression_function<E, CS>(
|
|||||||
d = c;
|
d = c;
|
||||||
c = b;
|
c = b;
|
||||||
b = new_a;
|
b = new_a;
|
||||||
a = Maybe::Deferred(temp1.iter().cloned().chain(temp2.iter().cloned()).collect::<Vec<_>>());
|
a = Maybe::Deferred(
|
||||||
|
temp1
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.chain(temp2.iter().cloned())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -261,42 +227,42 @@ fn sha256_compression_function<E, CS>(
|
|||||||
|
|
||||||
let h0 = a.compute(
|
let h0 = a.compute(
|
||||||
cs.namespace(|| "deferred h0 computation"),
|
cs.namespace(|| "deferred h0 computation"),
|
||||||
&[current_hash_value[0].clone()]
|
&[current_hash_value[0].clone()],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let h1 = UInt32::addmany(
|
let h1 = UInt32::addmany(
|
||||||
cs.namespace(|| "new h1"),
|
cs.namespace(|| "new h1"),
|
||||||
&[current_hash_value[1].clone(), b]
|
&[current_hash_value[1].clone(), b],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let h2 = UInt32::addmany(
|
let h2 = UInt32::addmany(
|
||||||
cs.namespace(|| "new h2"),
|
cs.namespace(|| "new h2"),
|
||||||
&[current_hash_value[2].clone(), c]
|
&[current_hash_value[2].clone(), c],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let h3 = UInt32::addmany(
|
let h3 = UInt32::addmany(
|
||||||
cs.namespace(|| "new h3"),
|
cs.namespace(|| "new h3"),
|
||||||
&[current_hash_value[3].clone(), d]
|
&[current_hash_value[3].clone(), d],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let h4 = e.compute(
|
let h4 = e.compute(
|
||||||
cs.namespace(|| "deferred h4 computation"),
|
cs.namespace(|| "deferred h4 computation"),
|
||||||
&[current_hash_value[4].clone()]
|
&[current_hash_value[4].clone()],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let h5 = UInt32::addmany(
|
let h5 = UInt32::addmany(
|
||||||
cs.namespace(|| "new h5"),
|
cs.namespace(|| "new h5"),
|
||||||
&[current_hash_value[5].clone(), f]
|
&[current_hash_value[5].clone(), f],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let h6 = UInt32::addmany(
|
let h6 = UInt32::addmany(
|
||||||
cs.namespace(|| "new h6"),
|
cs.namespace(|| "new h6"),
|
||||||
&[current_hash_value[6].clone(), g]
|
&[current_hash_value[6].clone(), g],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let h7 = UInt32::addmany(
|
let h7 = UInt32::addmany(
|
||||||
cs.namespace(|| "new h7"),
|
cs.namespace(|| "new h7"),
|
||||||
&[current_hash_value[7].clone(), h]
|
&[current_hash_value[7].clone(), h],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7])
|
Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7])
|
||||||
@@ -305,10 +271,12 @@ fn sha256_compression_function<E, CS>(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
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 pairing::bls12_381::Bls12;
|
||||||
use circuit::test::TestConstraintSystem;
|
use rand_core::{RngCore, SeedableRng};
|
||||||
use rand::{XorShiftRng, SeedableRng, Rng};
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_blank_hash() {
|
fn test_blank_hash() {
|
||||||
@@ -317,11 +285,7 @@ mod test {
|
|||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect();
|
let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect();
|
||||||
input_bits[0] = Boolean::Constant(true);
|
input_bits[0] = Boolean::Constant(true);
|
||||||
let out = sha256_compression_function(
|
let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap();
|
||||||
&mut cs,
|
|
||||||
&input_bits,
|
|
||||||
&iv
|
|
||||||
).unwrap();
|
|
||||||
let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect();
|
let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect();
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
@@ -330,7 +294,7 @@ mod test {
|
|||||||
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
||||||
|
|
||||||
let mut out = out_bits.into_iter();
|
let mut out = out_bits.into_iter();
|
||||||
for b in expected.into_iter() {
|
for b in expected.iter() {
|
||||||
for i in (0..8).rev() {
|
for i in (0..8).rev() {
|
||||||
let c = out.next().unwrap().get_value().unwrap();
|
let c = out.next().unwrap().get_value().unwrap();
|
||||||
|
|
||||||
@@ -341,25 +305,27 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_full_block() {
|
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 iv = get_sha256_iv();
|
||||||
|
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
let input_bits: Vec<_> = (0..512).map(|i| {
|
let input_bits: Vec<_> = (0..512)
|
||||||
Boolean::from(
|
.map(|i| {
|
||||||
AllocatedBit::alloc(
|
Boolean::from(
|
||||||
cs.namespace(|| format!("input bit {}", i)),
|
AllocatedBit::alloc(
|
||||||
Some(rng.gen())
|
cs.namespace(|| format!("input bit {}", i)),
|
||||||
).unwrap()
|
Some(rng.next_u32() % 2 != 0),
|
||||||
)
|
)
|
||||||
}).collect();
|
.unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
sha256_compression_function(
|
sha256_compression_function(cs.namespace(|| "sha256"), &input_bits, &iv).unwrap();
|
||||||
cs.namespace(|| "sha256"),
|
|
||||||
&input_bits,
|
|
||||||
&iv
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
assert_eq!(cs.num_constraints() - 512, 25840);
|
assert_eq!(cs.num_constraints() - 512, 25840);
|
||||||
@@ -367,18 +333,18 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_against_vectors() {
|
fn test_against_vectors() {
|
||||||
use crypto::sha2::Sha256;
|
use sha2::{Digest, Sha256};
|
||||||
use crypto::digest::Digest;
|
|
||||||
|
|
||||||
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 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);
|
h.input(&data);
|
||||||
let mut hash_result = [0u8; 32];
|
let hash_result = h.result();
|
||||||
h.result(&mut hash_result[..]);
|
|
||||||
|
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
let mut input_bits = vec![];
|
let mut input_bits = vec![];
|
||||||
@@ -387,7 +353,11 @@ mod test {
|
|||||||
for bit_i in (0..8).rev() {
|
for bit_i in (0..8).rev() {
|
||||||
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
|
||||||
|
|
||||||
input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into());
|
input_bits.push(
|
||||||
|
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,17 +365,19 @@ mod test {
|
|||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
let mut s = hash_result.as_ref().iter()
|
let mut s = hash_result
|
||||||
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
|
.as_ref()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
|
||||||
|
|
||||||
for b in r {
|
for b in r {
|
||||||
match b {
|
match b {
|
||||||
Boolean::Is(b) => {
|
Boolean::Is(b) => {
|
||||||
assert!(s.next().unwrap() == b.get_value().unwrap());
|
assert!(s.next().unwrap() == b.get_value().unwrap());
|
||||||
},
|
}
|
||||||
Boolean::Not(b) => {
|
Boolean::Not(b) => {
|
||||||
assert!(s.next().unwrap() != b.get_value().unwrap());
|
assert!(s.next().unwrap() != b.get_value().unwrap());
|
||||||
},
|
}
|
||||||
Boolean::Constant(b) => {
|
Boolean::Constant(b) => {
|
||||||
assert!(input_len == 0);
|
assert!(input_len == 0);
|
||||||
assert!(s.next().unwrap() == b);
|
assert!(s.next().unwrap() == b);
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
//! Helpers for testing circuit implementations.
|
||||||
use pairing::Engine;
|
|
||||||
|
|
||||||
use bellman::{
|
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||||
LinearCombination,
|
|
||||||
SynthesisError,
|
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||||
ConstraintSystem,
|
|
||||||
Variable,
|
|
||||||
Index
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
@@ -16,27 +11,27 @@ use byteorder::{BigEndian, ByteOrder};
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use blake2_rfc::blake2s::Blake2s;
|
use blake2s_simd::{Params as Blake2sParams, State as Blake2sState};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum NamedObject {
|
enum NamedObject {
|
||||||
Constraint(usize),
|
Constraint(usize),
|
||||||
Var(Variable),
|
Var(Variable),
|
||||||
Namespace
|
Namespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constraint system for testing purposes.
|
/// Constraint system for testing purposes.
|
||||||
pub struct TestConstraintSystem<E: Engine> {
|
pub struct TestConstraintSystem<E: ScalarEngine> {
|
||||||
named_objects: HashMap<String, NamedObject>,
|
named_objects: HashMap<String, NamedObject>,
|
||||||
current_namespace: Vec<String>,
|
current_namespace: Vec<String>,
|
||||||
constraints: Vec<(
|
constraints: Vec<(
|
||||||
LinearCombination<E>,
|
LinearCombination<E>,
|
||||||
LinearCombination<E>,
|
LinearCombination<E>,
|
||||||
LinearCombination<E>,
|
LinearCombination<E>,
|
||||||
String
|
String,
|
||||||
)>,
|
)>,
|
||||||
inputs: Vec<(E::Fr, String)>,
|
inputs: Vec<(E::Fr, String)>,
|
||||||
aux: Vec<(E::Fr, String)>
|
aux: Vec<(E::Fr, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@@ -48,7 +43,7 @@ impl PartialEq for OrderedVariable {
|
|||||||
match (self.0.get_unchecked(), other.0.get_unchecked()) {
|
match (self.0.get_unchecked(), other.0.get_unchecked()) {
|
||||||
(Index::Input(ref a), Index::Input(ref b)) => a == b,
|
(Index::Input(ref a), Index::Input(ref b)) => a == b,
|
||||||
(Index::Aux(ref a), Index::Aux(ref b)) => a == b,
|
(Index::Aux(ref a), Index::Aux(ref b)) => a == b,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,20 +58,17 @@ impl Ord for OrderedVariable {
|
|||||||
(Index::Input(ref a), Index::Input(ref b)) => a.cmp(b),
|
(Index::Input(ref a), Index::Input(ref b)) => a.cmp(b),
|
||||||
(Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b),
|
(Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b),
|
||||||
(Index::Input(_), Index::Aux(_)) => Ordering::Less,
|
(Index::Input(_), Index::Aux(_)) => Ordering::Less,
|
||||||
(Index::Aux(_), Index::Input(_)) => Ordering::Greater
|
(Index::Aux(_), Index::Input(_)) => Ordering::Greater,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proc_lc<E: Engine>(
|
fn proc_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
|
||||||
terms: &[(Variable, E::Fr)],
|
|
||||||
) -> BTreeMap<OrderedVariable, E::Fr>
|
|
||||||
{
|
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
for &(var, coeff) in terms {
|
for &(var, coeff) in terms {
|
||||||
map.entry(OrderedVariable(var))
|
map.entry(OrderedVariable(var))
|
||||||
.or_insert(E::Fr::zero())
|
.or_insert_with(E::Fr::zero)
|
||||||
.add_assign(&coeff);
|
.add_assign(&coeff);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove terms that have a zero coefficient to normalize
|
// Remove terms that have a zero coefficient to normalize
|
||||||
@@ -94,11 +86,7 @@ fn proc_lc<E: Engine>(
|
|||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_lc<E: Engine>(
|
fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
|
||||||
terms: &[(Variable, E::Fr)],
|
|
||||||
h: &mut Blake2s
|
|
||||||
)
|
|
||||||
{
|
|
||||||
let map = proc_lc::<E>(terms);
|
let map = proc_lc::<E>(terms);
|
||||||
|
|
||||||
let mut buf = [0u8; 9 + 32];
|
let mut buf = [0u8; 9 + 32];
|
||||||
@@ -110,31 +98,30 @@ fn hash_lc<E: Engine>(
|
|||||||
Index::Input(i) => {
|
Index::Input(i) => {
|
||||||
buf[0] = b'I';
|
buf[0] = b'I';
|
||||||
BigEndian::write_u64(&mut buf[1..9], i as u64);
|
BigEndian::write_u64(&mut buf[1..9], i as u64);
|
||||||
},
|
}
|
||||||
Index::Aux(i) => {
|
Index::Aux(i) => {
|
||||||
buf[0] = b'A';
|
buf[0] = b'A';
|
||||||
BigEndian::write_u64(&mut buf[1..9], i as u64);
|
BigEndian::write_u64(&mut buf[1..9], i as u64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coeff.into_repr().write_be(&mut buf[9..]).unwrap();
|
coeff.into_repr().write_be(&mut buf[9..]).unwrap();
|
||||||
|
|
||||||
h.update(&buf);
|
h.update(&buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_lc<E: Engine>(
|
fn eval_lc<E: ScalarEngine>(
|
||||||
terms: &[(Variable, E::Fr)],
|
terms: &[(Variable, E::Fr)],
|
||||||
inputs: &[(E::Fr, String)],
|
inputs: &[(E::Fr, String)],
|
||||||
aux: &[(E::Fr, String)]
|
aux: &[(E::Fr, String)],
|
||||||
) -> E::Fr
|
) -> E::Fr {
|
||||||
{
|
|
||||||
let mut acc = E::Fr::zero();
|
let mut acc = E::Fr::zero();
|
||||||
|
|
||||||
for &(var, ref coeff) in terms {
|
for &(var, ref coeff) in terms {
|
||||||
let mut tmp = match var.get_unchecked() {
|
let mut tmp = match var.get_unchecked() {
|
||||||
Index::Input(index) => inputs[index].0,
|
Index::Input(index) => inputs[index].0,
|
||||||
Index::Aux(index) => aux[index].0
|
Index::Aux(index) => aux[index].0,
|
||||||
};
|
};
|
||||||
|
|
||||||
tmp.mul_assign(&coeff);
|
tmp.mul_assign(&coeff);
|
||||||
@@ -144,17 +131,20 @@ fn eval_lc<E: Engine>(
|
|||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> TestConstraintSystem<E> {
|
impl<E: ScalarEngine> TestConstraintSystem<E> {
|
||||||
pub fn new() -> TestConstraintSystem<E> {
|
pub fn new() -> TestConstraintSystem<E> {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("ONE".into(), NamedObject::Var(TestConstraintSystem::<E>::one()));
|
map.insert(
|
||||||
|
"ONE".into(),
|
||||||
|
NamedObject::Var(TestConstraintSystem::<E>::one()),
|
||||||
|
);
|
||||||
|
|
||||||
TestConstraintSystem {
|
TestConstraintSystem {
|
||||||
named_objects: map,
|
named_objects: map,
|
||||||
current_namespace: vec![],
|
current_namespace: vec![],
|
||||||
constraints: vec![],
|
constraints: vec![],
|
||||||
inputs: vec![(E::Fr::one(), "ONE".into())],
|
inputs: vec![(E::Fr::one(), "ONE".into())],
|
||||||
aux: vec![]
|
aux: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,9 +157,9 @@ impl<E: Engine> TestConstraintSystem<E> {
|
|||||||
tmp
|
tmp
|
||||||
};
|
};
|
||||||
|
|
||||||
let powers_of_two = (0..E::Fr::NUM_BITS).map(|i| {
|
let powers_of_two = (0..E::Fr::NUM_BITS)
|
||||||
E::Fr::from_str("2").unwrap().pow(&[i as u64])
|
.map(|i| E::Fr::from_str("2").unwrap().pow(&[u64::from(i)]))
|
||||||
}).collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let pp = |s: &mut String, lc: &LinearCombination<E>| {
|
let pp = |s: &mut String, lc: &LinearCombination<E>| {
|
||||||
write!(s, "(").unwrap();
|
write!(s, "(").unwrap();
|
||||||
@@ -196,7 +186,7 @@ impl<E: Engine> TestConstraintSystem<E> {
|
|||||||
match var.0.get_unchecked() {
|
match var.0.get_unchecked() {
|
||||||
Index::Input(i) => {
|
Index::Input(i) => {
|
||||||
write!(s, "`{}`", &self.inputs[i].1).unwrap();
|
write!(s, "`{}`", &self.inputs[i].1).unwrap();
|
||||||
},
|
}
|
||||||
Index::Aux(i) => {
|
Index::Aux(i) => {
|
||||||
write!(s, "`{}`", &self.aux[i].1).unwrap();
|
write!(s, "`{}`", &self.aux[i].1).unwrap();
|
||||||
}
|
}
|
||||||
@@ -226,7 +216,7 @@ impl<E: Engine> TestConstraintSystem<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash(&self) -> String {
|
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];
|
let mut buf = [0u8; 24];
|
||||||
|
|
||||||
@@ -259,57 +249,52 @@ impl<E: Engine> TestConstraintSystem<E> {
|
|||||||
a.mul_assign(&b);
|
a.mul_assign(&b);
|
||||||
|
|
||||||
if a != c {
|
if a != c {
|
||||||
return Some(&*path)
|
return Some(&*path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_satisfied(&self) -> bool
|
pub fn is_satisfied(&self) -> bool {
|
||||||
{
|
|
||||||
self.which_is_unsatisfied().is_none()
|
self.which_is_unsatisfied().is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_constraints(&self) -> usize
|
pub fn num_constraints(&self) -> usize {
|
||||||
{
|
|
||||||
self.constraints.len()
|
self.constraints.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, path: &str, to: E::Fr)
|
pub fn set(&mut self, path: &str, to: E::Fr) {
|
||||||
{
|
|
||||||
match self.named_objects.get(path) {
|
match self.named_objects.get(path) {
|
||||||
Some(&NamedObject::Var(ref v)) => {
|
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
|
||||||
match v.get_unchecked() {
|
Index::Input(index) => self.inputs[index].0 = to,
|
||||||
Index::Input(index) => self.inputs[index].0 = to,
|
Index::Aux(index) => self.aux[index].0 = to,
|
||||||
Index::Aux(index) => self.aux[index].0 = to
|
},
|
||||||
}
|
Some(e) => panic!(
|
||||||
}
|
"tried to set path `{}` to value, but `{:?}` already exists there.",
|
||||||
Some(e) => panic!("tried to set path `{}` to value, but `{:?}` already exists there.", path, e),
|
path, e
|
||||||
_ => panic!("no variable exists at path: {}", path)
|
),
|
||||||
|
_ => panic!("no variable exists at path: {}", path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(&self, expected: &[E::Fr]) -> bool
|
pub fn verify(&self, expected: &[E::Fr]) -> bool {
|
||||||
{
|
|
||||||
assert_eq!(expected.len() + 1, self.inputs.len());
|
assert_eq!(expected.len() + 1, self.inputs.len());
|
||||||
|
|
||||||
for (a, b) in self.inputs.iter().skip(1).zip(expected.iter())
|
for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) {
|
||||||
{
|
|
||||||
if &a.0 != b {
|
if &a.0 != b {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_inputs(&self) -> usize {
|
pub fn num_inputs(&self) -> usize {
|
||||||
self.inputs.len()
|
self.inputs.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr
|
pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr {
|
||||||
{
|
|
||||||
let (assignment, name) = self.inputs[index].clone();
|
let (assignment, name) = self.inputs[index].clone();
|
||||||
|
|
||||||
assert_eq!(path, name);
|
assert_eq!(path, name);
|
||||||
@@ -317,17 +302,17 @@ impl<E: Engine> TestConstraintSystem<E> {
|
|||||||
assignment
|
assignment
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&mut self, path: &str) -> E::Fr
|
pub fn get(&mut self, path: &str) -> E::Fr {
|
||||||
{
|
|
||||||
match self.named_objects.get(path) {
|
match self.named_objects.get(path) {
|
||||||
Some(&NamedObject::Var(ref v)) => {
|
Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
|
||||||
match v.get_unchecked() {
|
Index::Input(index) => self.inputs[index].0,
|
||||||
Index::Input(index) => self.inputs[index].0,
|
Index::Aux(index) => self.aux[index].0,
|
||||||
Index::Aux(index) => self.aux[index].0
|
},
|
||||||
}
|
Some(e) => panic!(
|
||||||
}
|
"tried to get value of path `{}`, but `{:?}` exists there (not a variable)",
|
||||||
Some(e) => panic!("tried to get value of path `{}`, but `{:?}` exists there (not a variable)", path, e),
|
path, e
|
||||||
_ => panic!("no variable exists at path: {}", path)
|
),
|
||||||
|
_ => panic!("no variable exists at path: {}", path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,8 +333,7 @@ fn compute_path(ns: &[String], this: String) -> String {
|
|||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
|
|
||||||
let mut needs_separation = false;
|
let mut needs_separation = false;
|
||||||
for ns in ns.iter().chain(Some(&this).into_iter())
|
for ns in ns.iter().chain(Some(&this).into_iter()) {
|
||||||
{
|
|
||||||
if needs_separation {
|
if needs_separation {
|
||||||
name += "/";
|
name += "/";
|
||||||
}
|
}
|
||||||
@@ -361,15 +345,14 @@ fn compute_path(ns: &[String], this: String) -> String {
|
|||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
||||||
type Root = Self;
|
type Root = Self;
|
||||||
|
|
||||||
fn alloc<F, A, AR>(
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
let index = self.aux.len();
|
let index = self.aux.len();
|
||||||
let path = compute_path(&self.current_namespace, annotation().into());
|
let path = compute_path(&self.current_namespace, annotation().into());
|
||||||
@@ -380,12 +363,11 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
|||||||
Ok(var)
|
Ok(var)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_input<F, A, AR>(
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
let index = self.inputs.len();
|
let index = self.inputs.len();
|
||||||
let path = compute_path(&self.current_namespace, annotation().into());
|
let path = compute_path(&self.current_namespace, annotation().into());
|
||||||
@@ -396,17 +378,13 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
|||||||
Ok(var)
|
Ok(var)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce<A, AR, LA, LB, LC>(
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
A: FnOnce() -> AR,
|
||||||
a: LA,
|
AR: Into<String>,
|
||||||
b: LB,
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
c: LC
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
)
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
where A: FnOnce() -> AR, AR: Into<String>,
|
|
||||||
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
|
||||||
{
|
{
|
||||||
let path = compute_path(&self.current_namespace, annotation().into());
|
let path = compute_path(&self.current_namespace, annotation().into());
|
||||||
let index = self.constraints.len();
|
let index = self.constraints.len();
|
||||||
@@ -420,7 +398,9 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
{
|
{
|
||||||
let name = name_fn().into();
|
let name = name_fn().into();
|
||||||
let path = compute_path(&self.current_namespace, name.clone());
|
let path = compute_path(&self.current_namespace, name.clone());
|
||||||
@@ -428,13 +408,11 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
|||||||
self.current_namespace.push(name);
|
self.current_namespace.push(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
fn pop_namespace(&mut self) {
|
||||||
{
|
|
||||||
assert!(self.current_namespace.pop().is_some());
|
assert!(self.current_namespace.pop().is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_root(&mut self) -> &mut Self::Root
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
{
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,28 +425,26 @@ fn test_cs() {
|
|||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
assert_eq!(cs.num_constraints(), 0);
|
assert_eq!(cs.num_constraints(), 0);
|
||||||
let a = cs.namespace(|| "a").alloc(|| "var", || Ok(Fr::from_str("10").unwrap())).unwrap();
|
let a = cs
|
||||||
let b = cs.namespace(|| "b").alloc(|| "var", || Ok(Fr::from_str("4").unwrap())).unwrap();
|
.namespace(|| "a")
|
||||||
let c = cs.alloc(|| "product", || Ok(Fr::from_str("40").unwrap())).unwrap();
|
.alloc(|| "var", || Ok(Fr::from_str("10").unwrap()))
|
||||||
|
.unwrap();
|
||||||
|
let b = cs
|
||||||
|
.namespace(|| "b")
|
||||||
|
.alloc(|| "var", || Ok(Fr::from_str("4").unwrap()))
|
||||||
|
.unwrap();
|
||||||
|
let c = cs
|
||||||
|
.alloc(|| "product", || Ok(Fr::from_str("40").unwrap()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(|| "mult", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|
||||||
|| "mult",
|
|
||||||
|lc| lc + a,
|
|
||||||
|lc| lc + b,
|
|
||||||
|lc| lc + c
|
|
||||||
);
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
assert_eq!(cs.num_constraints(), 1);
|
assert_eq!(cs.num_constraints(), 1);
|
||||||
|
|
||||||
cs.set("a/var", Fr::from_str("4").unwrap());
|
cs.set("a/var", Fr::from_str("4").unwrap());
|
||||||
|
|
||||||
let one = TestConstraintSystem::<Bls12>::one();
|
let one = TestConstraintSystem::<Bls12>::one();
|
||||||
cs.enforce(
|
cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b);
|
||||||
|| "eq",
|
|
||||||
|lc| lc + a,
|
|
||||||
|lc| lc + one,
|
|
||||||
|lc| lc + b
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(!cs.is_satisfied());
|
assert!(!cs.is_satisfied());
|
||||||
assert!(cs.which_is_unsatisfied() == Some("mult"));
|
assert!(cs.which_is_unsatisfied() == Some("mult"));
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
use ff::{Field, PrimeField};
|
//! Circuit representation of a [`u32`], with helpers for the [`sha256`]
|
||||||
use pairing::Engine;
|
//! gadgets.
|
||||||
|
//!
|
||||||
|
//! [`sha256`]: crate::gadgets::sha256
|
||||||
|
|
||||||
use bellman::{
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
SynthesisError,
|
|
||||||
ConstraintSystem,
|
|
||||||
LinearCombination
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::boolean::{
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError};
|
||||||
Boolean,
|
|
||||||
AllocatedBit
|
use super::boolean::{AllocatedBit, Boolean};
|
||||||
};
|
|
||||||
|
|
||||||
use super::multieq::MultiEq;
|
use super::multieq::MultiEq;
|
||||||
|
|
||||||
@@ -20,13 +17,12 @@ use super::multieq::MultiEq;
|
|||||||
pub struct UInt32 {
|
pub struct UInt32 {
|
||||||
// Least significant bit first
|
// Least significant bit first
|
||||||
bits: Vec<Boolean>,
|
bits: Vec<Boolean>,
|
||||||
value: Option<u32>
|
value: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UInt32 {
|
impl UInt32 {
|
||||||
/// Construct a constant `UInt32` from a `u32`
|
/// Construct a constant `UInt32` from a `u32`
|
||||||
pub fn constant(value: u32) -> Self
|
pub fn constant(value: u32) -> Self {
|
||||||
{
|
|
||||||
let mut bits = Vec::with_capacity(32);
|
let mut bits = Vec::with_capacity(32);
|
||||||
|
|
||||||
let mut tmp = value;
|
let mut tmp = value;
|
||||||
@@ -41,18 +37,16 @@ impl UInt32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UInt32 {
|
UInt32 {
|
||||||
bits: bits,
|
bits,
|
||||||
value: Some(value)
|
value: Some(value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a `UInt32` in the constraint system
|
/// Allocate a `UInt32` in the constraint system
|
||||||
pub fn alloc<E, CS>(
|
pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
|
||||||
mut cs: CS,
|
where
|
||||||
value: Option<u32>
|
E: ScalarEngine,
|
||||||
) -> Result<Self, SynthesisError>
|
CS: ConstraintSystem<E>,
|
||||||
where E: Engine,
|
|
||||||
CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
let values = match value {
|
let values = match value {
|
||||||
Some(mut val) => {
|
Some(mut val) => {
|
||||||
@@ -64,28 +58,28 @@ impl UInt32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v
|
v
|
||||||
},
|
}
|
||||||
None => vec![None; 32]
|
None => vec![None; 32],
|
||||||
};
|
};
|
||||||
|
|
||||||
let bits = values.into_iter()
|
let bits = values
|
||||||
.enumerate()
|
.into_iter()
|
||||||
.map(|(i, v)| {
|
.enumerate()
|
||||||
Ok(Boolean::from(AllocatedBit::alloc(
|
.map(|(i, v)| {
|
||||||
cs.namespace(|| format!("allocated bit {}", i)),
|
Ok(Boolean::from(AllocatedBit::alloc(
|
||||||
v
|
cs.namespace(|| format!("allocated bit {}", i)),
|
||||||
)?))
|
v,
|
||||||
})
|
)?))
|
||||||
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
})
|
||||||
|
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
||||||
|
|
||||||
Ok(UInt32 {
|
Ok(UInt32 { bits, value })
|
||||||
bits: bits,
|
|
||||||
value: value
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_bits_be(&self) -> Vec<Boolean> {
|
pub fn into_bits_be(self) -> Vec<Boolean> {
|
||||||
self.bits.iter().rev().cloned().collect()
|
let mut ret = self.bits;
|
||||||
|
ret.reverse();
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bits_be(bits: &[Boolean]) -> Self {
|
pub fn from_bits_be(bits: &[Boolean]) -> Self {
|
||||||
@@ -96,28 +90,30 @@ impl UInt32 {
|
|||||||
value.as_mut().map(|v| *v <<= 1);
|
value.as_mut().map(|v| *v <<= 1);
|
||||||
|
|
||||||
match b.get_value() {
|
match b.get_value() {
|
||||||
Some(true) => { value.as_mut().map(|v| *v |= 1); },
|
Some(true) => {
|
||||||
Some(false) => {},
|
value.as_mut().map(|v| *v |= 1);
|
||||||
None => { value = None; }
|
}
|
||||||
|
Some(false) => {}
|
||||||
|
None => {
|
||||||
|
value = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt32 {
|
UInt32 {
|
||||||
value: value,
|
value,
|
||||||
bits: bits.iter().rev().cloned().collect()
|
bits: bits.iter().rev().cloned().collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Turns this `UInt32` into its little-endian byte order representation.
|
/// Turns this `UInt32` into its little-endian byte order representation.
|
||||||
pub fn into_bits(&self) -> Vec<Boolean> {
|
pub fn into_bits(self) -> Vec<Boolean> {
|
||||||
self.bits.clone()
|
self.bits
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a little-endian byte order representation of bits into a
|
/// Converts a little-endian byte order representation of bits into a
|
||||||
/// `UInt32`.
|
/// `UInt32`.
|
||||||
pub fn from_bits(bits: &[Boolean]) -> Self
|
pub fn from_bits(bits: &[Boolean]) -> Self {
|
||||||
{
|
|
||||||
assert_eq!(bits.len(), 32);
|
assert_eq!(bits.len(), 32);
|
||||||
|
|
||||||
let new_bits = bits.to_vec();
|
let new_bits = bits.to_vec();
|
||||||
@@ -126,48 +122,50 @@ impl UInt32 {
|
|||||||
for b in new_bits.iter().rev() {
|
for b in new_bits.iter().rev() {
|
||||||
value.as_mut().map(|v| *v <<= 1);
|
value.as_mut().map(|v| *v <<= 1);
|
||||||
|
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Constant(b) => {
|
Boolean::Constant(b) => {
|
||||||
if b {
|
if b {
|
||||||
value.as_mut().map(|v| *v |= 1);
|
value.as_mut().map(|v| *v |= 1);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
&Boolean::Is(ref b) => {
|
|
||||||
match b.get_value() {
|
|
||||||
Some(true) => { value.as_mut().map(|v| *v |= 1); },
|
|
||||||
Some(false) => {},
|
|
||||||
None => { value = None }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
&Boolean::Not(ref b) => {
|
|
||||||
match b.get_value() {
|
|
||||||
Some(false) => { value.as_mut().map(|v| *v |= 1); },
|
|
||||||
Some(true) => {},
|
|
||||||
None => { value = None }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Boolean::Is(ref b) => match b.get_value() {
|
||||||
|
Some(true) => {
|
||||||
|
value.as_mut().map(|v| *v |= 1);
|
||||||
|
}
|
||||||
|
Some(false) => {}
|
||||||
|
None => value = None,
|
||||||
|
},
|
||||||
|
Boolean::Not(ref b) => match b.get_value() {
|
||||||
|
Some(false) => {
|
||||||
|
value.as_mut().map(|v| *v |= 1);
|
||||||
|
}
|
||||||
|
Some(true) => {}
|
||||||
|
None => value = None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt32 {
|
UInt32 {
|
||||||
value: value,
|
value,
|
||||||
bits: new_bits
|
bits: new_bits,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rotr(&self, by: usize) -> Self {
|
pub fn rotr(&self, by: usize) -> Self {
|
||||||
let by = by % 32;
|
let by = by % 32;
|
||||||
|
|
||||||
let new_bits = self.bits.iter()
|
let new_bits = self
|
||||||
.skip(by)
|
.bits
|
||||||
.chain(self.bits.iter())
|
.iter()
|
||||||
.take(32)
|
.skip(by)
|
||||||
.cloned()
|
.chain(self.bits.iter())
|
||||||
.collect();
|
.take(32)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
UInt32 {
|
UInt32 {
|
||||||
bits: new_bits,
|
bits: new_bits,
|
||||||
value: self.value.map(|v| v.rotate_right(by as u32))
|
value: self.value.map(|v| v.rotate_right(by as u32)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,17 +174,18 @@ impl UInt32 {
|
|||||||
|
|
||||||
let fill = Boolean::constant(false);
|
let fill = Boolean::constant(false);
|
||||||
|
|
||||||
let new_bits = self.bits
|
let new_bits = self
|
||||||
.iter() // The bits are least significant first
|
.bits
|
||||||
.skip(by) // Skip the bits that will be lost during the shift
|
.iter() // The bits are least significant first
|
||||||
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
|
.skip(by) // Skip the bits that will be lost during the shift
|
||||||
.take(32) // Only 32 bits needed!
|
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
|
||||||
.cloned()
|
.take(32) // Only 32 bits needed!
|
||||||
.collect();
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
UInt32 {
|
UInt32 {
|
||||||
bits: new_bits,
|
bits: new_bits,
|
||||||
value: self.value.map(|v| v >> by as u32)
|
value: self.value.map(|v| v >> by as u32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,121 +195,99 @@ impl UInt32 {
|
|||||||
b: &Self,
|
b: &Self,
|
||||||
c: &Self,
|
c: &Self,
|
||||||
tri_fn: F,
|
tri_fn: F,
|
||||||
circuit_fn: U
|
circuit_fn: U,
|
||||||
) -> Result<Self, SynthesisError>
|
) -> Result<Self, SynthesisError>
|
||||||
where E: Engine,
|
where
|
||||||
CS: ConstraintSystem<E>,
|
E: ScalarEngine,
|
||||||
F: Fn(u32, u32, u32) -> u32,
|
CS: ConstraintSystem<E>,
|
||||||
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>
|
F: Fn(u32, u32, u32) -> u32,
|
||||||
|
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
|
||||||
{
|
{
|
||||||
let new_value = match (a.value, b.value, c.value) {
|
let new_value = match (a.value, b.value, c.value) {
|
||||||
(Some(a), Some(b), Some(c)) => {
|
(Some(a), Some(b), Some(c)) => Some(tri_fn(a, b, c)),
|
||||||
Some(tri_fn(a, b, c))
|
_ => None,
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let bits = a.bits.iter()
|
let bits = a
|
||||||
.zip(b.bits.iter())
|
.bits
|
||||||
.zip(c.bits.iter())
|
.iter()
|
||||||
.enumerate()
|
.zip(b.bits.iter())
|
||||||
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
|
.zip(c.bits.iter())
|
||||||
.collect::<Result<_, _>>()?;
|
.enumerate()
|
||||||
|
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
Ok(UInt32 {
|
Ok(UInt32 {
|
||||||
bits: bits,
|
bits,
|
||||||
value: new_value
|
value: new_value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the `maj` value (a and b) xor (a and c) xor (b and c)
|
/// Compute the `maj` value (a and b) xor (a and c) xor (b and c)
|
||||||
/// during SHA256.
|
/// during SHA256.
|
||||||
pub fn sha256_maj<E, CS>(
|
pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||||
cs: CS,
|
where
|
||||||
a: &Self,
|
E: ScalarEngine,
|
||||||
b: &Self,
|
CS: ConstraintSystem<E>,
|
||||||
c: &Self
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where E: Engine,
|
|
||||||
CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ (a & c) ^ (b & c),
|
Self::triop(
|
||||||
|cs, i, a, b, c| {
|
cs,
|
||||||
Boolean::sha256_maj(
|
a,
|
||||||
cs.namespace(|| format!("maj {}", i)),
|
b,
|
||||||
a,
|
c,
|
||||||
b,
|
|a, b, c| (a & b) ^ (a & c) ^ (b & c),
|
||||||
c
|
|cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c),
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the `ch` value `(a and b) xor ((not a) and c)`
|
/// Compute the `ch` value `(a and b) xor ((not a) and c)`
|
||||||
/// during SHA256.
|
/// during SHA256.
|
||||||
pub fn sha256_ch<E, CS>(
|
pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||||
cs: CS,
|
where
|
||||||
a: &Self,
|
E: ScalarEngine,
|
||||||
b: &Self,
|
CS: ConstraintSystem<E>,
|
||||||
c: &Self
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where E: Engine,
|
|
||||||
CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ ((!a) & c),
|
Self::triop(
|
||||||
|cs, i, a, b, c| {
|
cs,
|
||||||
Boolean::sha256_ch(
|
a,
|
||||||
cs.namespace(|| format!("ch {}", i)),
|
b,
|
||||||
a,
|
c,
|
||||||
b,
|
|a, b, c| (a & b) ^ ((!a) & c),
|
||||||
c
|
|cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c),
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// XOR this `UInt32` with another `UInt32`
|
/// XOR this `UInt32` with another `UInt32`
|
||||||
pub fn xor<E, CS>(
|
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||||
&self,
|
where
|
||||||
mut cs: CS,
|
E: ScalarEngine,
|
||||||
other: &Self
|
CS: ConstraintSystem<E>,
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where E: Engine,
|
|
||||||
CS: ConstraintSystem<E>
|
|
||||||
{
|
{
|
||||||
let new_value = match (self.value, other.value) {
|
let new_value = match (self.value, other.value) {
|
||||||
(Some(a), Some(b)) => {
|
(Some(a), Some(b)) => Some(a ^ b),
|
||||||
Some(a ^ b)
|
_ => None,
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let bits = self.bits.iter()
|
let bits = self
|
||||||
.zip(other.bits.iter())
|
.bits
|
||||||
.enumerate()
|
.iter()
|
||||||
.map(|(i, (a, b))| {
|
.zip(other.bits.iter())
|
||||||
Boolean::xor(
|
.enumerate()
|
||||||
cs.namespace(|| format!("xor of bit {}", i)),
|
.map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b))
|
||||||
a,
|
.collect::<Result<_, _>>()?;
|
||||||
b
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
|
|
||||||
Ok(UInt32 {
|
Ok(UInt32 {
|
||||||
bits: bits,
|
bits,
|
||||||
value: new_value
|
value: new_value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform modular addition of several `UInt32` objects.
|
/// Perform modular addition of several `UInt32` objects.
|
||||||
pub fn addmany<E, CS, M>(
|
pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
|
||||||
mut cs: M,
|
where
|
||||||
operands: &[Self]
|
E: ScalarEngine,
|
||||||
) -> Result<Self, SynthesisError>
|
CS: ConstraintSystem<E>,
|
||||||
where E: Engine,
|
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||||
CS: ConstraintSystem<E>,
|
|
||||||
M: ConstraintSystem<E, Root=MultiEq<E, CS>>
|
|
||||||
{
|
{
|
||||||
// Make some arbitrary bounds for ourselves to avoid overflows
|
// Make some arbitrary bounds for ourselves to avoid overflows
|
||||||
// in the scalar field
|
// in the scalar field
|
||||||
@@ -320,7 +297,7 @@ impl UInt32 {
|
|||||||
|
|
||||||
// Compute the maximum value of the sum so we allocate enough bits for
|
// Compute the maximum value of the sum so we allocate enough bits for
|
||||||
// the result
|
// the result
|
||||||
let mut max_value = (operands.len() as u64) * (u32::max_value() as u64);
|
let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value()));
|
||||||
|
|
||||||
// Keep track of the resulting value
|
// Keep track of the resulting value
|
||||||
let mut result_value = Some(0u64);
|
let mut result_value = Some(0u64);
|
||||||
@@ -336,8 +313,8 @@ impl UInt32 {
|
|||||||
// Accumulate the value
|
// Accumulate the value
|
||||||
match op.value {
|
match op.value {
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
result_value.as_mut().map(|v| *v += val as u64);
|
result_value.as_mut().map(|v| *v += u64::from(val));
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
// If any of our operands have unknown value, we won't
|
// If any of our operands have unknown value, we won't
|
||||||
// know the value of the result
|
// know the value of the result
|
||||||
@@ -381,7 +358,7 @@ impl UInt32 {
|
|||||||
// Allocate the bit
|
// Allocate the bit
|
||||||
let b = AllocatedBit::alloc(
|
let b = AllocatedBit::alloc(
|
||||||
cs.namespace(|| format!("result bit {}", i)),
|
cs.namespace(|| format!("result bit {}", i)),
|
||||||
result_value.map(|v| (v >> i) & 1 == 1)
|
result_value.map(|v| (v >> i) & 1 == 1),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Add this bit to the result combination
|
// Add this bit to the result combination
|
||||||
@@ -402,48 +379,53 @@ impl UInt32 {
|
|||||||
|
|
||||||
Ok(UInt32 {
|
Ok(UInt32 {
|
||||||
bits: result_bits,
|
bits: result_bits,
|
||||||
value: modular_value
|
value: modular_value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use rand::{XorShiftRng, SeedableRng, Rng};
|
use super::UInt32;
|
||||||
use ::circuit::boolean::{Boolean};
|
use crate::gadgets::boolean::Boolean;
|
||||||
use super::{UInt32};
|
use crate::gadgets::multieq::MultiEq;
|
||||||
|
use crate::gadgets::test::*;
|
||||||
|
use crate::ConstraintSystem;
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use pairing::bls12_381::{Bls12};
|
use pairing::bls12_381::Bls12;
|
||||||
use ::circuit::test::*;
|
use rand_core::{RngCore, SeedableRng};
|
||||||
use bellman::{ConstraintSystem};
|
use rand_xorshift::XorShiftRng;
|
||||||
use circuit::multieq::MultiEq;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uint32_from_bits_be() {
|
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 {
|
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);
|
let b = UInt32::from_bits_be(&v);
|
||||||
|
|
||||||
for (i, bit) in b.bits.iter().enumerate() {
|
for (i, bit) in b.bits.iter().enumerate() {
|
||||||
match bit {
|
match *bit {
|
||||||
&Boolean::Constant(bit) => {
|
Boolean::Constant(bit) => {
|
||||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||||
},
|
}
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected_to_be_same = b.into_bits_be();
|
let expected_to_be_same = b.into_bits_be();
|
||||||
|
|
||||||
for x in v.iter().zip(expected_to_be_same.iter())
|
for x in v.iter().zip(expected_to_be_same.iter()) {
|
||||||
{
|
|
||||||
match x {
|
match x {
|
||||||
(&Boolean::Constant(true), &Boolean::Constant(true)) => {},
|
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}
|
||||||
(&Boolean::Constant(false), &Boolean::Constant(false)) => {},
|
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -451,30 +433,34 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uint32_from_bits() {
|
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 {
|
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);
|
let b = UInt32::from_bits(&v);
|
||||||
|
|
||||||
for (i, bit) in b.bits.iter().enumerate() {
|
for (i, bit) in b.bits.iter().enumerate() {
|
||||||
match bit {
|
match *bit {
|
||||||
&Boolean::Constant(bit) => {
|
Boolean::Constant(bit) => {
|
||||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||||
},
|
}
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected_to_be_same = b.into_bits();
|
let expected_to_be_same = b.into_bits();
|
||||||
|
|
||||||
for x in v.iter().zip(expected_to_be_same.iter())
|
for x in v.iter().zip(expected_to_be_same.iter()) {
|
||||||
{
|
|
||||||
match x {
|
match x {
|
||||||
(&Boolean::Constant(true), &Boolean::Constant(true)) => {},
|
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}
|
||||||
(&Boolean::Constant(false), &Boolean::Constant(false)) => {},
|
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -482,14 +468,17 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uint32_xor() {
|
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 {
|
for _ in 0..1000 {
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let a: u32 = rng.gen();
|
let a = rng.next_u32();
|
||||||
let b: u32 = rng.gen();
|
let b = rng.next_u32();
|
||||||
let c: u32 = rng.gen();
|
let c = rng.next_u32();
|
||||||
|
|
||||||
let mut expected = a ^ b ^ c;
|
let mut expected = a ^ b ^ c;
|
||||||
|
|
||||||
@@ -505,14 +494,14 @@ mod test {
|
|||||||
assert!(r.value == Some(expected));
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
for b in r.bits.iter() {
|
for b in r.bits.iter() {
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Is(ref b) => {
|
Boolean::Is(ref b) => {
|
||||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
},
|
}
|
||||||
&Boolean::Not(ref b) => {
|
Boolean::Not(ref b) => {
|
||||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
},
|
}
|
||||||
&Boolean::Constant(b) => {
|
Boolean::Constant(b) => {
|
||||||
assert!(b == (expected & 1 == 1));
|
assert!(b == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,14 +513,17 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uint32_addmany_constants() {
|
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 {
|
for _ in 0..1000 {
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let a: u32 = rng.gen();
|
let a = rng.next_u32();
|
||||||
let b: u32 = rng.gen();
|
let b = rng.next_u32();
|
||||||
let c: u32 = rng.gen();
|
let c = rng.next_u32();
|
||||||
|
|
||||||
let a_bit = UInt32::constant(a);
|
let a_bit = UInt32::constant(a);
|
||||||
let b_bit = UInt32::constant(b);
|
let b_bit = UInt32::constant(b);
|
||||||
@@ -541,17 +533,18 @@ mod test {
|
|||||||
|
|
||||||
let r = {
|
let r = {
|
||||||
let mut cs = MultiEq::new(&mut cs);
|
let mut cs = MultiEq::new(&mut cs);
|
||||||
let r = UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
|
let r =
|
||||||
|
UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
|
||||||
r
|
r
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(r.value == Some(expected));
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
for b in r.bits.iter() {
|
for b in r.bits.iter() {
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Is(_) => panic!(),
|
Boolean::Is(_) => panic!(),
|
||||||
&Boolean::Not(_) => panic!(),
|
Boolean::Not(_) => panic!(),
|
||||||
&Boolean::Constant(b) => {
|
Boolean::Constant(b) => {
|
||||||
assert!(b == (expected & 1 == 1));
|
assert!(b == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -563,15 +556,18 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uint32_addmany() {
|
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 {
|
for _ in 0..1000 {
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let a: u32 = rng.gen();
|
let a = rng.next_u32();
|
||||||
let b: u32 = rng.gen();
|
let b = rng.next_u32();
|
||||||
let c: u32 = rng.gen();
|
let c = rng.next_u32();
|
||||||
let d: u32 = rng.gen();
|
let d = rng.next_u32();
|
||||||
|
|
||||||
let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d);
|
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 = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
|
||||||
let r = {
|
let r = {
|
||||||
let mut cs = MultiEq::new(&mut cs);
|
let mut cs = MultiEq::new(&mut cs);
|
||||||
let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap();
|
UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap()
|
||||||
r
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
@@ -592,16 +587,14 @@ mod test {
|
|||||||
assert!(r.value == Some(expected));
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
for b in r.bits.iter() {
|
for b in r.bits.iter() {
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Is(ref b) => {
|
Boolean::Is(ref b) => {
|
||||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
},
|
|
||||||
&Boolean::Not(ref b) => {
|
|
||||||
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;
|
expected >>= 1;
|
||||||
@@ -620,9 +613,12 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uint32_rotr() {
|
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);
|
let a = UInt32::constant(num);
|
||||||
|
|
||||||
@@ -634,11 +630,11 @@ mod test {
|
|||||||
|
|
||||||
let mut tmp = num;
|
let mut tmp = num;
|
||||||
for b in &b.bits {
|
for b in &b.bits {
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Constant(b) => {
|
Boolean::Constant(b) => {
|
||||||
assert_eq!(b, tmp & 1 == 1);
|
assert_eq!(b, tmp & 1 == 1);
|
||||||
},
|
}
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp >>= 1;
|
tmp >>= 1;
|
||||||
@@ -650,11 +646,14 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uint32_shr() {
|
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 _ in 0..50 {
|
||||||
for i in 0..60 {
|
for i in 0..60 {
|
||||||
let num = rng.gen();
|
let num = rng.next_u32();
|
||||||
let a = UInt32::constant(num).shr(i);
|
let a = UInt32::constant(num).shr(i);
|
||||||
let b = UInt32::constant(num.wrapping_shr(i as u32));
|
let b = UInt32::constant(num.wrapping_shr(i as u32));
|
||||||
|
|
||||||
@@ -670,14 +669,17 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uint32_sha256_maj() {
|
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 {
|
for _ in 0..1000 {
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let a: u32 = rng.gen();
|
let a = rng.next_u32();
|
||||||
let b: u32 = rng.gen();
|
let b = rng.next_u32();
|
||||||
let c: u32 = rng.gen();
|
let c = rng.next_u32();
|
||||||
|
|
||||||
let mut expected = (a & b) ^ (a & c) ^ (b & c);
|
let mut expected = (a & b) ^ (a & c) ^ (b & c);
|
||||||
|
|
||||||
@@ -695,10 +697,10 @@ mod test {
|
|||||||
match b {
|
match b {
|
||||||
&Boolean::Is(ref b) => {
|
&Boolean::Is(ref b) => {
|
||||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
},
|
}
|
||||||
&Boolean::Not(ref b) => {
|
&Boolean::Not(ref b) => {
|
||||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
},
|
}
|
||||||
&Boolean::Constant(b) => {
|
&Boolean::Constant(b) => {
|
||||||
assert!(b == (expected & 1 == 1));
|
assert!(b == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
@@ -711,14 +713,17 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uint32_sha256_ch() {
|
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 {
|
for _ in 0..1000 {
|
||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let a: u32 = rng.gen();
|
let a = rng.next_u32();
|
||||||
let b: u32 = rng.gen();
|
let b = rng.next_u32();
|
||||||
let c: u32 = rng.gen();
|
let c = rng.next_u32();
|
||||||
|
|
||||||
let mut expected = (a & b) ^ ((!a) & c);
|
let mut expected = (a & b) ^ ((!a) & c);
|
||||||
|
|
||||||
@@ -736,10 +741,10 @@ mod test {
|
|||||||
match b {
|
match b {
|
||||||
&Boolean::Is(ref b) => {
|
&Boolean::Is(ref b) => {
|
||||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
},
|
}
|
||||||
&Boolean::Not(ref b) => {
|
&Boolean::Not(ref b) => {
|
||||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
},
|
}
|
||||||
&Boolean::Constant(b) => {
|
&Boolean::Constant(b) => {
|
||||||
assert!(b == (expected & 1 == 1));
|
assert!(b == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use rand::Rng;
|
use rand_core::RngCore;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -6,55 +6,34 @@ use ff::{Field, PrimeField};
|
|||||||
use group::{CurveAffine, CurveProjective, Wnaf};
|
use group::{CurveAffine, CurveProjective, Wnaf};
|
||||||
use pairing::Engine;
|
use pairing::Engine;
|
||||||
|
|
||||||
use super::{
|
use super::{Parameters, VerifyingKey};
|
||||||
Parameters,
|
|
||||||
VerifyingKey
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::{
|
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||||
SynthesisError,
|
|
||||||
Circuit,
|
|
||||||
ConstraintSystem,
|
|
||||||
LinearCombination,
|
|
||||||
Variable,
|
|
||||||
Index
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::domain::{
|
use crate::domain::{EvaluationDomain, Scalar};
|
||||||
EvaluationDomain,
|
|
||||||
Scalar
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::multicore::{
|
use crate::multicore::Worker;
|
||||||
Worker
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Generates a random common reference string for
|
/// Generates a random common reference string for
|
||||||
/// a circuit.
|
/// a circuit.
|
||||||
pub fn generate_random_parameters<E, C, R>(
|
pub fn generate_random_parameters<E, C, R>(
|
||||||
circuit: C,
|
circuit: C,
|
||||||
rng: &mut R
|
rng: &mut R,
|
||||||
) -> Result<Parameters<E>, SynthesisError>
|
) -> Result<Parameters<E>, SynthesisError>
|
||||||
where E: Engine, C: Circuit<E>, R: Rng
|
where
|
||||||
|
E: Engine,
|
||||||
|
C: Circuit<E>,
|
||||||
|
R: RngCore,
|
||||||
{
|
{
|
||||||
let g1 = rng.gen();
|
let g1 = E::G1::random(rng);
|
||||||
let g2 = rng.gen();
|
let g2 = E::G2::random(rng);
|
||||||
let alpha = rng.gen();
|
let alpha = E::Fr::random(rng);
|
||||||
let beta = rng.gen();
|
let beta = E::Fr::random(rng);
|
||||||
let gamma = rng.gen();
|
let gamma = E::Fr::random(rng);
|
||||||
let delta = rng.gen();
|
let delta = E::Fr::random(rng);
|
||||||
let tau = rng.gen();
|
let tau = E::Fr::random(rng);
|
||||||
|
|
||||||
generate_parameters::<E, C>(
|
generate_parameters::<E, C>(circuit, g1, g2, alpha, beta, gamma, delta, tau)
|
||||||
circuit,
|
|
||||||
g1,
|
|
||||||
g2,
|
|
||||||
alpha,
|
|
||||||
beta,
|
|
||||||
gamma,
|
|
||||||
delta,
|
|
||||||
tau
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is our assembly structure that we'll use to synthesize the
|
/// This is our assembly structure that we'll use to synthesize the
|
||||||
@@ -68,18 +47,17 @@ struct KeypairAssembly<E: Engine> {
|
|||||||
ct_inputs: Vec<Vec<(E::Fr, usize)>>,
|
ct_inputs: Vec<Vec<(E::Fr, usize)>>,
|
||||||
at_aux: Vec<Vec<(E::Fr, usize)>>,
|
at_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
bt_aux: Vec<Vec<(E::Fr, usize)>>,
|
bt_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
ct_aux: Vec<Vec<(E::Fr, usize)>>
|
ct_aux: Vec<Vec<(E::Fr, usize)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
|
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
|
||||||
type Root = Self;
|
type Root = Self;
|
||||||
|
|
||||||
fn alloc<F, A, AR>(
|
fn alloc<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
_: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
_: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
// There is no assignment, so we don't even invoke the
|
// There is no assignment, so we don't even invoke the
|
||||||
// function for obtaining one.
|
// function for obtaining one.
|
||||||
@@ -94,12 +72,11 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
|
|||||||
Ok(Variable(Index::Aux(index)))
|
Ok(Variable(Index::Aux(index)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_input<F, A, AR>(
|
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
_: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
_: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
// There is no assignment, so we don't even invoke the
|
// There is no assignment, so we don't even invoke the
|
||||||
// function for obtaining one.
|
// function for obtaining one.
|
||||||
@@ -114,48 +91,59 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
|
|||||||
Ok(Variable(Index::Input(index)))
|
Ok(Variable(Index::Input(index)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce<A, AR, LA, LB, LC>(
|
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
|
||||||
&mut self,
|
where
|
||||||
_: A,
|
A: FnOnce() -> AR,
|
||||||
a: LA,
|
AR: Into<String>,
|
||||||
b: LB,
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
c: LC
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
)
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
where A: FnOnce() -> AR, AR: Into<String>,
|
|
||||||
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
|
||||||
{
|
{
|
||||||
fn eval<E: Engine>(
|
fn eval<E: Engine>(
|
||||||
l: LinearCombination<E>,
|
l: LinearCombination<E>,
|
||||||
inputs: &mut [Vec<(E::Fr, usize)>],
|
inputs: &mut [Vec<(E::Fr, usize)>],
|
||||||
aux: &mut [Vec<(E::Fr, usize)>],
|
aux: &mut [Vec<(E::Fr, usize)>],
|
||||||
this_constraint: usize
|
this_constraint: usize,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
for (index, coeff) in l.0 {
|
for (index, coeff) in l.0 {
|
||||||
match index {
|
match index {
|
||||||
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
|
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
|
||||||
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint))
|
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(a(LinearCombination::zero()), &mut self.at_inputs, &mut self.at_aux, self.num_constraints);
|
eval(
|
||||||
eval(b(LinearCombination::zero()), &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints);
|
a(LinearCombination::zero()),
|
||||||
eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints);
|
&mut self.at_inputs,
|
||||||
|
&mut self.at_aux,
|
||||||
|
self.num_constraints,
|
||||||
|
);
|
||||||
|
eval(
|
||||||
|
b(LinearCombination::zero()),
|
||||||
|
&mut self.bt_inputs,
|
||||||
|
&mut self.bt_aux,
|
||||||
|
self.num_constraints,
|
||||||
|
);
|
||||||
|
eval(
|
||||||
|
c(LinearCombination::zero()),
|
||||||
|
&mut self.ct_inputs,
|
||||||
|
&mut self.ct_aux,
|
||||||
|
self.num_constraints,
|
||||||
|
);
|
||||||
|
|
||||||
self.num_constraints += 1;
|
self.num_constraints += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, _: N)
|
fn push_namespace<NR, N>(&mut self, _: N)
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
{
|
{
|
||||||
// Do nothing; we don't care about namespaces in this context.
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
fn pop_namespace(&mut self) {
|
||||||
{
|
|
||||||
// Do nothing; we don't care about namespaces in this context.
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,9 +161,11 @@ pub fn generate_parameters<E, C>(
|
|||||||
beta: E::Fr,
|
beta: E::Fr,
|
||||||
gamma: E::Fr,
|
gamma: E::Fr,
|
||||||
delta: E::Fr,
|
delta: E::Fr,
|
||||||
tau: E::Fr
|
tau: E::Fr,
|
||||||
) -> Result<Parameters<E>, SynthesisError>
|
) -> Result<Parameters<E>, SynthesisError>
|
||||||
where E: Engine, C: Circuit<E>
|
where
|
||||||
|
E: Engine,
|
||||||
|
C: Circuit<E>,
|
||||||
{
|
{
|
||||||
let mut assembly = KeypairAssembly {
|
let mut assembly = KeypairAssembly {
|
||||||
num_inputs: 0,
|
num_inputs: 0,
|
||||||
@@ -186,7 +176,7 @@ pub fn generate_parameters<E, C>(
|
|||||||
ct_inputs: vec![],
|
ct_inputs: vec![],
|
||||||
at_aux: vec![],
|
at_aux: vec![],
|
||||||
bt_aux: vec![],
|
bt_aux: vec![],
|
||||||
ct_aux: vec![]
|
ct_aux: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Allocate the "one" input variable
|
// Allocate the "one" input variable
|
||||||
@@ -198,11 +188,7 @@ pub fn generate_parameters<E, C>(
|
|||||||
// Input constraints to ensure full density of IC query
|
// Input constraints to ensure full density of IC query
|
||||||
// x * 0 = 0
|
// x * 0 = 0
|
||||||
for i in 0..assembly.num_inputs {
|
for i in 0..assembly.num_inputs {
|
||||||
assembly.enforce(|| "",
|
assembly.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|
||||||
|lc| lc + Variable(Index::Input(i)),
|
|
||||||
|lc| lc,
|
|
||||||
|lc| lc,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create bases for blind evaluation of polynomials at tau
|
// Create bases for blind evaluation of polynomials at tau
|
||||||
@@ -240,10 +226,9 @@ pub fn generate_parameters<E, C>(
|
|||||||
{
|
{
|
||||||
let powers_of_tau = powers_of_tau.as_mut();
|
let powers_of_tau = powers_of_tau.as_mut();
|
||||||
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
||||||
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate()
|
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
|
||||||
{
|
scope.spawn(move |_scope| {
|
||||||
scope.spawn(move || {
|
let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
|
||||||
let mut current_tau_power = tau.pow(&[(i*chunk) as u64]);
|
|
||||||
|
|
||||||
for p in powers_of_tau {
|
for p in powers_of_tau {
|
||||||
p.0 = current_tau_power;
|
p.0 = current_tau_power;
|
||||||
@@ -260,14 +245,15 @@ pub fn generate_parameters<E, C>(
|
|||||||
|
|
||||||
// Compute the H query with multiple threads
|
// Compute the H query with multiple threads
|
||||||
worker.scope(h.len(), |scope, chunk| {
|
worker.scope(h.len(), |scope, chunk| {
|
||||||
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
|
for (h, p) in h
|
||||||
|
.chunks_mut(chunk)
|
||||||
|
.zip(powers_of_tau.as_ref().chunks(chunk))
|
||||||
{
|
{
|
||||||
let mut g1_wnaf = g1_wnaf.shared();
|
let mut g1_wnaf = g1_wnaf.shared();
|
||||||
|
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
||||||
for (h, p) in h.iter_mut().zip(p.iter())
|
for (h, p) in h.iter_mut().zip(p.iter()) {
|
||||||
{
|
|
||||||
// Compute final exponent
|
// Compute final exponent
|
||||||
let mut exp = p.0;
|
let mut exp = p.0;
|
||||||
exp.mul_assign(&coeff);
|
exp.mul_assign(&coeff);
|
||||||
@@ -320,9 +306,8 @@ pub fn generate_parameters<E, C>(
|
|||||||
beta: &E::Fr,
|
beta: &E::Fr,
|
||||||
|
|
||||||
// Worker
|
// Worker
|
||||||
worker: &Worker
|
worker: &Worker,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
assert_eq!(a.len(), at.len());
|
assert_eq!(a.len(), at.len());
|
||||||
assert_eq!(a.len(), bt.len());
|
assert_eq!(a.len(), bt.len());
|
||||||
@@ -333,31 +318,32 @@ pub fn generate_parameters<E, C>(
|
|||||||
|
|
||||||
// Evaluate polynomials in multiple threads
|
// Evaluate polynomials in multiple threads
|
||||||
worker.scope(a.len(), |scope, chunk| {
|
worker.scope(a.len(), |scope, chunk| {
|
||||||
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk)
|
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
|
||||||
.zip(b_g1.chunks_mut(chunk))
|
.chunks_mut(chunk)
|
||||||
.zip(b_g2.chunks_mut(chunk))
|
.zip(b_g1.chunks_mut(chunk))
|
||||||
.zip(ext.chunks_mut(chunk))
|
.zip(b_g2.chunks_mut(chunk))
|
||||||
.zip(at.chunks(chunk))
|
.zip(ext.chunks_mut(chunk))
|
||||||
.zip(bt.chunks(chunk))
|
.zip(at.chunks(chunk))
|
||||||
.zip(ct.chunks(chunk))
|
.zip(bt.chunks(chunk))
|
||||||
|
.zip(ct.chunks(chunk))
|
||||||
{
|
{
|
||||||
let mut g1_wnaf = g1_wnaf.shared();
|
let mut g1_wnaf = g1_wnaf.shared();
|
||||||
let mut g2_wnaf = g2_wnaf.shared();
|
let mut g2_wnaf = g2_wnaf.shared();
|
||||||
|
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut()
|
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
|
||||||
.zip(b_g1.iter_mut())
|
.iter_mut()
|
||||||
.zip(b_g2.iter_mut())
|
.zip(b_g1.iter_mut())
|
||||||
.zip(ext.iter_mut())
|
.zip(b_g2.iter_mut())
|
||||||
.zip(at.iter())
|
.zip(ext.iter_mut())
|
||||||
.zip(bt.iter())
|
.zip(at.iter())
|
||||||
.zip(ct.iter())
|
.zip(bt.iter())
|
||||||
|
.zip(ct.iter())
|
||||||
{
|
{
|
||||||
fn eval_at_tau<E: Engine>(
|
fn eval_at_tau<E: Engine>(
|
||||||
powers_of_tau: &[Scalar<E>],
|
powers_of_tau: &[Scalar<E>],
|
||||||
p: &[(E::Fr, usize)]
|
p: &[(E::Fr, usize)],
|
||||||
) -> E::Fr
|
) -> E::Fr {
|
||||||
{
|
|
||||||
let mut acc = E::Fr::zero();
|
let mut acc = E::Fr::zero();
|
||||||
|
|
||||||
for &(ref coeff, index) in p {
|
for &(ref coeff, index) in p {
|
||||||
@@ -422,7 +408,7 @@ pub fn generate_parameters<E, C>(
|
|||||||
&gamma_inverse,
|
&gamma_inverse,
|
||||||
&alpha,
|
&alpha,
|
||||||
&beta,
|
&beta,
|
||||||
&worker
|
&worker,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Evaluate for auxiliary variables.
|
// Evaluate for auxiliary variables.
|
||||||
@@ -440,7 +426,7 @@ pub fn generate_parameters<E, C>(
|
|||||||
&delta_inverse,
|
&delta_inverse,
|
||||||
&alpha,
|
&alpha,
|
||||||
&beta,
|
&beta,
|
||||||
&worker
|
&worker,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Don't allow any elements be unconstrained, so that
|
// Don't allow any elements be unconstrained, so that
|
||||||
@@ -461,17 +447,32 @@ pub fn generate_parameters<E, C>(
|
|||||||
gamma_g2: g2.mul(gamma).into_affine(),
|
gamma_g2: g2.mul(gamma).into_affine(),
|
||||||
delta_g1: g1.mul(delta).into_affine(),
|
delta_g1: g1.mul(delta).into_affine(),
|
||||||
delta_g2: g2.mul(delta).into_affine(),
|
delta_g2: g2.mul(delta).into_affine(),
|
||||||
ic: ic.into_iter().map(|e| e.into_affine()).collect()
|
ic: ic.into_iter().map(|e| e.into_affine()).collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Parameters {
|
Ok(Parameters {
|
||||||
vk: vk,
|
vk,
|
||||||
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
|
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
|
||||||
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
|
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
|
||||||
|
|
||||||
// Filter points at infinity away from A/B queries
|
// Filter points at infinity away from A/B queries
|
||||||
a: Arc::new(a.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
|
a: Arc::new(
|
||||||
b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()),
|
a.into_iter()
|
||||||
b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect())
|
.filter(|e| !e.is_zero())
|
||||||
|
.map(|e| e.into_affine())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
b_g1: Arc::new(
|
||||||
|
b_g1.into_iter()
|
||||||
|
.filter(|e| !e.is_zero())
|
||||||
|
.map(|e| e.into_affine())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
b_g2: Arc::new(
|
||||||
|
b_g2.into_iter()
|
||||||
|
.filter(|e| !e.is_zero())
|
||||||
|
.map(|e| e.into_affine())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
|
//! The [Groth16] proving system.
|
||||||
|
//!
|
||||||
|
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||||
|
|
||||||
use group::{CurveAffine, EncodedPoint};
|
use group::{CurveAffine, EncodedPoint};
|
||||||
use pairing::{
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
Engine,
|
|
||||||
PairingCurveAffine,
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::{
|
use crate::SynthesisError;
|
||||||
SynthesisError
|
|
||||||
};
|
|
||||||
|
|
||||||
use multiexp::SourceBuilder;
|
use crate::multiexp::SourceBuilder;
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@@ -28,23 +27,17 @@ pub use self::verifier::*;
|
|||||||
pub struct Proof<E: Engine> {
|
pub struct Proof<E: Engine> {
|
||||||
pub a: E::G1Affine,
|
pub a: E::G1Affine,
|
||||||
pub b: E::G2Affine,
|
pub b: E::G2Affine,
|
||||||
pub c: E::G1Affine
|
pub c: E::G1Affine,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for Proof<E> {
|
impl<E: Engine> PartialEq for Proof<E> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.a == other.a &&
|
self.a == other.a && self.b == other.b && self.c == other.c
|
||||||
self.b == other.b &&
|
|
||||||
self.c == other.c
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Proof<E> {
|
impl<E: Engine> Proof<E> {
|
||||||
pub fn write<W: Write>(
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
&self,
|
|
||||||
mut writer: W
|
|
||||||
) -> io::Result<()>
|
|
||||||
{
|
|
||||||
writer.write_all(self.a.into_compressed().as_ref())?;
|
writer.write_all(self.a.into_compressed().as_ref())?;
|
||||||
writer.write_all(self.b.into_compressed().as_ref())?;
|
writer.write_all(self.b.into_compressed().as_ref())?;
|
||||||
writer.write_all(self.c.into_compressed().as_ref())?;
|
writer.write_all(self.c.into_compressed().as_ref())?;
|
||||||
@@ -52,48 +45,56 @@ impl<E: Engine> Proof<E> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
mut reader: R
|
|
||||||
) -> io::Result<Self>
|
|
||||||
{
|
|
||||||
let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty();
|
let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty();
|
||||||
let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty();
|
let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty();
|
||||||
|
|
||||||
reader.read_exact(g1_repr.as_mut())?;
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
let a = g1_repr
|
let a = g1_repr
|
||||||
.into_affine()
|
.into_affine()
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
.and_then(|e| if e.is_zero() {
|
.and_then(|e| {
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
|
if e.is_zero() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(e)
|
Ok(e)
|
||||||
})?;
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
reader.read_exact(g2_repr.as_mut())?;
|
reader.read_exact(g2_repr.as_mut())?;
|
||||||
let b = g2_repr
|
let b = g2_repr
|
||||||
.into_affine()
|
.into_affine()
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
.and_then(|e| if e.is_zero() {
|
.and_then(|e| {
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
|
if e.is_zero() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(e)
|
Ok(e)
|
||||||
})?;
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
reader.read_exact(g1_repr.as_mut())?;
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
let c = g1_repr
|
let c = g1_repr
|
||||||
.into_affine()
|
.into_affine()
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
.and_then(|e| if e.is_zero() {
|
.and_then(|e| {
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
|
if e.is_zero() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(e)
|
Ok(e)
|
||||||
})?;
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(Proof {
|
Ok(Proof { a, b, c })
|
||||||
a: a,
|
|
||||||
b: b,
|
|
||||||
c: c
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,27 +123,23 @@ pub struct VerifyingKey<E: Engine> {
|
|||||||
// for all public inputs. Because all public inputs have a dummy constraint,
|
// for all public inputs. Because all public inputs have a dummy constraint,
|
||||||
// this is the same size as the number of inputs, and never contains points
|
// this is the same size as the number of inputs, and never contains points
|
||||||
// at infinity.
|
// at infinity.
|
||||||
pub ic: Vec<E::G1Affine>
|
pub ic: Vec<E::G1Affine>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for VerifyingKey<E> {
|
impl<E: Engine> PartialEq for VerifyingKey<E> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.alpha_g1 == other.alpha_g1 &&
|
self.alpha_g1 == other.alpha_g1
|
||||||
self.beta_g1 == other.beta_g1 &&
|
&& self.beta_g1 == other.beta_g1
|
||||||
self.beta_g2 == other.beta_g2 &&
|
&& self.beta_g2 == other.beta_g2
|
||||||
self.gamma_g2 == other.gamma_g2 &&
|
&& self.gamma_g2 == other.gamma_g2
|
||||||
self.delta_g1 == other.delta_g1 &&
|
&& self.delta_g1 == other.delta_g1
|
||||||
self.delta_g2 == other.delta_g2 &&
|
&& self.delta_g2 == other.delta_g2
|
||||||
self.ic == other.ic
|
&& self.ic == other.ic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> VerifyingKey<E> {
|
impl<E: Engine> VerifyingKey<E> {
|
||||||
pub fn write<W: Write>(
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
&self,
|
|
||||||
mut writer: W
|
|
||||||
) -> io::Result<()>
|
|
||||||
{
|
|
||||||
writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?;
|
writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?;
|
||||||
writer.write_all(self.beta_g1.into_uncompressed().as_ref())?;
|
writer.write_all(self.beta_g1.into_uncompressed().as_ref())?;
|
||||||
writer.write_all(self.beta_g2.into_uncompressed().as_ref())?;
|
writer.write_all(self.beta_g2.into_uncompressed().as_ref())?;
|
||||||
@@ -157,30 +154,39 @@ impl<E: Engine> VerifyingKey<E> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
mut reader: R
|
|
||||||
) -> io::Result<Self>
|
|
||||||
{
|
|
||||||
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
|
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
|
||||||
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
|
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
|
||||||
|
|
||||||
reader.read_exact(g1_repr.as_mut())?;
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
let alpha_g1 = g1_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
reader.read_exact(g1_repr.as_mut())?;
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
let beta_g1 = g1_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
reader.read_exact(g2_repr.as_mut())?;
|
reader.read_exact(g2_repr.as_mut())?;
|
||||||
let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
let beta_g2 = g2_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
reader.read_exact(g2_repr.as_mut())?;
|
reader.read_exact(g2_repr.as_mut())?;
|
||||||
let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
let gamma_g2 = g2_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
reader.read_exact(g1_repr.as_mut())?;
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
let delta_g1 = g1_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
reader.read_exact(g2_repr.as_mut())?;
|
reader.read_exact(g2_repr.as_mut())?;
|
||||||
let delta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
let delta_g2 = g2_repr
|
||||||
|
.into_affine()
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
let ic_len = reader.read_u32::<BigEndian>()? as usize;
|
let ic_len = reader.read_u32::<BigEndian>()? as usize;
|
||||||
|
|
||||||
@@ -189,25 +195,30 @@ impl<E: Engine> VerifyingKey<E> {
|
|||||||
for _ in 0..ic_len {
|
for _ in 0..ic_len {
|
||||||
reader.read_exact(g1_repr.as_mut())?;
|
reader.read_exact(g1_repr.as_mut())?;
|
||||||
let g1 = g1_repr
|
let g1 = g1_repr
|
||||||
.into_affine()
|
.into_affine()
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
.and_then(|e| if e.is_zero() {
|
.and_then(|e| {
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
|
if e.is_zero() {
|
||||||
} else {
|
Err(io::Error::new(
|
||||||
Ok(e)
|
io::ErrorKind::InvalidData,
|
||||||
})?;
|
"point at infinity",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(e)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
ic.push(g1);
|
ic.push(g1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(VerifyingKey {
|
Ok(VerifyingKey {
|
||||||
alpha_g1: alpha_g1,
|
alpha_g1,
|
||||||
beta_g1: beta_g1,
|
beta_g1,
|
||||||
beta_g2: beta_g2,
|
beta_g2,
|
||||||
gamma_g2: gamma_g2,
|
gamma_g2,
|
||||||
delta_g1: delta_g1,
|
delta_g1,
|
||||||
delta_g2: delta_g2,
|
delta_g2,
|
||||||
ic: ic
|
ic,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,7 +227,7 @@ impl<E: Engine> VerifyingKey<E> {
|
|||||||
pub struct Parameters<E: Engine> {
|
pub struct Parameters<E: Engine> {
|
||||||
pub vk: VerifyingKey<E>,
|
pub vk: VerifyingKey<E>,
|
||||||
|
|
||||||
// Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and
|
// Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and
|
||||||
// m-2 inclusive. Never contains points at infinity.
|
// m-2 inclusive. Never contains points at infinity.
|
||||||
pub h: Arc<Vec<E::G1Affine>>,
|
pub h: Arc<Vec<E::G1Affine>>,
|
||||||
|
|
||||||
@@ -234,26 +245,22 @@ pub struct Parameters<E: Engine> {
|
|||||||
// G1 and G2 for C/B queries, respectively. Never contains points at
|
// G1 and G2 for C/B queries, respectively. Never contains points at
|
||||||
// infinity for the same reason as the "A" polynomials.
|
// infinity for the same reason as the "A" polynomials.
|
||||||
pub b_g1: Arc<Vec<E::G1Affine>>,
|
pub b_g1: Arc<Vec<E::G1Affine>>,
|
||||||
pub b_g2: Arc<Vec<E::G2Affine>>
|
pub b_g2: Arc<Vec<E::G2Affine>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for Parameters<E> {
|
impl<E: Engine> PartialEq for Parameters<E> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.vk == other.vk &&
|
self.vk == other.vk
|
||||||
self.h == other.h &&
|
&& self.h == other.h
|
||||||
self.l == other.l &&
|
&& self.l == other.l
|
||||||
self.a == other.a &&
|
&& self.a == other.a
|
||||||
self.b_g1 == other.b_g1 &&
|
&& self.b_g1 == other.b_g1
|
||||||
self.b_g2 == other.b_g2
|
&& self.b_g2 == other.b_g2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Parameters<E> {
|
impl<E: Engine> Parameters<E> {
|
||||||
pub fn write<W: Write>(
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
&self,
|
|
||||||
mut writer: W
|
|
||||||
) -> io::Result<()>
|
|
||||||
{
|
|
||||||
self.vk.write(&mut writer)?;
|
self.vk.write(&mut writer)?;
|
||||||
|
|
||||||
writer.write_u32::<BigEndian>(self.h.len() as u32)?;
|
writer.write_u32::<BigEndian>(self.h.len() as u32)?;
|
||||||
@@ -284,27 +291,26 @@ impl<E: Engine> Parameters<E> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(
|
pub fn read<R: Read>(mut reader: R, checked: bool) -> io::Result<Self> {
|
||||||
mut reader: R,
|
|
||||||
checked: bool
|
|
||||||
) -> io::Result<Self>
|
|
||||||
{
|
|
||||||
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
|
||||||
let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
|
let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
|
||||||
reader.read_exact(repr.as_mut())?;
|
reader.read_exact(repr.as_mut())?;
|
||||||
|
|
||||||
if checked {
|
if checked {
|
||||||
repr
|
repr.into_affine()
|
||||||
.into_affine()
|
|
||||||
} else {
|
} else {
|
||||||
repr
|
repr.into_affine_unchecked()
|
||||||
.into_affine_unchecked()
|
|
||||||
}
|
}
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
.and_then(|e| if e.is_zero() {
|
.and_then(|e| {
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
|
if e.is_zero() {
|
||||||
} else {
|
Err(io::Error::new(
|
||||||
Ok(e)
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -313,17 +319,20 @@ impl<E: Engine> Parameters<E> {
|
|||||||
reader.read_exact(repr.as_mut())?;
|
reader.read_exact(repr.as_mut())?;
|
||||||
|
|
||||||
if checked {
|
if checked {
|
||||||
repr
|
repr.into_affine()
|
||||||
.into_affine()
|
|
||||||
} else {
|
} else {
|
||||||
repr
|
repr.into_affine_unchecked()
|
||||||
.into_affine_unchecked()
|
|
||||||
}
|
}
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
.and_then(|e| if e.is_zero() {
|
.and_then(|e| {
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
|
if e.is_zero() {
|
||||||
} else {
|
Err(io::Error::new(
|
||||||
Ok(e)
|
io::ErrorKind::InvalidData,
|
||||||
|
"point at infinity",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -371,12 +380,12 @@ impl<E: Engine> Parameters<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(Parameters {
|
Ok(Parameters {
|
||||||
vk: vk,
|
vk,
|
||||||
h: Arc::new(h),
|
h: Arc::new(h),
|
||||||
l: Arc::new(l),
|
l: Arc::new(l),
|
||||||
a: Arc::new(a),
|
a: Arc::new(a),
|
||||||
b_g1: Arc::new(b_g1),
|
b_g1: Arc::new(b_g1),
|
||||||
b_g2: Arc::new(b_g2)
|
b_g2: Arc::new(b_g2),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -389,39 +398,30 @@ pub struct PreparedVerifyingKey<E: Engine> {
|
|||||||
/// -delta in G2
|
/// -delta in G2
|
||||||
neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
|
neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
|
||||||
/// Copy of IC from `VerifiyingKey`.
|
/// Copy of IC from `VerifiyingKey`.
|
||||||
ic: Vec<E::G1Affine>
|
ic: Vec<E::G1Affine>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ParameterSource<E: Engine> {
|
pub trait ParameterSource<E: Engine> {
|
||||||
type G1Builder: SourceBuilder<E::G1Affine>;
|
type G1Builder: SourceBuilder<E::G1Affine>;
|
||||||
type G2Builder: SourceBuilder<E::G2Affine>;
|
type G2Builder: SourceBuilder<E::G2Affine>;
|
||||||
|
|
||||||
fn get_vk(
|
fn get_vk(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, SynthesisError>;
|
||||||
&mut self,
|
fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, SynthesisError>;
|
||||||
num_ic: usize
|
fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, SynthesisError>;
|
||||||
) -> Result<VerifyingKey<E>, SynthesisError>;
|
|
||||||
fn get_h(
|
|
||||||
&mut self,
|
|
||||||
num_h: usize
|
|
||||||
) -> Result<Self::G1Builder, SynthesisError>;
|
|
||||||
fn get_l(
|
|
||||||
&mut self,
|
|
||||||
num_l: usize
|
|
||||||
) -> Result<Self::G1Builder, SynthesisError>;
|
|
||||||
fn get_a(
|
fn get_a(
|
||||||
&mut self,
|
&mut self,
|
||||||
num_inputs: usize,
|
num_inputs: usize,
|
||||||
num_aux: usize
|
num_aux: usize,
|
||||||
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
||||||
fn get_b_g1(
|
fn get_b_g1(
|
||||||
&mut self,
|
&mut self,
|
||||||
num_inputs: usize,
|
num_inputs: usize,
|
||||||
num_aux: usize
|
num_aux: usize,
|
||||||
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
|
||||||
fn get_b_g2(
|
fn get_b_g2(
|
||||||
&mut self,
|
&mut self,
|
||||||
num_inputs: usize,
|
num_inputs: usize,
|
||||||
num_aux: usize
|
num_aux: usize,
|
||||||
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
|
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,54 +429,39 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
|
|||||||
type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
|
type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
|
||||||
type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
|
type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
|
||||||
|
|
||||||
fn get_vk(
|
fn get_vk(&mut self, _: usize) -> Result<VerifyingKey<E>, SynthesisError> {
|
||||||
&mut self,
|
|
||||||
_: usize
|
|
||||||
) -> Result<VerifyingKey<E>, SynthesisError>
|
|
||||||
{
|
|
||||||
Ok(self.vk.clone())
|
Ok(self.vk.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_h(
|
fn get_h(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
|
||||||
&mut self,
|
|
||||||
_: usize
|
|
||||||
) -> Result<Self::G1Builder, SynthesisError>
|
|
||||||
{
|
|
||||||
Ok((self.h.clone(), 0))
|
Ok((self.h.clone(), 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_l(
|
fn get_l(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
|
||||||
&mut self,
|
|
||||||
_: usize
|
|
||||||
) -> Result<Self::G1Builder, SynthesisError>
|
|
||||||
{
|
|
||||||
Ok((self.l.clone(), 0))
|
Ok((self.l.clone(), 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_a(
|
fn get_a(
|
||||||
&mut self,
|
&mut self,
|
||||||
num_inputs: usize,
|
num_inputs: usize,
|
||||||
_: usize
|
_: usize,
|
||||||
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
|
||||||
{
|
|
||||||
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
|
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_b_g1(
|
fn get_b_g1(
|
||||||
&mut self,
|
&mut self,
|
||||||
num_inputs: usize,
|
num_inputs: usize,
|
||||||
_: usize
|
_: usize,
|
||||||
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
|
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
|
||||||
{
|
|
||||||
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
|
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_b_g2(
|
fn get_b_g2(
|
||||||
&mut self,
|
&mut self,
|
||||||
num_inputs: usize,
|
num_inputs: usize,
|
||||||
_: usize
|
_: usize,
|
||||||
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>
|
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> {
|
||||||
{
|
|
||||||
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
|
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,41 +469,38 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_with_bls12_381 {
|
mod test_with_bls12_381 {
|
||||||
use super::*;
|
use super::*;
|
||||||
use {Circuit, SynthesisError, ConstraintSystem};
|
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use rand::{Rand, thread_rng};
|
|
||||||
use pairing::bls12_381::{Bls12, Fr};
|
use pairing::bls12_381::{Bls12, Fr};
|
||||||
|
use rand::thread_rng;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialization() {
|
fn serialization() {
|
||||||
struct MySillyCircuit<E: Engine> {
|
struct MySillyCircuit<E: Engine> {
|
||||||
a: Option<E::Fr>,
|
a: Option<E::Fr>,
|
||||||
b: Option<E::Fr>
|
b: Option<E::Fr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Circuit<E> for MySillyCircuit<E> {
|
impl<E: Engine> Circuit<E> for MySillyCircuit<E> {
|
||||||
fn synthesize<CS: ConstraintSystem<E>>(
|
fn synthesize<CS: ConstraintSystem<E>>(
|
||||||
self,
|
self,
|
||||||
cs: &mut CS
|
cs: &mut CS,
|
||||||
) -> Result<(), SynthesisError>
|
) -> Result<(), SynthesisError> {
|
||||||
{
|
|
||||||
let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
|
let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
|
||||||
let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
|
let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
|
||||||
let c = cs.alloc_input(|| "c", || {
|
let c = cs.alloc_input(
|
||||||
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
|
|| "c",
|
||||||
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
|
|| {
|
||||||
|
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
|
||||||
|
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
|
||||||
|
|
||||||
a.mul_assign(&b);
|
a.mul_assign(&b);
|
||||||
Ok(a)
|
Ok(a)
|
||||||
})?;
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(|| "a*b=c", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|
||||||
|| "a*b=c",
|
|
||||||
|lc| lc + a,
|
|
||||||
|lc| lc + b,
|
|
||||||
|lc| lc + c
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -526,10 +508,9 @@ mod test_with_bls12_381 {
|
|||||||
|
|
||||||
let rng = &mut thread_rng();
|
let rng = &mut thread_rng();
|
||||||
|
|
||||||
let params = generate_random_parameters::<Bls12, _, _>(
|
let params =
|
||||||
MySillyCircuit { a: None, b: None },
|
generate_random_parameters::<Bls12, _, _>(MySillyCircuit { a: None, b: None }, rng)
|
||||||
rng
|
.unwrap();
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
@@ -547,19 +528,20 @@ mod test_with_bls12_381 {
|
|||||||
let pvk = prepare_verifying_key::<Bls12>(¶ms.vk);
|
let pvk = prepare_verifying_key::<Bls12>(¶ms.vk);
|
||||||
|
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
let a = Fr::rand(rng);
|
let a = Fr::random(rng);
|
||||||
let b = Fr::rand(rng);
|
let b = Fr::random(rng);
|
||||||
let mut c = a;
|
let mut c = a;
|
||||||
c.mul_assign(&b);
|
c.mul_assign(&b);
|
||||||
|
|
||||||
let proof = create_random_proof(
|
let proof = create_random_proof(
|
||||||
MySillyCircuit {
|
MySillyCircuit {
|
||||||
a: Some(a),
|
a: Some(a),
|
||||||
b: Some(b)
|
b: Some(b),
|
||||||
},
|
},
|
||||||
¶ms,
|
¶ms,
|
||||||
rng
|
rng,
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
proof.write(&mut v).unwrap();
|
proof.write(&mut v).unwrap();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use rand::Rng;
|
use rand_core::RngCore;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -8,43 +8,23 @@ use ff::{Field, PrimeField};
|
|||||||
use group::{CurveAffine, CurveProjective};
|
use group::{CurveAffine, CurveProjective};
|
||||||
use pairing::Engine;
|
use pairing::Engine;
|
||||||
|
|
||||||
use super::{
|
use super::{ParameterSource, Proof};
|
||||||
ParameterSource,
|
|
||||||
Proof
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::{
|
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||||
SynthesisError,
|
|
||||||
Circuit,
|
|
||||||
ConstraintSystem,
|
|
||||||
LinearCombination,
|
|
||||||
Variable,
|
|
||||||
Index
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::domain::{
|
use crate::domain::{EvaluationDomain, Scalar};
|
||||||
EvaluationDomain,
|
|
||||||
Scalar
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::multiexp::{
|
use crate::multiexp::{multiexp, DensityTracker, FullDensity};
|
||||||
DensityTracker,
|
|
||||||
FullDensity,
|
|
||||||
multiexp
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::multicore::{
|
use crate::multicore::Worker;
|
||||||
Worker
|
|
||||||
};
|
|
||||||
|
|
||||||
fn eval<E: Engine>(
|
fn eval<E: Engine>(
|
||||||
lc: &LinearCombination<E>,
|
lc: &LinearCombination<E>,
|
||||||
mut input_density: Option<&mut DensityTracker>,
|
mut input_density: Option<&mut DensityTracker>,
|
||||||
mut aux_density: Option<&mut DensityTracker>,
|
mut aux_density: Option<&mut DensityTracker>,
|
||||||
input_assignment: &[E::Fr],
|
input_assignment: &[E::Fr],
|
||||||
aux_assignment: &[E::Fr]
|
aux_assignment: &[E::Fr],
|
||||||
) -> E::Fr
|
) -> E::Fr {
|
||||||
{
|
|
||||||
let mut acc = E::Fr::zero();
|
let mut acc = E::Fr::zero();
|
||||||
|
|
||||||
for &(index, coeff) in lc.0.iter() {
|
for &(index, coeff) in lc.0.iter() {
|
||||||
@@ -56,7 +36,7 @@ fn eval<E: Engine>(
|
|||||||
if let Some(ref mut v) = input_density {
|
if let Some(ref mut v) = input_density {
|
||||||
v.inc(i);
|
v.inc(i);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Variable(Index::Aux(i)) => {
|
Variable(Index::Aux(i)) => {
|
||||||
tmp = aux_assignment[i];
|
tmp = aux_assignment[i];
|
||||||
if let Some(ref mut v) = aux_density {
|
if let Some(ref mut v) = aux_density {
|
||||||
@@ -66,10 +46,10 @@ fn eval<E: Engine>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if coeff == E::Fr::one() {
|
if coeff == E::Fr::one() {
|
||||||
acc.add_assign(&tmp);
|
acc.add_assign(&tmp);
|
||||||
} else {
|
} else {
|
||||||
tmp.mul_assign(&coeff);
|
tmp.mul_assign(&coeff);
|
||||||
acc.add_assign(&tmp);
|
acc.add_assign(&tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,18 +69,17 @@ struct ProvingAssignment<E: Engine> {
|
|||||||
|
|
||||||
// Assignments of variables
|
// Assignments of variables
|
||||||
input_assignment: Vec<E::Fr>,
|
input_assignment: Vec<E::Fr>,
|
||||||
aux_assignment: Vec<E::Fr>
|
aux_assignment: Vec<E::Fr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
||||||
type Root = Self;
|
type Root = Self;
|
||||||
|
|
||||||
fn alloc<F, A, AR>(
|
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
_: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
self.aux_assignment.push(f()?);
|
self.aux_assignment.push(f()?);
|
||||||
self.a_aux_density.add_element();
|
self.a_aux_density.add_element();
|
||||||
@@ -109,12 +88,11 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
|||||||
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
|
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_input<F, A, AR>(
|
fn alloc_input<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
_: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
self.input_assignment.push(f()?);
|
self.input_assignment.push(f()?);
|
||||||
self.b_input_density.add_element();
|
self.b_input_density.add_element();
|
||||||
@@ -122,17 +100,13 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
|||||||
Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
|
Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce<A, AR, LA, LB, LC>(
|
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
|
||||||
&mut self,
|
where
|
||||||
_: A,
|
A: FnOnce() -> AR,
|
||||||
a: LA,
|
AR: Into<String>,
|
||||||
b: LB,
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
c: LC
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
)
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
where A: FnOnce() -> AR, AR: Into<String>,
|
|
||||||
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
|
||||||
{
|
{
|
||||||
let a = a(LinearCombination::zero());
|
let a = a(LinearCombination::zero());
|
||||||
let b = b(LinearCombination::zero());
|
let b = b(LinearCombination::zero());
|
||||||
@@ -146,14 +120,14 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
|||||||
None,
|
None,
|
||||||
Some(&mut self.a_aux_density),
|
Some(&mut self.a_aux_density),
|
||||||
&self.input_assignment,
|
&self.input_assignment,
|
||||||
&self.aux_assignment
|
&self.aux_assignment,
|
||||||
)));
|
)));
|
||||||
self.b.push(Scalar(eval(
|
self.b.push(Scalar(eval(
|
||||||
&b,
|
&b,
|
||||||
Some(&mut self.b_input_density),
|
Some(&mut self.b_input_density),
|
||||||
Some(&mut self.b_aux_density),
|
Some(&mut self.b_aux_density),
|
||||||
&self.input_assignment,
|
&self.input_assignment,
|
||||||
&self.aux_assignment
|
&self.aux_assignment,
|
||||||
)));
|
)));
|
||||||
self.c.push(Scalar(eval(
|
self.c.push(Scalar(eval(
|
||||||
&c,
|
&c,
|
||||||
@@ -164,18 +138,19 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
&self.input_assignment,
|
&self.input_assignment,
|
||||||
&self.aux_assignment
|
&self.aux_assignment,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, _: N)
|
fn push_namespace<NR, N>(&mut self, _: N)
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
{
|
{
|
||||||
// Do nothing; we don't care about namespaces in this context.
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
fn pop_namespace(&mut self) {
|
||||||
{
|
|
||||||
// Do nothing; we don't care about namespaces in this context.
|
// Do nothing; we don't care about namespaces in this context.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,12 +162,15 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
|
|||||||
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
|
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
|
||||||
circuit: C,
|
circuit: C,
|
||||||
params: P,
|
params: P,
|
||||||
rng: &mut R
|
rng: &mut R,
|
||||||
) -> Result<Proof<E>, SynthesisError>
|
) -> Result<Proof<E>, SynthesisError>
|
||||||
where E: Engine, C: Circuit<E>, R: Rng
|
where
|
||||||
|
E: Engine,
|
||||||
|
C: Circuit<E>,
|
||||||
|
R: RngCore,
|
||||||
{
|
{
|
||||||
let r = rng.gen();
|
let r = E::Fr::random(rng);
|
||||||
let s = rng.gen();
|
let s = E::Fr::random(rng);
|
||||||
|
|
||||||
create_proof::<E, C, P>(circuit, params, r, s)
|
create_proof::<E, C, P>(circuit, params, r, s)
|
||||||
}
|
}
|
||||||
@@ -201,9 +179,11 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
|
|||||||
circuit: C,
|
circuit: C,
|
||||||
mut params: P,
|
mut params: P,
|
||||||
r: E::Fr,
|
r: E::Fr,
|
||||||
s: E::Fr
|
s: E::Fr,
|
||||||
) -> Result<Proof<E>, SynthesisError>
|
) -> Result<Proof<E>, SynthesisError>
|
||||||
where E: Engine, C: Circuit<E>
|
where
|
||||||
|
E: Engine,
|
||||||
|
C: Circuit<E>,
|
||||||
{
|
{
|
||||||
let mut prover = ProvingAssignment {
|
let mut prover = ProvingAssignment {
|
||||||
a_aux_density: DensityTracker::new(),
|
a_aux_density: DensityTracker::new(),
|
||||||
@@ -213,7 +193,7 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
|
|||||||
b: vec![],
|
b: vec![],
|
||||||
c: vec![],
|
c: vec![],
|
||||||
input_assignment: vec![],
|
input_assignment: vec![],
|
||||||
aux_assignment: vec![]
|
aux_assignment: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
|
prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
|
||||||
@@ -221,11 +201,7 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
|
|||||||
circuit.synthesize(&mut prover)?;
|
circuit.synthesize(&mut prover)?;
|
||||||
|
|
||||||
for i in 0..prover.input_assignment.len() {
|
for i in 0..prover.input_assignment.len() {
|
||||||
prover.enforce(|| "",
|
prover.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|
||||||
|lc| lc + Variable(Index::Input(i)),
|
|
||||||
|lc| lc,
|
|
||||||
|lc| lc,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let worker = Worker::new();
|
let worker = Worker::new();
|
||||||
@@ -259,31 +235,76 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: parallelize if it's even helpful
|
// TODO: parallelize if it's even helpful
|
||||||
let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
|
let input_assignment = Arc::new(
|
||||||
let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
|
prover
|
||||||
|
.input_assignment
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.into_repr())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
let aux_assignment = Arc::new(
|
||||||
|
prover
|
||||||
|
.aux_assignment
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.into_repr())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
let l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone());
|
let l = multiexp(
|
||||||
|
&worker,
|
||||||
|
params.get_l(aux_assignment.len())?,
|
||||||
|
FullDensity,
|
||||||
|
aux_assignment.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let a_aux_density_total = prover.a_aux_density.get_total_density();
|
let a_aux_density_total = prover.a_aux_density.get_total_density();
|
||||||
|
|
||||||
let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?;
|
let (a_inputs_source, a_aux_source) =
|
||||||
|
params.get_a(input_assignment.len(), a_aux_density_total)?;
|
||||||
|
|
||||||
let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone());
|
let a_inputs = multiexp(
|
||||||
let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone());
|
&worker,
|
||||||
|
a_inputs_source,
|
||||||
|
FullDensity,
|
||||||
|
input_assignment.clone(),
|
||||||
|
);
|
||||||
|
let a_aux = multiexp(
|
||||||
|
&worker,
|
||||||
|
a_aux_source,
|
||||||
|
Arc::new(prover.a_aux_density),
|
||||||
|
aux_assignment.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let b_input_density = Arc::new(prover.b_input_density);
|
let b_input_density = Arc::new(prover.b_input_density);
|
||||||
let b_input_density_total = b_input_density.get_total_density();
|
let b_input_density_total = b_input_density.get_total_density();
|
||||||
let b_aux_density = Arc::new(prover.b_aux_density);
|
let b_aux_density = Arc::new(prover.b_aux_density);
|
||||||
let b_aux_density_total = b_aux_density.get_total_density();
|
let b_aux_density_total = b_aux_density.get_total_density();
|
||||||
|
|
||||||
let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?;
|
let (b_g1_inputs_source, b_g1_aux_source) =
|
||||||
|
params.get_b_g1(b_input_density_total, b_aux_density_total)?;
|
||||||
|
|
||||||
let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone());
|
let b_g1_inputs = multiexp(
|
||||||
let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone());
|
&worker,
|
||||||
|
b_g1_inputs_source,
|
||||||
|
b_input_density.clone(),
|
||||||
|
input_assignment.clone(),
|
||||||
|
);
|
||||||
|
let b_g1_aux = multiexp(
|
||||||
|
&worker,
|
||||||
|
b_g1_aux_source,
|
||||||
|
b_aux_density.clone(),
|
||||||
|
aux_assignment.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?;
|
let (b_g2_inputs_source, b_g2_aux_source) =
|
||||||
|
params.get_b_g2(b_input_density_total, b_aux_density_total)?;
|
||||||
let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment);
|
|
||||||
|
let b_g2_inputs = multiexp(
|
||||||
|
&worker,
|
||||||
|
b_g2_inputs_source,
|
||||||
|
b_input_density,
|
||||||
|
input_assignment,
|
||||||
|
);
|
||||||
let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment);
|
let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment);
|
||||||
|
|
||||||
if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() {
|
if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() {
|
||||||
@@ -325,6 +346,6 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
|
|||||||
Ok(Proof {
|
Ok(Proof {
|
||||||
a: g_a.into_affine(),
|
a: g_a.into_affine(),
|
||||||
b: g_b.into_affine(),
|
b: g_b.into_affine(),
|
||||||
c: g_c.into_affine()
|
c: g_c.into_affine(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
use ff::{
|
use ff::{
|
||||||
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError,
|
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine,
|
||||||
PrimeFieldRepr, ScalarEngine, SqrtField};
|
SqrtField,
|
||||||
|
};
|
||||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||||
use pairing::{Engine, PairingCurveAffine};
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
|
use rand_core::RngCore;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use rand::{Rand, Rng};
|
|
||||||
use std::num::Wrapping;
|
use std::num::Wrapping;
|
||||||
|
|
||||||
const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
||||||
@@ -15,18 +16,16 @@ const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
|||||||
pub struct Fr(Wrapping<u32>);
|
pub struct Fr(Wrapping<u32>);
|
||||||
|
|
||||||
impl fmt::Display for Fr {
|
impl fmt::Display for Fr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
write!(f, "{}", (self.0).0)
|
write!(f, "{}", (self.0).0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for Fr {
|
|
||||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
|
||||||
Fr(Wrapping(rng.gen()) % MODULUS_R)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Field for Fr {
|
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 {
|
fn zero() -> Self {
|
||||||
Fr(Wrapping(0))
|
Fr(Wrapping(0))
|
||||||
}
|
}
|
||||||
@@ -82,9 +81,13 @@ impl SqrtField for Fr {
|
|||||||
fn legendre(&self) -> LegendreSymbol {
|
fn legendre(&self) -> LegendreSymbol {
|
||||||
// s = self^((r - 1) // 2)
|
// s = self^((r - 1) // 2)
|
||||||
let s = self.pow([32256]);
|
let s = self.pow([32256]);
|
||||||
if s == <Fr as Field>::zero() { LegendreSymbol::Zero }
|
if s == <Fr as Field>::zero() {
|
||||||
else if s == <Fr as Field>::one() { LegendreSymbol::QuadraticResidue }
|
LegendreSymbol::Zero
|
||||||
else { LegendreSymbol::QuadraticNonResidue }
|
} else if s == <Fr as Field>::one() {
|
||||||
|
LegendreSymbol::QuadraticResidue
|
||||||
|
} else {
|
||||||
|
LegendreSymbol::QuadraticNonResidue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sqrt(&self) -> Option<Self> {
|
fn sqrt(&self) -> Option<Self> {
|
||||||
@@ -102,7 +105,7 @@ impl SqrtField for Fr {
|
|||||||
let mut m = Fr::S;
|
let mut m = Fr::S;
|
||||||
|
|
||||||
while t != <Fr as Field>::one() {
|
while t != <Fr as Field>::one() {
|
||||||
let mut i = 1;
|
let mut i = 1;
|
||||||
{
|
{
|
||||||
let mut t2i = t;
|
let mut t2i = t;
|
||||||
t2i.square();
|
t2i.square();
|
||||||
@@ -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 {
|
impl fmt::Display for FrRepr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
write!(f, "{}", (self.0)[0])
|
write!(f, "{}", (self.0)[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,15 +263,18 @@ impl Engine for DummyEngine {
|
|||||||
type G2Affine = Fr;
|
type G2Affine = Fr;
|
||||||
type Fq = Fr;
|
type Fq = Fr;
|
||||||
type Fqe = Fr;
|
type Fqe = Fr;
|
||||||
|
|
||||||
// TODO: This should be F_645131 or something. Doesn't matter for now.
|
// TODO: This should be F_645131 or something. Doesn't matter for now.
|
||||||
type Fqk = Fr;
|
type Fqk = Fr;
|
||||||
|
|
||||||
fn miller_loop<'a, I>(i: I) -> Self::Fqk
|
fn miller_loop<'a, I>(i: I) -> Self::Fqk
|
||||||
where I: IntoIterator<Item=&'a (
|
where
|
||||||
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
|
I: IntoIterator<
|
||||||
&'a <Self::G2Affine as PairingCurveAffine>::Prepared
|
Item = &'a (
|
||||||
)>
|
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
|
||||||
|
&'a <Self::G2Affine as PairingCurveAffine>::Prepared,
|
||||||
|
),
|
||||||
|
>,
|
||||||
{
|
{
|
||||||
let mut acc = <Fr as Field>::zero();
|
let mut acc = <Fr as Field>::zero();
|
||||||
|
|
||||||
@@ -288,8 +288,7 @@ impl Engine for DummyEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Perform final exponentiation of the result of a miller loop.
|
/// Perform final exponentiation of the result of a miller loop.
|
||||||
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk>
|
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk> {
|
||||||
{
|
|
||||||
Some(*this)
|
Some(*this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,6 +299,10 @@ impl CurveProjective for Fr {
|
|||||||
type Scalar = Fr;
|
type Scalar = Fr;
|
||||||
type Engine = DummyEngine;
|
type Engine = DummyEngine;
|
||||||
|
|
||||||
|
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||||
|
<Fr as Field>::random(rng)
|
||||||
|
}
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
<Fr as Field>::zero()
|
<Fr as Field>::zero()
|
||||||
}
|
}
|
||||||
@@ -312,9 +315,7 @@ impl CurveProjective for Fr {
|
|||||||
<Fr as Field>::is_zero(self)
|
<Fr as Field>::is_zero(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn batch_normalization(_: &mut [Self]) {
|
fn batch_normalization(_: &mut [Self]) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_normalized(&self) -> bool {
|
fn is_normalized(&self) -> bool {
|
||||||
true
|
true
|
||||||
@@ -336,8 +337,7 @@ impl CurveProjective for Fr {
|
|||||||
<Fr as Field>::negate(self);
|
<Fr as Field>::negate(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S)
|
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) {
|
||||||
{
|
|
||||||
let tmp = Fr::from_repr(other.into()).unwrap();
|
let tmp = Fr::from_repr(other.into()).unwrap();
|
||||||
|
|
||||||
<Fr as Field>::mul_assign(self, &tmp);
|
<Fr as Field>::mul_assign(self, &tmp);
|
||||||
@@ -419,8 +419,7 @@ impl CurveAffine for Fr {
|
|||||||
<Fr as Field>::negate(self);
|
<Fr as Field>::negate(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective
|
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective {
|
||||||
{
|
|
||||||
let mut res = *self;
|
let mut res = *self;
|
||||||
let tmp = Fr::from_repr(other.into()).unwrap();
|
let tmp = Fr::from_repr(other.into()).unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -6,86 +6,82 @@ use self::dummy_engine::*;
|
|||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use ::{
|
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||||
Circuit,
|
|
||||||
ConstraintSystem,
|
|
||||||
SynthesisError
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof};
|
||||||
generate_parameters,
|
|
||||||
prepare_verifying_key,
|
|
||||||
create_proof,
|
|
||||||
verify_proof
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XORDemo<E: Engine> {
|
struct XORDemo<E: Engine> {
|
||||||
a: Option<bool>,
|
a: Option<bool>,
|
||||||
b: Option<bool>,
|
b: Option<bool>,
|
||||||
_marker: PhantomData<E>
|
_marker: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Circuit<E> for XORDemo<E> {
|
impl<E: Engine> Circuit<E> for XORDemo<E> {
|
||||||
fn synthesize<CS: ConstraintSystem<E>>(
|
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||||
self,
|
let a_var = cs.alloc(
|
||||||
cs: &mut CS
|
|| "a",
|
||||||
) -> Result<(), SynthesisError>
|
|| {
|
||||||
{
|
if self.a.is_some() {
|
||||||
let a_var = cs.alloc(|| "a", || {
|
if self.a.unwrap() {
|
||||||
if self.a.is_some() {
|
Ok(E::Fr::one())
|
||||||
if self.a.unwrap() {
|
} else {
|
||||||
Ok(E::Fr::one())
|
Ok(E::Fr::zero())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(E::Fr::zero())
|
Err(SynthesisError::AssignmentMissing)
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
Err(SynthesisError::AssignmentMissing)
|
)?;
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "a_boolean_constraint",
|
|| "a_boolean_constraint",
|
||||||
|lc| lc + CS::one() - a_var,
|
|lc| lc + CS::one() - a_var,
|
||||||
|lc| lc + a_var,
|
|lc| lc + a_var,
|
||||||
|lc| lc
|
|lc| lc,
|
||||||
);
|
);
|
||||||
|
|
||||||
let b_var = cs.alloc(|| "b", || {
|
let b_var = cs.alloc(
|
||||||
if self.b.is_some() {
|
|| "b",
|
||||||
if self.b.unwrap() {
|
|| {
|
||||||
Ok(E::Fr::one())
|
if self.b.is_some() {
|
||||||
|
if self.b.unwrap() {
|
||||||
|
Ok(E::Fr::one())
|
||||||
|
} else {
|
||||||
|
Ok(E::Fr::zero())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(E::Fr::zero())
|
Err(SynthesisError::AssignmentMissing)
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
Err(SynthesisError::AssignmentMissing)
|
)?;
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "b_boolean_constraint",
|
|| "b_boolean_constraint",
|
||||||
|lc| lc + CS::one() - b_var,
|
|lc| lc + CS::one() - b_var,
|
||||||
|lc| lc + b_var,
|
|lc| lc + b_var,
|
||||||
|lc| lc
|
|lc| lc,
|
||||||
);
|
);
|
||||||
|
|
||||||
let c_var = cs.alloc_input(|| "c", || {
|
let c_var = cs.alloc_input(
|
||||||
if self.a.is_some() && self.b.is_some() {
|
|| "c",
|
||||||
if self.a.unwrap() ^ self.b.unwrap() {
|
|| {
|
||||||
Ok(E::Fr::one())
|
if self.a.is_some() && self.b.is_some() {
|
||||||
|
if self.a.unwrap() ^ self.b.unwrap() {
|
||||||
|
Ok(E::Fr::one())
|
||||||
|
} else {
|
||||||
|
Ok(E::Fr::zero())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(E::Fr::zero())
|
Err(SynthesisError::AssignmentMissing)
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
Err(SynthesisError::AssignmentMissing)
|
)?;
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "c_xor_constraint",
|
|| "c_xor_constraint",
|
||||||
|lc| lc + a_var + a_var,
|
|lc| lc + a_var + a_var,
|
||||||
|lc| lc + b_var,
|
|lc| lc + b_var,
|
||||||
|lc| lc + a_var + b_var - c_var
|
|lc| lc + a_var + b_var - c_var,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -106,19 +102,10 @@ fn test_xordemo() {
|
|||||||
let c = XORDemo::<DummyEngine> {
|
let c = XORDemo::<DummyEngine> {
|
||||||
a: None,
|
a: None,
|
||||||
b: None,
|
b: None,
|
||||||
_marker: PhantomData
|
_marker: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
generate_parameters(
|
generate_parameters(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap()
|
||||||
c,
|
|
||||||
g1,
|
|
||||||
g2,
|
|
||||||
alpha,
|
|
||||||
beta,
|
|
||||||
gamma,
|
|
||||||
delta,
|
|
||||||
tau
|
|
||||||
).unwrap()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This will synthesize the constraint system:
|
// This will synthesize the constraint system:
|
||||||
@@ -226,32 +213,35 @@ fn test_xordemo() {
|
|||||||
59158
|
59158
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let u_i = [59158, 48317, 21767, 10402].iter().map(|e| {
|
let u_i = [59158, 48317, 21767, 10402]
|
||||||
Fr::from_str(&format!("{}", e)).unwrap()
|
.iter()
|
||||||
}).collect::<Vec<Fr>>();
|
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
|
||||||
let v_i = [0, 0, 60619, 30791].iter().map(|e| {
|
.collect::<Vec<Fr>>();
|
||||||
Fr::from_str(&format!("{}", e)).unwrap()
|
let v_i = [0, 0, 60619, 30791]
|
||||||
}).collect::<Vec<Fr>>();
|
.iter()
|
||||||
let w_i = [0, 23320, 41193, 41193].iter().map(|e| {
|
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
|
||||||
Fr::from_str(&format!("{}", e)).unwrap()
|
.collect::<Vec<Fr>>();
|
||||||
}).collect::<Vec<Fr>>();
|
let w_i = [0, 23320, 41193, 41193]
|
||||||
|
.iter()
|
||||||
|
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
|
||||||
|
.collect::<Vec<Fr>>();
|
||||||
|
|
||||||
for (u, a) in u_i.iter()
|
for (u, a) in u_i.iter().zip(¶ms.a[..]) {
|
||||||
.zip(¶ms.a[..])
|
|
||||||
{
|
|
||||||
assert_eq!(u, a);
|
assert_eq!(u, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (v, b) in v_i.iter()
|
for (v, b) in v_i
|
||||||
.filter(|&&e| e != Fr::zero())
|
.iter()
|
||||||
.zip(¶ms.b_g1[..])
|
.filter(|&&e| e != Fr::zero())
|
||||||
|
.zip(¶ms.b_g1[..])
|
||||||
{
|
{
|
||||||
assert_eq!(v, b);
|
assert_eq!(v, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (v, b) in v_i.iter()
|
for (v, b) in v_i
|
||||||
.filter(|&&e| e != Fr::zero())
|
.iter()
|
||||||
.zip(¶ms.b_g2[..])
|
.filter(|&&e| e != Fr::zero())
|
||||||
|
.zip(¶ms.b_g2[..])
|
||||||
{
|
{
|
||||||
assert_eq!(v, b);
|
assert_eq!(v, b);
|
||||||
}
|
}
|
||||||
@@ -296,15 +286,10 @@ fn test_xordemo() {
|
|||||||
let c = XORDemo {
|
let c = XORDemo {
|
||||||
a: Some(true),
|
a: Some(true),
|
||||||
b: Some(false),
|
b: Some(false),
|
||||||
_marker: PhantomData
|
_marker: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
create_proof(
|
create_proof(c, ¶ms, r, s).unwrap()
|
||||||
c,
|
|
||||||
¶ms,
|
|
||||||
r,
|
|
||||||
s
|
|
||||||
).unwrap()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A(x) =
|
// A(x) =
|
||||||
@@ -320,7 +305,7 @@ fn test_xordemo() {
|
|||||||
expected_a.add_assign(&u_i[0]); // a_0 = 1
|
expected_a.add_assign(&u_i[0]); // a_0 = 1
|
||||||
expected_a.add_assign(&u_i[1]); // a_1 = 1
|
expected_a.add_assign(&u_i[1]); // a_1 = 1
|
||||||
expected_a.add_assign(&u_i[2]); // a_2 = 1
|
expected_a.add_assign(&u_i[2]); // a_2 = 1
|
||||||
// a_3 = 0
|
// a_3 = 0
|
||||||
assert_eq!(proof.a, expected_a);
|
assert_eq!(proof.a, expected_a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +322,7 @@ fn test_xordemo() {
|
|||||||
expected_b.add_assign(&v_i[0]); // a_0 = 1
|
expected_b.add_assign(&v_i[0]); // a_0 = 1
|
||||||
expected_b.add_assign(&v_i[1]); // a_1 = 1
|
expected_b.add_assign(&v_i[1]); // a_1 = 1
|
||||||
expected_b.add_assign(&v_i[2]); // a_2 = 1
|
expected_b.add_assign(&v_i[2]); // a_2 = 1
|
||||||
// a_3 = 0
|
// a_3 = 0
|
||||||
assert_eq!(proof.b, expected_b);
|
assert_eq!(proof.b, expected_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +363,10 @@ fn test_xordemo() {
|
|||||||
expected_c.add_assign(¶ms.l[0]);
|
expected_c.add_assign(¶ms.l[0]);
|
||||||
|
|
||||||
// H query answer
|
// H query answer
|
||||||
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() {
|
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739]
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
|
let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
|
||||||
|
|
||||||
let mut tmp = params.h[i];
|
let mut tmp = params.h[i];
|
||||||
@@ -389,9 +377,5 @@ fn test_xordemo() {
|
|||||||
assert_eq!(expected_c, proof.c);
|
assert_eq!(expected_c, proof.c);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(verify_proof(
|
assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap());
|
||||||
&pvk,
|
|
||||||
&proof,
|
|
||||||
&[Fr::one()]
|
|
||||||
).unwrap());
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,11 @@ use ff::PrimeField;
|
|||||||
use group::{CurveAffine, CurveProjective};
|
use group::{CurveAffine, CurveProjective};
|
||||||
use pairing::{Engine, PairingCurveAffine};
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
use super::{
|
use super::{PreparedVerifyingKey, Proof, VerifyingKey};
|
||||||
Proof,
|
|
||||||
VerifyingKey,
|
|
||||||
PreparedVerifyingKey
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::{
|
use crate::SynthesisError;
|
||||||
SynthesisError
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn prepare_verifying_key<E: Engine>(
|
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
|
||||||
vk: &VerifyingKey<E>
|
|
||||||
) -> PreparedVerifyingKey<E>
|
|
||||||
{
|
|
||||||
let mut gamma = vk.gamma_g2;
|
let mut gamma = vk.gamma_g2;
|
||||||
gamma.negate();
|
gamma.negate();
|
||||||
let mut delta = vk.delta_g2;
|
let mut delta = vk.delta_g2;
|
||||||
@@ -25,16 +16,15 @@ pub fn prepare_verifying_key<E: Engine>(
|
|||||||
alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2),
|
alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2),
|
||||||
neg_gamma_g2: gamma.prepare(),
|
neg_gamma_g2: gamma.prepare(),
|
||||||
neg_delta_g2: delta.prepare(),
|
neg_delta_g2: delta.prepare(),
|
||||||
ic: vk.ic.clone()
|
ic: vk.ic.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_proof<'a, E: Engine>(
|
pub fn verify_proof<'a, E: Engine>(
|
||||||
pvk: &'a PreparedVerifyingKey<E>,
|
pvk: &'a PreparedVerifyingKey<E>,
|
||||||
proof: &Proof<E>,
|
proof: &Proof<E>,
|
||||||
public_inputs: &[E::Fr]
|
public_inputs: &[E::Fr],
|
||||||
) -> Result<bool, SynthesisError>
|
) -> Result<bool, SynthesisError> {
|
||||||
{
|
|
||||||
if (public_inputs.len() + 1) != pvk.ic.len() {
|
if (public_inputs.len() + 1) != pvk.ic.len() {
|
||||||
return Err(SynthesisError::MalformedVerifyingKey);
|
return Err(SynthesisError::MalformedVerifyingKey);
|
||||||
}
|
}
|
||||||
@@ -53,11 +43,14 @@ pub fn verify_proof<'a, E: Engine>(
|
|||||||
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
|
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
|
||||||
// which allows us to do a single final exponentiation.
|
// which allows us to do a single final exponentiation.
|
||||||
|
|
||||||
Ok(E::final_exponentiation(
|
Ok(E::final_exponentiation(&E::miller_loop(
|
||||||
&E::miller_loop([
|
[
|
||||||
(&proof.a.prepare(), &proof.b.prepare()),
|
(&proof.a.prepare(), &proof.b.prepare()),
|
||||||
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
|
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
|
||||||
(&proof.c.prepare(), &pvk.neg_delta_g2)
|
(&proof.c.prepare(), &pvk.neg_delta_g2),
|
||||||
].into_iter())
|
]
|
||||||
).unwrap() == pvk.alpha_g1_beta_g2)
|
.iter(),
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
== pvk.alpha_g1_beta_g2)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,154 @@
|
|||||||
extern crate ff;
|
//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
|
||||||
extern crate group;
|
//! traits and and primitive structures, as well as basic gadget implementations
|
||||||
#[cfg(feature = "pairing")]
|
//! such as booleans and number abstractions.
|
||||||
extern crate pairing;
|
//!
|
||||||
extern crate rand;
|
//! # 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(¶ms.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, ¶ms, &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;
|
// Catch documentation errors caused by code changes.
|
||||||
extern crate bit_vec;
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
extern crate byteorder;
|
|
||||||
|
|
||||||
#[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 domain;
|
||||||
|
pub mod gadgets;
|
||||||
#[cfg(feature = "groth16")]
|
#[cfg(feature = "groth16")]
|
||||||
pub mod groth16;
|
pub mod groth16;
|
||||||
|
pub mod multicore;
|
||||||
|
mod multiexp;
|
||||||
|
|
||||||
use ff::{Field, ScalarEngine};
|
use ff::{Field, ScalarEngine};
|
||||||
|
|
||||||
use std::ops::{Add, Sub};
|
|
||||||
use std::fmt;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
/// Computations are expressed in terms of arithmetic circuits, in particular
|
/// Computations are expressed in terms of arithmetic circuits, in particular
|
||||||
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a
|
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a
|
||||||
@@ -35,10 +156,7 @@ use std::marker::PhantomData;
|
|||||||
/// CRS generation and during proving.
|
/// CRS generation and during proving.
|
||||||
pub trait Circuit<E: ScalarEngine> {
|
pub trait Circuit<E: ScalarEngine> {
|
||||||
/// Synthesize the circuit into a rank-1 quadratic constraint system
|
/// Synthesize the circuit into a rank-1 quadratic constraint system
|
||||||
fn synthesize<CS: ConstraintSystem<E>>(
|
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError>;
|
||||||
self,
|
|
||||||
cs: &mut CS
|
|
||||||
) -> Result<(), SynthesisError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a variable in our constraint system.
|
/// Represents a variable in our constraint system.
|
||||||
@@ -64,7 +182,7 @@ impl Variable {
|
|||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum Index {
|
pub enum Index {
|
||||||
Input(usize),
|
Input(usize),
|
||||||
Aux(usize)
|
Aux(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This represents a linear combination of some variables, with coefficients
|
/// This represents a linear combination of some variables, with coefficients
|
||||||
@@ -97,6 +215,7 @@ impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
|
|||||||
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
|
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||||
coeff.negate();
|
coeff.negate();
|
||||||
|
|
||||||
@@ -191,7 +310,7 @@ pub enum SynthesisError {
|
|||||||
/// During verification, our verifying key was malformed.
|
/// During verification, our verifying key was malformed.
|
||||||
MalformedVerifyingKey,
|
MalformedVerifyingKey,
|
||||||
/// During CRS generation, we observed an unconstrained auxiliary variable
|
/// During CRS generation, we observed an unconstrained auxiliary variable
|
||||||
UnconstrainedVariable
|
UnconstrainedVariable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for SynthesisError {
|
impl From<io::Error> for SynthesisError {
|
||||||
@@ -203,21 +322,23 @@ impl From<io::Error> for SynthesisError {
|
|||||||
impl Error for SynthesisError {
|
impl Error for SynthesisError {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed",
|
SynthesisError::AssignmentMissing => {
|
||||||
|
"an assignment for a variable could not be computed"
|
||||||
|
}
|
||||||
SynthesisError::DivisionByZero => "division by zero",
|
SynthesisError::DivisionByZero => "division by zero",
|
||||||
SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
|
SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
|
||||||
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
|
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
|
||||||
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
|
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
|
||||||
SynthesisError::IoError(_) => "encountered an I/O error",
|
SynthesisError::IoError(_) => "encountered an I/O error",
|
||||||
SynthesisError::MalformedVerifyingKey => "malformed verifying key",
|
SynthesisError::MalformedVerifyingKey => "malformed verifying key",
|
||||||
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained"
|
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SynthesisError {
|
impl fmt::Display for SynthesisError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
if let &SynthesisError::IoError(ref e) = self {
|
if let SynthesisError::IoError(ref e) = *self {
|
||||||
write!(f, "I/O error: ")?;
|
write!(f, "I/O error: ")?;
|
||||||
e.fmt(f)
|
e.fmt(f)
|
||||||
} else {
|
} else {
|
||||||
@@ -242,40 +363,36 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
|
|||||||
/// determine the assignment of the variable. The given `annotation` function is invoked
|
/// determine the assignment of the variable. The given `annotation` function is invoked
|
||||||
/// in testing contexts in order to derive a unique name for this variable in the current
|
/// in testing contexts in order to derive a unique name for this variable in the current
|
||||||
/// namespace.
|
/// namespace.
|
||||||
fn alloc<F, A, AR>(
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>;
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
|
|
||||||
|
|
||||||
/// Allocate a public variable in the constraint system. The provided function is used to
|
/// Allocate a public variable in the constraint system. The provided function is used to
|
||||||
/// determine the assignment of the variable.
|
/// determine the assignment of the variable.
|
||||||
fn alloc_input<F, A, AR>(
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>;
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
|
|
||||||
|
|
||||||
/// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
|
/// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
|
||||||
/// in order to derive a unique name for the constraint in the current namespace.
|
/// in order to derive a unique name for the constraint in the current namespace.
|
||||||
fn enforce<A, AR, LA, LB, LC>(
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
A: FnOnce() -> AR,
|
||||||
a: LA,
|
AR: Into<String>,
|
||||||
b: LB,
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
c: LC
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
)
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
|
||||||
where A: FnOnce() -> AR, AR: Into<String>,
|
|
||||||
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
|
|
||||||
|
|
||||||
/// Create a new (sub)namespace and enter into it. Not intended
|
/// Create a new (sub)namespace and enter into it. Not intended
|
||||||
/// for downstream use; use `namespace` instead.
|
/// for downstream use; use `namespace` instead.
|
||||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||||
where NR: Into<String>, N: FnOnce() -> NR;
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR;
|
||||||
|
|
||||||
/// Exit out of the existing namespace. Not intended for
|
/// Exit out of the existing namespace. Not intended for
|
||||||
/// downstream use; use `namespace` instead.
|
/// downstream use; use `namespace` instead.
|
||||||
@@ -286,11 +403,10 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
|
|||||||
fn get_root(&mut self) -> &mut Self::Root;
|
fn get_root(&mut self) -> &mut Self::Root;
|
||||||
|
|
||||||
/// Begin a namespace for this constraint system.
|
/// Begin a namespace for this constraint system.
|
||||||
fn namespace<'a, NR, N>(
|
fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root>
|
||||||
&'a mut self,
|
where
|
||||||
name_fn: N
|
NR: Into<String>,
|
||||||
) -> Namespace<'a, E, Self::Root>
|
N: FnOnce() -> NR,
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
|
||||||
{
|
{
|
||||||
self.get_root().push_namespace(name_fn);
|
self.get_root().push_namespace(name_fn);
|
||||||
|
|
||||||
@@ -300,7 +416,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
|
|||||||
|
|
||||||
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
|
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
|
||||||
/// a namespace context) and, when dropped, pops out of the namespace context.
|
/// a namespace context) and, when dropped, pops out of the namespace context.
|
||||||
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E> + 'a>(&'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> {
|
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
|
||||||
type Root = CS::Root;
|
type Root = CS::Root;
|
||||||
@@ -309,37 +425,31 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
|
|||||||
CS::one()
|
CS::one()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc<F, A, AR>(
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
self.0.alloc(annotation, f)
|
self.0.alloc(annotation, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_input<F, A, AR>(
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
self.0.alloc_input(annotation, f)
|
self.0.alloc_input(annotation, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce<A, AR, LA, LB, LC>(
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
A: FnOnce() -> AR,
|
||||||
a: LA,
|
AR: Into<String>,
|
||||||
b: LB,
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
c: LC
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
)
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
where A: FnOnce() -> AR, AR: Into<String>,
|
|
||||||
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
|
||||||
{
|
{
|
||||||
self.0.enforce(annotation, a, b, c)
|
self.0.enforce(annotation, a, b, c)
|
||||||
}
|
}
|
||||||
@@ -349,18 +459,18 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
|
|||||||
// never a root constraint system.
|
// never a root constraint system.
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, _: N)
|
fn push_namespace<NR, N>(&mut self, _: N)
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
{
|
{
|
||||||
panic!("only the root's push_namespace should be called");
|
panic!("only the root's push_namespace should be called");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
fn pop_namespace(&mut self) {
|
||||||
{
|
|
||||||
panic!("only the root's pop_namespace should be called");
|
panic!("only the root's pop_namespace should be called");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_root(&mut self) -> &mut Self::Root
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
{
|
|
||||||
self.0.get_root()
|
self.0.get_root()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -380,54 +490,48 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs
|
|||||||
CS::one()
|
CS::one()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc<F, A, AR>(
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
(**self).alloc(annotation, f)
|
(**self).alloc(annotation, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_input<F, A, AR>(
|
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||||
f: F
|
A: FnOnce() -> AR,
|
||||||
) -> Result<Variable, SynthesisError>
|
AR: Into<String>,
|
||||||
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
|
|
||||||
{
|
{
|
||||||
(**self).alloc_input(annotation, f)
|
(**self).alloc_input(annotation, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce<A, AR, LA, LB, LC>(
|
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
|
||||||
&mut self,
|
where
|
||||||
annotation: A,
|
A: FnOnce() -> AR,
|
||||||
a: LA,
|
AR: Into<String>,
|
||||||
b: LB,
|
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
c: LC
|
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
)
|
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||||
where A: FnOnce() -> AR, AR: Into<String>,
|
|
||||||
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
|
||||||
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
|
|
||||||
{
|
{
|
||||||
(**self).enforce(annotation, a, b, c)
|
(**self).enforce(annotation, a, b, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
fn push_namespace<NR, N>(&mut self, name_fn: N)
|
||||||
where NR: Into<String>, N: FnOnce() -> NR
|
where
|
||||||
|
NR: Into<String>,
|
||||||
|
N: FnOnce() -> NR,
|
||||||
{
|
{
|
||||||
(**self).push_namespace(name_fn)
|
(**self).push_namespace(name_fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_namespace(&mut self)
|
fn pop_namespace(&mut self) {
|
||||||
{
|
|
||||||
(**self).pop_namespace()
|
(**self).pop_namespace()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_root(&mut self) -> &mut Self::Root
|
fn get_root(&mut self) -> &mut Self::Root {
|
||||||
{
|
|
||||||
(**self).get_root()
|
(**self).get_root()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
//! This is an interface for dealing with the kinds of
|
//! An interface for dealing with the kinds of parallel computations involved in
|
||||||
//! parallel computations involved in bellman. It's
|
//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and
|
||||||
//! currently just a thin wrapper around CpuPool and
|
//! [`crossbeam`] but may be extended in the future to allow for various
|
||||||
//! crossbeam but may be extended in the future to
|
//! parallelism strategies.
|
||||||
//! allow for various parallelism strategies.
|
//!
|
||||||
|
//! [`CpuPool`]: futures_cpupool::CpuPool
|
||||||
|
|
||||||
#[cfg(feature = "multicore")]
|
#[cfg(feature = "multicore")]
|
||||||
mod implementation {
|
mod implementation {
|
||||||
use num_cpus;
|
use crossbeam::{self, thread::Scope};
|
||||||
use futures::{Future, IntoFuture, Poll};
|
use futures::{Future, IntoFuture, Poll};
|
||||||
use futures_cpupool::{CpuPool, CpuFuture};
|
use futures_cpupool::{CpuFuture, CpuPool};
|
||||||
use crossbeam::{self, Scope};
|
use num_cpus;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Worker {
|
pub struct Worker {
|
||||||
cpus: usize,
|
cpus: usize,
|
||||||
pool: CpuPool
|
pool: CpuPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Worker {
|
impl Worker {
|
||||||
@@ -23,8 +24,8 @@ mod implementation {
|
|||||||
// CPUs configured.
|
// CPUs configured.
|
||||||
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
|
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
|
||||||
Worker {
|
Worker {
|
||||||
cpus: cpus,
|
cpus,
|
||||||
pool: CpuPool::new(cpus)
|
pool: CpuPool::new(cpus),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,26 +37,22 @@ mod implementation {
|
|||||||
log2_floor(self.cpus)
|
log2_floor(self.cpus)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute<F, R>(
|
pub fn compute<F, R>(&self, f: F) -> WorkerFuture<R::Item, R::Error>
|
||||||
&self, f: F
|
where
|
||||||
) -> WorkerFuture<R::Item, R::Error>
|
F: FnOnce() -> R + Send + 'static,
|
||||||
where F: FnOnce() -> R + Send + 'static,
|
R: IntoFuture + 'static,
|
||||||
R: IntoFuture + 'static,
|
R::Future: Send + 'static,
|
||||||
R::Future: Send + 'static,
|
R::Item: Send + 'static,
|
||||||
R::Item: Send + 'static,
|
R::Error: Send + 'static,
|
||||||
R::Error: Send + 'static
|
|
||||||
{
|
{
|
||||||
WorkerFuture {
|
WorkerFuture {
|
||||||
future: self.pool.spawn_fn(f)
|
future: self.pool.spawn_fn(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scope<'a, F, R>(
|
pub fn scope<'a, F, R>(&self, elements: usize, f: F) -> R
|
||||||
&self,
|
where
|
||||||
elements: usize,
|
F: FnOnce(&Scope<'a>, usize) -> R,
|
||||||
f: F
|
|
||||||
) -> R
|
|
||||||
where F: FnOnce(&Scope<'a>, usize) -> R
|
|
||||||
{
|
{
|
||||||
let chunk_size = if elements < self.cpus {
|
let chunk_size = if elements < self.cpus {
|
||||||
1
|
1
|
||||||
@@ -63,22 +60,21 @@ mod implementation {
|
|||||||
elements / self.cpus
|
elements / self.cpus
|
||||||
};
|
};
|
||||||
|
|
||||||
crossbeam::scope(|scope| {
|
// TODO: Handle case where threads fail
|
||||||
f(scope, chunk_size)
|
crossbeam::scope(|scope| f(scope, chunk_size))
|
||||||
})
|
.expect("Threads aren't allowed to fail yet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WorkerFuture<T, E> {
|
pub struct WorkerFuture<T, E> {
|
||||||
future: CpuFuture<T, E>
|
future: CpuFuture<T, E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
|
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
type Error = E;
|
type Error = E;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error>
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
{
|
|
||||||
self.future.poll()
|
self.future.poll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,7 +84,7 @@ mod implementation {
|
|||||||
|
|
||||||
let mut pow = 0;
|
let mut pow = 0;
|
||||||
|
|
||||||
while (1 << (pow+1)) <= num {
|
while (1 << (pow + 1)) <= num {
|
||||||
pow += 1;
|
pow += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +155,8 @@ mod implementation {
|
|||||||
pub struct DummyScope;
|
pub struct DummyScope;
|
||||||
|
|
||||||
impl DummyScope {
|
impl DummyScope {
|
||||||
pub fn spawn<F: FnOnce()>(&self, f: F) {
|
pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
|
||||||
f();
|
f(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
|
||||||
use group::{CurveAffine, CurveProjective};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::io;
|
|
||||||
use bit_vec::{self, BitVec};
|
|
||||||
use std::iter;
|
|
||||||
use futures::{Future};
|
|
||||||
use super::multicore::Worker;
|
use super::multicore::Worker;
|
||||||
|
use bit_vec::{self, BitVec};
|
||||||
|
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||||
|
use futures::Future;
|
||||||
|
use group::{CurveAffine, CurveProjective};
|
||||||
|
use std::io;
|
||||||
|
use std::iter;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::SynthesisError;
|
use super::SynthesisError;
|
||||||
|
|
||||||
@@ -19,7 +19,10 @@ pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
|
|||||||
/// A source of bases, like an iterator.
|
/// A source of bases, like an iterator.
|
||||||
pub trait Source<G: CurveAffine> {
|
pub trait Source<G: CurveAffine> {
|
||||||
/// Parses the element from the source. Fails if the point is at infinity.
|
/// Parses the element from the source. Fails if the point is at infinity.
|
||||||
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError>;
|
fn add_assign_mixed(
|
||||||
|
&mut self,
|
||||||
|
to: &mut <G as CurveAffine>::Projective,
|
||||||
|
) -> Result<(), SynthesisError>;
|
||||||
|
|
||||||
/// Skips `amt` elements from the source, avoiding deserialization.
|
/// Skips `amt` elements from the source, avoiding deserialization.
|
||||||
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
|
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
|
||||||
@@ -34,13 +37,20 @@ impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
||||||
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError> {
|
fn add_assign_mixed(
|
||||||
|
&mut self,
|
||||||
|
to: &mut <G as CurveAffine>::Projective,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
if self.0.len() <= self.1 {
|
if self.0.len() <= self.1 {
|
||||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
"expected more bases from source",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.0[self.1].is_zero() {
|
if self.0[self.1].is_zero() {
|
||||||
return Err(SynthesisError::UnexpectedIdentity)
|
return Err(SynthesisError::UnexpectedIdentity);
|
||||||
}
|
}
|
||||||
|
|
||||||
to.add_assign_mixed(&self.0[self.1]);
|
to.add_assign_mixed(&self.0[self.1]);
|
||||||
@@ -52,7 +62,11 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
|||||||
|
|
||||||
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
|
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
|
||||||
if self.0.len() <= self.1 {
|
if self.0.len() <= self.1 {
|
||||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
"expected more bases from source",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.1 += amt;
|
self.1 += amt;
|
||||||
@@ -63,7 +77,7 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
|||||||
|
|
||||||
pub trait QueryDensity {
|
pub trait QueryDensity {
|
||||||
/// Returns whether the base exists.
|
/// Returns whether the base exists.
|
||||||
type Iter: Iterator<Item=bool>;
|
type Iter: Iterator<Item = bool>;
|
||||||
|
|
||||||
fn iter(self) -> Self::Iter;
|
fn iter(self) -> Self::Iter;
|
||||||
fn get_query_size(self) -> Option<usize>;
|
fn get_query_size(self) -> Option<usize>;
|
||||||
@@ -92,7 +106,7 @@ impl<'a> QueryDensity for &'a FullDensity {
|
|||||||
|
|
||||||
pub struct DensityTracker {
|
pub struct DensityTracker {
|
||||||
bv: BitVec,
|
bv: BitVec,
|
||||||
total_density: usize
|
total_density: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> QueryDensity for &'a DensityTracker {
|
impl<'a> QueryDensity for &'a DensityTracker {
|
||||||
@@ -111,7 +125,7 @@ impl DensityTracker {
|
|||||||
pub fn new() -> DensityTracker {
|
pub fn new() -> DensityTracker {
|
||||||
DensityTracker {
|
DensityTracker {
|
||||||
bv: BitVec::new(),
|
bv: BitVec::new(),
|
||||||
total_density: 0
|
total_density: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,12 +152,13 @@ fn multiexp_inner<Q, D, G, S>(
|
|||||||
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
||||||
mut skip: u32,
|
mut skip: u32,
|
||||||
c: u32,
|
c: u32,
|
||||||
handle_trivial: bool
|
handle_trivial: bool,
|
||||||
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
|
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||||
where for<'a> &'a Q: QueryDensity,
|
where
|
||||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
for<'a> &'a Q: QueryDensity,
|
||||||
G: CurveAffine,
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
S: SourceBuilder<G>
|
G: CurveAffine,
|
||||||
|
S: SourceBuilder<G>,
|
||||||
{
|
{
|
||||||
// Perform this region of the multiexp
|
// Perform this region of the multiexp
|
||||||
let this = {
|
let this = {
|
||||||
@@ -212,16 +227,24 @@ fn multiexp_inner<Q, D, G, S>(
|
|||||||
// There's another region more significant. Calculate and join it with
|
// There's another region more significant. Calculate and join it with
|
||||||
// this region recursively.
|
// this region recursively.
|
||||||
Box::new(
|
Box::new(
|
||||||
this.join(multiexp_inner(pool, bases, density_map, exponents, skip, c, false))
|
this.join(multiexp_inner(
|
||||||
.map(move |(this, mut higher)| {
|
pool,
|
||||||
for _ in 0..c {
|
bases,
|
||||||
higher.double();
|
density_map,
|
||||||
}
|
exponents,
|
||||||
|
skip,
|
||||||
|
c,
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
.map(move |(this, mut higher)| {
|
||||||
|
for _ in 0..c {
|
||||||
|
higher.double();
|
||||||
|
}
|
||||||
|
|
||||||
higher.add_assign(&this);
|
higher.add_assign(&this);
|
||||||
|
|
||||||
higher
|
higher
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,12 +255,13 @@ pub fn multiexp<Q, D, G, S>(
|
|||||||
pool: &Worker,
|
pool: &Worker,
|
||||||
bases: S,
|
bases: S,
|
||||||
density_map: D,
|
density_map: D,
|
||||||
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>
|
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
||||||
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
|
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||||
where for<'a> &'a Q: QueryDensity,
|
where
|
||||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
for<'a> &'a Q: QueryDensity,
|
||||||
G: CurveAffine,
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
S: SourceBuilder<G>
|
G: CurveAffine,
|
||||||
|
S: SourceBuilder<G>,
|
||||||
{
|
{
|
||||||
let c = if exponents.len() < 32 {
|
let c = if exponents.len() < 32 {
|
||||||
3u32
|
3u32
|
||||||
@@ -260,9 +284,8 @@ pub fn multiexp<Q, D, G, S>(
|
|||||||
fn test_with_bls12() {
|
fn test_with_bls12() {
|
||||||
fn naive_multiexp<G: CurveAffine>(
|
fn naive_multiexp<G: CurveAffine>(
|
||||||
bases: Arc<Vec<G>>,
|
bases: Arc<Vec<G>>,
|
||||||
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>
|
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
|
||||||
) -> G::Projective
|
) -> G::Projective {
|
||||||
{
|
|
||||||
assert_eq!(bases.len(), exponents.len());
|
assert_eq!(bases.len(), exponents.len());
|
||||||
|
|
||||||
let mut acc = G::Projective::zero();
|
let mut acc = G::Projective::zero();
|
||||||
@@ -274,25 +297,28 @@ fn test_with_bls12() {
|
|||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
use rand::{self, Rand};
|
|
||||||
use pairing::{bls12_381::Bls12, Engine};
|
use pairing::{bls12_381::Bls12, Engine};
|
||||||
|
use rand;
|
||||||
|
|
||||||
const SAMPLES: usize = 1 << 14;
|
const SAMPLES: usize = 1 << 14;
|
||||||
|
|
||||||
let rng = &mut rand::thread_rng();
|
let rng = &mut rand::thread_rng();
|
||||||
let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as ScalarEngine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>());
|
let v = Arc::new(
|
||||||
let g = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::G1::rand(rng).into_affine()).collect::<Vec<_>>());
|
(0..SAMPLES)
|
||||||
|
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng).into_repr())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
let g = Arc::new(
|
||||||
|
(0..SAMPLES)
|
||||||
|
.map(|_| <Bls12 as Engine>::G1::random(rng).into_affine())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
let naive = naive_multiexp(g.clone(), v.clone());
|
let naive = naive_multiexp(g.clone(), v.clone());
|
||||||
|
|
||||||
let pool = Worker::new();
|
let pool = Worker::new();
|
||||||
|
|
||||||
let fast = multiexp(
|
let fast = multiexp(&pool, (g, 0), FullDensity, v).wait().unwrap();
|
||||||
&pool,
|
|
||||||
(g, 0),
|
|
||||||
FullDensity,
|
|
||||||
v
|
|
||||||
).wait().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(naive, fast);
|
assert_eq!(naive, fast);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,29 @@
|
|||||||
extern crate bellman;
|
|
||||||
extern crate ff;
|
|
||||||
extern crate pairing;
|
|
||||||
extern crate rand;
|
|
||||||
|
|
||||||
// For randomness (during paramgen and proof generation)
|
// For randomness (during paramgen and proof generation)
|
||||||
use rand::{thread_rng, Rng};
|
use rand::thread_rng;
|
||||||
|
|
||||||
// For benchmarking
|
// For benchmarking
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
// Bring in some tools for using pairing-friendly curves
|
// Bring in some tools for using pairing-friendly curves
|
||||||
use ff::Field;
|
use ff::{Field, ScalarEngine};
|
||||||
use pairing::Engine;
|
use pairing::Engine;
|
||||||
|
|
||||||
// We're going to use the BLS12-381 pairing-friendly elliptic curve.
|
// We're going to use the BLS12-381 pairing-friendly elliptic curve.
|
||||||
use pairing::bls12_381::{
|
use pairing::bls12_381::Bls12;
|
||||||
Bls12
|
|
||||||
};
|
|
||||||
|
|
||||||
// We'll use these interfaces to construct our circuit.
|
// We'll use these interfaces to construct our circuit.
|
||||||
use bellman::{
|
use bellman::{Circuit, ConstraintSystem, SynthesisError};
|
||||||
Circuit,
|
|
||||||
ConstraintSystem,
|
|
||||||
SynthesisError
|
|
||||||
};
|
|
||||||
|
|
||||||
// We're going to use the Groth16 proving system.
|
// We're going to use the Groth16 proving system.
|
||||||
use bellman::groth16::{
|
use bellman::groth16::{
|
||||||
Proof,
|
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof,
|
||||||
generate_random_parameters,
|
|
||||||
prepare_verifying_key,
|
|
||||||
create_random_proof,
|
|
||||||
verify_proof,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const MIMC_ROUNDS: usize = 322;
|
const MIMC_ROUNDS: usize = 322;
|
||||||
|
|
||||||
/// This is an implementation of MiMC, specifically a
|
/// This is an implementation of MiMC, specifically a
|
||||||
/// variant named `LongsightF322p3` for BLS12-381.
|
/// variant named `LongsightF322p3` for BLS12-381.
|
||||||
/// See http://eprint.iacr.org/2016/492 for more
|
/// See http://eprint.iacr.org/2016/492 for more
|
||||||
/// information about this construction.
|
/// information about this construction.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@@ -49,12 +34,7 @@ const MIMC_ROUNDS: usize = 322;
|
|||||||
/// return xL
|
/// return xL
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn mimc<E: Engine>(
|
fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr {
|
||||||
mut xl: E::Fr,
|
|
||||||
mut xr: E::Fr,
|
|
||||||
constants: &[E::Fr]
|
|
||||||
) -> E::Fr
|
|
||||||
{
|
|
||||||
assert_eq!(constants.len(), MIMC_ROUNDS);
|
assert_eq!(constants.len(), MIMC_ROUNDS);
|
||||||
|
|
||||||
for i in 0..MIMC_ROUNDS {
|
for i in 0..MIMC_ROUNDS {
|
||||||
@@ -76,80 +56,81 @@ fn mimc<E: Engine>(
|
|||||||
struct MiMCDemo<'a, E: Engine> {
|
struct MiMCDemo<'a, E: Engine> {
|
||||||
xl: Option<E::Fr>,
|
xl: Option<E::Fr>,
|
||||||
xr: Option<E::Fr>,
|
xr: Option<E::Fr>,
|
||||||
constants: &'a [E::Fr]
|
constants: &'a [E::Fr],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Our demo circuit implements this `Circuit` trait which
|
/// Our demo circuit implements this `Circuit` trait which
|
||||||
/// is used during paramgen and proving in order to
|
/// is used during paramgen and proving in order to
|
||||||
/// synthesize the constraint system.
|
/// synthesize the constraint system.
|
||||||
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
|
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
|
||||||
fn synthesize<CS: ConstraintSystem<E>>(
|
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||||
self,
|
|
||||||
cs: &mut CS
|
|
||||||
) -> Result<(), SynthesisError>
|
|
||||||
{
|
|
||||||
assert_eq!(self.constants.len(), MIMC_ROUNDS);
|
assert_eq!(self.constants.len(), MIMC_ROUNDS);
|
||||||
|
|
||||||
// Allocate the first component of the preimage.
|
// Allocate the first component of the preimage.
|
||||||
let mut xl_value = self.xl;
|
let mut xl_value = self.xl;
|
||||||
let mut xl = cs.alloc(|| "preimage xl", || {
|
let mut xl = cs.alloc(
|
||||||
xl_value.ok_or(SynthesisError::AssignmentMissing)
|
|| "preimage xl",
|
||||||
})?;
|
|| xl_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?;
|
||||||
|
|
||||||
// Allocate the second component of the preimage.
|
// Allocate the second component of the preimage.
|
||||||
let mut xr_value = self.xr;
|
let mut xr_value = self.xr;
|
||||||
let mut xr = cs.alloc(|| "preimage xr", || {
|
let mut xr = cs.alloc(
|
||||||
xr_value.ok_or(SynthesisError::AssignmentMissing)
|
|| "preimage xr",
|
||||||
})?;
|
|| xr_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?;
|
||||||
|
|
||||||
for i in 0..MIMC_ROUNDS {
|
for i in 0..MIMC_ROUNDS {
|
||||||
// xL, xR := xR + (xL + Ci)^3, xL
|
// xL, xR := xR + (xL + Ci)^3, xL
|
||||||
let cs = &mut cs.namespace(|| format!("round {}", i));
|
let cs = &mut cs.namespace(|| format!("round {}", i));
|
||||||
|
|
||||||
// tmp = (xL + Ci)^2
|
// tmp = (xL + Ci)^2
|
||||||
let mut tmp_value = xl_value.map(|mut e| {
|
let tmp_value = xl_value.map(|mut e| {
|
||||||
e.add_assign(&self.constants[i]);
|
e.add_assign(&self.constants[i]);
|
||||||
e.square();
|
e.square();
|
||||||
e
|
e
|
||||||
});
|
});
|
||||||
let mut tmp = cs.alloc(|| "tmp", || {
|
let tmp = cs.alloc(
|
||||||
tmp_value.ok_or(SynthesisError::AssignmentMissing)
|
|| "tmp",
|
||||||
})?;
|
|| tmp_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?;
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "tmp = (xL + Ci)^2",
|
|| "tmp = (xL + Ci)^2",
|
||||||
|lc| lc + xl + (self.constants[i], CS::one()),
|
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||||
|lc| lc + xl + (self.constants[i], CS::one()),
|
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||||
|lc| lc + tmp
|
|lc| lc + tmp,
|
||||||
);
|
);
|
||||||
|
|
||||||
// new_xL = xR + (xL + Ci)^3
|
// new_xL = xR + (xL + Ci)^3
|
||||||
// new_xL = xR + tmp * (xL + Ci)
|
// new_xL = xR + tmp * (xL + Ci)
|
||||||
// new_xL - xR = tmp * (xL + Ci)
|
// new_xL - xR = tmp * (xL + Ci)
|
||||||
let 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.add_assign(&self.constants[i]);
|
||||||
e.mul_assign(&tmp_value.unwrap());
|
e.mul_assign(&tmp_value.unwrap());
|
||||||
e.add_assign(&xr_value.unwrap());
|
e.add_assign(&xr_value.unwrap());
|
||||||
e
|
e
|
||||||
});
|
});
|
||||||
|
|
||||||
let 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
|
// This is the last round, xL is our image and so
|
||||||
// we allocate a public input.
|
// we allocate a public input.
|
||||||
cs.alloc_input(|| "image", || {
|
cs.alloc_input(
|
||||||
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
|
|| "image",
|
||||||
})?
|
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?
|
||||||
} else {
|
} else {
|
||||||
cs.alloc(|| "new_xl", || {
|
cs.alloc(
|
||||||
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
|
|| "new_xl",
|
||||||
})?
|
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
|
||||||
|
)?
|
||||||
};
|
};
|
||||||
|
|
||||||
cs.enforce(
|
cs.enforce(
|
||||||
|| "new_xL = xR + (xL + Ci)^3",
|
|| "new_xL = xR + (xL + Ci)^3",
|
||||||
|lc| lc + tmp,
|
|lc| lc + tmp,
|
||||||
|lc| lc + xl + (self.constants[i], CS::one()),
|
|lc| lc + xl + (self.constants[i], CS::one()),
|
||||||
|lc| lc + new_xl - xr
|
|lc| lc + new_xl - xr,
|
||||||
);
|
);
|
||||||
|
|
||||||
// xR = xL
|
// xR = xL
|
||||||
@@ -172,7 +153,9 @@ fn test_mimc() {
|
|||||||
let rng = &mut thread_rng();
|
let rng = &mut thread_rng();
|
||||||
|
|
||||||
// Generate the MiMC round constants
|
// Generate the MiMC round constants
|
||||||
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
|
let constants = (0..MIMC_ROUNDS)
|
||||||
|
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
println!("Creating parameters...");
|
println!("Creating parameters...");
|
||||||
|
|
||||||
@@ -181,7 +164,7 @@ fn test_mimc() {
|
|||||||
let c = MiMCDemo::<Bls12> {
|
let c = MiMCDemo::<Bls12> {
|
||||||
xl: None,
|
xl: None,
|
||||||
xr: None,
|
xr: None,
|
||||||
constants: &constants
|
constants: &constants,
|
||||||
};
|
};
|
||||||
|
|
||||||
generate_random_parameters(c, rng).unwrap()
|
generate_random_parameters(c, rng).unwrap()
|
||||||
@@ -203,8 +186,8 @@ fn test_mimc() {
|
|||||||
|
|
||||||
for _ in 0..SAMPLES {
|
for _ in 0..SAMPLES {
|
||||||
// Generate a random preimage and compute the image
|
// Generate a random preimage and compute the image
|
||||||
let xl = rng.gen();
|
let xl = <Bls12 as ScalarEngine>::Fr::random(rng);
|
||||||
let xr = rng.gen();
|
let xr = <Bls12 as ScalarEngine>::Fr::random(rng);
|
||||||
let image = mimc::<Bls12>(xl, xr, &constants);
|
let image = mimc::<Bls12>(xl, xr, &constants);
|
||||||
|
|
||||||
proof_vec.truncate(0);
|
proof_vec.truncate(0);
|
||||||
@@ -216,7 +199,7 @@ fn test_mimc() {
|
|||||||
let c = MiMCDemo {
|
let c = MiMCDemo {
|
||||||
xl: Some(xl),
|
xl: Some(xl),
|
||||||
xr: Some(xr),
|
xr: Some(xr),
|
||||||
constants: &constants
|
constants: &constants,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a groth16 proof with our parameters.
|
// Create a groth16 proof with our parameters.
|
||||||
@@ -230,20 +213,16 @@ fn test_mimc() {
|
|||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let proof = Proof::read(&proof_vec[..]).unwrap();
|
let proof = Proof::read(&proof_vec[..]).unwrap();
|
||||||
// Check the proof
|
// Check the proof
|
||||||
assert!(verify_proof(
|
assert!(verify_proof(&pvk, &proof, &[image]).unwrap());
|
||||||
&pvk,
|
|
||||||
&proof,
|
|
||||||
&[image]
|
|
||||||
).unwrap());
|
|
||||||
total_verifying += start.elapsed();
|
total_verifying += start.elapsed();
|
||||||
}
|
}
|
||||||
let proving_avg = total_proving / SAMPLES;
|
let proving_avg = total_proving / SAMPLES;
|
||||||
let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64
|
let proving_avg =
|
||||||
+ (proving_avg.as_secs() as f64);
|
proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64);
|
||||||
|
|
||||||
let verifying_avg = total_verifying / SAMPLES;
|
let verifying_avg = total_verifying / SAMPLES;
|
||||||
let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64
|
let verifying_avg =
|
||||||
+ (verifying_avg.as_secs() as f64);
|
verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64);
|
||||||
|
|
||||||
println!("Average proving time: {:?} seconds", proving_avg);
|
println!("Average proving time: {:?} seconds", proving_avg);
|
||||||
println!("Average verifying time: {:?} seconds", verifying_avg);
|
println!("Average verifying time: {:?} seconds", verifying_avg);
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
version = "0.4.0"
|
version = "0.5.2"
|
||||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||||
description = "Library for building and interfacing with finite fields"
|
description = "Library for building and interfacing with finite fields"
|
||||||
|
readme = "README.md"
|
||||||
documentation = "https://docs.rs/ff/"
|
documentation = "https://docs.rs/ff/"
|
||||||
homepage = "https://github.com/ebfull/ff"
|
homepage = "https://github.com/ebfull/ff"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
repository = "https://github.com/ebfull/ff"
|
repository = "https://github.com/ebfull/ff"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
rand = "0.4"
|
ff_derive = { version = "^0.4.1", path = "ff_derive", optional = true }
|
||||||
ff_derive = { version = "0.3.0", path = "ff_derive", optional = true }
|
rand_core = "0.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
derive = ["ff_derive"]
|
derive = ["ff_derive"]
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
17
ff/README.md
17
ff/README.md
@@ -12,14 +12,18 @@ Add the `ff` crate to your `Cargo.toml`:
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[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)]
|
### #![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:
|
First, enable the `derive` crate feature:
|
||||||
|
|
||||||
@@ -41,13 +45,16 @@ extern crate ff;
|
|||||||
struct Fp(FpRepr);
|
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
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
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)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ff_derive"
|
name = "ff_derive"
|
||||||
version = "0.3.0"
|
version = "0.4.1"
|
||||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||||
description = "Procedural macro library used to build custom prime field implementations"
|
description = "Procedural macro library used to build custom prime field implementations"
|
||||||
documentation = "https://docs.rs/ff/"
|
documentation = "https://docs.rs/ff/"
|
||||||
homepage = "https://github.com/ebfull/ff"
|
homepage = "https://github.com/ebfull/ff"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
repository = "https://github.com/ebfull/ff"
|
repository = "https://github.com/ebfull/ff"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
@@ -15,6 +16,9 @@ proc-macro = true
|
|||||||
num-bigint = "0.2"
|
num-bigint = "0.2"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num-integer = "0.1"
|
num-integer = "0.1"
|
||||||
proc-macro2 = "0.4"
|
proc-macro2 = "1"
|
||||||
quote = "0.6"
|
quote = "1"
|
||||||
syn = "0.14"
|
syn = "1"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "passively-maintained" }
|
||||||
|
|||||||
@@ -2,17 +2,11 @@
|
|||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
extern crate proc_macro2;
|
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_bigint::BigUint;
|
||||||
use num_integer::Integer;
|
use num_integer::Integer;
|
||||||
use num_traits::{One, ToPrimitive, Zero};
|
use num_traits::{One, ToPrimitive, Zero};
|
||||||
|
use quote::quote;
|
||||||
use quote::TokenStreamExt;
|
use quote::TokenStreamExt;
|
||||||
use std::str::FromStr;
|
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 mut gen = proc_macro2::TokenStream::new();
|
||||||
|
|
||||||
let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt(
|
let (constants_impl, sqrt_impl) =
|
||||||
&ast.ident,
|
prime_field_constants_and_sqrt(&ast.ident, &repr_ident, modulus, limbs, generator);
|
||||||
&repr_ident,
|
|
||||||
modulus,
|
|
||||||
limbs,
|
|
||||||
generator,
|
|
||||||
);
|
|
||||||
|
|
||||||
gen.extend(constants_impl);
|
gen.extend(constants_impl);
|
||||||
gen.extend(prime_field_repr_impl(&repr_ident, limbs));
|
gen.extend(prime_field_repr_impl(&repr_ident, limbs));
|
||||||
@@ -96,10 +85,10 @@ fn fetch_wrapped_ident(body: &syn::Data) -> Option<syn::Ident> {
|
|||||||
/// Fetch an attribute string from the derived struct.
|
/// Fetch an attribute string from the derived struct.
|
||||||
fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> {
|
fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> {
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if let Some(meta) = attr.interpret_meta() {
|
if let Ok(meta) = attr.parse_meta() {
|
||||||
match meta {
|
match meta {
|
||||||
syn::Meta::NameValue(nv) => {
|
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 {
|
match nv.lit {
|
||||||
syn::Lit::Str(ref s) => return Some(s.value()),
|
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
|
impl ::std::fmt::Debug for #repr
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
try!(write!(f, "0x"));
|
write!(f, "0x")?;
|
||||||
for i in self.0.iter().rev() {
|
for i in self.0.iter().rev() {
|
||||||
try!(write!(f, "{:016x}", *i));
|
write!(f, "{:016x}", *i)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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 {
|
impl ::std::fmt::Display for #repr {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
try!(write!(f, "0x"));
|
write!(f, "0x")?;
|
||||||
for i in self.0.iter().rev() {
|
for i in self.0.iter().rev() {
|
||||||
try!(write!(f, "{:016x}", *i));
|
write!(f, "{:016x}", *i)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -366,7 +348,8 @@ fn biguint_num_bits(mut v: BigUint) -> u32 {
|
|||||||
fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
|
fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
|
||||||
let mut ret = BigUint::one();
|
let mut ret = BigUint::one();
|
||||||
|
|
||||||
for i in exp.to_bytes_be()
|
for i in exp
|
||||||
|
.to_bytes_be()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd()))
|
.flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd()))
|
||||||
{
|
{
|
||||||
@@ -387,11 +370,13 @@ fn test_exp() {
|
|||||||
&BigUint::from_str("5489673498567349856734895").unwrap(),
|
&BigUint::from_str("5489673498567349856734895").unwrap(),
|
||||||
&BigUint::from_str(
|
&BigUint::from_str(
|
||||||
"52435875175126190479447740508185965837690552500527637822603658699938581184513"
|
"52435875175126190479447740508185965837690552500527637822603658699938581184513"
|
||||||
).unwrap()
|
)
|
||||||
|
.unwrap()
|
||||||
),
|
),
|
||||||
BigUint::from_str(
|
BigUint::from_str(
|
||||||
"4371221214068404307866768905142520595925044802278091865033317963560480051536"
|
"4371221214068404307866768905142520595925044802278091865033317963560480051536"
|
||||||
).unwrap()
|
)
|
||||||
|
.unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +415,7 @@ fn prime_field_constants_and_sqrt(
|
|||||||
|
|
||||||
let mod_minus_1_over_2 =
|
let mod_minus_1_over_2 =
|
||||||
biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs);
|
biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs);
|
||||||
let legendre_impl = quote!{
|
let legendre_impl = quote! {
|
||||||
fn legendre(&self) -> ::ff::LegendreSymbol {
|
fn legendre(&self) -> ::ff::LegendreSymbol {
|
||||||
// s = self^((modulus - 1) // 2)
|
// s = self^((modulus - 1) // 2)
|
||||||
let s = self.pow(#mod_minus_1_over_2);
|
let s = self.pow(#mod_minus_1_over_2);
|
||||||
@@ -452,7 +437,7 @@ fn prime_field_constants_and_sqrt(
|
|||||||
// Compute -R as (m - r)
|
// Compute -R as (m - r)
|
||||||
let rneg = biguint_to_u64_vec(&modulus - &r, limbs);
|
let rneg = biguint_to_u64_vec(&modulus - &r, limbs);
|
||||||
|
|
||||||
quote!{
|
quote! {
|
||||||
impl ::ff::SqrtField for #name {
|
impl ::ff::SqrtField for #name {
|
||||||
#legendre_impl
|
#legendre_impl
|
||||||
|
|
||||||
@@ -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_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs);
|
||||||
let t = biguint_to_u64_vec(t.clone(), limbs);
|
let t = biguint_to_u64_vec(t.clone(), limbs);
|
||||||
|
|
||||||
quote!{
|
quote! {
|
||||||
impl ::ff::SqrtField for #name {
|
impl ::ff::SqrtField for #name {
|
||||||
#legendre_impl
|
#legendre_impl
|
||||||
|
|
||||||
@@ -526,7 +511,7 @@ fn prime_field_constants_and_sqrt(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote!{}
|
quote! {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compute R^2 mod m
|
// Compute R^2 mod m
|
||||||
@@ -543,36 +528,39 @@ fn prime_field_constants_and_sqrt(
|
|||||||
}
|
}
|
||||||
inv = inv.wrapping_neg();
|
inv = inv.wrapping_neg();
|
||||||
|
|
||||||
(quote! {
|
(
|
||||||
/// This is the modulus m of the prime field
|
quote! {
|
||||||
const MODULUS: #repr = #repr([#(#modulus,)*]);
|
/// This is the modulus m of the prime field
|
||||||
|
const MODULUS: #repr = #repr([#(#modulus,)*]);
|
||||||
|
|
||||||
/// The number of bits needed to represent the modulus.
|
/// The number of bits needed to represent the modulus.
|
||||||
const MODULUS_BITS: u32 = #modulus_num_bits;
|
const MODULUS_BITS: u32 = #modulus_num_bits;
|
||||||
|
|
||||||
/// The number of bits that must be shaved from the beginning of
|
/// The number of bits that must be shaved from the beginning of
|
||||||
/// the representation when randomly sampling.
|
/// the representation when randomly sampling.
|
||||||
const REPR_SHAVE_BITS: u32 = #repr_shave_bits;
|
const REPR_SHAVE_BITS: u32 = #repr_shave_bits;
|
||||||
|
|
||||||
/// 2^{limbs*64} mod m
|
/// 2^{limbs*64} mod m
|
||||||
const R: #repr = #repr(#r);
|
const R: #repr = #repr(#r);
|
||||||
|
|
||||||
/// 2^{limbs*64*2} mod m
|
/// 2^{limbs*64*2} mod m
|
||||||
const R2: #repr = #repr(#r2);
|
const R2: #repr = #repr(#r2);
|
||||||
|
|
||||||
/// -(m^{-1} mod m) mod m
|
/// -(m^{-1} mod m) mod m
|
||||||
const INV: u64 = #inv;
|
const INV: u64 = #inv;
|
||||||
|
|
||||||
/// Multiplicative generator of `MODULUS` - 1 order, also quadratic
|
/// Multiplicative generator of `MODULUS` - 1 order, also quadratic
|
||||||
/// nonresidue.
|
/// nonresidue.
|
||||||
const GENERATOR: #repr = #repr(#generator);
|
const GENERATOR: #repr = #repr(#generator);
|
||||||
|
|
||||||
/// 2^s * t = MODULUS - 1 with t odd
|
/// 2^s * t = MODULUS - 1 with t odd
|
||||||
const S: u32 = #s;
|
const S: u32 = #s;
|
||||||
|
|
||||||
/// 2^s root of unity computed by GENERATOR^t
|
/// 2^s root of unity computed by GENERATOR^t
|
||||||
const ROOT_OF_UNITY: #repr = #repr(#root_of_unity);
|
const ROOT_OF_UNITY: #repr = #repr(#root_of_unity);
|
||||||
}, sqrt_impl)
|
},
|
||||||
|
sqrt_impl,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implement PrimeField for the derived type.
|
/// Implement PrimeField for the derived type.
|
||||||
@@ -592,9 +580,9 @@ fn prime_field_impl(
|
|||||||
mont_paramlist.append_separated(
|
mont_paramlist.append_separated(
|
||||||
(0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| {
|
(0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
quote!{mut #x: u64}
|
quote! {mut #x: u64}
|
||||||
} else {
|
} else {
|
||||||
quote!{#x: u64}
|
quote! {#x: u64}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
||||||
@@ -607,7 +595,7 @@ fn prime_field_impl(
|
|||||||
for i in 0..limbs {
|
for i in 0..limbs {
|
||||||
{
|
{
|
||||||
let temp = get_temp(i);
|
let temp = get_temp(i);
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let k = #temp.wrapping_mul(INV);
|
let k = #temp.wrapping_mul(INV);
|
||||||
let mut carry = 0;
|
let mut carry = 0;
|
||||||
::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry);
|
::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry);
|
||||||
@@ -616,7 +604,7 @@ fn prime_field_impl(
|
|||||||
|
|
||||||
for j in 1..limbs {
|
for j in 1..limbs {
|
||||||
let temp = get_temp(i + j);
|
let temp = get_temp(i + j);
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
#temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry);
|
#temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -624,17 +612,17 @@ fn prime_field_impl(
|
|||||||
let temp = get_temp(i + limbs);
|
let temp = get_temp(i + limbs);
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
#temp = ::ff::adc(#temp, 0, &mut carry);
|
#temp = ::ff::adc(#temp, 0, &mut carry);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
#temp = ::ff::adc(#temp, carry2, &mut carry);
|
#temp = ::ff::adc(#temp, carry2, &mut carry);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if i != (limbs - 1) {
|
if i != (limbs - 1) {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let carry2 = carry;
|
let carry2 = carry;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -643,7 +631,7 @@ fn prime_field_impl(
|
|||||||
for i in 0..limbs {
|
for i in 0..limbs {
|
||||||
let temp = get_temp(limbs + i);
|
let temp = get_temp(limbs + i);
|
||||||
|
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
(self.0).0[#i] = #temp;
|
(self.0).0[#i] = #temp;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -655,14 +643,14 @@ fn prime_field_impl(
|
|||||||
let mut gen = proc_macro2::TokenStream::new();
|
let mut gen = proc_macro2::TokenStream::new();
|
||||||
|
|
||||||
for i in 0..(limbs - 1) {
|
for i in 0..(limbs - 1) {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let mut carry = 0;
|
let mut carry = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
for j in (i + 1)..limbs {
|
for j in (i + 1)..limbs {
|
||||||
let temp = get_temp(i + j);
|
let temp = get_temp(i + j);
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry);
|
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -674,7 +662,7 @@ fn prime_field_impl(
|
|||||||
|
|
||||||
let temp = get_temp(i + limbs);
|
let temp = get_temp(i + limbs);
|
||||||
|
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let #temp = carry;
|
let #temp = carry;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -684,21 +672,21 @@ fn prime_field_impl(
|
|||||||
let temp1 = get_temp(limbs * 2 - i - 1);
|
let temp1 = get_temp(limbs * 2 - i - 1);
|
||||||
|
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let #temp0 = #temp1 >> 63;
|
let #temp0 = #temp1 >> 63;
|
||||||
});
|
});
|
||||||
} else if i == (limbs * 2 - 1) {
|
} else if i == (limbs * 2 - 1) {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let #temp0 = #temp0 << 1;
|
let #temp0 = #temp0 << 1;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let #temp0 = (#temp0 << 1) | (#temp1 >> 63);
|
let #temp0 = (#temp0 << 1) | (#temp1 >> 63);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let mut carry = 0;
|
let mut carry = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -706,7 +694,7 @@ fn prime_field_impl(
|
|||||||
let temp0 = get_temp(i * 2);
|
let temp0 = get_temp(i * 2);
|
||||||
let temp1 = get_temp(i * 2 + 1);
|
let temp1 = get_temp(i * 2 + 1);
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry);
|
let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -715,7 +703,7 @@ fn prime_field_impl(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let #temp1 = ::ff::adc(#temp1, 0, &mut carry);
|
let #temp1 = ::ff::adc(#temp1, 0, &mut carry);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -726,7 +714,7 @@ fn prime_field_impl(
|
|||||||
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
||||||
);
|
);
|
||||||
|
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
self.mont_reduce(#mont_calling);
|
self.mont_reduce(#mont_calling);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -741,7 +729,7 @@ fn prime_field_impl(
|
|||||||
let mut gen = proc_macro2::TokenStream::new();
|
let mut gen = proc_macro2::TokenStream::new();
|
||||||
|
|
||||||
for i in 0..limbs {
|
for i in 0..limbs {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let mut carry = 0;
|
let mut carry = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -749,7 +737,7 @@ fn prime_field_impl(
|
|||||||
let temp = get_temp(i + j);
|
let temp = get_temp(i + j);
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry);
|
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -761,7 +749,7 @@ fn prime_field_impl(
|
|||||||
|
|
||||||
let temp = get_temp(i + limbs);
|
let temp = get_temp(i + limbs);
|
||||||
|
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
let #temp = carry;
|
let #temp = carry;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -772,29 +760,29 @@ fn prime_field_impl(
|
|||||||
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
||||||
);
|
);
|
||||||
|
|
||||||
gen.extend(quote!{
|
gen.extend(quote! {
|
||||||
self.mont_reduce(#mont_calling);
|
self.mont_reduce(#mont_calling);
|
||||||
});
|
});
|
||||||
|
|
||||||
gen
|
gen
|
||||||
}
|
}
|
||||||
|
|
||||||
let squaring_impl = sqr_impl(quote!{self}, limbs);
|
let squaring_impl = sqr_impl(quote! {self}, limbs);
|
||||||
let multiply_impl = mul_impl(quote!{self}, quote!{other}, limbs);
|
let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs);
|
||||||
let montgomery_impl = mont_impl(limbs);
|
let montgomery_impl = mont_impl(limbs);
|
||||||
|
|
||||||
// (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ...
|
// (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ...
|
||||||
let mut into_repr_params = proc_macro2::TokenStream::new();
|
let mut into_repr_params = proc_macro2::TokenStream::new();
|
||||||
into_repr_params.append_separated(
|
into_repr_params.append_separated(
|
||||||
(0..limbs)
|
(0..limbs)
|
||||||
.map(|i| quote!{ (self.0).0[#i] })
|
.map(|i| quote! { (self.0).0[#i] })
|
||||||
.chain((0..limbs).map(|_| quote!{0})),
|
.chain((0..limbs).map(|_| quote! {0})),
|
||||||
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
||||||
);
|
);
|
||||||
|
|
||||||
let top_limb_index = limbs - 1;
|
let top_limb_index = limbs - 1;
|
||||||
|
|
||||||
quote!{
|
quote! {
|
||||||
impl ::std::marker::Copy for #name { }
|
impl ::std::marker::Copy for #name { }
|
||||||
|
|
||||||
impl ::std::clone::Clone for #name {
|
impl ::std::clone::Clone for #name {
|
||||||
@@ -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 {
|
impl From<#name> for #repr {
|
||||||
fn from(e: #name) -> #repr {
|
fn from(e: #name) -> #repr {
|
||||||
e.into_repr()
|
e.into_repr()
|
||||||
@@ -904,6 +876,26 @@ fn prime_field_impl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::ff::Field for #name {
|
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]
|
#[inline]
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
#name(#repr::from(0))
|
#name(#repr::from(0))
|
||||||
|
|||||||
@@ -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)]
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
extern crate byteorder;
|
|
||||||
extern crate rand;
|
|
||||||
|
|
||||||
#[cfg(feature = "derive")]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate ff_derive;
|
|
||||||
|
|
||||||
#[cfg(feature = "derive")]
|
#[cfg(feature = "derive")]
|
||||||
pub use ff_derive::*;
|
pub use ff_derive::*;
|
||||||
|
|
||||||
|
use rand_core::RngCore;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
/// This trait represents an element of a field.
|
/// This trait represents an element of a field.
|
||||||
pub trait 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.
|
/// Returns the zero element of the field, the additive identity.
|
||||||
fn zero() -> Self;
|
fn zero() -> Self;
|
||||||
|
|
||||||
@@ -100,7 +101,6 @@ pub trait PrimeFieldRepr:
|
|||||||
+ fmt::Debug
|
+ fmt::Debug
|
||||||
+ fmt::Display
|
+ fmt::Display
|
||||||
+ 'static
|
+ 'static
|
||||||
+ rand::Rand
|
|
||||||
+ AsRef<[u64]>
|
+ AsRef<[u64]>
|
||||||
+ AsMut<[u64]>
|
+ AsMut<[u64]>
|
||||||
+ From<u64>
|
+ From<u64>
|
||||||
@@ -207,7 +207,7 @@ impl Error for PrimeFieldDecodingError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display 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 {
|
match *self {
|
||||||
PrimeFieldDecodingError::NotInField(ref repr) => {
|
PrimeFieldDecodingError::NotInField(ref repr) => {
|
||||||
write!(f, "{} is not an element of the field", 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.
|
/// 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
|
/// Convert a biginteger representation into a prime field element, if
|
||||||
/// the number is an element of the field.
|
/// the number is an element of the field.
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "group"
|
name = "group"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Sean Bowe <ewillbefull@gmail.com>",
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
"Jack Grigg <jack@z.cash>",
|
"Jack Grigg <jack@z.cash>",
|
||||||
]
|
]
|
||||||
|
readme = "README.md"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
|
|
||||||
description = "Elliptic curve group traits and utilities"
|
description = "Elliptic curve group traits and utilities"
|
||||||
documentation = "https://docs.rs/group/"
|
documentation = "https://docs.rs/group/"
|
||||||
homepage = "https://github.com/ebfull/group"
|
homepage = "https://github.com/ebfull/group"
|
||||||
repository = "https://github.com/ebfull/group"
|
repository = "https://github.com/ebfull/group"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ff = { path = "../ff" }
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
rand = "0.4"
|
rand = "0.7"
|
||||||
|
rand_xorshift = "0.2"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
# group [](https://crates.io/crates/group) #
|
# group [](https://crates.io/crates/group) #
|
||||||
|
|
||||||
|
`group` is a crate for working with groups over elliptic curves.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
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)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
extern crate ff;
|
// Catch documentation errors caused by code changes.
|
||||||
extern crate rand;
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
|
|
||||||
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
|
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
|
||||||
|
use rand::RngCore;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
@@ -13,23 +14,16 @@ pub use self::wnaf::Wnaf;
|
|||||||
/// Projective representation of an elliptic curve point guaranteed to be
|
/// Projective representation of an elliptic curve point guaranteed to be
|
||||||
/// in the correct prime order subgroup.
|
/// in the correct prime order subgroup.
|
||||||
pub trait CurveProjective:
|
pub trait CurveProjective:
|
||||||
PartialEq
|
PartialEq + Eq + Sized + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
|
||||||
+ Eq
|
|
||||||
+ Sized
|
|
||||||
+ Copy
|
|
||||||
+ Clone
|
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ fmt::Debug
|
|
||||||
+ fmt::Display
|
|
||||||
+ rand::Rand
|
|
||||||
+ 'static
|
|
||||||
{
|
{
|
||||||
type Engine: ScalarEngine<Fr = Self::Scalar>;
|
type Engine: ScalarEngine<Fr = Self::Scalar>;
|
||||||
type Scalar: PrimeField + SqrtField;
|
type Scalar: PrimeField + SqrtField;
|
||||||
type Base: SqrtField;
|
type Base: SqrtField;
|
||||||
type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>;
|
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.
|
/// Returns the additive identity.
|
||||||
fn zero() -> Self;
|
fn zero() -> Self;
|
||||||
|
|
||||||
@@ -185,7 +179,7 @@ impl Error for GroupDecodingError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display 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 {
|
match *self {
|
||||||
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
|
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
|
||||||
write!(f, "{} decoding error: {}", description, err)
|
write!(f, "{} decoding error: {}", description, err)
|
||||||
|
|||||||
@@ -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>() {
|
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.
|
// Negation edge case with zero.
|
||||||
{
|
{
|
||||||
@@ -21,7 +26,7 @@ pub fn curve_tests<G: CurveProjective>() {
|
|||||||
|
|
||||||
// Addition edge cases with zero
|
// Addition edge cases with zero
|
||||||
{
|
{
|
||||||
let mut r = G::rand(&mut rng);
|
let mut r = G::random(&mut rng);
|
||||||
let rcopy = r;
|
let rcopy = r;
|
||||||
r.add_assign(&G::zero());
|
r.add_assign(&G::zero());
|
||||||
assert_eq!(r, rcopy);
|
assert_eq!(r, rcopy);
|
||||||
@@ -45,9 +50,10 @@ pub fn curve_tests<G: CurveProjective>() {
|
|||||||
|
|
||||||
// Transformations
|
// Transformations
|
||||||
{
|
{
|
||||||
let a = G::rand(&mut rng);
|
let a = G::random(&mut rng);
|
||||||
let b = a.into_affine().into_projective();
|
let b = a.into_affine().into_projective();
|
||||||
let c = a.into_affine()
|
let c = a
|
||||||
|
.into_affine()
|
||||||
.into_projective()
|
.into_projective()
|
||||||
.into_affine()
|
.into_affine()
|
||||||
.into_projective();
|
.into_projective();
|
||||||
@@ -65,11 +71,12 @@ pub fn curve_tests<G: CurveProjective>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_wnaf_tests<G: CurveProjective>() {
|
fn random_wnaf_tests<G: CurveProjective>() {
|
||||||
use ff::PrimeField;
|
use crate::wnaf::*;
|
||||||
|
|
||||||
use wnaf::*;
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut table = vec![];
|
let mut table = vec![];
|
||||||
@@ -77,8 +84,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
|
|||||||
|
|
||||||
for w in 2..14 {
|
for w in 2..14 {
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
let g = G::rand(&mut rng);
|
let g = G::random(&mut rng);
|
||||||
let s = G::Scalar::rand(&mut rng).into_repr();
|
let s = G::Scalar::random(&mut rng).into_repr();
|
||||||
let mut g1 = g;
|
let mut g1 = g;
|
||||||
g1.mul_assign(s);
|
g1.mul_assign(s);
|
||||||
|
|
||||||
@@ -95,8 +102,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
|
|||||||
fn only_compiles_if_send<S: Send>(_: &S) {}
|
fn only_compiles_if_send<S: Send>(_: &S) {}
|
||||||
|
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
let g = G::rand(&mut rng);
|
let g = G::random(&mut rng);
|
||||||
let s = G::Scalar::rand(&mut rng).into_repr();
|
let s = G::Scalar::random(&mut rng).into_repr();
|
||||||
let mut g1 = g;
|
let mut g1 = g;
|
||||||
g1.mul_assign(s);
|
g1.mul_assign(s);
|
||||||
|
|
||||||
@@ -129,7 +136,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
|
|||||||
let mut wnaf = Wnaf::new();
|
let mut wnaf = Wnaf::new();
|
||||||
{
|
{
|
||||||
// Populate the vectors.
|
// 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)
|
wnaf.base(g, 1).scalar(s)
|
||||||
};
|
};
|
||||||
@@ -137,7 +145,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
|
|||||||
let mut wnaf = Wnaf::new();
|
let mut wnaf = Wnaf::new();
|
||||||
{
|
{
|
||||||
// Populate the vectors.
|
// 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)
|
wnaf.scalar(s).base(g)
|
||||||
};
|
};
|
||||||
@@ -145,7 +154,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
|
|||||||
let mut wnaf = Wnaf::new();
|
let mut wnaf = Wnaf::new();
|
||||||
{
|
{
|
||||||
// Populate the vectors.
|
// 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();
|
let mut shared = wnaf.base(g, 1).shared();
|
||||||
|
|
||||||
@@ -157,7 +167,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
|
|||||||
let mut wnaf = Wnaf::new();
|
let mut wnaf = Wnaf::new();
|
||||||
{
|
{
|
||||||
// Populate the vectors.
|
// 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();
|
let mut shared = wnaf.scalar(s).shared();
|
||||||
|
|
||||||
@@ -179,14 +190,15 @@ fn random_wnaf_tests<G: CurveProjective>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_negation_tests<G: CurveProjective>() {
|
fn random_negation_tests<G: CurveProjective>() {
|
||||||
use ff::Field;
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
for _ in 0..1000 {
|
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;
|
let mut sneg = s;
|
||||||
sneg.negate();
|
sneg.negate();
|
||||||
|
|
||||||
@@ -210,11 +222,14 @@ fn random_negation_tests<G: CurveProjective>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_doubling_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 {
|
for _ in 0..1000 {
|
||||||
let mut a = G::rand(&mut rng);
|
let mut a = G::random(&mut rng);
|
||||||
let mut b = G::rand(&mut rng);
|
let mut b = G::random(&mut rng);
|
||||||
|
|
||||||
// 2(a + b)
|
// 2(a + b)
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
@@ -237,15 +252,18 @@ fn random_doubling_tests<G: CurveProjective>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_multiplication_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 {
|
for _ in 0..1000 {
|
||||||
let mut a = G::rand(&mut rng);
|
let mut a = G::random(&mut rng);
|
||||||
let mut b = G::rand(&mut rng);
|
let mut b = G::random(&mut rng);
|
||||||
let a_affine = a.into_affine();
|
let a_affine = a.into_affine();
|
||||||
let b_affine = b.into_affine();
|
let b_affine = b.into_affine();
|
||||||
|
|
||||||
let s = G::Scalar::rand(&mut rng);
|
let s = G::Scalar::random(&mut rng);
|
||||||
|
|
||||||
// s ( a + b )
|
// s ( a + b )
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
@@ -269,12 +287,15 @@ fn random_multiplication_tests<G: CurveProjective>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_addition_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 {
|
for _ in 0..1000 {
|
||||||
let a = G::rand(&mut rng);
|
let a = G::random(&mut rng);
|
||||||
let b = G::rand(&mut rng);
|
let b = G::random(&mut rng);
|
||||||
let c = G::rand(&mut rng);
|
let c = G::random(&mut rng);
|
||||||
let a_affine = a.into_affine();
|
let a_affine = a.into_affine();
|
||||||
let b_affine = b.into_affine();
|
let b_affine = b.into_affine();
|
||||||
let c_affine = c.into_affine();
|
let c_affine = c.into_affine();
|
||||||
@@ -347,10 +368,13 @@ fn random_addition_tests<G: CurveProjective>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_transformation_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 {
|
for _ in 0..1000 {
|
||||||
let g = G::rand(&mut rng);
|
let g = G::random(&mut rng);
|
||||||
let g_affine = g.into_affine();
|
let g_affine = g.into_affine();
|
||||||
let g_projective = g_affine.into_projective();
|
let g_projective = g_affine.into_projective();
|
||||||
assert_eq!(g, g_projective);
|
assert_eq!(g, g_projective);
|
||||||
@@ -358,24 +382,25 @@ fn random_transformation_tests<G: CurveProjective>() {
|
|||||||
|
|
||||||
// Batch normalization
|
// Batch normalization
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
let mut v = (0..1000).map(|_| G::rand(&mut rng)).collect::<Vec<_>>();
|
let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::<Vec<_>>();
|
||||||
|
|
||||||
for i in &v {
|
for i in &v {
|
||||||
assert!(!i.is_normalized());
|
assert!(!i.is_normalized());
|
||||||
}
|
}
|
||||||
|
|
||||||
use rand::distributions::{IndependentSample, Range};
|
use rand::distributions::{Distribution, Uniform};
|
||||||
let between = Range::new(0, 1000);
|
let between = Uniform::new(0, 1000);
|
||||||
// Sprinkle in some normalized points
|
// Sprinkle in some normalized points
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
v[between.ind_sample(&mut rng)] = G::zero();
|
v[between.sample(&mut rng)] = G::zero();
|
||||||
}
|
}
|
||||||
for _ in 0..5 {
|
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();
|
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())
|
.map(|v| v.into_affine().into_projective())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
G::batch_normalization(&mut v);
|
G::batch_normalization(&mut v);
|
||||||
@@ -389,7 +414,10 @@ fn random_transformation_tests<G: CurveProjective>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_encoding_tests<G: CurveAffine>() {
|
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!(
|
assert_eq!(
|
||||||
G::zero().into_uncompressed().into_affine().unwrap(),
|
G::zero().into_uncompressed().into_affine().unwrap(),
|
||||||
@@ -402,7 +430,7 @@ fn random_encoding_tests<G: CurveAffine>() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for _ in 0..1000 {
|
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 uncompressed = r.into_uncompressed();
|
||||||
let de_uncompressed = uncompressed.into_affine().unwrap();
|
let de_uncompressed = uncompressed.into_affine().unwrap();
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "librustzcash"
|
name = "librustzcash"
|
||||||
version = "0.1.0"
|
description = "Rust FFI used by the zcashd binary. Not an official API."
|
||||||
|
version = "0.2.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Sean Bowe <ewillbefull@gmail.com>",
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
"Jack Grigg <jack@z.cash>",
|
"Jack Grigg <jack@z.cash>",
|
||||||
"Jay Graber <jay@z.cash>",
|
"Jay Graber <jay@z.cash>",
|
||||||
"Simon Liu <simon@z.cash>"
|
"Simon Liu <simon@z.cash>"
|
||||||
]
|
]
|
||||||
|
homepage = "https://github.com/zcash/librustzcash"
|
||||||
|
repository = "https://github.com/zcash/librustzcash"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "rustzcash"
|
name = "rustzcash"
|
||||||
@@ -14,18 +20,17 @@ path = "src/rustzcash.rs"
|
|||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bellman = { path = "../bellman" }
|
bellman = { version = "0.2.0", path = "../bellman" }
|
||||||
ff = { path = "../ff" }
|
blake2b_simd = "0.5"
|
||||||
|
blake2s_simd = "0.5"
|
||||||
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
pairing = { path = "../pairing" }
|
pairing = { version = "0.15.0", path = "../pairing" }
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
byteorder = "1"
|
rand_core = "0.5.1"
|
||||||
rand = "0.4"
|
zcash_history = { version = "0.0.1", path = "../zcash_history" }
|
||||||
sapling-crypto = { path = "../sapling-crypto" }
|
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||||
zcash_primitives = { path = "../zcash_primitives" }
|
zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
|
||||||
zcash_proofs = { path = "../zcash_proofs" }
|
|
||||||
zip32 = { path = "../zip32" }
|
|
||||||
|
|
||||||
[dependencies.blake2-rfc]
|
[badges]
|
||||||
git = "https://github.com/gtank/blake2-rfc"
|
maintenance = { status = "deprecated" }
|
||||||
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
|
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
# librustzcash
|
# 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
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
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)
|
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -112,8 +112,7 @@ extern "C" {
|
|||||||
bool librustzcash_sapling_output_proof(
|
bool librustzcash_sapling_output_proof(
|
||||||
void *ctx,
|
void *ctx,
|
||||||
const unsigned char *esk,
|
const unsigned char *esk,
|
||||||
const unsigned char *diversifier,
|
const unsigned char *payment_address,
|
||||||
const unsigned char *pk_d,
|
|
||||||
const unsigned char *rcm,
|
const unsigned char *rcm,
|
||||||
const uint64_t value,
|
const uint64_t value,
|
||||||
unsigned char *cv,
|
unsigned char *cv,
|
||||||
@@ -308,6 +307,33 @@ extern "C" {
|
|||||||
unsigned char *j_ret,
|
unsigned char *j_ret,
|
||||||
unsigned char *addr_ret
|
unsigned char *addr_ret
|
||||||
);
|
);
|
||||||
|
|
||||||
|
uint32_t librustzcash_mmr_append(
|
||||||
|
uint32_t cbranch,
|
||||||
|
uint32_t t_len,
|
||||||
|
const uint32_t *ni_ptr,
|
||||||
|
const unsigned char *n_ptr,
|
||||||
|
size_t p_len,
|
||||||
|
const unsigned char *nn_ptr,
|
||||||
|
unsigned char *rt_ret,
|
||||||
|
unsigned char *buf_ret
|
||||||
|
);
|
||||||
|
|
||||||
|
uint32_t librustzcash_mmr_delete(
|
||||||
|
uint32_t cbranch,
|
||||||
|
uint32_t t_len,
|
||||||
|
const uint32_t *ni_ptr,
|
||||||
|
const unsigned char *n_ptr,
|
||||||
|
size_t p_len,
|
||||||
|
size_t e_len,
|
||||||
|
unsigned char *rt_ret
|
||||||
|
);
|
||||||
|
|
||||||
|
uint32_t librustzcash_mmr_hash_node(
|
||||||
|
uint32_t cbranch,
|
||||||
|
const unsigned char *n_ptr,
|
||||||
|
unsigned char *h_ret
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // LIBRUSTZCASH_INCLUDE_H_
|
#endif // LIBRUSTZCASH_INCLUDE_H_
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
|||||||
use ff::{PrimeField, PrimeFieldRepr};
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use rand::{OsRng, Rng};
|
use rand_core::{OsRng, RngCore};
|
||||||
use sapling_crypto::jubjub::{edwards, JubjubBls12};
|
use zcash_primitives::jubjub::{edwards, JubjubBls12};
|
||||||
use sapling_crypto::primitives::{Diversifier, ViewingKey};
|
use zcash_primitives::primitives::{Diversifier, ViewingKey};
|
||||||
|
|
||||||
use {
|
use crate::{
|
||||||
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
|
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
|
||||||
librustzcash_sapling_ka_derivepublic,
|
librustzcash_sapling_ka_derivepublic,
|
||||||
};
|
};
|
||||||
@@ -12,7 +12,7 @@ use {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_key_agreement() {
|
fn test_key_agreement() {
|
||||||
let params = JubjubBls12::new();
|
let params = JubjubBls12::new();
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng;
|
||||||
|
|
||||||
// Create random viewing key
|
// Create random viewing key
|
||||||
let vk = ViewingKey::<Bls12> {
|
let vk = ViewingKey::<Bls12> {
|
||||||
@@ -22,7 +22,9 @@ fn test_key_agreement() {
|
|||||||
|
|
||||||
// Create a random address with the viewing key
|
// Create a random address with the viewing key
|
||||||
let addr = loop {
|
let addr = loop {
|
||||||
match vk.into_payment_address(Diversifier(rng.gen()), ¶ms) {
|
let mut d = [0; 11];
|
||||||
|
rng.fill_bytes(&mut d);
|
||||||
|
match vk.to_payment_address(Diversifier(d), ¶ms) {
|
||||||
Some(a) => break a,
|
Some(a) => break a,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
@@ -44,7 +46,7 @@ fn test_key_agreement() {
|
|||||||
|
|
||||||
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
||||||
let mut addr_pk_d = [0u8; 32];
|
let mut addr_pk_d = [0u8; 32];
|
||||||
addr.pk_d.write(&mut addr_pk_d[..]).unwrap();
|
addr.pk_d().write(&mut addr_pk_d[..]).unwrap();
|
||||||
|
|
||||||
assert!(librustzcash_sapling_ka_agree(
|
assert!(librustzcash_sapling_ka_agree(
|
||||||
&addr_pk_d,
|
&addr_pk_d,
|
||||||
@@ -56,7 +58,7 @@ fn test_key_agreement() {
|
|||||||
// using the diversifier and esk.
|
// using the diversifier and esk.
|
||||||
let mut epk = [0u8; 32];
|
let mut epk = [0u8; 32];
|
||||||
assert!(librustzcash_sapling_ka_derivepublic(
|
assert!(librustzcash_sapling_ka_derivepublic(
|
||||||
&addr.diversifier.0,
|
&addr.diversifier().0,
|
||||||
&esk,
|
&esk,
|
||||||
&mut epk
|
&mut epk
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use ff::{PrimeField, PrimeFieldRepr};
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use sapling_crypto::{
|
use zcash_primitives::{
|
||||||
jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams},
|
jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams},
|
||||||
primitives::{Diversifier, ProofGenerationKey},
|
primitives::{Diversifier, ProofGenerationKey},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::JUBJUB;
|
use super::JUBJUB;
|
||||||
|
|
||||||
use {
|
use crate::{
|
||||||
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
|
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
|
||||||
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
|
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
|
||||||
};
|
};
|
||||||
@@ -28,6 +28,8 @@ fn key_components() {
|
|||||||
note_v: u64,
|
note_v: u64,
|
||||||
note_r: [u8; 32],
|
note_r: [u8; 32],
|
||||||
note_cm: [u8; 32],
|
note_cm: [u8; 32],
|
||||||
|
note_pos: u64,
|
||||||
|
note_nf: [u8; 32],
|
||||||
};
|
};
|
||||||
|
|
||||||
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
|
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
|
||||||
@@ -87,6 +89,12 @@ fn key_components() {
|
|||||||
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
|
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
|
||||||
0xdd, 0x07, 0x64, 0x39,
|
0xdd, 0x07, 0x64, 0x39,
|
||||||
],
|
],
|
||||||
|
note_pos: 0,
|
||||||
|
note_nf: [
|
||||||
|
0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86,
|
||||||
|
0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27,
|
||||||
|
0x47, 0xab, 0x40, 0x63,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -143,6 +151,12 @@ fn key_components() {
|
|||||||
0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72,
|
0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72,
|
||||||
0xca, 0xd4, 0x69, 0x50,
|
0xca, 0xd4, 0x69, 0x50,
|
||||||
],
|
],
|
||||||
|
note_pos: 763714296,
|
||||||
|
note_nf: [
|
||||||
|
0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2,
|
||||||
|
0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17,
|
||||||
|
0x4c, 0x2e, 0x9d, 0x93,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -199,6 +213,12 @@ fn key_components() {
|
|||||||
0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f,
|
0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f,
|
||||||
0x4e, 0x55, 0xf1, 0x51,
|
0x4e, 0x55, 0xf1, 0x51,
|
||||||
],
|
],
|
||||||
|
note_pos: 1527428592,
|
||||||
|
note_nf: [
|
||||||
|
0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f,
|
||||||
|
0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f,
|
||||||
|
0x59, 0xae, 0x1d, 0x14,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -255,6 +275,12 @@ fn key_components() {
|
|||||||
0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa,
|
0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa,
|
||||||
0x60, 0xd1, 0x9b, 0x6c,
|
0x60, 0xd1, 0x9b, 0x6c,
|
||||||
],
|
],
|
||||||
|
note_pos: 2291142888,
|
||||||
|
note_nf: [
|
||||||
|
0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56,
|
||||||
|
0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75,
|
||||||
|
0x8b, 0xec, 0x47, 0xa1,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -311,6 +337,12 @@ fn key_components() {
|
|||||||
0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b,
|
0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b,
|
||||||
0xc7, 0x1b, 0x7f, 0x36,
|
0xc7, 0x1b, 0x7f, 0x36,
|
||||||
],
|
],
|
||||||
|
note_pos: 3054857184,
|
||||||
|
note_nf: [
|
||||||
|
0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f,
|
||||||
|
0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03,
|
||||||
|
0x01, 0xbf, 0x3d, 0x13,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -367,6 +399,12 @@ fn key_components() {
|
|||||||
0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69,
|
0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69,
|
||||||
0xde, 0x1a, 0x5b, 0x4c,
|
0xde, 0x1a, 0x5b, 0x4c,
|
||||||
],
|
],
|
||||||
|
note_pos: 3818571480,
|
||||||
|
note_nf: [
|
||||||
|
0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2,
|
||||||
|
0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20,
|
||||||
|
0xb6, 0xc9, 0xa7, 0xe1,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -423,6 +461,12 @@ fn key_components() {
|
|||||||
0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b,
|
0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b,
|
||||||
0x78, 0x3a, 0x1e, 0x55,
|
0x78, 0x3a, 0x1e, 0x55,
|
||||||
],
|
],
|
||||||
|
note_pos: 287318480,
|
||||||
|
note_nf: [
|
||||||
|
0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae,
|
||||||
|
0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4,
|
||||||
|
0x07, 0xd5, 0x1e, 0x11,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -479,6 +523,12 @@ fn key_components() {
|
|||||||
0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36,
|
0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36,
|
||||||
0xf7, 0x8c, 0x2b, 0x23,
|
0xf7, 0x8c, 0x2b, 0x23,
|
||||||
],
|
],
|
||||||
|
note_pos: 1051032776,
|
||||||
|
note_nf: [
|
||||||
|
0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f,
|
||||||
|
0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc,
|
||||||
|
0x8b, 0xb6, 0x98, 0x90,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -535,6 +585,12 @@ fn key_components() {
|
|||||||
0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07,
|
0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07,
|
||||||
0x8f, 0xea, 0x4d, 0x04,
|
0x8f, 0xea, 0x4d, 0x04,
|
||||||
],
|
],
|
||||||
|
note_pos: 1814747072,
|
||||||
|
note_nf: [
|
||||||
|
0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20,
|
||||||
|
0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe,
|
||||||
|
0xb1, 0x7c, 0xd6, 0x20,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
TestVector {
|
TestVector {
|
||||||
sk: [
|
sk: [
|
||||||
@@ -591,6 +647,12 @@ fn key_components() {
|
|||||||
0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63,
|
0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63,
|
||||||
0xe4, 0x1d, 0xeb, 0x37,
|
0xe4, 0x1d, 0xeb, 0x37,
|
||||||
],
|
],
|
||||||
|
note_pos: 2578461368,
|
||||||
|
note_nf: [
|
||||||
|
0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe,
|
||||||
|
0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71,
|
||||||
|
0x0d, 0x93, 0xc9, 0xe9,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -616,7 +678,7 @@ fn key_components() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pgk = ProofGenerationKey { ak, nsk };
|
let pgk = ProofGenerationKey { ak, nsk };
|
||||||
let fvk = pgk.into_viewing_key(&JUBJUB);
|
let fvk = pgk.to_viewing_key(&JUBJUB);
|
||||||
{
|
{
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
fvk.nk.write(&mut vec).unwrap();
|
fvk.nk.write(&mut vec).unwrap();
|
||||||
@@ -642,10 +704,10 @@ fn key_components() {
|
|||||||
let diversifier = Diversifier(tv.default_d);
|
let diversifier = Diversifier(tv.default_d);
|
||||||
assert!(librustzcash_check_diversifier(&tv.default_d));
|
assert!(librustzcash_check_diversifier(&tv.default_d));
|
||||||
|
|
||||||
let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap();
|
let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
|
||||||
{
|
{
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
addr.pk_d.write(&mut vec).unwrap();
|
addr.pk_d().write(&mut vec).unwrap();
|
||||||
assert_eq!(&vec, &tv.default_pk_d);
|
assert_eq!(&vec, &tv.default_pk_d);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -663,5 +725,7 @@ fn key_components() {
|
|||||||
note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap();
|
note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap();
|
||||||
assert_eq!(&vec, &tv.note_cm);
|
assert_eq!(&vec, &tv.note_cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_eq!(note.nf(&fvk, tv.note_pos, &JUBJUB), tv.note_nf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
225
librustzcash/src/tests/mmr.rs
Normal file
225
librustzcash/src/tests/mmr.rs
Normal 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);
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
use sapling_crypto::jubjub::{FixedGenerators, JubjubParams};
|
use zcash_primitives::jubjub::{FixedGenerators, JubjubParams};
|
||||||
|
|
||||||
use super::JUBJUB;
|
use super::JUBJUB;
|
||||||
|
|
||||||
mod key_agreement;
|
mod key_agreement;
|
||||||
mod key_components;
|
mod key_components;
|
||||||
|
mod mmr;
|
||||||
mod notes;
|
mod notes;
|
||||||
mod signatures;
|
mod signatures;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use librustzcash_sapling_compute_cm;
|
use crate::librustzcash_sapling_compute_cm;
|
||||||
use librustzcash_sapling_compute_nf;
|
use crate::librustzcash_sapling_compute_nf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn notes() {
|
fn notes() {
|
||||||
|
|||||||
BIN
librustzcash/src/tests/res/tree1023.dat
Normal file
BIN
librustzcash/src/tests/res/tree1023.dat
Normal file
Binary file not shown.
BIN
librustzcash/src/tests/res/tree16.dat
Normal file
BIN
librustzcash/src/tests/res/tree16.dat
Normal file
Binary file not shown.
@@ -1,9 +1,7 @@
|
|||||||
use ff::{PrimeField, PrimeFieldRepr};
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use sapling_crypto::{
|
use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine};
|
||||||
jubjub::{FixedGenerators, JubjubEngine},
|
use zcash_primitives::redjubjub::{PrivateKey, PublicKey, Signature};
|
||||||
redjubjub::{PrivateKey, PublicKey, Signature},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::JUBJUB;
|
use super::JUBJUB;
|
||||||
|
|
||||||
|
|||||||
@@ -2,25 +2,33 @@
|
|||||||
name = "pairing"
|
name = "pairing"
|
||||||
|
|
||||||
# Remember to change version string in README.md.
|
# Remember to change version string in README.md.
|
||||||
version = "0.14.2"
|
version = "0.15.1"
|
||||||
authors = [
|
authors = [
|
||||||
"Sean Bowe <ewillbefull@gmail.com>",
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
"Jack Grigg <jack@z.cash>",
|
"Jack Grigg <jack@z.cash>",
|
||||||
]
|
]
|
||||||
|
readme = "README.md"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
|
|
||||||
description = "Pairing-friendly elliptic curve library"
|
description = "Pairing-friendly elliptic curve library"
|
||||||
documentation = "https://docs.rs/pairing/"
|
documentation = "https://docs.rs/pairing/"
|
||||||
homepage = "https://github.com/ebfull/pairing"
|
homepage = "https://github.com/ebfull/pairing"
|
||||||
repository = "https://github.com/ebfull/pairing"
|
repository = "https://github.com/ebfull/pairing"
|
||||||
|
edition ="2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.4"
|
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
ff = { path = "../ff", features = ["derive"] }
|
ff = { version = "^0.5.2", path = "../ff", features = ["derive"] }
|
||||||
group = { path = "../group" }
|
group = { version = "0.2.0", path = "../group" }
|
||||||
|
rand_core = "0.5"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand_xorshift = "0.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
unstable-features = ["expose-arith"]
|
unstable-features = ["expose-arith"]
|
||||||
expose-arith = []
|
expose-arith = []
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
# pairing [](https://crates.io/crates/pairing) #
|
# pairing [](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/)
|
## [Documentation](https://docs.rs/pairing/)
|
||||||
|
|
||||||
@@ -8,13 +18,15 @@ Bring the `pairing` crate into your project just as you normally would.
|
|||||||
|
|
||||||
## Security Warnings
|
## 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
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
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)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
mod g1 {
|
mod g1 {
|
||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::Field;
|
||||||
use group::CurveProjective;
|
use group::CurveProjective;
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
@@ -8,10 +10,13 @@ mod g1 {
|
|||||||
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
|
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -27,10 +32,13 @@ mod g1 {
|
|||||||
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
|
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -46,10 +54,13 @@ mod g1 {
|
|||||||
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
|
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -63,8 +74,10 @@ mod g1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod g2 {
|
mod g2 {
|
||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::Field;
|
||||||
use group::CurveProjective;
|
use group::CurveProjective;
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
@@ -72,10 +85,13 @@ mod g2 {
|
|||||||
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
|
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -91,10 +107,13 @@ mod g2 {
|
|||||||
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
|
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -110,10 +129,13 @@ mod g2 {
|
|||||||
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
|
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
@@ -7,12 +8,15 @@ use pairing::bls12_381::*;
|
|||||||
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut tmp1 = FqRepr::rand(&mut rng);
|
let mut tmp1 = Fq::random(&mut rng).into_repr();
|
||||||
let mut tmp2 = FqRepr::rand(&mut rng);
|
let mut tmp2 = Fq::random(&mut rng).into_repr();
|
||||||
// Shave a few bits off to avoid overflow.
|
// Shave a few bits off to avoid overflow.
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
tmp1.div2();
|
tmp1.div2();
|
||||||
@@ -35,11 +39,14 @@ fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let tmp1 = FqRepr::rand(&mut rng);
|
let tmp1 = Fq::random(&mut rng).into_repr();
|
||||||
let mut tmp2 = tmp1;
|
let mut tmp2 = tmp1;
|
||||||
// Ensure tmp2 is smaller than tmp1.
|
// Ensure tmp2 is smaller than tmp1.
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
@@ -62,9 +69,14 @@ fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
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) {
|
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -95,9 +112,14 @@ fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -112,10 +134,13 @@ fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -186,9 +220,12 @@ fn bench_fq_square(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -201,9 +238,12 @@ fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_negate(b: &mut ::test::Bencher) {
|
fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -218,11 +258,14 @@ fn bench_fq_negate(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<Fq> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut tmp = Fq::rand(&mut rng);
|
let mut tmp = Fq::random(&mut rng);
|
||||||
tmp.square();
|
tmp.square();
|
||||||
tmp
|
tmp
|
||||||
})
|
})
|
||||||
@@ -239,9 +282,12 @@ fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -254,10 +300,13 @@ fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||||
.map(|_| Fq::rand(&mut rng).into_repr())
|
.map(|_| Fq::random(&mut rng).into_repr())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
@@ -7,10 +8,13 @@ use pairing::bls12_381::*;
|
|||||||
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -81,9 +94,12 @@ fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use ff::{Field, SqrtField};
|
use ff::{Field, SqrtField};
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
@@ -7,10 +8,13 @@ use pairing::bls12_381::*;
|
|||||||
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -81,9 +94,12 @@ fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -97,9 +113,12 @@ fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
@@ -7,12 +8,15 @@ use pairing::bls12_381::*;
|
|||||||
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut tmp1 = FrRepr::rand(&mut rng);
|
let mut tmp1 = Fr::random(&mut rng).into_repr();
|
||||||
let mut tmp2 = FrRepr::rand(&mut rng);
|
let mut tmp2 = Fr::random(&mut rng).into_repr();
|
||||||
// Shave a few bits off to avoid overflow.
|
// Shave a few bits off to avoid overflow.
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
tmp1.div2();
|
tmp1.div2();
|
||||||
@@ -35,11 +39,14 @@ fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let tmp1 = FrRepr::rand(&mut rng);
|
let tmp1 = Fr::random(&mut rng).into_repr();
|
||||||
let mut tmp2 = tmp1;
|
let mut tmp2 = tmp1;
|
||||||
// Ensure tmp2 is smaller than tmp1.
|
// Ensure tmp2 is smaller than tmp1.
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
@@ -62,9 +69,14 @@ fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
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) {
|
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -95,9 +112,14 @@ fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -112,10 +134,13 @@ fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
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) {
|
fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -186,9 +220,12 @@ fn bench_fr_square(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -201,9 +238,12 @@ fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_negate(b: &mut ::test::Bencher) {
|
fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -218,11 +258,14 @@ fn bench_fr_negate(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<Fr> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut tmp = Fr::rand(&mut rng);
|
let mut tmp = Fr::random(&mut rng);
|
||||||
tmp.square();
|
tmp.square();
|
||||||
tmp
|
tmp
|
||||||
})
|
})
|
||||||
@@ -239,9 +282,12 @@ fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -254,10 +300,13 @@ fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||||
.map(|_| Fr::rand(&mut rng).into_repr())
|
.map(|_| Fr::random(&mut rng).into_repr())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ mod fq12;
|
|||||||
mod fq2;
|
mod fq2;
|
||||||
mod fr;
|
mod fr;
|
||||||
|
|
||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use group::CurveProjective;
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
use pairing::{Engine, PairingCurveAffine};
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
@@ -13,9 +15,12 @@ use pairing::{Engine, PairingCurveAffine};
|
|||||||
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -29,9 +34,12 @@ fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
|||||||
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -45,13 +53,16 @@ fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
|||||||
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
(
|
(
|
||||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -68,13 +79,16 @@ fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
|||||||
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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)
|
let v: Vec<Fq12> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
(
|
(
|
||||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)]))
|
.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) {
|
fn bench_pairing_full(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
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)
|
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();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
extern crate ff;
|
extern crate ff;
|
||||||
extern crate group;
|
extern crate group;
|
||||||
extern crate pairing;
|
extern crate pairing;
|
||||||
extern crate rand;
|
extern crate rand_core;
|
||||||
|
extern crate rand_xorshift;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
mod bls12_381;
|
mod bls12_381;
|
||||||
|
|||||||
@@ -14,12 +14,11 @@ macro_rules! curve_impl {
|
|||||||
pub struct $affine {
|
pub struct $affine {
|
||||||
pub(crate) x: $basefield,
|
pub(crate) x: $basefield,
|
||||||
pub(crate) y: $basefield,
|
pub(crate) y: $basefield,
|
||||||
pub(crate) infinity: bool
|
pub(crate) infinity: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for $affine
|
impl ::std::fmt::Display for $affine {
|
||||||
{
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
|
||||||
if self.infinity {
|
if self.infinity {
|
||||||
write!(f, "{}(Infinity)", $name)
|
write!(f, "{}(Infinity)", $name)
|
||||||
} else {
|
} else {
|
||||||
@@ -30,14 +29,13 @@ macro_rules! curve_impl {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq)]
|
#[derive(Copy, Clone, Debug, Eq)]
|
||||||
pub struct $projective {
|
pub struct $projective {
|
||||||
pub(crate) x: $basefield,
|
pub(crate) x: $basefield,
|
||||||
pub(crate) y: $basefield,
|
pub(crate) y: $basefield,
|
||||||
pub(crate) z: $basefield
|
pub(crate) z: $basefield,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for $projective
|
impl ::std::fmt::Display for $projective {
|
||||||
{
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
|
||||||
write!(f, "{}", self.into_affine())
|
write!(f, "{}", self.into_affine())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +87,9 @@ macro_rules! curve_impl {
|
|||||||
let mut res = $projective::zero();
|
let mut res = $projective::zero();
|
||||||
for i in bits {
|
for i in bits {
|
||||||
res.double();
|
res.double();
|
||||||
if i { res.add_assign_mixed(self) }
|
if i {
|
||||||
|
res.add_assign_mixed(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -112,12 +112,8 @@ macro_rules! curve_impl {
|
|||||||
|
|
||||||
$affine {
|
$affine {
|
||||||
x: x,
|
x: x,
|
||||||
y: if (y < negy) ^ greatest {
|
y: if (y < negy) ^ greatest { y } else { negy },
|
||||||
y
|
infinity: false,
|
||||||
} else {
|
|
||||||
negy
|
|
||||||
},
|
|
||||||
infinity: false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -156,7 +152,7 @@ macro_rules! curve_impl {
|
|||||||
$affine {
|
$affine {
|
||||||
x: $basefield::zero(),
|
x: $basefield::zero(),
|
||||||
y: $basefield::one(),
|
y: $basefield::one(),
|
||||||
infinity: true
|
infinity: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +178,6 @@ macro_rules! curve_impl {
|
|||||||
fn into_projective(&self) -> $projective {
|
fn into_projective(&self) -> $projective {
|
||||||
(*self).into()
|
(*self).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PairingCurveAffine for $affine {
|
impl PairingCurveAffine for $affine {
|
||||||
@@ -197,14 +192,18 @@ macro_rules! curve_impl {
|
|||||||
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
|
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
|
||||||
self.perform_pairing(other)
|
self.perform_pairing(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for $projective {
|
impl CurveProjective for $projective {
|
||||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
type Engine = Bls12;
|
||||||
|
type Scalar = $scalarfield;
|
||||||
|
type Base = $basefield;
|
||||||
|
type Affine = $affine;
|
||||||
|
|
||||||
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||||
loop {
|
loop {
|
||||||
let x = rng.gen();
|
let x = $basefield::random(rng);
|
||||||
let greatest = rng.gen();
|
let greatest = rng.next_u32() % 2 != 0;
|
||||||
|
|
||||||
if let Some(p) = $affine::get_point_from_x(x, greatest) {
|
if let Some(p) = $affine::get_point_from_x(x, greatest) {
|
||||||
let p = p.scale_by_cofactor();
|
let p = p.scale_by_cofactor();
|
||||||
@@ -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
|
// The point at infinity is always represented by
|
||||||
// Z = 0.
|
// Z = 0.
|
||||||
@@ -229,7 +221,7 @@ macro_rules! curve_impl {
|
|||||||
$projective {
|
$projective {
|
||||||
x: $basefield::zero(),
|
x: $basefield::zero(),
|
||||||
y: $basefield::one(),
|
y: $basefield::one(),
|
||||||
z: $basefield::zero()
|
z: $basefield::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,8 +239,7 @@ macro_rules! curve_impl {
|
|||||||
self.is_zero() || self.z == $basefield::one()
|
self.is_zero() || self.z == $basefield::one()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn batch_normalization(v: &mut [Self])
|
fn batch_normalization(v: &mut [Self]) {
|
||||||
{
|
|
||||||
// Montgomery’s Trick and Fast Implementation of Masked AES
|
// Montgomery’s Trick and Fast Implementation of Masked AES
|
||||||
// Genelle, Prouff and Quisquater
|
// Genelle, Prouff and Quisquater
|
||||||
// Section 3.2
|
// Section 3.2
|
||||||
@@ -256,9 +247,10 @@ macro_rules! curve_impl {
|
|||||||
// First pass: compute [a, ab, abc, ...]
|
// First pass: compute [a, ab, abc, ...]
|
||||||
let mut prod = Vec::with_capacity(v.len());
|
let mut prod = Vec::with_capacity(v.len());
|
||||||
let mut tmp = $basefield::one();
|
let mut tmp = $basefield::one();
|
||||||
for g in v.iter_mut()
|
for g in v
|
||||||
// Ignore normalized elements
|
.iter_mut()
|
||||||
.filter(|g| !g.is_normalized())
|
// Ignore normalized elements
|
||||||
|
.filter(|g| !g.is_normalized())
|
||||||
{
|
{
|
||||||
tmp.mul_assign(&g.z);
|
tmp.mul_assign(&g.z);
|
||||||
prod.push(tmp);
|
prod.push(tmp);
|
||||||
@@ -268,13 +260,19 @@ macro_rules! curve_impl {
|
|||||||
tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero.
|
tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero.
|
||||||
|
|
||||||
// Second pass: iterate backwards to compute inverses
|
// Second pass: iterate backwards to compute inverses
|
||||||
for (g, s) in v.iter_mut()
|
for (g, s) in v
|
||||||
// Backwards
|
.iter_mut()
|
||||||
.rev()
|
// Backwards
|
||||||
// Ignore normalized elements
|
.rev()
|
||||||
.filter(|g| !g.is_normalized())
|
// Ignore normalized elements
|
||||||
// Backwards, skip last element, fill in one for last term.
|
.filter(|g| !g.is_normalized())
|
||||||
.zip(prod.into_iter().rev().skip(1).chain(Some($basefield::one())))
|
// Backwards, skip last element, fill in one for last term.
|
||||||
|
.zip(
|
||||||
|
prod.into_iter()
|
||||||
|
.rev()
|
||||||
|
.skip(1)
|
||||||
|
.chain(Some($basefield::one())),
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// tmp := tmp * g.z; g.z := tmp * s = 1/z
|
// tmp := tmp * g.z; g.z := tmp * s = 1/z
|
||||||
let mut newtmp = tmp;
|
let mut newtmp = tmp;
|
||||||
@@ -285,9 +283,7 @@ macro_rules! curve_impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform affine transformations
|
// Perform affine transformations
|
||||||
for g in v.iter_mut()
|
for g in v.iter_mut().filter(|g| !g.is_normalized()) {
|
||||||
.filter(|g| !g.is_normalized())
|
|
||||||
{
|
|
||||||
let mut z = g.z; // 1/z
|
let mut z = g.z; // 1/z
|
||||||
z.square(); // 1/z^2
|
z.square(); // 1/z^2
|
||||||
g.x.mul_assign(&z); // x/z^2
|
g.x.mul_assign(&z); // x/z^2
|
||||||
@@ -540,8 +536,7 @@ macro_rules! curve_impl {
|
|||||||
|
|
||||||
let mut found_one = false;
|
let mut found_one = false;
|
||||||
|
|
||||||
for i in BitIterator::new(other.into())
|
for i in BitIterator::new(other.into()) {
|
||||||
{
|
|
||||||
if found_one {
|
if found_one {
|
||||||
res.double();
|
res.double();
|
||||||
} else {
|
} else {
|
||||||
@@ -579,7 +574,7 @@ macro_rules! curve_impl {
|
|||||||
$projective {
|
$projective {
|
||||||
x: p.x,
|
x: p.x,
|
||||||
y: p.y,
|
y: p.y,
|
||||||
z: $basefield::one()
|
z: $basefield::one(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -596,7 +591,7 @@ macro_rules! curve_impl {
|
|||||||
$affine {
|
$affine {
|
||||||
x: p.x,
|
x: p.x,
|
||||||
y: p.y,
|
y: p.y,
|
||||||
infinity: false
|
infinity: false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Z is nonzero, so it must have an inverse in a field.
|
// Z is nonzero, so it must have an inverse in a field.
|
||||||
@@ -616,22 +611,22 @@ macro_rules! curve_impl {
|
|||||||
$affine {
|
$affine {
|
||||||
x: x,
|
x: x,
|
||||||
y: y,
|
y: y,
|
||||||
infinity: false
|
infinity: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod g1 {
|
pub mod g1 {
|
||||||
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
|
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
|
||||||
use super::g2::G2Affine;
|
use super::g2::G2Affine;
|
||||||
|
use crate::{Engine, PairingCurveAffine};
|
||||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||||
use rand::{Rand, Rng};
|
use rand_core::RngCore;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use {Engine, PairingCurveAffine};
|
|
||||||
|
|
||||||
curve_impl!(
|
curve_impl!(
|
||||||
"G1",
|
"G1",
|
||||||
@@ -661,7 +656,7 @@ pub mod g1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for G1Uncompressed {
|
impl fmt::Debug for G1Uncompressed {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
self.0[..].fmt(formatter)
|
self.0[..].fmt(formatter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -771,7 +766,7 @@ pub mod g1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for G1Compressed {
|
impl fmt::Debug for G1Compressed {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
self.0[..].fmt(formatter)
|
self.0[..].fmt(formatter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -939,7 +934,7 @@ pub mod g1 {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn g1_generator() {
|
fn g1_generator() {
|
||||||
use SqrtField;
|
use crate::SqrtField;
|
||||||
|
|
||||||
let mut x = Fq::zero();
|
let mut x = Fq::zero();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@@ -957,7 +952,7 @@ pub mod g1 {
|
|||||||
let negyrepr = negy.into_repr();
|
let negyrepr = negy.into_repr();
|
||||||
|
|
||||||
let p = G1Affine {
|
let p = G1Affine {
|
||||||
x: x,
|
x,
|
||||||
y: if yrepr < negyrepr { y } else { negy },
|
y: if yrepr < negyrepr { y } else { negy },
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
@@ -992,7 +987,8 @@ pub mod g1 {
|
|||||||
0x9fe83b1b4a5d648d,
|
0x9fe83b1b4a5d648d,
|
||||||
0xf583cc5a508f6a40,
|
0xf583cc5a508f6a40,
|
||||||
0xc3ad2aefde0bb13,
|
0xc3ad2aefde0bb13,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0x60aa6f9552f03aae,
|
0x60aa6f9552f03aae,
|
||||||
0xecd01d5181300d35,
|
0xecd01d5181300d35,
|
||||||
@@ -1000,7 +996,8 @@ pub mod g1 {
|
|||||||
0xe760f57922998c9d,
|
0xe760f57922998c9d,
|
||||||
0x953703f5795a39e5,
|
0x953703f5795a39e5,
|
||||||
0xfe3ae0922df702c,
|
0xfe3ae0922df702c,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
assert!(!p.is_on_curve());
|
assert!(!p.is_on_curve());
|
||||||
@@ -1017,7 +1014,8 @@ pub mod g1 {
|
|||||||
0xea034ee2928b30a8,
|
0xea034ee2928b30a8,
|
||||||
0xbd8833dc7c79a7f7,
|
0xbd8833dc7c79a7f7,
|
||||||
0xe45c9f0c0438675,
|
0xe45c9f0c0438675,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0x3b450eb1ab7b5dad,
|
0x3b450eb1ab7b5dad,
|
||||||
0xa65cb81e975e8675,
|
0xa65cb81e975e8675,
|
||||||
@@ -1025,7 +1023,8 @@ pub mod g1 {
|
|||||||
0x753ddf21a2601d20,
|
0x753ddf21a2601d20,
|
||||||
0x532d0b640bd3ff8b,
|
0x532d0b640bd3ff8b,
|
||||||
0x118d2c543f031102,
|
0x118d2c543f031102,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
assert!(!p.is_on_curve());
|
assert!(!p.is_on_curve());
|
||||||
@@ -1043,7 +1042,8 @@ pub mod g1 {
|
|||||||
0xf35de9ce0d6b4e84,
|
0xf35de9ce0d6b4e84,
|
||||||
0x265bddd23d1dec54,
|
0x265bddd23d1dec54,
|
||||||
0x12a8778088458308,
|
0x12a8778088458308,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0x8a22defa0d526256,
|
0x8a22defa0d526256,
|
||||||
0xc57ca55456fcb9ae,
|
0xc57ca55456fcb9ae,
|
||||||
@@ -1051,7 +1051,8 @@ pub mod g1 {
|
|||||||
0x921beef89d4f29df,
|
0x921beef89d4f29df,
|
||||||
0x5b6fda44ad85fa78,
|
0x5b6fda44ad85fa78,
|
||||||
0xed74ab9f302cbe0,
|
0xed74ab9f302cbe0,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
assert!(p.is_on_curve());
|
assert!(p.is_on_curve());
|
||||||
@@ -1069,7 +1070,8 @@ pub mod g1 {
|
|||||||
0x485e77d50a5df10d,
|
0x485e77d50a5df10d,
|
||||||
0x4c6fcac4b55fd479,
|
0x4c6fcac4b55fd479,
|
||||||
0x86ed4d9906fb064,
|
0x86ed4d9906fb064,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0xd25ee6461538c65,
|
0xd25ee6461538c65,
|
||||||
0x9f3bbb2ecd3719b9,
|
0x9f3bbb2ecd3719b9,
|
||||||
@@ -1077,7 +1079,8 @@ pub mod g1 {
|
|||||||
0xcefca68333c35288,
|
0xcefca68333c35288,
|
||||||
0x570c8005f8573fa6,
|
0x570c8005f8573fa6,
|
||||||
0x152ca696fe034442,
|
0x152ca696fe034442,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
z: Fq::one(),
|
z: Fq::one(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1089,7 +1092,8 @@ pub mod g1 {
|
|||||||
0x5f44314ec5e3fb03,
|
0x5f44314ec5e3fb03,
|
||||||
0x24e8538737c6e675,
|
0x24e8538737c6e675,
|
||||||
0x8abd623a594fba8,
|
0x8abd623a594fba8,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0x6b0528f088bb7044,
|
0x6b0528f088bb7044,
|
||||||
0x2fdeb5c82917ff9e,
|
0x2fdeb5c82917ff9e,
|
||||||
@@ -1097,7 +1101,8 @@ pub mod g1 {
|
|||||||
0xd65104c6f95a872a,
|
0xd65104c6f95a872a,
|
||||||
0x1f2998a5a9c61253,
|
0x1f2998a5a9c61253,
|
||||||
0xe74846154a9e44,
|
0xe74846154a9e44,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
z: Fq::one(),
|
z: Fq::one(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1113,7 +1118,8 @@ pub mod g1 {
|
|||||||
0xc4f9a52a428e23bb,
|
0xc4f9a52a428e23bb,
|
||||||
0xd178b28dd4f407ef,
|
0xd178b28dd4f407ef,
|
||||||
0x17fb8905e9183c69
|
0x17fb8905e9183c69
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0xd0de9d65292b7710,
|
0xd0de9d65292b7710,
|
||||||
0xf6a05f2bcf1d9ca7,
|
0xf6a05f2bcf1d9ca7,
|
||||||
@@ -1121,7 +1127,8 @@ pub mod g1 {
|
|||||||
0xeec8d1a5b7466c58,
|
0xeec8d1a5b7466c58,
|
||||||
0x4bc362649dce6376,
|
0x4bc362649dce6376,
|
||||||
0x430cbdc5455b00a
|
0x430cbdc5455b00a
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
infinity: false,
|
infinity: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1137,7 +1144,8 @@ pub mod g1 {
|
|||||||
0x485e77d50a5df10d,
|
0x485e77d50a5df10d,
|
||||||
0x4c6fcac4b55fd479,
|
0x4c6fcac4b55fd479,
|
||||||
0x86ed4d9906fb064,
|
0x86ed4d9906fb064,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0xd25ee6461538c65,
|
0xd25ee6461538c65,
|
||||||
0x9f3bbb2ecd3719b9,
|
0x9f3bbb2ecd3719b9,
|
||||||
@@ -1145,7 +1153,8 @@ pub mod g1 {
|
|||||||
0xcefca68333c35288,
|
0xcefca68333c35288,
|
||||||
0x570c8005f8573fa6,
|
0x570c8005f8573fa6,
|
||||||
0x152ca696fe034442,
|
0x152ca696fe034442,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
z: Fq::one(),
|
z: Fq::one(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1163,7 +1172,8 @@ pub mod g1 {
|
|||||||
0x4b914c16687dcde0,
|
0x4b914c16687dcde0,
|
||||||
0x66c8baf177d20533,
|
0x66c8baf177d20533,
|
||||||
0xaf960cff3d83833
|
0xaf960cff3d83833
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0x3f0675695f5177a8,
|
0x3f0675695f5177a8,
|
||||||
0x2b6d82ae178a1ba0,
|
0x2b6d82ae178a1ba0,
|
||||||
@@ -1171,7 +1181,8 @@ pub mod g1 {
|
|||||||
0x1771a65b60572f4e,
|
0x1771a65b60572f4e,
|
||||||
0x8b547c1313b27555,
|
0x8b547c1313b27555,
|
||||||
0x135075589a687b1e
|
0x135075589a687b1e
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
infinity: false,
|
infinity: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1194,7 +1205,8 @@ pub mod g1 {
|
|||||||
0x71ffa8021531705,
|
0x71ffa8021531705,
|
||||||
0x7418d484386d267,
|
0x7418d484386d267,
|
||||||
0xd5108d8ff1fbd6,
|
0xd5108d8ff1fbd6,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0xa776ccbfe9981766,
|
0xa776ccbfe9981766,
|
||||||
0x255632964ff40f4a,
|
0x255632964ff40f4a,
|
||||||
@@ -1202,7 +1214,8 @@ pub mod g1 {
|
|||||||
0x520f74773e74c8c3,
|
0x520f74773e74c8c3,
|
||||||
0x484c8fc982008f0,
|
0x484c8fc982008f0,
|
||||||
0xee2c3d922008cc6,
|
0xee2c3d922008cc6,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1214,7 +1227,8 @@ pub mod g1 {
|
|||||||
0xc6e05201e5f83991,
|
0xc6e05201e5f83991,
|
||||||
0xf7c75910816f207c,
|
0xf7c75910816f207c,
|
||||||
0x18d4043e78103106,
|
0x18d4043e78103106,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0xa776ccbfe9981766,
|
0xa776ccbfe9981766,
|
||||||
0x255632964ff40f4a,
|
0x255632964ff40f4a,
|
||||||
@@ -1222,7 +1236,8 @@ pub mod g1 {
|
|||||||
0x520f74773e74c8c3,
|
0x520f74773e74c8c3,
|
||||||
0x484c8fc982008f0,
|
0x484c8fc982008f0,
|
||||||
0xee2c3d922008cc6,
|
0xee2c3d922008cc6,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1237,7 +1252,8 @@ pub mod g1 {
|
|||||||
0x9676ff02ec39c227,
|
0x9676ff02ec39c227,
|
||||||
0x4c12c15d7e55b9f3,
|
0x4c12c15d7e55b9f3,
|
||||||
0x57fd1e317db9bd,
|
0x57fd1e317db9bd,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
y: Fq::from_repr(FqRepr([
|
y: Fq::from_repr(FqRepr([
|
||||||
0x1288334016679345,
|
0x1288334016679345,
|
||||||
0xf955cd68615ff0b5,
|
0xf955cd68615ff0b5,
|
||||||
@@ -1245,7 +1261,8 @@ pub mod g1 {
|
|||||||
0x1267d70db51049fb,
|
0x1267d70db51049fb,
|
||||||
0x4696deb9ab2ba3e7,
|
0x4696deb9ab2ba3e7,
|
||||||
0xb1e4e11177f59d4,
|
0xb1e4e11177f59d4,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1274,11 +1291,11 @@ pub mod g1 {
|
|||||||
pub mod g2 {
|
pub mod g2 {
|
||||||
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
|
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
|
||||||
use super::g1::G1Affine;
|
use super::g1::G1Affine;
|
||||||
|
use crate::{Engine, PairingCurveAffine};
|
||||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||||
use rand::{Rand, Rng};
|
use rand_core::RngCore;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use {Engine, PairingCurveAffine};
|
|
||||||
|
|
||||||
curve_impl!(
|
curve_impl!(
|
||||||
"G2",
|
"G2",
|
||||||
@@ -1308,7 +1325,7 @@ pub mod g2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for G2Uncompressed {
|
impl fmt::Debug for G2Uncompressed {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
self.0[..].fmt(formatter)
|
self.0[..].fmt(formatter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1434,7 +1451,7 @@ pub mod g2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for G2Compressed {
|
impl fmt::Debug for G2Compressed {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
self.0[..].fmt(formatter)
|
self.0[..].fmt(formatter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1623,7 +1640,7 @@ pub mod g2 {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn g2_generator() {
|
fn g2_generator() {
|
||||||
use SqrtField;
|
use crate::SqrtField;
|
||||||
|
|
||||||
let mut x = Fq2::zero();
|
let mut x = Fq2::zero();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@@ -1639,7 +1656,7 @@ pub mod g2 {
|
|||||||
negy.negate();
|
negy.negate();
|
||||||
|
|
||||||
let p = G2Affine {
|
let p = G2Affine {
|
||||||
x: x,
|
x,
|
||||||
y: if y < negy { y } else { negy },
|
y: if y < negy { y } else { negy },
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
@@ -1675,7 +1692,8 @@ pub mod g2 {
|
|||||||
0x7a17a004747e3dbe,
|
0x7a17a004747e3dbe,
|
||||||
0xcc65406a7c2e5a73,
|
0xcc65406a7c2e5a73,
|
||||||
0x10b8c03d64db4d0c,
|
0x10b8c03d64db4d0c,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xd30e70fe2f029778,
|
0xd30e70fe2f029778,
|
||||||
0xda30772df0f5212e,
|
0xda30772df0f5212e,
|
||||||
@@ -1683,7 +1701,8 @@ pub mod g2 {
|
|||||||
0xfb777e5b9b568608,
|
0xfb777e5b9b568608,
|
||||||
0x789bac1fec71a2b9,
|
0x789bac1fec71a2b9,
|
||||||
0x1342f02e2da54405,
|
0x1342f02e2da54405,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
y: Fq2 {
|
y: Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -1693,7 +1712,8 @@ pub mod g2 {
|
|||||||
0x663015d9410eb608,
|
0x663015d9410eb608,
|
||||||
0x78e82a79d829a544,
|
0x78e82a79d829a544,
|
||||||
0x40a00545bb3c1e,
|
0x40a00545bb3c1e,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x4709802348e79377,
|
0x4709802348e79377,
|
||||||
0xb5ac4dc9204bcfbd,
|
0xb5ac4dc9204bcfbd,
|
||||||
@@ -1701,7 +1721,8 @@ pub mod g2 {
|
|||||||
0x15008b1dc399e8df,
|
0x15008b1dc399e8df,
|
||||||
0x68128fd0548a3829,
|
0x68128fd0548a3829,
|
||||||
0x16a613db5c873aaa,
|
0x16a613db5c873aaa,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
@@ -1720,7 +1741,8 @@ pub mod g2 {
|
|||||||
0x41abba710d6c692c,
|
0x41abba710d6c692c,
|
||||||
0xffcc4b2b62ce8484,
|
0xffcc4b2b62ce8484,
|
||||||
0x6993ec01b8934ed,
|
0x6993ec01b8934ed,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xb94e92d5f874e26,
|
0xb94e92d5f874e26,
|
||||||
0x44516408bc115d95,
|
0x44516408bc115d95,
|
||||||
@@ -1728,7 +1750,8 @@ pub mod g2 {
|
|||||||
0xa5a0c2b7131f3555,
|
0xa5a0c2b7131f3555,
|
||||||
0x83800965822367e7,
|
0x83800965822367e7,
|
||||||
0x10cf1d3ad8d90bfa,
|
0x10cf1d3ad8d90bfa,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
y: Fq2 {
|
y: Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -1738,7 +1761,8 @@ pub mod g2 {
|
|||||||
0x5a9171720e73eb51,
|
0x5a9171720e73eb51,
|
||||||
0x38eb4fd8d658adb7,
|
0x38eb4fd8d658adb7,
|
||||||
0xb649051bbc1164d,
|
0xb649051bbc1164d,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x9225814253d7df75,
|
0x9225814253d7df75,
|
||||||
0xc196c2513477f887,
|
0xc196c2513477f887,
|
||||||
@@ -1746,7 +1770,8 @@ pub mod g2 {
|
|||||||
0x55f2b8efad953e04,
|
0x55f2b8efad953e04,
|
||||||
0x7379345eda55265e,
|
0x7379345eda55265e,
|
||||||
0x377f2e6208fd4cb,
|
0x377f2e6208fd4cb,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
@@ -1766,7 +1791,8 @@ pub mod g2 {
|
|||||||
0x2199bc19c48c393d,
|
0x2199bc19c48c393d,
|
||||||
0x4a151b732a6075bf,
|
0x4a151b732a6075bf,
|
||||||
0x17762a3b9108c4a7,
|
0x17762a3b9108c4a7,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x26f461e944bbd3d1,
|
0x26f461e944bbd3d1,
|
||||||
0x298f3189a9cf6ed6,
|
0x298f3189a9cf6ed6,
|
||||||
@@ -1774,7 +1800,8 @@ pub mod g2 {
|
|||||||
0x7e147f3f9e6e241,
|
0x7e147f3f9e6e241,
|
||||||
0x72a9b63583963fff,
|
0x72a9b63583963fff,
|
||||||
0x158b0083c000462,
|
0x158b0083c000462,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
y: Fq2 {
|
y: Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -1784,7 +1811,8 @@ pub mod g2 {
|
|||||||
0x68cad19430706b4d,
|
0x68cad19430706b4d,
|
||||||
0x3ccfb97b924dcea8,
|
0x3ccfb97b924dcea8,
|
||||||
0x1660f93434588f8d,
|
0x1660f93434588f8d,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xaaed3985b6dcb9c7,
|
0xaaed3985b6dcb9c7,
|
||||||
0xc1e985d6d898d9f4,
|
0xc1e985d6d898d9f4,
|
||||||
@@ -1792,7 +1820,8 @@ pub mod g2 {
|
|||||||
0x3940a2dbb914b529,
|
0x3940a2dbb914b529,
|
||||||
0xbeb88137cf34f3e7,
|
0xbeb88137cf34f3e7,
|
||||||
0x1699ee577c61b694,
|
0x1699ee577c61b694,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
infinity: false,
|
infinity: false,
|
||||||
};
|
};
|
||||||
@@ -1812,7 +1841,8 @@ pub mod g2 {
|
|||||||
0x72556c999f3707ac,
|
0x72556c999f3707ac,
|
||||||
0x4617f2e6774e9711,
|
0x4617f2e6774e9711,
|
||||||
0x100b2fe5bffe030b,
|
0x100b2fe5bffe030b,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x7a33555977ec608,
|
0x7a33555977ec608,
|
||||||
0xe23039d1fe9c0881,
|
0xe23039d1fe9c0881,
|
||||||
@@ -1820,7 +1850,8 @@ pub mod g2 {
|
|||||||
0x4637c4f417667e2e,
|
0x4637c4f417667e2e,
|
||||||
0x93ebe7c3e41f6acc,
|
0x93ebe7c3e41f6acc,
|
||||||
0xde884f89a9a371b,
|
0xde884f89a9a371b,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
y: Fq2 {
|
y: Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -1830,7 +1861,8 @@ pub mod g2 {
|
|||||||
0x25fd427b4122f231,
|
0x25fd427b4122f231,
|
||||||
0xd83112aace35cae,
|
0xd83112aace35cae,
|
||||||
0x191b2432407cbb7f,
|
0x191b2432407cbb7f,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xf68ae82fe97662f5,
|
0xf68ae82fe97662f5,
|
||||||
0xe986057068b50b7d,
|
0xe986057068b50b7d,
|
||||||
@@ -1838,7 +1870,8 @@ pub mod g2 {
|
|||||||
0x9eaa6d19de569196,
|
0x9eaa6d19de569196,
|
||||||
0xf6a03d31e2ec2183,
|
0xf6a03d31e2ec2183,
|
||||||
0x3bdafaf7ca9b39b,
|
0x3bdafaf7ca9b39b,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
z: Fq2::one(),
|
z: Fq2::one(),
|
||||||
};
|
};
|
||||||
@@ -1852,7 +1885,8 @@ pub mod g2 {
|
|||||||
0x8e73a96b329ad190,
|
0x8e73a96b329ad190,
|
||||||
0x27c546f75ee1f3ab,
|
0x27c546f75ee1f3ab,
|
||||||
0xa33d27add5e7e82,
|
0xa33d27add5e7e82,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x93b1ebcd54870dfe,
|
0x93b1ebcd54870dfe,
|
||||||
0xf1578300e1342e11,
|
0xf1578300e1342e11,
|
||||||
@@ -1860,7 +1894,8 @@ pub mod g2 {
|
|||||||
0x2089faf462438296,
|
0x2089faf462438296,
|
||||||
0x828e5848cd48ea66,
|
0x828e5848cd48ea66,
|
||||||
0x141ecbac1deb038b,
|
0x141ecbac1deb038b,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
y: Fq2 {
|
y: Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -1870,7 +1905,8 @@ pub mod g2 {
|
|||||||
0x2767032fc37cc31d,
|
0x2767032fc37cc31d,
|
||||||
0xd5ee2aba84fd10fe,
|
0xd5ee2aba84fd10fe,
|
||||||
0x16576ccd3dd0a4e8,
|
0x16576ccd3dd0a4e8,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x4da9b6f6a96d1dd2,
|
0x4da9b6f6a96d1dd2,
|
||||||
0x9657f7da77f1650e,
|
0x9657f7da77f1650e,
|
||||||
@@ -1878,7 +1914,8 @@ pub mod g2 {
|
|||||||
0x31898db63f87363a,
|
0x31898db63f87363a,
|
||||||
0xabab040ddbd097cc,
|
0xabab040ddbd097cc,
|
||||||
0x11ad236b9ba02990,
|
0x11ad236b9ba02990,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
z: Fq2::one(),
|
z: Fq2::one(),
|
||||||
});
|
});
|
||||||
@@ -1896,7 +1933,8 @@ pub mod g2 {
|
|||||||
0xf1273e6406eef9cc,
|
0xf1273e6406eef9cc,
|
||||||
0xababd760ff05cb92,
|
0xababd760ff05cb92,
|
||||||
0xd7c20456617e89
|
0xd7c20456617e89
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xd1a50b8572cbd2b8,
|
0xd1a50b8572cbd2b8,
|
||||||
0x238f0ac6119d07df,
|
0x238f0ac6119d07df,
|
||||||
@@ -1904,7 +1942,8 @@ pub mod g2 {
|
|||||||
0x8b203284c51edf6b,
|
0x8b203284c51edf6b,
|
||||||
0xc8a0b730bbb21f5e,
|
0xc8a0b730bbb21f5e,
|
||||||
0x1a3b59d29a31274
|
0x1a3b59d29a31274
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
y: Fq2 {
|
y: Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -1914,7 +1953,8 @@ pub mod g2 {
|
|||||||
0x64528ab3863633dc,
|
0x64528ab3863633dc,
|
||||||
0x159384333d7cba97,
|
0x159384333d7cba97,
|
||||||
0x4cb84741f3cafe8
|
0x4cb84741f3cafe8
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x242af0dc3640e1a4,
|
0x242af0dc3640e1a4,
|
||||||
0xe90a73ad65c66919,
|
0xe90a73ad65c66919,
|
||||||
@@ -1922,7 +1962,8 @@ pub mod g2 {
|
|||||||
0x38528f92b689644d,
|
0x38528f92b689644d,
|
||||||
0xb6884deec59fb21f,
|
0xb6884deec59fb21f,
|
||||||
0x3c075d3ec52ba90
|
0x3c075d3ec52ba90
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
infinity: false,
|
infinity: false,
|
||||||
}
|
}
|
||||||
@@ -1940,7 +1981,8 @@ pub mod g2 {
|
|||||||
0x72556c999f3707ac,
|
0x72556c999f3707ac,
|
||||||
0x4617f2e6774e9711,
|
0x4617f2e6774e9711,
|
||||||
0x100b2fe5bffe030b,
|
0x100b2fe5bffe030b,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x7a33555977ec608,
|
0x7a33555977ec608,
|
||||||
0xe23039d1fe9c0881,
|
0xe23039d1fe9c0881,
|
||||||
@@ -1948,7 +1990,8 @@ pub mod g2 {
|
|||||||
0x4637c4f417667e2e,
|
0x4637c4f417667e2e,
|
||||||
0x93ebe7c3e41f6acc,
|
0x93ebe7c3e41f6acc,
|
||||||
0xde884f89a9a371b,
|
0xde884f89a9a371b,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
y: Fq2 {
|
y: Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -1958,7 +2001,8 @@ pub mod g2 {
|
|||||||
0x25fd427b4122f231,
|
0x25fd427b4122f231,
|
||||||
0xd83112aace35cae,
|
0xd83112aace35cae,
|
||||||
0x191b2432407cbb7f,
|
0x191b2432407cbb7f,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xf68ae82fe97662f5,
|
0xf68ae82fe97662f5,
|
||||||
0xe986057068b50b7d,
|
0xe986057068b50b7d,
|
||||||
@@ -1966,7 +2010,8 @@ pub mod g2 {
|
|||||||
0x9eaa6d19de569196,
|
0x9eaa6d19de569196,
|
||||||
0xf6a03d31e2ec2183,
|
0xf6a03d31e2ec2183,
|
||||||
0x3bdafaf7ca9b39b,
|
0x3bdafaf7ca9b39b,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
z: Fq2::one(),
|
z: Fq2::one(),
|
||||||
};
|
};
|
||||||
@@ -1986,7 +2031,8 @@ pub mod g2 {
|
|||||||
0xbcedcfce1e52d986,
|
0xbcedcfce1e52d986,
|
||||||
0x9755d4a3926e9862,
|
0x9755d4a3926e9862,
|
||||||
0x18bab73760fd8024
|
0x18bab73760fd8024
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x4e7c5e0a2ae5b99e,
|
0x4e7c5e0a2ae5b99e,
|
||||||
0x96e582a27f028961,
|
0x96e582a27f028961,
|
||||||
@@ -1994,7 +2040,8 @@ pub mod g2 {
|
|||||||
0xeb0cf5e610ef4fe7,
|
0xeb0cf5e610ef4fe7,
|
||||||
0x7b4c2bae8db6e70b,
|
0x7b4c2bae8db6e70b,
|
||||||
0xf136e43909fca0
|
0xf136e43909fca0
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
y: Fq2 {
|
y: Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -2004,7 +2051,8 @@ pub mod g2 {
|
|||||||
0xa5a2a51f7fde787b,
|
0xa5a2a51f7fde787b,
|
||||||
0x8b92866bc6384188,
|
0x8b92866bc6384188,
|
||||||
0x81a53fe531d64ef
|
0x81a53fe531d64ef
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x4c5d607666239b34,
|
0x4c5d607666239b34,
|
||||||
0xeddb5f48304d14b3,
|
0xeddb5f48304d14b3,
|
||||||
@@ -2012,7 +2060,8 @@ pub mod g2 {
|
|||||||
0xb271f52f12ead742,
|
0xb271f52f12ead742,
|
||||||
0x244e6c2015c83348,
|
0x244e6c2015c83348,
|
||||||
0x19e2deae6eb9b441
|
0x19e2deae6eb9b441
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
infinity: false,
|
infinity: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1173,7 +1173,9 @@ fn test_neg_one() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq_repr_ordering() {
|
fn test_fq_repr_ordering() {
|
||||||
@@ -1396,7 +1398,10 @@ fn test_fq_repr_num_bits() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq_repr_sub_noborrow() {
|
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([
|
let mut t = FqRepr([
|
||||||
0x827a4a08041ebd9,
|
0x827a4a08041ebd9,
|
||||||
@@ -1426,7 +1431,7 @@ fn test_fq_repr_sub_noborrow() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let mut a = FqRepr::rand(&mut rng);
|
let mut a = Fq::random(&mut rng).into_repr();
|
||||||
a.0[5] >>= 30;
|
a.0[5] >>= 30;
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
@@ -1483,7 +1488,10 @@ fn test_fq_repr_sub_noborrow() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq_repr_add_nocarry() {
|
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([
|
let mut t = FqRepr([
|
||||||
0x827a4a08041ebd9,
|
0x827a4a08041ebd9,
|
||||||
@@ -1514,9 +1522,9 @@ fn test_fq_repr_add_nocarry() {
|
|||||||
|
|
||||||
// Test for the associativity of addition.
|
// Test for the associativity of addition.
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let mut a = FqRepr::rand(&mut rng);
|
let mut a = Fq::random(&mut rng).into_repr();
|
||||||
let mut b = FqRepr::rand(&mut rng);
|
let mut b = Fq::random(&mut rng).into_repr();
|
||||||
let mut c = FqRepr::rand(&mut rng);
|
let mut c = Fq::random(&mut rng).into_repr();
|
||||||
|
|
||||||
// Unset the first few bits, so that overflow won't occur.
|
// Unset the first few bits, so that overflow won't occur.
|
||||||
a.0[5] >>= 3;
|
a.0[5] >>= 3;
|
||||||
@@ -1574,31 +1582,32 @@ fn test_fq_is_valid() {
|
|||||||
a.0.sub_noborrow(&FqRepr::from(1));
|
a.0.sub_noborrow(&FqRepr::from(1));
|
||||||
assert!(a.is_valid());
|
assert!(a.is_valid());
|
||||||
assert!(Fq(FqRepr::from(0)).is_valid());
|
assert!(Fq(FqRepr::from(0)).is_valid());
|
||||||
assert!(
|
assert!(Fq(FqRepr([
|
||||||
Fq(FqRepr([
|
0xdf4671abd14dab3e,
|
||||||
0xdf4671abd14dab3e,
|
0xe2dc0c9f534fbd33,
|
||||||
0xe2dc0c9f534fbd33,
|
0x31ca6c880cc444a6,
|
||||||
0x31ca6c880cc444a6,
|
0x257a67e70ef33359,
|
||||||
0x257a67e70ef33359,
|
0xf9b29e493f899b36,
|
||||||
0xf9b29e493f899b36,
|
0x17c8be1800b9f059
|
||||||
0x17c8be1800b9f059
|
]))
|
||||||
])).is_valid()
|
.is_valid());
|
||||||
);
|
assert!(!Fq(FqRepr([
|
||||||
assert!(
|
0xffffffffffffffff,
|
||||||
!Fq(FqRepr([
|
0xffffffffffffffff,
|
||||||
0xffffffffffffffff,
|
0xffffffffffffffff,
|
||||||
0xffffffffffffffff,
|
0xffffffffffffffff,
|
||||||
0xffffffffffffffff,
|
0xffffffffffffffff,
|
||||||
0xffffffffffffffff,
|
0xffffffffffffffff
|
||||||
0xffffffffffffffff,
|
]))
|
||||||
0xffffffffffffffff
|
.is_valid());
|
||||||
])).is_valid()
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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 {
|
for _ in 0..1000 {
|
||||||
let a = Fq::rand(&mut rng);
|
let a = Fq::random(&mut rng);
|
||||||
assert!(a.is_valid());
|
assert!(a.is_valid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1708,13 +1717,16 @@ fn test_fq_add_assign() {
|
|||||||
|
|
||||||
// Test associativity
|
// 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 {
|
for _ in 0..1000 {
|
||||||
// Generate a, b, c and ensure (a + b) + c == a + (b + c).
|
// Generate a, b, c and ensure (a + b) + c == a + (b + c).
|
||||||
let a = Fq::rand(&mut rng);
|
let a = Fq::random(&mut rng);
|
||||||
let b = Fq::rand(&mut rng);
|
let b = Fq::random(&mut rng);
|
||||||
let c = Fq::rand(&mut rng);
|
let c = Fq::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
tmp1.add_assign(&b);
|
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 {
|
for _ in 0..1000 {
|
||||||
// Ensure that (a - b) + (b - a) = 0.
|
// Ensure that (a - b) + (b - a) = 0.
|
||||||
let a = Fq::rand(&mut rng);
|
let a = Fq::random(&mut rng);
|
||||||
let b = Fq::rand(&mut rng);
|
let b = Fq::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
tmp1.sub_assign(&b);
|
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 {
|
for _ in 0..1000000 {
|
||||||
// Ensure that (a * b) * c = a * (b * c)
|
// Ensure that (a * b) * c = a * (b * c)
|
||||||
let a = Fq::rand(&mut rng);
|
let a = Fq::random(&mut rng);
|
||||||
let b = Fq::rand(&mut rng);
|
let b = Fq::random(&mut rng);
|
||||||
let c = Fq::rand(&mut rng);
|
let c = Fq::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
tmp1.mul_assign(&b);
|
tmp1.mul_assign(&b);
|
||||||
@@ -1887,10 +1905,10 @@ fn test_fq_mul_assign() {
|
|||||||
for _ in 0..1000000 {
|
for _ in 0..1000000 {
|
||||||
// Ensure that r * (a + b + c) = r*a + r*b + r*c
|
// Ensure that r * (a + b + c) = r*a + r*b + r*c
|
||||||
|
|
||||||
let r = Fq::rand(&mut rng);
|
let r = Fq::random(&mut rng);
|
||||||
let mut a = Fq::rand(&mut rng);
|
let mut a = Fq::random(&mut rng);
|
||||||
let mut b = Fq::rand(&mut rng);
|
let mut b = Fq::random(&mut rng);
|
||||||
let mut c = Fq::rand(&mut rng);
|
let mut c = Fq::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
tmp1.add_assign(&b);
|
tmp1.add_assign(&b);
|
||||||
@@ -1929,14 +1947,18 @@ fn test_fq_squaring() {
|
|||||||
0xdc05c659b4e15b27,
|
0xdc05c659b4e15b27,
|
||||||
0x79361e5a802c6a23,
|
0x79361e5a802c6a23,
|
||||||
0x24bcbe5d51b9a6f
|
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 {
|
for _ in 0..1000000 {
|
||||||
// Ensure that (a * a) = a^2
|
// Ensure that (a * a) = a^2
|
||||||
let a = Fq::rand(&mut rng);
|
let a = Fq::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp = a;
|
let mut tmp = a;
|
||||||
tmp.square();
|
tmp.square();
|
||||||
@@ -1952,13 +1974,16 @@ fn test_fq_squaring() {
|
|||||||
fn test_fq_inverse() {
|
fn test_fq_inverse() {
|
||||||
assert!(Fq::zero().inverse().is_none());
|
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();
|
let one = Fq::one();
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
// Ensure that a * a^-1 = 1
|
// 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();
|
let ainv = a.inverse().unwrap();
|
||||||
a.mul_assign(&ainv);
|
a.mul_assign(&ainv);
|
||||||
assert_eq!(a, one);
|
assert_eq!(a, one);
|
||||||
@@ -1967,11 +1992,14 @@ fn test_fq_inverse() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq_double() {
|
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 {
|
for _ in 0..1000 {
|
||||||
// Ensure doubling a is equivalent to adding a to itself.
|
// 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;
|
let mut b = a;
|
||||||
b.add_assign(&a);
|
b.add_assign(&a);
|
||||||
a.double();
|
a.double();
|
||||||
@@ -1988,11 +2016,14 @@ fn test_fq_negate() {
|
|||||||
assert!(a.is_zero());
|
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 {
|
for _ in 0..1000 {
|
||||||
// Ensure (a - (-a)) = 0.
|
// Ensure (a - (-a)) = 0.
|
||||||
let mut a = Fq::rand(&mut rng);
|
let mut a = Fq::random(&mut rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
b.negate();
|
b.negate();
|
||||||
a.add_assign(&b);
|
a.add_assign(&b);
|
||||||
@@ -2003,12 +2034,15 @@ fn test_fq_negate() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq_pow() {
|
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 {
|
for i in 0..1000 {
|
||||||
// Exponentiate by various small numbers and ensure it consists with repeated
|
// Exponentiate by various small numbers and ensure it consists with repeated
|
||||||
// multiplication.
|
// multiplication.
|
||||||
let a = Fq::rand(&mut rng);
|
let a = Fq::random(&mut rng);
|
||||||
let target = a.pow(&[i]);
|
let target = a.pow(&[i]);
|
||||||
let mut c = Fq::one();
|
let mut c = Fq::one();
|
||||||
for _ in 0..i {
|
for _ in 0..i {
|
||||||
@@ -2019,7 +2053,7 @@ fn test_fq_pow() {
|
|||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
// Exponentiating by the modulus should have no effect in a prime field.
|
// 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()));
|
assert_eq!(a, a.pow(Fq::char()));
|
||||||
}
|
}
|
||||||
@@ -2029,13 +2063,16 @@ fn test_fq_pow() {
|
|||||||
fn test_fq_sqrt() {
|
fn test_fq_sqrt() {
|
||||||
use ff::SqrtField;
|
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());
|
assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero());
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
// Ensure sqrt(a^2) = a or -a
|
// Ensure sqrt(a^2) = a or -a
|
||||||
let a = Fq::rand(&mut rng);
|
let a = Fq::random(&mut rng);
|
||||||
let mut nega = a;
|
let mut nega = a;
|
||||||
nega.negate();
|
nega.negate();
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
@@ -2048,7 +2085,7 @@ fn test_fq_sqrt() {
|
|||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
// Ensure sqrt(a)^2 = a for random a
|
// 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() {
|
if let Some(mut tmp) = a.sqrt() {
|
||||||
tmp.square();
|
tmp.square();
|
||||||
@@ -2061,16 +2098,15 @@ fn test_fq_sqrt() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_fq_from_into_repr() {
|
fn test_fq_from_into_repr() {
|
||||||
// q + 1 should not be in the field
|
// q + 1 should not be in the field
|
||||||
assert!(
|
assert!(Fq::from_repr(FqRepr([
|
||||||
Fq::from_repr(FqRepr([
|
0xb9feffffffffaaac,
|
||||||
0xb9feffffffffaaac,
|
0x1eabfffeb153ffff,
|
||||||
0x1eabfffeb153ffff,
|
0x6730d2a0f6b0f624,
|
||||||
0x6730d2a0f6b0f624,
|
0x64774b84f38512bf,
|
||||||
0x64774b84f38512bf,
|
0x4b1ba7b6434bacd7,
|
||||||
0x4b1ba7b6434bacd7,
|
0x1a0111ea397fe69a
|
||||||
0x1a0111ea397fe69a
|
]))
|
||||||
])).is_err()
|
.is_err());
|
||||||
);
|
|
||||||
|
|
||||||
// q should not be in the field
|
// q should not be in the field
|
||||||
assert!(Fq::from_repr(Fq::char()).is_err());
|
assert!(Fq::from_repr(Fq::char()).is_err());
|
||||||
@@ -2108,11 +2144,14 @@ fn test_fq_from_into_repr() {
|
|||||||
// Zero should be in the field.
|
// Zero should be in the field.
|
||||||
assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero());
|
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 {
|
for _ in 0..1000 {
|
||||||
// Try to turn Fq elements into representations and back again, and compare.
|
// 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 a_repr = a.into_repr();
|
||||||
let b_repr = FqRepr::from(a);
|
let b_repr = FqRepr::from(a);
|
||||||
assert_eq!(a_repr, b_repr);
|
assert_eq!(a_repr, b_repr);
|
||||||
@@ -2186,10 +2225,10 @@ fn test_fq_root_of_unity() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fq_field_tests() {
|
fn fq_field_tests() {
|
||||||
::tests::field::random_field_tests::<Fq>();
|
crate::tests::field::random_field_tests::<Fq>();
|
||||||
::tests::field::random_sqrt_tests::<Fq>();
|
crate::tests::field::random_sqrt_tests::<Fq>();
|
||||||
::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
|
||||||
::tests::field::from_str_tests::<Fq>();
|
crate::tests::field::from_str_tests::<Fq>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2205,7 +2244,7 @@ fn test_fq_ordering() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fq_repr_tests() {
|
fn fq_repr_tests() {
|
||||||
::tests::repr::random_repr_tests::<FqRepr>();
|
crate::tests::repr::random_repr_tests::<Fq>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::fq::FROBENIUS_COEFF_FQ12_C1;
|
|||||||
use super::fq2::Fq2;
|
use super::fq2::Fq2;
|
||||||
use super::fq6::Fq6;
|
use super::fq6::Fq6;
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use rand::{Rand, Rng};
|
use rand_core::RngCore;
|
||||||
|
|
||||||
/// An element of Fq12, represented by c0 + c1 * w.
|
/// An element of Fq12, represented by c0 + c1 * w.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
@@ -12,20 +12,11 @@ pub struct Fq12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for Fq12 {
|
impl ::std::fmt::Display for Fq12 {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
|
write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for Fq12 {
|
|
||||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
|
||||||
Fq12 {
|
|
||||||
c0: rng.gen(),
|
|
||||||
c1: rng.gen(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fq12 {
|
impl Fq12 {
|
||||||
pub fn conjugate(&mut self) {
|
pub fn conjugate(&mut self) {
|
||||||
self.c1.negate();
|
self.c1.negate();
|
||||||
@@ -49,6 +40,13 @@ impl Fq12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Field for Fq12 {
|
impl Field for Fq12 {
|
||||||
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||||
|
Fq12 {
|
||||||
|
c0: Fq6::random(rng),
|
||||||
|
c1: Fq6::random(rng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
Fq12 {
|
Fq12 {
|
||||||
c0: Fq6::zero(),
|
c0: Fq6::zero(),
|
||||||
@@ -149,24 +147,29 @@ impl Field for Fq12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use rand::{SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq12_mul_by_014() {
|
fn test_fq12_mul_by_014() {
|
||||||
let mut rng = XorShiftRng::from_seed([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 {
|
for _ in 0..1000 {
|
||||||
let c0 = Fq2::rand(&mut rng);
|
let c0 = Fq2::random(&mut rng);
|
||||||
let c1 = Fq2::rand(&mut rng);
|
let c1 = Fq2::random(&mut rng);
|
||||||
let c5 = Fq2::rand(&mut rng);
|
let c5 = Fq2::random(&mut rng);
|
||||||
let mut a = Fq12::rand(&mut rng);
|
let mut a = Fq12::random(&mut rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
|
|
||||||
a.mul_by_014(&c0, &c1, &c5);
|
a.mul_by_014(&c0, &c1, &c5);
|
||||||
b.mul_assign(&Fq12 {
|
b.mul_assign(&Fq12 {
|
||||||
c0: Fq6 {
|
c0: Fq6 {
|
||||||
c0: c0,
|
c0,
|
||||||
c1: c1,
|
c1,
|
||||||
c2: Fq2::zero(),
|
c2: Fq2::zero(),
|
||||||
},
|
},
|
||||||
c1: Fq6 {
|
c1: Fq6 {
|
||||||
@@ -184,6 +187,6 @@ fn test_fq12_mul_by_014() {
|
|||||||
fn fq12_field_tests() {
|
fn fq12_field_tests() {
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
|
|
||||||
::tests::field::random_field_tests::<Fq12>();
|
crate::tests::field::random_field_tests::<Fq12>();
|
||||||
::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ff::{Field, SqrtField};
|
||||||
use rand::{Rand, Rng};
|
use rand_core::RngCore;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ pub struct Fq2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for Fq2 {
|
impl ::std::fmt::Display for Fq2 {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
|
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,16 +56,14 @@ impl Fq2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for Fq2 {
|
impl Field for Fq2 {
|
||||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||||
Fq2 {
|
Fq2 {
|
||||||
c0: rng.gen(),
|
c0: Fq::random(rng),
|
||||||
c1: rng.gen(),
|
c1: Fq::random(rng),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Field for Fq2 {
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
Fq2 {
|
Fq2 {
|
||||||
c0: Fq::zero(),
|
c0: Fq::zero(),
|
||||||
@@ -263,12 +261,11 @@ fn test_fq2_basics() {
|
|||||||
);
|
);
|
||||||
assert!(Fq2::zero().is_zero());
|
assert!(Fq2::zero().is_zero());
|
||||||
assert!(!Fq2::one().is_zero());
|
assert!(!Fq2::one().is_zero());
|
||||||
assert!(
|
assert!(!Fq2 {
|
||||||
!Fq2 {
|
c0: Fq::zero(),
|
||||||
c0: Fq::zero(),
|
c1: Fq::one(),
|
||||||
c1: Fq::one(),
|
}
|
||||||
}.is_zero()
|
.is_zero());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -311,7 +308,8 @@ fn test_fq2_squaring() {
|
|||||||
0xf7f295a94e58ae7c,
|
0xf7f295a94e58ae7c,
|
||||||
0x41b76dcc1c3fbe5e,
|
0x41b76dcc1c3fbe5e,
|
||||||
0x7080c5fa1d8e042,
|
0x7080c5fa1d8e042,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x38f473b3c870a4ab,
|
0x38f473b3c870a4ab,
|
||||||
0x6ad3291177c8c7e5,
|
0x6ad3291177c8c7e5,
|
||||||
@@ -319,7 +317,8 @@ fn test_fq2_squaring() {
|
|||||||
0xbfb99020604137a0,
|
0xbfb99020604137a0,
|
||||||
0xfc58a7b7be815407,
|
0xfc58a7b7be815407,
|
||||||
0x10d1615e75250a21,
|
0x10d1615e75250a21,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
};
|
};
|
||||||
a.square();
|
a.square();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -332,7 +331,8 @@ fn test_fq2_squaring() {
|
|||||||
0xcb674157618da176,
|
0xcb674157618da176,
|
||||||
0x4cf17b5893c3d327,
|
0x4cf17b5893c3d327,
|
||||||
0x7eac81369c43361
|
0x7eac81369c43361
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xc1579cf58e980cf8,
|
0xc1579cf58e980cf8,
|
||||||
0xa23eb7e12dd54d98,
|
0xa23eb7e12dd54d98,
|
||||||
@@ -340,7 +340,8 @@ fn test_fq2_squaring() {
|
|||||||
0x38d0d7275a9689e1,
|
0x38d0d7275a9689e1,
|
||||||
0x739c983042779a65,
|
0x739c983042779a65,
|
||||||
0x1542a61c8a8db994
|
0x1542a61c8a8db994
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -358,7 +359,8 @@ fn test_fq2_mul() {
|
|||||||
0x9ee53e7e84d7532e,
|
0x9ee53e7e84d7532e,
|
||||||
0x1c202d8ed97afb45,
|
0x1c202d8ed97afb45,
|
||||||
0x51d3f9253e2516f,
|
0x51d3f9253e2516f,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xa7348a8b511aedcf,
|
0xa7348a8b511aedcf,
|
||||||
0x143c215d8176b319,
|
0x143c215d8176b319,
|
||||||
@@ -366,7 +368,8 @@ fn test_fq2_mul() {
|
|||||||
0x9533e4a9a5158be,
|
0x9533e4a9a5158be,
|
||||||
0x7a5e1ecb676d65f9,
|
0x7a5e1ecb676d65f9,
|
||||||
0x180c3ee46656b008,
|
0x180c3ee46656b008,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
};
|
};
|
||||||
a.mul_assign(&Fq2 {
|
a.mul_assign(&Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -376,7 +379,8 @@ fn test_fq2_mul() {
|
|||||||
0xcd460f9f0c23e430,
|
0xcd460f9f0c23e430,
|
||||||
0x6c9110292bfa409,
|
0x6c9110292bfa409,
|
||||||
0x2c93a72eb8af83e,
|
0x2c93a72eb8af83e,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x4b1c3f936d8992d4,
|
0x4b1c3f936d8992d4,
|
||||||
0x1d2a72916dba4c8a,
|
0x1d2a72916dba4c8a,
|
||||||
@@ -384,7 +388,8 @@ fn test_fq2_mul() {
|
|||||||
0x57a06d3135a752ae,
|
0x57a06d3135a752ae,
|
||||||
0x634cd3c6c565096d,
|
0x634cd3c6c565096d,
|
||||||
0x19e17334d4e93558,
|
0x19e17334d4e93558,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
a,
|
a,
|
||||||
@@ -396,7 +401,8 @@ fn test_fq2_mul() {
|
|||||||
0x5511fe4d84ee5f78,
|
0x5511fe4d84ee5f78,
|
||||||
0x5310a202d92f9963,
|
0x5310a202d92f9963,
|
||||||
0x1751afbe166e5399
|
0x1751afbe166e5399
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x84af0e1bd630117a,
|
0x84af0e1bd630117a,
|
||||||
0x6c63cd4da2c2aa7,
|
0x6c63cd4da2c2aa7,
|
||||||
@@ -404,7 +410,8 @@ fn test_fq2_mul() {
|
|||||||
0xc975106579c275ee,
|
0xc975106579c275ee,
|
||||||
0x33a9ac82ce4c5083,
|
0x33a9ac82ce4c5083,
|
||||||
0x1ef1a36c201589d
|
0x1ef1a36c201589d
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -424,7 +431,8 @@ fn test_fq2_inverse() {
|
|||||||
0x9ee53e7e84d7532e,
|
0x9ee53e7e84d7532e,
|
||||||
0x1c202d8ed97afb45,
|
0x1c202d8ed97afb45,
|
||||||
0x51d3f9253e2516f,
|
0x51d3f9253e2516f,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xa7348a8b511aedcf,
|
0xa7348a8b511aedcf,
|
||||||
0x143c215d8176b319,
|
0x143c215d8176b319,
|
||||||
@@ -432,7 +440,8 @@ fn test_fq2_inverse() {
|
|||||||
0x9533e4a9a5158be,
|
0x9533e4a9a5158be,
|
||||||
0x7a5e1ecb676d65f9,
|
0x7a5e1ecb676d65f9,
|
||||||
0x180c3ee46656b008,
|
0x180c3ee46656b008,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
};
|
};
|
||||||
let a = a.inverse().unwrap();
|
let a = a.inverse().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -445,7 +454,8 @@ fn test_fq2_inverse() {
|
|||||||
0xdfba703293941c30,
|
0xdfba703293941c30,
|
||||||
0xa6c3d8f9586f2636,
|
0xa6c3d8f9586f2636,
|
||||||
0x1351ef01941b70c4
|
0x1351ef01941b70c4
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x8c39fd76a8312cb4,
|
0x8c39fd76a8312cb4,
|
||||||
0x15d7b6b95defbff0,
|
0x15d7b6b95defbff0,
|
||||||
@@ -453,7 +463,8 @@ fn test_fq2_inverse() {
|
|||||||
0xcbf651a0f367afb2,
|
0xcbf651a0f367afb2,
|
||||||
0xdf4e54f0d3ef15a6,
|
0xdf4e54f0d3ef15a6,
|
||||||
0x103bdf241afb0019
|
0x103bdf241afb0019
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -471,7 +482,8 @@ fn test_fq2_addition() {
|
|||||||
0xb966ce3bc2108b13,
|
0xb966ce3bc2108b13,
|
||||||
0xccc649c4b9532bf3,
|
0xccc649c4b9532bf3,
|
||||||
0xf8d295b2ded9dc,
|
0xf8d295b2ded9dc,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x977df6efcdaee0db,
|
0x977df6efcdaee0db,
|
||||||
0x946ae52d684fa7ed,
|
0x946ae52d684fa7ed,
|
||||||
@@ -479,7 +491,8 @@ fn test_fq2_addition() {
|
|||||||
0xb3f8afc0ee248cad,
|
0xb3f8afc0ee248cad,
|
||||||
0x4e464dea5bcfd41e,
|
0x4e464dea5bcfd41e,
|
||||||
0x12d1137b8a6a837,
|
0x12d1137b8a6a837,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
};
|
};
|
||||||
a.add_assign(&Fq2 {
|
a.add_assign(&Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -489,7 +502,8 @@ fn test_fq2_addition() {
|
|||||||
0x3b88899a42a6318f,
|
0x3b88899a42a6318f,
|
||||||
0x986a4a62fa82a49d,
|
0x986a4a62fa82a49d,
|
||||||
0x13ce433fa26027f5,
|
0x13ce433fa26027f5,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x66323bf80b58b9b9,
|
0x66323bf80b58b9b9,
|
||||||
0xa1379b6facf6e596,
|
0xa1379b6facf6e596,
|
||||||
@@ -497,7 +511,8 @@ fn test_fq2_addition() {
|
|||||||
0x2236f55246d0d44d,
|
0x2236f55246d0d44d,
|
||||||
0x4c8c1800eb104566,
|
0x4c8c1800eb104566,
|
||||||
0x11d6e20e986c2085,
|
0x11d6e20e986c2085,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
a,
|
a,
|
||||||
@@ -509,7 +524,8 @@ fn test_fq2_addition() {
|
|||||||
0xf4ef57d604b6bca2,
|
0xf4ef57d604b6bca2,
|
||||||
0x65309427b3d5d090,
|
0x65309427b3d5d090,
|
||||||
0x14c715d5553f01d2
|
0x14c715d5553f01d2
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xfdb032e7d9079a94,
|
0xfdb032e7d9079a94,
|
||||||
0x35a2809d15468d83,
|
0x35a2809d15468d83,
|
||||||
@@ -517,7 +533,8 @@ fn test_fq2_addition() {
|
|||||||
0xd62fa51334f560fa,
|
0xd62fa51334f560fa,
|
||||||
0x9ad265eb46e01984,
|
0x9ad265eb46e01984,
|
||||||
0x1303f3465112c8bc
|
0x1303f3465112c8bc
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -535,7 +552,8 @@ fn test_fq2_subtraction() {
|
|||||||
0xb966ce3bc2108b13,
|
0xb966ce3bc2108b13,
|
||||||
0xccc649c4b9532bf3,
|
0xccc649c4b9532bf3,
|
||||||
0xf8d295b2ded9dc,
|
0xf8d295b2ded9dc,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x977df6efcdaee0db,
|
0x977df6efcdaee0db,
|
||||||
0x946ae52d684fa7ed,
|
0x946ae52d684fa7ed,
|
||||||
@@ -543,7 +561,8 @@ fn test_fq2_subtraction() {
|
|||||||
0xb3f8afc0ee248cad,
|
0xb3f8afc0ee248cad,
|
||||||
0x4e464dea5bcfd41e,
|
0x4e464dea5bcfd41e,
|
||||||
0x12d1137b8a6a837,
|
0x12d1137b8a6a837,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
};
|
};
|
||||||
a.sub_assign(&Fq2 {
|
a.sub_assign(&Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
@@ -553,7 +572,8 @@ fn test_fq2_subtraction() {
|
|||||||
0x3b88899a42a6318f,
|
0x3b88899a42a6318f,
|
||||||
0x986a4a62fa82a49d,
|
0x986a4a62fa82a49d,
|
||||||
0x13ce433fa26027f5,
|
0x13ce433fa26027f5,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x66323bf80b58b9b9,
|
0x66323bf80b58b9b9,
|
||||||
0xa1379b6facf6e596,
|
0xa1379b6facf6e596,
|
||||||
@@ -561,7 +581,8 @@ fn test_fq2_subtraction() {
|
|||||||
0x2236f55246d0d44d,
|
0x2236f55246d0d44d,
|
||||||
0x4c8c1800eb104566,
|
0x4c8c1800eb104566,
|
||||||
0x11d6e20e986c2085,
|
0x11d6e20e986c2085,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
a,
|
a,
|
||||||
@@ -573,7 +594,8 @@ fn test_fq2_subtraction() {
|
|||||||
0xe255902672ef6c43,
|
0xe255902672ef6c43,
|
||||||
0x7f77a718021c342d,
|
0x7f77a718021c342d,
|
||||||
0x72ba14049fe9881
|
0x72ba14049fe9881
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xeb4abaf7c255d1cd,
|
0xeb4abaf7c255d1cd,
|
||||||
0x11df49bc6cacc256,
|
0x11df49bc6cacc256,
|
||||||
@@ -581,7 +603,8 @@ fn test_fq2_subtraction() {
|
|||||||
0xf63905f39ad8cb1f,
|
0xf63905f39ad8cb1f,
|
||||||
0x4cd5dd9fb40b3b8f,
|
0x4cd5dd9fb40b3b8f,
|
||||||
0x957411359ba6e4c
|
0x957411359ba6e4c
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -599,7 +622,8 @@ fn test_fq2_negation() {
|
|||||||
0xb966ce3bc2108b13,
|
0xb966ce3bc2108b13,
|
||||||
0xccc649c4b9532bf3,
|
0xccc649c4b9532bf3,
|
||||||
0xf8d295b2ded9dc,
|
0xf8d295b2ded9dc,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x977df6efcdaee0db,
|
0x977df6efcdaee0db,
|
||||||
0x946ae52d684fa7ed,
|
0x946ae52d684fa7ed,
|
||||||
@@ -607,7 +631,8 @@ fn test_fq2_negation() {
|
|||||||
0xb3f8afc0ee248cad,
|
0xb3f8afc0ee248cad,
|
||||||
0x4e464dea5bcfd41e,
|
0x4e464dea5bcfd41e,
|
||||||
0x12d1137b8a6a837,
|
0x12d1137b8a6a837,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
};
|
};
|
||||||
a.negate();
|
a.negate();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -620,7 +645,8 @@ fn test_fq2_negation() {
|
|||||||
0xab107d49317487ab,
|
0xab107d49317487ab,
|
||||||
0x7e555df189f880e3,
|
0x7e555df189f880e3,
|
||||||
0x19083f5486a10cbd
|
0x19083f5486a10cbd
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x228109103250c9d0,
|
0x228109103250c9d0,
|
||||||
0x8a411ad149045812,
|
0x8a411ad149045812,
|
||||||
@@ -628,7 +654,8 @@ fn test_fq2_negation() {
|
|||||||
0xb07e9bc405608611,
|
0xb07e9bc405608611,
|
||||||
0xfcd559cbe77bd8b8,
|
0xfcd559cbe77bd8b8,
|
||||||
0x18d400b280d93e62
|
0x18d400b280d93e62
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -646,7 +673,8 @@ fn test_fq2_doubling() {
|
|||||||
0xb966ce3bc2108b13,
|
0xb966ce3bc2108b13,
|
||||||
0xccc649c4b9532bf3,
|
0xccc649c4b9532bf3,
|
||||||
0xf8d295b2ded9dc,
|
0xf8d295b2ded9dc,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x977df6efcdaee0db,
|
0x977df6efcdaee0db,
|
||||||
0x946ae52d684fa7ed,
|
0x946ae52d684fa7ed,
|
||||||
@@ -654,7 +682,8 @@ fn test_fq2_doubling() {
|
|||||||
0xb3f8afc0ee248cad,
|
0xb3f8afc0ee248cad,
|
||||||
0x4e464dea5bcfd41e,
|
0x4e464dea5bcfd41e,
|
||||||
0x12d1137b8a6a837,
|
0x12d1137b8a6a837,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
};
|
};
|
||||||
a.double();
|
a.double();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -667,7 +696,8 @@ fn test_fq2_doubling() {
|
|||||||
0x72cd9c7784211627,
|
0x72cd9c7784211627,
|
||||||
0x998c938972a657e7,
|
0x998c938972a657e7,
|
||||||
0x1f1a52b65bdb3b9
|
0x1f1a52b65bdb3b9
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x2efbeddf9b5dc1b6,
|
0x2efbeddf9b5dc1b6,
|
||||||
0x28d5ca5ad09f4fdb,
|
0x28d5ca5ad09f4fdb,
|
||||||
@@ -675,7 +705,8 @@ fn test_fq2_doubling() {
|
|||||||
0x67f15f81dc49195b,
|
0x67f15f81dc49195b,
|
||||||
0x9c8c9bd4b79fa83d,
|
0x9c8c9bd4b79fa83d,
|
||||||
0x25a226f714d506e
|
0x25a226f714d506e
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -693,7 +724,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb966ce3bc2108b13,
|
0xb966ce3bc2108b13,
|
||||||
0xccc649c4b9532bf3,
|
0xccc649c4b9532bf3,
|
||||||
0xf8d295b2ded9dc,
|
0xf8d295b2ded9dc,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x977df6efcdaee0db,
|
0x977df6efcdaee0db,
|
||||||
0x946ae52d684fa7ed,
|
0x946ae52d684fa7ed,
|
||||||
@@ -701,7 +733,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb3f8afc0ee248cad,
|
0xb3f8afc0ee248cad,
|
||||||
0x4e464dea5bcfd41e,
|
0x4e464dea5bcfd41e,
|
||||||
0x12d1137b8a6a837,
|
0x12d1137b8a6a837,
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
};
|
};
|
||||||
a.frobenius_map(0);
|
a.frobenius_map(0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -714,7 +747,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb966ce3bc2108b13,
|
0xb966ce3bc2108b13,
|
||||||
0xccc649c4b9532bf3,
|
0xccc649c4b9532bf3,
|
||||||
0xf8d295b2ded9dc
|
0xf8d295b2ded9dc
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x977df6efcdaee0db,
|
0x977df6efcdaee0db,
|
||||||
0x946ae52d684fa7ed,
|
0x946ae52d684fa7ed,
|
||||||
@@ -722,7 +756,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb3f8afc0ee248cad,
|
0xb3f8afc0ee248cad,
|
||||||
0x4e464dea5bcfd41e,
|
0x4e464dea5bcfd41e,
|
||||||
0x12d1137b8a6a837
|
0x12d1137b8a6a837
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
a.frobenius_map(1);
|
a.frobenius_map(1);
|
||||||
@@ -736,7 +771,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb966ce3bc2108b13,
|
0xb966ce3bc2108b13,
|
||||||
0xccc649c4b9532bf3,
|
0xccc649c4b9532bf3,
|
||||||
0xf8d295b2ded9dc
|
0xf8d295b2ded9dc
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x228109103250c9d0,
|
0x228109103250c9d0,
|
||||||
0x8a411ad149045812,
|
0x8a411ad149045812,
|
||||||
@@ -744,7 +780,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb07e9bc405608611,
|
0xb07e9bc405608611,
|
||||||
0xfcd559cbe77bd8b8,
|
0xfcd559cbe77bd8b8,
|
||||||
0x18d400b280d93e62
|
0x18d400b280d93e62
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
a.frobenius_map(1);
|
a.frobenius_map(1);
|
||||||
@@ -758,7 +795,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb966ce3bc2108b13,
|
0xb966ce3bc2108b13,
|
||||||
0xccc649c4b9532bf3,
|
0xccc649c4b9532bf3,
|
||||||
0xf8d295b2ded9dc
|
0xf8d295b2ded9dc
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x977df6efcdaee0db,
|
0x977df6efcdaee0db,
|
||||||
0x946ae52d684fa7ed,
|
0x946ae52d684fa7ed,
|
||||||
@@ -766,7 +804,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb3f8afc0ee248cad,
|
0xb3f8afc0ee248cad,
|
||||||
0x4e464dea5bcfd41e,
|
0x4e464dea5bcfd41e,
|
||||||
0x12d1137b8a6a837
|
0x12d1137b8a6a837
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
a.frobenius_map(2);
|
a.frobenius_map(2);
|
||||||
@@ -780,7 +819,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb966ce3bc2108b13,
|
0xb966ce3bc2108b13,
|
||||||
0xccc649c4b9532bf3,
|
0xccc649c4b9532bf3,
|
||||||
0xf8d295b2ded9dc
|
0xf8d295b2ded9dc
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0x977df6efcdaee0db,
|
0x977df6efcdaee0db,
|
||||||
0x946ae52d684fa7ed,
|
0x946ae52d684fa7ed,
|
||||||
@@ -788,7 +828,8 @@ fn test_fq2_frobenius_map() {
|
|||||||
0xb3f8afc0ee248cad,
|
0xb3f8afc0ee248cad,
|
||||||
0x4e464dea5bcfd41e,
|
0x4e464dea5bcfd41e,
|
||||||
0x12d1137b8a6a837
|
0x12d1137b8a6a837
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -807,7 +848,8 @@ fn test_fq2_sqrt() {
|
|||||||
0xdb4a116b5bf74aa1,
|
0xdb4a116b5bf74aa1,
|
||||||
0x1e58b2159dfe10e2,
|
0x1e58b2159dfe10e2,
|
||||||
0x7ca7da1f13606ac
|
0x7ca7da1f13606ac
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xfa8de88b7516d2c3,
|
0xfa8de88b7516d2c3,
|
||||||
0x371a75ed14f41629,
|
0x371a75ed14f41629,
|
||||||
@@ -815,9 +857,11 @@ fn test_fq2_sqrt() {
|
|||||||
0x212611bca4e99121,
|
0x212611bca4e99121,
|
||||||
0x8ee5394d77afb3d,
|
0x8ee5394d77afb3d,
|
||||||
0xec92336650e49d5
|
0xec92336650e49d5
|
||||||
])).unwrap(),
|
]))
|
||||||
}.sqrt()
|
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
}
|
||||||
|
.sqrt()
|
||||||
|
.unwrap(),
|
||||||
Fq2 {
|
Fq2 {
|
||||||
c0: Fq::from_repr(FqRepr([
|
c0: Fq::from_repr(FqRepr([
|
||||||
0x40b299b2704258c5,
|
0x40b299b2704258c5,
|
||||||
@@ -826,7 +870,8 @@ fn test_fq2_sqrt() {
|
|||||||
0x8d7f1f723d02c1d3,
|
0x8d7f1f723d02c1d3,
|
||||||
0x881b3e01b611c070,
|
0x881b3e01b611c070,
|
||||||
0x10f6963bbad2ebc5
|
0x10f6963bbad2ebc5
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
0xc099534fc209e752,
|
0xc099534fc209e752,
|
||||||
0x7670594665676447,
|
0x7670594665676447,
|
||||||
@@ -834,7 +879,8 @@ fn test_fq2_sqrt() {
|
|||||||
0x6b852aeaf2afcb1b,
|
0x6b852aeaf2afcb1b,
|
||||||
0xa4c93b08105d71a9,
|
0xa4c93b08105d71a9,
|
||||||
0x8d7cfff94216330
|
0x8d7cfff94216330
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -847,10 +893,12 @@ fn test_fq2_sqrt() {
|
|||||||
0x64774b84f38512bf,
|
0x64774b84f38512bf,
|
||||||
0x4b1ba7b6434bacd7,
|
0x4b1ba7b6434bacd7,
|
||||||
0x1a0111ea397fe69a
|
0x1a0111ea397fe69a
|
||||||
])).unwrap(),
|
]))
|
||||||
c1: Fq::zero(),
|
|
||||||
}.sqrt()
|
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
c1: Fq::zero(),
|
||||||
|
}
|
||||||
|
.sqrt()
|
||||||
|
.unwrap(),
|
||||||
Fq2 {
|
Fq2 {
|
||||||
c0: Fq::zero(),
|
c0: Fq::zero(),
|
||||||
c1: Fq::from_repr(FqRepr([
|
c1: Fq::from_repr(FqRepr([
|
||||||
@@ -860,7 +908,8 @@ fn test_fq2_sqrt() {
|
|||||||
0x64774b84f38512bf,
|
0x64774b84f38512bf,
|
||||||
0x4b1ba7b6434bacd7,
|
0x4b1ba7b6434bacd7,
|
||||||
0x1a0111ea397fe69a
|
0x1a0111ea397fe69a
|
||||||
])).unwrap(),
|
]))
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -879,11 +928,16 @@ fn test_fq2_legendre() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use rand::{SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq2_mul_nonresidue() {
|
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 {
|
let nqr = Fq2 {
|
||||||
c0: Fq::one(),
|
c0: Fq::one(),
|
||||||
@@ -891,7 +945,7 @@ fn test_fq2_mul_nonresidue() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let mut a = Fq2::rand(&mut rng);
|
let mut a = Fq2::random(&mut rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
a.mul_by_nonresidue();
|
a.mul_by_nonresidue();
|
||||||
b.mul_assign(&nqr);
|
b.mul_assign(&nqr);
|
||||||
@@ -904,7 +958,7 @@ fn test_fq2_mul_nonresidue() {
|
|||||||
fn fq2_field_tests() {
|
fn fq2_field_tests() {
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
|
|
||||||
::tests::field::random_field_tests::<Fq2>();
|
crate::tests::field::random_field_tests::<Fq2>();
|
||||||
::tests::field::random_sqrt_tests::<Fq2>();
|
crate::tests::field::random_sqrt_tests::<Fq2>();
|
||||||
::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2};
|
use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2};
|
||||||
use super::fq2::Fq2;
|
use super::fq2::Fq2;
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use rand::{Rand, Rng};
|
use rand_core::RngCore;
|
||||||
|
|
||||||
/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2).
|
/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2).
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
@@ -12,21 +12,11 @@ pub struct Fq6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for Fq6 {
|
impl ::std::fmt::Display for Fq6 {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
|
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for Fq6 {
|
|
||||||
fn rand<R: Rng>(rng: &mut R) -> Self {
|
|
||||||
Fq6 {
|
|
||||||
c0: rng.gen(),
|
|
||||||
c1: rng.gen(),
|
|
||||||
c2: rng.gen(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fq6 {
|
impl Fq6 {
|
||||||
/// Multiply by quadratic nonresidue v.
|
/// Multiply by quadratic nonresidue v.
|
||||||
pub fn mul_by_nonresidue(&mut self) {
|
pub fn mul_by_nonresidue(&mut self) {
|
||||||
@@ -110,6 +100,14 @@ impl Fq6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Field for Fq6 {
|
impl Field for Fq6 {
|
||||||
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||||
|
Fq6 {
|
||||||
|
c0: Fq2::random(rng),
|
||||||
|
c1: Fq2::random(rng),
|
||||||
|
c2: Fq2::random(rng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
Fq6 {
|
Fq6 {
|
||||||
c0: Fq2::zero(),
|
c0: Fq2::zero(),
|
||||||
@@ -302,11 +300,16 @@ impl Field for Fq6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use rand::{SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq6_mul_nonresidue() {
|
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 {
|
let nqr = Fq6 {
|
||||||
c0: Fq2::zero(),
|
c0: Fq2::zero(),
|
||||||
@@ -315,7 +318,7 @@ fn test_fq6_mul_nonresidue() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let mut a = Fq6::rand(&mut rng);
|
let mut a = Fq6::random(&mut rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
a.mul_by_nonresidue();
|
a.mul_by_nonresidue();
|
||||||
b.mul_assign(&nqr);
|
b.mul_assign(&nqr);
|
||||||
@@ -326,17 +329,20 @@ fn test_fq6_mul_nonresidue() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq6_mul_by_1() {
|
fn test_fq6_mul_by_1() {
|
||||||
let mut rng = XorShiftRng::from_seed([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 {
|
for _ in 0..1000 {
|
||||||
let c1 = Fq2::rand(&mut rng);
|
let c1 = Fq2::random(&mut rng);
|
||||||
let mut a = Fq6::rand(&mut rng);
|
let mut a = Fq6::random(&mut rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
|
|
||||||
a.mul_by_1(&c1);
|
a.mul_by_1(&c1);
|
||||||
b.mul_assign(&Fq6 {
|
b.mul_assign(&Fq6 {
|
||||||
c0: Fq2::zero(),
|
c0: Fq2::zero(),
|
||||||
c1: c1,
|
c1,
|
||||||
c2: Fq2::zero(),
|
c2: Fq2::zero(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -346,18 +352,21 @@ fn test_fq6_mul_by_1() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fq6_mul_by_01() {
|
fn test_fq6_mul_by_01() {
|
||||||
let mut rng = XorShiftRng::from_seed([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 {
|
for _ in 0..1000 {
|
||||||
let c0 = Fq2::rand(&mut rng);
|
let c0 = Fq2::random(&mut rng);
|
||||||
let c1 = Fq2::rand(&mut rng);
|
let c1 = Fq2::random(&mut rng);
|
||||||
let mut a = Fq6::rand(&mut rng);
|
let mut a = Fq6::random(&mut rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
|
|
||||||
a.mul_by_01(&c0, &c1);
|
a.mul_by_01(&c0, &c1);
|
||||||
b.mul_assign(&Fq6 {
|
b.mul_assign(&Fq6 {
|
||||||
c0: c0,
|
c0,
|
||||||
c1: c1,
|
c1,
|
||||||
c2: Fq2::zero(),
|
c2: Fq2::zero(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -369,6 +378,6 @@ fn test_fq6_mul_by_01() {
|
|||||||
fn fq6_field_tests() {
|
fn fq6_field_tests() {
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
|
|
||||||
::tests::field::random_field_tests::<Fq6>();
|
crate::tests::field::random_field_tests::<Fq6>();
|
||||||
::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr};
|
|||||||
pub struct Fr(FrRepr);
|
pub struct Fr(FrRepr);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
#[cfg(test)]
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fr_repr_ordering() {
|
fn test_fr_repr_ordering() {
|
||||||
@@ -197,7 +199,10 @@ fn test_fr_repr_num_bits() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fr_repr_sub_noborrow() {
|
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([
|
let mut t = FrRepr([
|
||||||
0x8e62a7e85264e2c3,
|
0x8e62a7e85264e2c3,
|
||||||
@@ -221,7 +226,7 @@ fn test_fr_repr_sub_noborrow() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let mut a = FrRepr::rand(&mut rng);
|
let mut a = Fr::random(&mut rng).into_repr();
|
||||||
a.0[3] >>= 30;
|
a.0[3] >>= 30;
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
@@ -296,7 +301,10 @@ fn test_fr_legendre() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fr_repr_add_nocarry() {
|
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([
|
let mut t = FrRepr([
|
||||||
0xd64f669809cbc6a4,
|
0xd64f669809cbc6a4,
|
||||||
@@ -322,9 +330,9 @@ fn test_fr_repr_add_nocarry() {
|
|||||||
|
|
||||||
// Test for the associativity of addition.
|
// Test for the associativity of addition.
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let mut a = FrRepr::rand(&mut rng);
|
let mut a = Fr::random(&mut rng).into_repr();
|
||||||
let mut b = FrRepr::rand(&mut rng);
|
let mut b = Fr::random(&mut rng).into_repr();
|
||||||
let mut c = FrRepr::rand(&mut rng);
|
let mut c = Fr::random(&mut rng).into_repr();
|
||||||
|
|
||||||
// Unset the first few bits, so that overflow won't occur.
|
// Unset the first few bits, so that overflow won't occur.
|
||||||
a.0[3] >>= 3;
|
a.0[3] >>= 3;
|
||||||
@@ -380,27 +388,28 @@ fn test_fr_is_valid() {
|
|||||||
a.0.sub_noborrow(&FrRepr::from(1));
|
a.0.sub_noborrow(&FrRepr::from(1));
|
||||||
assert!(a.is_valid());
|
assert!(a.is_valid());
|
||||||
assert!(Fr(FrRepr::from(0)).is_valid());
|
assert!(Fr(FrRepr::from(0)).is_valid());
|
||||||
assert!(
|
assert!(Fr(FrRepr([
|
||||||
Fr(FrRepr([
|
0xffffffff00000000,
|
||||||
0xffffffff00000000,
|
0x53bda402fffe5bfe,
|
||||||
0x53bda402fffe5bfe,
|
0x3339d80809a1d805,
|
||||||
0x3339d80809a1d805,
|
0x73eda753299d7d48
|
||||||
0x73eda753299d7d48
|
]))
|
||||||
])).is_valid()
|
.is_valid());
|
||||||
);
|
assert!(!Fr(FrRepr([
|
||||||
assert!(
|
0xffffffffffffffff,
|
||||||
!Fr(FrRepr([
|
0xffffffffffffffff,
|
||||||
0xffffffffffffffff,
|
0xffffffffffffffff,
|
||||||
0xffffffffffffffff,
|
0xffffffffffffffff
|
||||||
0xffffffffffffffff,
|
]))
|
||||||
0xffffffffffffffff
|
.is_valid());
|
||||||
])).is_valid()
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([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 {
|
for _ in 0..1000 {
|
||||||
let a = Fr::rand(&mut rng);
|
let a = Fr::random(&mut rng);
|
||||||
assert!(a.is_valid());
|
assert!(a.is_valid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -492,13 +501,16 @@ fn test_fr_add_assign() {
|
|||||||
|
|
||||||
// Test associativity
|
// 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 {
|
for _ in 0..1000 {
|
||||||
// Generate a, b, c and ensure (a + b) + c == a + (b + c).
|
// Generate a, b, c and ensure (a + b) + c == a + (b + c).
|
||||||
let a = Fr::rand(&mut rng);
|
let a = Fr::random(&mut rng);
|
||||||
let b = Fr::rand(&mut rng);
|
let b = Fr::random(&mut rng);
|
||||||
let c = Fr::rand(&mut rng);
|
let c = Fr::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
tmp1.add_assign(&b);
|
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 {
|
for _ in 0..1000 {
|
||||||
// Ensure that (a - b) + (b - a) = 0.
|
// Ensure that (a - b) + (b - a) = 0.
|
||||||
let a = Fr::rand(&mut rng);
|
let a = Fr::random(&mut rng);
|
||||||
let b = Fr::rand(&mut rng);
|
let b = Fr::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
tmp1.sub_assign(&b);
|
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 {
|
for _ in 0..1000000 {
|
||||||
// Ensure that (a * b) * c = a * (b * c)
|
// Ensure that (a * b) * c = a * (b * c)
|
||||||
let a = Fr::rand(&mut rng);
|
let a = Fr::random(&mut rng);
|
||||||
let b = Fr::rand(&mut rng);
|
let b = Fr::random(&mut rng);
|
||||||
let c = Fr::rand(&mut rng);
|
let c = Fr::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
tmp1.mul_assign(&b);
|
tmp1.mul_assign(&b);
|
||||||
@@ -649,10 +667,10 @@ fn test_fr_mul_assign() {
|
|||||||
for _ in 0..1000000 {
|
for _ in 0..1000000 {
|
||||||
// Ensure that r * (a + b + c) = r*a + r*b + r*c
|
// Ensure that r * (a + b + c) = r*a + r*b + r*c
|
||||||
|
|
||||||
let r = Fr::rand(&mut rng);
|
let r = Fr::random(&mut rng);
|
||||||
let mut a = Fr::rand(&mut rng);
|
let mut a = Fr::random(&mut rng);
|
||||||
let mut b = Fr::rand(&mut rng);
|
let mut b = Fr::random(&mut rng);
|
||||||
let mut c = Fr::rand(&mut rng);
|
let mut c = Fr::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp1 = a;
|
let mut tmp1 = a;
|
||||||
tmp1.add_assign(&b);
|
tmp1.add_assign(&b);
|
||||||
@@ -687,14 +705,18 @@ fn test_fr_squaring() {
|
|||||||
0xb79a310579e76ec2,
|
0xb79a310579e76ec2,
|
||||||
0xac1da8d0a9af4e5f,
|
0xac1da8d0a9af4e5f,
|
||||||
0x13f629c49bf23e97
|
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 {
|
for _ in 0..1000000 {
|
||||||
// Ensure that (a * a) = a^2
|
// Ensure that (a * a) = a^2
|
||||||
let a = Fr::rand(&mut rng);
|
let a = Fr::random(&mut rng);
|
||||||
|
|
||||||
let mut tmp = a;
|
let mut tmp = a;
|
||||||
tmp.square();
|
tmp.square();
|
||||||
@@ -710,13 +732,16 @@ fn test_fr_squaring() {
|
|||||||
fn test_fr_inverse() {
|
fn test_fr_inverse() {
|
||||||
assert!(Fr::zero().inverse().is_none());
|
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();
|
let one = Fr::one();
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
// Ensure that a * a^-1 = 1
|
// 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();
|
let ainv = a.inverse().unwrap();
|
||||||
a.mul_assign(&ainv);
|
a.mul_assign(&ainv);
|
||||||
assert_eq!(a, one);
|
assert_eq!(a, one);
|
||||||
@@ -725,11 +750,14 @@ fn test_fr_inverse() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fr_double() {
|
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 {
|
for _ in 0..1000 {
|
||||||
// Ensure doubling a is equivalent to adding a to itself.
|
// 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;
|
let mut b = a;
|
||||||
b.add_assign(&a);
|
b.add_assign(&a);
|
||||||
a.double();
|
a.double();
|
||||||
@@ -746,11 +774,14 @@ fn test_fr_negate() {
|
|||||||
assert!(a.is_zero());
|
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 {
|
for _ in 0..1000 {
|
||||||
// Ensure (a - (-a)) = 0.
|
// Ensure (a - (-a)) = 0.
|
||||||
let mut a = Fr::rand(&mut rng);
|
let mut a = Fr::random(&mut rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
b.negate();
|
b.negate();
|
||||||
a.add_assign(&b);
|
a.add_assign(&b);
|
||||||
@@ -761,12 +792,15 @@ fn test_fr_negate() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fr_pow() {
|
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 {
|
for i in 0..1000 {
|
||||||
// Exponentiate by various small numbers and ensure it consists with repeated
|
// Exponentiate by various small numbers and ensure it consists with repeated
|
||||||
// multiplication.
|
// multiplication.
|
||||||
let a = Fr::rand(&mut rng);
|
let a = Fr::random(&mut rng);
|
||||||
let target = a.pow(&[i]);
|
let target = a.pow(&[i]);
|
||||||
let mut c = Fr::one();
|
let mut c = Fr::one();
|
||||||
for _ in 0..i {
|
for _ in 0..i {
|
||||||
@@ -777,7 +811,7 @@ fn test_fr_pow() {
|
|||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
// Exponentiating by the modulus should have no effect in a prime field.
|
// 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()));
|
assert_eq!(a, a.pow(Fr::char()));
|
||||||
}
|
}
|
||||||
@@ -787,13 +821,16 @@ fn test_fr_pow() {
|
|||||||
fn test_fr_sqrt() {
|
fn test_fr_sqrt() {
|
||||||
use ff::SqrtField;
|
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());
|
assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero());
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
// Ensure sqrt(a^2) = a or -a
|
// Ensure sqrt(a^2) = a or -a
|
||||||
let a = Fr::rand(&mut rng);
|
let a = Fr::random(&mut rng);
|
||||||
let mut nega = a;
|
let mut nega = a;
|
||||||
nega.negate();
|
nega.negate();
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
@@ -806,7 +843,7 @@ fn test_fr_sqrt() {
|
|||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
// Ensure sqrt(a)^2 = a for random a
|
// 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() {
|
if let Some(mut tmp) = a.sqrt() {
|
||||||
tmp.square();
|
tmp.square();
|
||||||
@@ -819,14 +856,13 @@ fn test_fr_sqrt() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_fr_from_into_repr() {
|
fn test_fr_from_into_repr() {
|
||||||
// r + 1 should not be in the field
|
// r + 1 should not be in the field
|
||||||
assert!(
|
assert!(Fr::from_repr(FrRepr([
|
||||||
Fr::from_repr(FrRepr([
|
0xffffffff00000002,
|
||||||
0xffffffff00000002,
|
0x53bda402fffe5bfe,
|
||||||
0x53bda402fffe5bfe,
|
0x3339d80809a1d805,
|
||||||
0x3339d80809a1d805,
|
0x73eda753299d7d48
|
||||||
0x73eda753299d7d48
|
]))
|
||||||
])).is_err()
|
.is_err());
|
||||||
);
|
|
||||||
|
|
||||||
// r should not be in the field
|
// r should not be in the field
|
||||||
assert!(Fr::from_repr(Fr::char()).is_err());
|
assert!(Fr::from_repr(Fr::char()).is_err());
|
||||||
@@ -858,11 +894,14 @@ fn test_fr_from_into_repr() {
|
|||||||
// Zero should be in the field.
|
// Zero should be in the field.
|
||||||
assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero());
|
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 {
|
for _ in 0..1000 {
|
||||||
// Try to turn Fr elements into representations and back again, and compare.
|
// 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 a_repr = a.into_repr();
|
||||||
let b_repr = FrRepr::from(a);
|
let b_repr = FrRepr::from(a);
|
||||||
assert_eq!(a_repr, b_repr);
|
assert_eq!(a_repr, b_repr);
|
||||||
@@ -926,7 +965,8 @@ fn test_fr_display() {
|
|||||||
0x185ec8eb3f5b5aee,
|
0x185ec8eb3f5b5aee,
|
||||||
0x684499ffe4b9dd99,
|
0x684499ffe4b9dd99,
|
||||||
0x7c9bba7afb68faa
|
0x7c9bba7afb68faa
|
||||||
])).unwrap()
|
]))
|
||||||
|
.unwrap()
|
||||||
),
|
),
|
||||||
"Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string()
|
"Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string()
|
||||||
);
|
);
|
||||||
@@ -938,7 +978,8 @@ fn test_fr_display() {
|
|||||||
0xb0ad10817df79b6a,
|
0xb0ad10817df79b6a,
|
||||||
0xd034a80a2b74132b,
|
0xd034a80a2b74132b,
|
||||||
0x41cf9a1336f50719
|
0x41cf9a1336f50719
|
||||||
])).unwrap()
|
]))
|
||||||
|
.unwrap()
|
||||||
),
|
),
|
||||||
"Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string()
|
"Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string()
|
||||||
);
|
);
|
||||||
@@ -974,13 +1015,13 @@ fn test_fr_root_of_unity() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fr_field_tests() {
|
fn fr_field_tests() {
|
||||||
::tests::field::random_field_tests::<Fr>();
|
crate::tests::field::random_field_tests::<Fr>();
|
||||||
::tests::field::random_sqrt_tests::<Fr>();
|
crate::tests::field::random_sqrt_tests::<Fr>();
|
||||||
::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
|
||||||
::tests::field::from_str_tests::<Fr>();
|
crate::tests::field::from_str_tests::<Fr>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fr_repr_tests() {
|
fn fr_repr_tests() {
|
||||||
::tests::repr::random_repr_tests::<FrRepr>();
|
crate::tests::repr::random_repr_tests::<Fr>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//! An implementation of the BLS12-381 pairing-friendly elliptic curve
|
||||||
|
//! construction.
|
||||||
|
|
||||||
mod ec;
|
mod ec;
|
||||||
mod fq;
|
mod fq;
|
||||||
mod fq12;
|
mod fq12;
|
||||||
@@ -9,8 +12,8 @@ mod fr;
|
|||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use self::ec::{
|
pub use self::ec::{
|
||||||
G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2, G2Affine, G2Compressed, G2Prepared,
|
G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared,
|
||||||
G2Uncompressed,
|
G2Uncompressed, G1, G2,
|
||||||
};
|
};
|
||||||
pub use self::fq::{Fq, FqRepr};
|
pub use self::fq::{Fq, FqRepr};
|
||||||
pub use self::fq12::Fq12;
|
pub use self::fq12::Fq12;
|
||||||
@@ -366,5 +369,5 @@ impl G2Prepared {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bls12_engine_tests() {
|
fn bls12_engine_tests() {
|
||||||
::tests::engine::engine_tests::<Bls12>();
|
crate::tests::engine::engine_tests::<Bls12>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use ff::PrimeFieldRepr;
|
|||||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use *;
|
use crate::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pairing_result_against_relic() {
|
fn test_pairing_result_against_relic() {
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
|
//! A library for working with pairing-friendly curves.
|
||||||
|
|
||||||
// `clippy` is a code linting tool for improving code quality by catching
|
// `clippy` is a code linting tool for improving code quality by catching
|
||||||
// common mistakes or strange code patterns. If the `cargo-clippy` feature
|
// common mistakes or strange code patterns. If the `cargo-clippy` feature
|
||||||
// is provided, all compiler warnings are prohibited.
|
// is provided, all compiler warnings are prohibited.
|
||||||
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
|
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(inline_always))]
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))]
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))]
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))]
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(write_literal))]
|
#![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
|
// Force public structures to implement Debug
|
||||||
#![deny(missing_debug_implementations)]
|
#![deny(missing_debug_implementations)]
|
||||||
|
|
||||||
extern crate byteorder;
|
|
||||||
extern crate ff;
|
|
||||||
extern crate group;
|
|
||||||
extern crate rand;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
|
|
||||||
@@ -34,8 +33,7 @@ pub trait Engine: ScalarEngine {
|
|||||||
Base = Self::Fq,
|
Base = Self::Fq,
|
||||||
Scalar = Self::Fr,
|
Scalar = Self::Fr,
|
||||||
Affine = Self::G1Affine,
|
Affine = Self::G1Affine,
|
||||||
>
|
> + From<Self::G1Affine>;
|
||||||
+ From<Self::G1Affine>;
|
|
||||||
|
|
||||||
/// The affine representation of an element in G1.
|
/// The affine representation of an element in G1.
|
||||||
type G1Affine: PairingCurveAffine<
|
type G1Affine: PairingCurveAffine<
|
||||||
@@ -45,8 +43,7 @@ pub trait Engine: ScalarEngine {
|
|||||||
Projective = Self::G1,
|
Projective = Self::G1,
|
||||||
Pair = Self::G2Affine,
|
Pair = Self::G2Affine,
|
||||||
PairingResult = Self::Fqk,
|
PairingResult = Self::Fqk,
|
||||||
>
|
> + From<Self::G1>;
|
||||||
+ From<Self::G1>;
|
|
||||||
|
|
||||||
/// The projective representation of an element in G2.
|
/// The projective representation of an element in G2.
|
||||||
type G2: CurveProjective<
|
type G2: CurveProjective<
|
||||||
@@ -54,8 +51,7 @@ pub trait Engine: ScalarEngine {
|
|||||||
Base = Self::Fqe,
|
Base = Self::Fqe,
|
||||||
Scalar = Self::Fr,
|
Scalar = Self::Fr,
|
||||||
Affine = Self::G2Affine,
|
Affine = Self::G2Affine,
|
||||||
>
|
> + From<Self::G2Affine>;
|
||||||
+ From<Self::G2Affine>;
|
|
||||||
|
|
||||||
/// The affine representation of an element in G2.
|
/// The affine representation of an element in G2.
|
||||||
type G2Affine: PairingCurveAffine<
|
type G2Affine: PairingCurveAffine<
|
||||||
@@ -65,8 +61,7 @@ pub trait Engine: ScalarEngine {
|
|||||||
Projective = Self::G2,
|
Projective = Self::G2,
|
||||||
Pair = Self::G1Affine,
|
Pair = Self::G1Affine,
|
||||||
PairingResult = Self::Fqk,
|
PairingResult = Self::Fqk,
|
||||||
>
|
> + From<Self::G2>;
|
||||||
+ From<Self::G2>;
|
|
||||||
|
|
||||||
/// The base field that hosts G1.
|
/// The base field that hosts G1.
|
||||||
type Fq: PrimeField + SqrtField;
|
type Fq: PrimeField + SqrtField;
|
||||||
@@ -88,7 +83,7 @@ pub trait Engine: ScalarEngine {
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
/// Perform final exponentiation of the result of a miller loop.
|
/// Perform final exponentiation of the result of a miller loop.
|
||||||
fn final_exponentiation(&Self::Fqk) -> Option<Self::Fqk>;
|
fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk>;
|
||||||
|
|
||||||
/// Performs a complete pairing operation `(p, q)`.
|
/// Performs a complete pairing operation `(p, q)`.
|
||||||
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
|
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
|
||||||
@@ -97,8 +92,9 @@ pub trait Engine: ScalarEngine {
|
|||||||
G2: Into<Self::G2Affine>,
|
G2: Into<Self::G2Affine>,
|
||||||
{
|
{
|
||||||
Self::final_exponentiation(&Self::miller_loop(
|
Self::final_exponentiation(&Self::miller_loop(
|
||||||
[(&(p.into().prepare()), &(q.into().prepare()))].into_iter(),
|
[(&(p.into().prepare()), &(q.into().prepare()))].iter(),
|
||||||
)).unwrap()
|
))
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
use group::{CurveAffine, CurveProjective};
|
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>() {
|
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 {
|
for _ in 0..10 {
|
||||||
let a = E::G1::rand(&mut rng).into_affine();
|
let a = E::G1::random(&mut rng).into_affine();
|
||||||
let b = E::G2::rand(&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) == b.pairing_with(&a));
|
||||||
assert!(a.pairing_with(&b) == E::pairing(a, b));
|
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 z1 = E::G1Affine::zero().prepare();
|
||||||
let z2 = E::G2Affine::zero().prepare();
|
let z2 = E::G2Affine::zero().prepare();
|
||||||
|
|
||||||
let a = E::G1::rand(&mut rng).into_affine().prepare();
|
let a = E::G1::random(&mut rng).into_affine().prepare();
|
||||||
let b = E::G2::rand(&mut rng).into_affine().prepare();
|
let b = E::G2::random(&mut rng).into_affine().prepare();
|
||||||
let c = E::G1::rand(&mut rng).into_affine().prepare();
|
let c = E::G1::random(&mut rng).into_affine().prepare();
|
||||||
let d = E::G2::rand(&mut rng).into_affine().prepare();
|
let d = E::G2::random(&mut rng).into_affine().prepare();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
E::Fqk::one(),
|
E::Fqk::one(),
|
||||||
@@ -49,12 +53,15 @@ pub fn engine_tests<E: Engine>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_miller_loop_tests<E: Engine>() {
|
fn random_miller_loop_tests<E: Engine>() {
|
||||||
let mut rng = XorShiftRng::from_seed([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
|
// Exercise the miller loop for a reduced pairing
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let a = E::G1::rand(&mut rng);
|
let a = E::G1::random(&mut rng);
|
||||||
let b = E::G2::rand(&mut rng);
|
let b = E::G2::random(&mut rng);
|
||||||
|
|
||||||
let p2 = E::pairing(a, b);
|
let p2 = E::pairing(a, b);
|
||||||
|
|
||||||
@@ -68,10 +75,10 @@ fn random_miller_loop_tests<E: Engine>() {
|
|||||||
|
|
||||||
// Exercise a double miller loop
|
// Exercise a double miller loop
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let a = E::G1::rand(&mut rng);
|
let a = E::G1::random(&mut rng);
|
||||||
let b = E::G2::rand(&mut rng);
|
let b = E::G2::random(&mut rng);
|
||||||
let c = E::G1::rand(&mut rng);
|
let c = E::G1::random(&mut rng);
|
||||||
let d = E::G2::rand(&mut rng);
|
let d = E::G2::random(&mut rng);
|
||||||
|
|
||||||
let ab = E::pairing(a, b);
|
let ab = E::pairing(a, b);
|
||||||
let cd = E::pairing(c, d);
|
let cd = E::pairing(c, d);
|
||||||
@@ -92,14 +99,17 @@ fn random_miller_loop_tests<E: Engine>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_bilinearity_tests<E: Engine>() {
|
fn random_bilinearity_tests<E: Engine>() {
|
||||||
let mut rng = XorShiftRng::from_seed([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 {
|
for _ in 0..1000 {
|
||||||
let a = E::G1::rand(&mut rng);
|
let a = E::G1::random(&mut rng);
|
||||||
let b = E::G2::rand(&mut rng);
|
let b = E::G2::random(&mut rng);
|
||||||
|
|
||||||
let c = E::Fr::rand(&mut rng);
|
let c = E::Fr::random(&mut rng);
|
||||||
let d = E::Fr::rand(&mut rng);
|
let d = E::Fr::random(&mut rng);
|
||||||
|
|
||||||
let mut ac = a;
|
let mut ac = a;
|
||||||
ac.mul_assign(c);
|
ac.mul_assign(c);
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
use ff::{Field, LegendreSymbol, PrimeField, SqrtField};
|
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) {
|
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 _ in 0..100 {
|
||||||
for i in 0..(maxpower + 1) {
|
for i in 0..(maxpower + 1) {
|
||||||
let mut a = F::rand(&mut rng);
|
let mut a = F::random(&mut rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
|
|
||||||
for _ in 0..i {
|
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>() {
|
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 {
|
for _ in 0..10000 {
|
||||||
let a = F::rand(&mut rng);
|
let a = F::random(&mut rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
b.square();
|
b.square();
|
||||||
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
||||||
@@ -54,7 +61,10 @@ pub fn random_sqrt_tests<F: SqrtField>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn random_field_tests<F: Field>() {
|
pub fn random_field_tests<F: Field>() {
|
||||||
let mut rng = XorShiftRng::from_seed([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_multiplication_tests::<F, _>(&mut rng);
|
||||||
random_addition_tests::<F, _>(&mut rng);
|
random_addition_tests::<F, _>(&mut rng);
|
||||||
@@ -76,14 +86,14 @@ pub fn random_field_tests<F: Field>() {
|
|||||||
|
|
||||||
// Multiplication by zero
|
// Multiplication by zero
|
||||||
{
|
{
|
||||||
let mut a = F::rand(&mut rng);
|
let mut a = F::random(&mut rng);
|
||||||
a.mul_assign(&F::zero());
|
a.mul_assign(&F::zero());
|
||||||
assert!(a.is_zero());
|
assert!(a.is_zero());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Addition by zero
|
// Addition by zero
|
||||||
{
|
{
|
||||||
let mut a = F::rand(&mut rng);
|
let mut a = F::random(&mut rng);
|
||||||
let copy = a;
|
let copy = a;
|
||||||
a.add_assign(&F::zero());
|
a.add_assign(&F::zero());
|
||||||
assert_eq!(a, copy);
|
assert_eq!(a, copy);
|
||||||
@@ -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 {
|
for _ in 0..1000 {
|
||||||
let n: u64 = rng.gen();
|
let n = rng.next_u64();
|
||||||
|
|
||||||
let a = F::from_str(&format!("{}", n)).unwrap();
|
let a = F::from_str(&format!("{}", n)).unwrap();
|
||||||
let b = F::from_repr(n.into()).unwrap();
|
let b = F::from_repr(n.into()).unwrap();
|
||||||
@@ -124,11 +137,11 @@ pub fn from_str_tests<F: PrimeField>() {
|
|||||||
assert!(F::from_str("00000000000").is_none());
|
assert!(F::from_str("00000000000").is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_multiplication_tests<F: Field, R: Rng>(rng: &mut R) {
|
fn random_multiplication_tests<F: Field, R: RngCore>(rng: &mut R) {
|
||||||
for _ in 0..10000 {
|
for _ in 0..10000 {
|
||||||
let a = F::rand(rng);
|
let a = F::random(rng);
|
||||||
let b = F::rand(rng);
|
let b = F::random(rng);
|
||||||
let c = F::rand(rng);
|
let c = F::random(rng);
|
||||||
|
|
||||||
let mut t0 = a; // (a * b) * c
|
let mut t0 = a; // (a * b) * c
|
||||||
t0.mul_assign(&b);
|
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 {
|
for _ in 0..10000 {
|
||||||
let a = F::rand(rng);
|
let a = F::random(rng);
|
||||||
let b = F::rand(rng);
|
let b = F::random(rng);
|
||||||
let c = F::rand(rng);
|
let c = F::random(rng);
|
||||||
|
|
||||||
let mut t0 = a; // (a + b) + c
|
let mut t0 = a; // (a + b) + c
|
||||||
t0.add_assign(&b);
|
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 {
|
for _ in 0..10000 {
|
||||||
let a = F::rand(rng);
|
let b = F::random(rng);
|
||||||
let b = F::rand(rng);
|
let a = F::random(rng);
|
||||||
|
|
||||||
let mut t0 = a; // (a - b)
|
let mut t0 = a; // (a - b)
|
||||||
t0.sub_assign(&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 {
|
for _ in 0..10000 {
|
||||||
let a = F::rand(rng);
|
let a = F::random(rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
b.negate();
|
b.negate();
|
||||||
b.add_assign(&a);
|
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 {
|
for _ in 0..10000 {
|
||||||
let mut a = F::rand(rng);
|
let mut a = F::random(rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
a.add_assign(&b);
|
a.add_assign(&b);
|
||||||
b.double();
|
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 {
|
for _ in 0..10000 {
|
||||||
let mut a = F::rand(rng);
|
let mut a = F::random(rng);
|
||||||
let mut b = a;
|
let mut b = a;
|
||||||
a.mul_assign(&b);
|
a.mul_assign(&b);
|
||||||
b.square();
|
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());
|
assert!(F::zero().inverse().is_none());
|
||||||
|
|
||||||
for _ in 0..10000 {
|
for _ in 0..10000 {
|
||||||
let mut a = F::rand(rng);
|
let mut a = F::random(rng);
|
||||||
let b = a.inverse().unwrap(); // probablistically nonzero
|
let b = a.inverse().unwrap(); // probablistically nonzero
|
||||||
a.mul_assign(&b);
|
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 {
|
for _ in 0..10000 {
|
||||||
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
|
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
|
||||||
|
|
||||||
let a = F::rand(rng);
|
let a = F::random(rng);
|
||||||
let b = F::rand(rng);
|
let b = F::random(rng);
|
||||||
let c = F::rand(rng);
|
let c = F::random(rng);
|
||||||
let d = F::rand(rng);
|
let d = F::random(rng);
|
||||||
|
|
||||||
let mut t0 = a;
|
let mut t0 = a;
|
||||||
t0.add_assign(&b);
|
t0.add_assign(&b);
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
use ff::PrimeFieldRepr;
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
use rand::{SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
pub fn random_repr_tests<R: PrimeFieldRepr>() {
|
pub fn random_repr_tests<P: PrimeField>() {
|
||||||
random_encoding_tests::<R>();
|
random_encoding_tests::<P>();
|
||||||
random_shl_tests::<R>();
|
random_shl_tests::<P>();
|
||||||
random_shr_tests::<R>();
|
random_shr_tests::<P>();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_encoding_tests<R: PrimeFieldRepr>() {
|
fn random_encoding_tests<P: 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 {
|
for _ in 0..1000 {
|
||||||
let r = R::rand(&mut rng);
|
let r = P::random(&mut rng).into_repr();
|
||||||
|
|
||||||
// Big endian
|
// Big endian
|
||||||
{
|
{
|
||||||
let mut rdecoded = R::default();
|
let mut rdecoded = <P as PrimeField>::Repr::default();
|
||||||
|
|
||||||
let mut v: Vec<u8> = vec![];
|
let mut v: Vec<u8> = vec![];
|
||||||
r.write_be(&mut v).unwrap();
|
r.write_be(&mut v).unwrap();
|
||||||
@@ -26,7 +30,7 @@ fn random_encoding_tests<R: PrimeFieldRepr>() {
|
|||||||
|
|
||||||
// Little endian
|
// Little endian
|
||||||
{
|
{
|
||||||
let mut rdecoded = R::default();
|
let mut rdecoded = <P as PrimeField>::Repr::default();
|
||||||
|
|
||||||
let mut v: Vec<u8> = vec![];
|
let mut v: Vec<u8> = vec![];
|
||||||
r.write_le(&mut v).unwrap();
|
r.write_le(&mut v).unwrap();
|
||||||
@@ -36,8 +40,8 @@ fn random_encoding_tests<R: PrimeFieldRepr>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut rdecoded_le = R::default();
|
let mut rdecoded_le = <P as PrimeField>::Repr::default();
|
||||||
let mut rdecoded_be_flip = R::default();
|
let mut rdecoded_be_flip = <P as PrimeField>::Repr::default();
|
||||||
|
|
||||||
let mut v: Vec<u8> = vec![];
|
let mut v: Vec<u8> = vec![];
|
||||||
r.write_le(&mut v).unwrap();
|
r.write_le(&mut v).unwrap();
|
||||||
@@ -55,11 +59,14 @@ fn random_encoding_tests<R: PrimeFieldRepr>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_shl_tests<R: PrimeFieldRepr>() {
|
fn random_shl_tests<P: 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..100 {
|
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) {
|
for shift in 0..(r.num_bits() + 1) {
|
||||||
let mut r1 = r;
|
let mut r1 = r;
|
||||||
@@ -76,11 +83,14 @@ fn random_shl_tests<R: PrimeFieldRepr>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_shr_tests<R: PrimeFieldRepr>() {
|
fn random_shr_tests<P: 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..100 {
|
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) {
|
for shift in 0..(r.num_bits() + 1) {
|
||||||
let mut r1 = r;
|
let mut r1 = r;
|
||||||
|
|||||||
3
sapling-crypto/.gitignore
vendored
3
sapling-crypto/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
/target/
|
|
||||||
**/*.rs.bk
|
|
||||||
Cargo.lock
|
|
||||||
@@ -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.
|
|
||||||
@@ -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"
|
|
||||||
@@ -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.
|
|
||||||
@@ -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.
|
|
||||||
@@ -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(), ¶ms)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
@@ -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(
|
|
||||||
¶ms.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(
|
|
||||||
¶ms.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(
|
|
||||||
¶ms.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
2
zcash_client_backend/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Protobufs
|
||||||
|
src/proto/
|
||||||
31
zcash_client_backend/Cargo.toml
Normal file
31
zcash_client_backend/Cargo.toml
Normal 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" }
|
||||||
@@ -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
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
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)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
11
zcash_client_backend/build.rs
Normal file
11
zcash_client_backend/build.rs
Normal 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");
|
||||||
|
}
|
||||||
48
zcash_client_backend/proto/compact_formats.proto
Normal file
48
zcash_client_backend/proto/compact_formats.proto
Normal 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;
|
||||||
|
}
|
||||||
5
zcash_client_backend/src/constants.rs
Normal file
5
zcash_client_backend/src/constants.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
//! Zcash global and per-network constants.
|
||||||
|
|
||||||
|
pub mod mainnet;
|
||||||
|
pub mod regtest;
|
||||||
|
pub mod testnet;
|
||||||
30
zcash_client_backend/src/constants/mainnet.rs
Normal file
30
zcash_client_backend/src/constants/mainnet.rs
Normal 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";
|
||||||
45
zcash_client_backend/src/constants/regtest.rs
Normal file
45
zcash_client_backend/src/constants/regtest.rs
Normal 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];
|
||||||
30
zcash_client_backend/src/constants/testnet.rs
Normal file
30
zcash_client_backend/src/constants/testnet.rs
Normal 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";
|
||||||
76
zcash_client_backend/src/decrypt.rs
Normal file
76
zcash_client_backend/src/decrypt.rs
Normal 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
|
||||||
|
}
|
||||||
347
zcash_client_backend/src/encoding.rs
Normal file
347
zcash_client_backend/src/encoding.rs
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
zcash_client_backend/src/keys.rs
Normal file
43
zcash_client_backend/src/keys.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
zcash_client_backend/src/lib.rs
Normal file
17
zcash_client_backend/src/lib.rs
Normal 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};
|
||||||
83
zcash_client_backend/src/proto/mod.rs
Normal file
83
zcash_client_backend/src/proto/mod.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
46
zcash_client_backend/src/wallet.rs
Normal file
46
zcash_client_backend/src/wallet.rs
Normal 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>,
|
||||||
|
}
|
||||||
397
zcash_client_backend/src/welding_rig.rs
Normal file
397
zcash_client_backend/src/welding_rig.rs
Normal 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
Reference in New Issue
Block a user