Compare commits
297 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aabbc856c7 | ||
|
|
d9ac7bd19b | ||
|
|
0f0d84e9ca | ||
|
|
49459d030f | ||
|
|
cafbe61eba | ||
|
|
fc247fc898 | ||
|
|
46e88a5e69 | ||
|
|
5e1a2f9d3f | ||
|
|
9379eec1b8 | ||
|
|
4f0f10a53c | ||
|
|
be0ee9eb82 | ||
|
|
b52e4aac3a | ||
|
|
c49aaceb5d | ||
|
|
f337eb1f5c | ||
|
|
805aa45f9e | ||
|
|
05a9f3360f | ||
|
|
31d887c943 | ||
|
|
d7f78db121 | ||
|
|
d4bae99cb7 | ||
|
|
2d30c29d06 | ||
|
|
76e0f658c1 | ||
|
|
3a3008caf9 | ||
|
|
8a210ec271 | ||
|
|
2064d1c801 | ||
|
|
ee32f7facb | ||
|
|
903c02b991 | ||
|
|
1f38523fff | ||
|
|
865275e2a2 | ||
|
|
18aceea225 | ||
|
|
0aa127d7b1 | ||
|
|
8626cc7f26 | ||
|
|
eed7e8199f | ||
|
|
c3f9e2d73c | ||
|
|
cca1670248 | ||
|
|
f1619f896c | ||
|
|
edcd884fe8 | ||
|
|
583a04b4de | ||
|
|
573510115d | ||
|
|
8ad33e50a6 | ||
|
|
fb8c73c950 | ||
|
|
9ea0427678 | ||
|
|
dd112813a1 | ||
|
|
0907adf608 | ||
|
|
e2c131fdc3 | ||
|
|
9059f53873 | ||
|
|
26be46573e | ||
|
|
7820548ea3 | ||
|
|
d5ed684701 | ||
|
|
e6a8630b35 | ||
|
|
cd326f2b6a | ||
|
|
c24024b8e1 | ||
|
|
7eb4a6d1d9 | ||
|
|
141821d3c1 | ||
|
|
219391ac92 | ||
|
|
67d700feab | ||
|
|
ac983b7612 | ||
|
|
8d6f882510 | ||
|
|
8d967c8609 | ||
|
|
d9d50b98ab | ||
|
|
88e02329eb | ||
|
|
60eac4e8b7 | ||
|
|
4ae238ea1f | ||
|
|
8d7f27ffb9 | ||
|
|
2ecdbc2036 | ||
|
|
bb39037798 | ||
|
|
7be66ad8f8 | ||
|
|
7fda177da8 | ||
|
|
d1bc61800c | ||
|
|
de5943aea4 | ||
|
|
8651bb41ce | ||
|
|
9cb8c0b3c4 | ||
|
|
7181d603be | ||
|
|
5d03619466 | ||
|
|
7722b1a50b | ||
|
|
0028048800 | ||
|
|
720ee6476f | ||
|
|
5823a695e2 | ||
|
|
bc35a62e10 | ||
|
|
40d22ea2cc | ||
|
|
ac4acfa26e | ||
|
|
4d290e7a32 | ||
|
|
b479981689 | ||
|
|
885e09a82f | ||
|
|
fab9160b26 | ||
|
|
5a177eea27 | ||
|
|
3425eabda2 | ||
|
|
70857b0eed | ||
|
|
42b7f328fb | ||
|
|
cd1083e086 | ||
|
|
71c6188bf8 | ||
|
|
acad37924b | ||
|
|
481e43689c | ||
|
|
f24ec04340 | ||
|
|
b9bfc07146 | ||
|
|
3082593fcc | ||
|
|
6b7a3dec9c | ||
|
|
e701687b69 | ||
|
|
29b8d7a756 | ||
|
|
cb818ecbe3 | ||
|
|
9d4412103b | ||
|
|
84dc3bf73c | ||
|
|
2cd8a7f4d2 | ||
|
|
274f860202 | ||
|
|
e9f94119bc | ||
|
|
b44653e686 | ||
|
|
7fa51e38c0 | ||
|
|
1e2bc7f65c | ||
|
|
2bbd25b36b | ||
|
|
a3b85b8fe6 | ||
|
|
b66ac11775 | ||
|
|
e746f7b6f9 | ||
|
|
3b9dfc1e0b | ||
|
|
fb9e9bb12f | ||
|
|
2b71121681 | ||
|
|
f899ecfce5 | ||
|
|
591b1fc28f | ||
|
|
af7e263bcc | ||
|
|
a57dc7f478 | ||
|
|
7844394364 | ||
|
|
98731c8374 | ||
|
|
b0ba7fe4d2 | ||
|
|
d6bc2fe0af | ||
|
|
8b08528bb0 | ||
|
|
25558893ab | ||
|
|
68cada53cf | ||
|
|
aa37783c37 | ||
|
|
216f0f62a0 | ||
|
|
80d339848f | ||
|
|
b872e9fc49 | ||
|
|
39971891d5 | ||
|
|
2399d9f3e3 | ||
|
|
212b6e67d0 | ||
|
|
6de1cb1397 | ||
|
|
93563c3c65 | ||
|
|
524055bbb4 | ||
|
|
730d2cbc7e | ||
|
|
7476e4ea28 | ||
|
|
c68e15e4f3 | ||
|
|
b42477a0bf | ||
|
|
4c9ebcce9a | ||
|
|
da0d0a669a | ||
|
|
443f45f430 | ||
|
|
e7b5329e95 | ||
|
|
43efdf992b | ||
|
|
4ad3988e43 | ||
|
|
7f3036d2c8 | ||
|
|
08806cc109 | ||
|
|
1eb4fb91f6 | ||
|
|
7879b63321 | ||
|
|
ce2416623f | ||
|
|
1fbf38280e | ||
|
|
d9a0b9c83f | ||
|
|
fb6fef4e9c | ||
|
|
38aa09b6a1 | ||
|
|
4cbc0451c1 | ||
|
|
1d02363752 | ||
|
|
30660e1d1c | ||
|
|
d2da9cfcb0 | ||
|
|
450087e280 | ||
|
|
9377b78b35 | ||
|
|
39a73c8eda | ||
|
|
3701c2b442 | ||
|
|
4dff8055bf | ||
|
|
caab37c336 | ||
|
|
e391594e9e | ||
|
|
15b4c37ab0 | ||
|
|
3efb7f9146 | ||
|
|
d56758d426 | ||
|
|
f3533e291f | ||
|
|
e0c5ef22bc | ||
|
|
ace929c5ba | ||
|
|
bb0a769162 | ||
|
|
68ba93f5fd | ||
|
|
6240c02208 | ||
|
|
be18eb240c | ||
|
|
9080b4ccf3 | ||
|
|
f5dfe07355 | ||
|
|
2ee7b108af | ||
|
|
2edcc12e8e | ||
|
|
2b92493a45 | ||
|
|
abd03928b4 | ||
|
|
cc2a41d86c | ||
|
|
804f4cba67 | ||
|
|
7ee61c4f94 | ||
|
|
43496857c9 | ||
|
|
4835be05b0 | ||
|
|
15633ad434 | ||
|
|
414d651c9c | ||
|
|
8541b2bde4 | ||
|
|
28dcc1c346 | ||
|
|
b397a9c405 | ||
|
|
0a3b0a9341 | ||
|
|
89dad572ef | ||
|
|
79cba2e500 | ||
|
|
4ec651d172 | ||
|
|
f5c1381c4c | ||
|
|
a0c33945ab | ||
|
|
96b130e034 | ||
|
|
636f3e3751 | ||
|
|
b73f49c968 | ||
|
|
a5c4d51652 | ||
|
|
c87122561f | ||
|
|
9470610b75 | ||
|
|
0afa122970 | ||
|
|
0bfd1d6b0d | ||
|
|
ad403f1cca | ||
|
|
a9d2ce7150 | ||
|
|
6d9deefb93 | ||
|
|
49763d1c01 | ||
|
|
de053e1d8f | ||
|
|
03524ba7d0 | ||
|
|
265c6e5303 | ||
|
|
c4f8f8ea04 | ||
|
|
5d0aa521c2 | ||
|
|
bef3e7e336 | ||
|
|
2b6fbfd2d1 | ||
|
|
a46ace4c2d | ||
|
|
942a976ef5 | ||
|
|
5d2f84a154 | ||
|
|
6b36cb5a51 | ||
|
|
d6f6b50ecd | ||
|
|
abbd43ff57 | ||
|
|
86142d044c | ||
|
|
73ee19239c | ||
|
|
b19b40ccf0 | ||
|
|
872ac5af7b | ||
|
|
efac432128 | ||
|
|
b03b4cf958 | ||
|
|
d8c04e8143 | ||
|
|
f432983f09 | ||
|
|
bce88797f4 | ||
|
|
771aa867c1 | ||
|
|
615c4f662e | ||
|
|
49f20e6735 | ||
|
|
d58b33fb78 | ||
|
|
4c49f09515 | ||
|
|
68983dc0c1 | ||
|
|
fa04929891 | ||
|
|
c05446d2ae | ||
|
|
0da4921055 | ||
|
|
c00b65bb78 | ||
|
|
ee9c88ecb0 | ||
|
|
f9f17b291f | ||
|
|
563961e606 | ||
|
|
0c7eb84d36 | ||
|
|
7c1d4d9a5b | ||
|
|
9016548698 | ||
|
|
fe93f2ff6b | ||
|
|
91541675e2 | ||
|
|
3a8efd9e67 | ||
|
|
d65fe2cda9 | ||
|
|
d78c94b2a2 | ||
|
|
ad33798244 | ||
|
|
e7d67364cc | ||
|
|
ba3705cb5e | ||
|
|
5e706d5d84 | ||
|
|
894421f3b5 | ||
|
|
4014f08105 | ||
|
|
5a479363ff | ||
|
|
724e822d2d | ||
|
|
27337f1bb0 | ||
|
|
bd8eea97b7 | ||
|
|
7ad0452c01 | ||
|
|
3a09eef6b3 | ||
|
|
767d73f777 | ||
|
|
5c6d856714 | ||
|
|
f316c1b439 | ||
|
|
261ad90d33 | ||
|
|
d63fa334ff | ||
|
|
53182aa08e | ||
|
|
fec961777c | ||
|
|
37531ed747 | ||
|
|
76795a9014 | ||
|
|
c28ae31c71 | ||
|
|
573ffc4e06 | ||
|
|
9807a5c1cc | ||
|
|
07c690cf73 | ||
|
|
a5f25c5058 | ||
|
|
4b021fcf4d | ||
|
|
09882c6d08 | ||
|
|
b35a819a09 | ||
|
|
7809711a81 | ||
|
|
f523ac285d | ||
|
|
cc0fc98c22 | ||
|
|
b0d8747697 | ||
|
|
f1b6e88f9f | ||
|
|
e12d315ab9 | ||
|
|
fc3dd8198b | ||
|
|
4991e53f48 | ||
|
|
e59738b4ee | ||
|
|
66c31be6c5 | ||
|
|
fec7e07764 | ||
|
|
845babc3c2 | ||
|
|
c794cdc680 | ||
|
|
ae364f3ae7 | ||
|
|
081937162c | ||
|
|
3891fe57d4 |
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
|
||||
@@ -1,6 +1,6 @@
|
||||
language: rust
|
||||
rust:
|
||||
- 1.36.0
|
||||
- 1.37.0
|
||||
|
||||
cache: cargo
|
||||
|
||||
@@ -8,5 +8,12 @@ before_script:
|
||||
- rustup component add rustfmt
|
||||
|
||||
script:
|
||||
- cargo build --verbose --release --all
|
||||
- cargo fmt --all -- --check
|
||||
- cargo test --verbose --release --all
|
||||
- cargo test --verbose --release --all -- --ignored
|
||||
|
||||
before_cache:
|
||||
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
|
||||
- cargo install cargo-update || echo "cargo-update already installed"
|
||||
- cargo install-update -a # update outdated cached binaries
|
||||
|
||||
663
Cargo.lock
generated
663
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ members = [
|
||||
"librustzcash",
|
||||
"pairing",
|
||||
"zcash_client_backend",
|
||||
"zcash_history",
|
||||
"zcash_primitives",
|
||||
"zcash_proofs",
|
||||
]
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
[package]
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
description = "zk-SNARK library"
|
||||
documentation = "https://github.com/ebfull/bellman"
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/ebfull/bellman"
|
||||
license = "MIT/Apache-2.0"
|
||||
name = "bellman"
|
||||
repository = "https://github.com/ebfull/bellman"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bit-vec = "0.4.4"
|
||||
blake2s_simd = "0.5"
|
||||
ff = { path = "../ff" }
|
||||
ff = { version = "0.5.0", path = "../ff" }
|
||||
futures = "0.1"
|
||||
futures-cpupool = { version = "0.1", optional = true }
|
||||
group = { path = "../group" }
|
||||
group = { version = "0.2.0", path = "../group" }
|
||||
num_cpus = { version = "1", optional = true }
|
||||
crossbeam = { version = "0.3", optional = true }
|
||||
pairing = { path = "../pairing", optional = true }
|
||||
crossbeam = { version = "0.7", optional = true }
|
||||
pairing = { version = "0.15.0", path = "../pairing", optional = true }
|
||||
rand_core = "0.5"
|
||||
byteorder = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.1"
|
||||
hex-literal = "0.2"
|
||||
rand = "0.7"
|
||||
rand_xorshift = "0.2"
|
||||
sha2 = "0.8"
|
||||
@@ -36,3 +37,6 @@ default = ["groth16", "multicore"]
|
||||
name = "mimc"
|
||||
path = "tests/mimc.rs"
|
||||
required-features = ["groth16"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
# 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
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
//! This module contains an `EvaluationDomain` abstraction for
|
||||
//! performing various kinds of polynomial arithmetic on top of
|
||||
//! the scalar field.
|
||||
//! This module contains an [`EvaluationDomain`] abstraction for performing
|
||||
//! various kinds of polynomial arithmetic on top of the scalar field.
|
||||
//!
|
||||
//! In pairing-based SNARKs like Groth16, we need to calculate
|
||||
//! a quotient polynomial over a target polynomial with roots
|
||||
//! at distinct points associated with each constraint of the
|
||||
//! constraint system. In order to be efficient, we choose these
|
||||
//! roots to be the powers of a 2^n root of unity in the field.
|
||||
//! This allows us to perform polynomial operations in O(n)
|
||||
//! by performing an O(n log n) FFT over such a domain.
|
||||
//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient
|
||||
//! polynomial over a target polynomial with roots at distinct points associated
|
||||
//! with each constraint of the constraint system. In order to be efficient, we
|
||||
//! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the
|
||||
//! field. This allows us to perform polynomial operations in O(n) by performing
|
||||
//! an O(n log n) FFT over such a domain.
|
||||
//!
|
||||
//! [`EvaluationDomain`]: crate::domain::EvaluationDomain
|
||||
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||
|
||||
use ff::{Field, PrimeField, ScalarEngine};
|
||||
use group::CurveProjective;
|
||||
@@ -26,15 +27,19 @@ pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
|
||||
minv: E::Fr,
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
pub fn as_ref(&self) -> &[G] {
|
||||
impl<E: ScalarEngine, G: Group<E>> AsRef<[G]> for EvaluationDomain<E, G> {
|
||||
fn as_ref(&self) -> &[G] {
|
||||
&self.coeffs
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut(&mut self) -> &mut [G] {
|
||||
impl<E: ScalarEngine, G: Group<E>> AsMut<[G]> for EvaluationDomain<E, G> {
|
||||
fn as_mut(&mut self) -> &mut [G] {
|
||||
&mut self.coeffs
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
pub fn into_coeffs(self) -> Vec<G> {
|
||||
self.coeffs
|
||||
}
|
||||
@@ -64,9 +69,9 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
coeffs.resize(m, G::group_zero());
|
||||
|
||||
Ok(EvaluationDomain {
|
||||
coeffs: coeffs,
|
||||
exp: exp,
|
||||
omega: omega,
|
||||
coeffs,
|
||||
exp,
|
||||
omega,
|
||||
omegainv: omega.inverse().unwrap(),
|
||||
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
|
||||
minv: E::Fr::from_str(&format!("{}", m))
|
||||
@@ -87,7 +92,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
let minv = self.minv;
|
||||
|
||||
for v in self.coeffs.chunks_mut(chunk) {
|
||||
scope.spawn(move || {
|
||||
scope.spawn(move |_scope| {
|
||||
for v in v {
|
||||
v.group_mul_assign(&minv);
|
||||
}
|
||||
@@ -99,7 +104,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) {
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move || {
|
||||
scope.spawn(move |_scope| {
|
||||
let mut u = g.pow(&[(i * chunk) as u64]);
|
||||
for v in v.iter_mut() {
|
||||
v.group_mul_assign(&u);
|
||||
@@ -142,7 +147,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for v in self.coeffs.chunks_mut(chunk) {
|
||||
scope.spawn(move || {
|
||||
scope.spawn(move |_scope| {
|
||||
for v in v {
|
||||
v.group_mul_assign(&i);
|
||||
}
|
||||
@@ -161,7 +166,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
.chunks_mut(chunk)
|
||||
.zip(other.coeffs.chunks(chunk))
|
||||
{
|
||||
scope.spawn(move || {
|
||||
scope.spawn(move |_scope| {
|
||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||
a.group_mul_assign(&b.0);
|
||||
}
|
||||
@@ -180,7 +185,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
.chunks_mut(chunk)
|
||||
.zip(other.coeffs.chunks(chunk))
|
||||
{
|
||||
scope.spawn(move || {
|
||||
scope.spawn(move |_scope| {
|
||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||
a.group_sub_assign(&b);
|
||||
}
|
||||
@@ -291,7 +296,7 @@ fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u
|
||||
|
||||
let mut m = 1;
|
||||
for _ in 0..log_n {
|
||||
let w_m = omega.pow(&[(n / (2 * m)) as u64]);
|
||||
let w_m = omega.pow(&[u64::from(n / (2 * m))]);
|
||||
|
||||
let mut k = 0;
|
||||
while k < n {
|
||||
@@ -331,18 +336,18 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
||||
let a = &*a;
|
||||
|
||||
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||
scope.spawn(move || {
|
||||
scope.spawn(move |_scope| {
|
||||
// Shuffle into a sub-FFT
|
||||
let omega_j = omega.pow(&[j as u64]);
|
||||
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
|
||||
|
||||
let mut elt = E::Fr::one();
|
||||
for i in 0..(1 << log_new_n) {
|
||||
for (i, tmp) in tmp.iter_mut().enumerate() {
|
||||
for s in 0..num_cpus {
|
||||
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
||||
let mut t = a[idx];
|
||||
t.group_mul_assign(&elt);
|
||||
tmp[i].group_add_assign(&t);
|
||||
tmp.group_add_assign(&t);
|
||||
elt.mul_assign(&omega_step);
|
||||
}
|
||||
elt.mul_assign(&omega_j);
|
||||
@@ -359,7 +364,7 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
||||
let tmp = &tmp;
|
||||
|
||||
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move || {
|
||||
scope.spawn(move |_scope| {
|
||||
let mut idx = idx * chunk;
|
||||
let mask = (1 << log_cpus) - 1;
|
||||
for a in a {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Self-contained sub-circuit implementations for various primitives.
|
||||
|
||||
pub mod test;
|
||||
|
||||
pub mod blake2s;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use pairing::Engine;
|
||||
//! 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 super::boolean::Boolean;
|
||||
|
||||
use super::uint32::UInt32;
|
||||
|
||||
use super::multieq::MultiEq;
|
||||
use ff::ScalarEngine;
|
||||
|
||||
/*
|
||||
2.1. Parameters
|
||||
@@ -81,7 +79,7 @@ const SIGMA: [[usize; 16]; 10] = [
|
||||
END FUNCTION.
|
||||
*/
|
||||
|
||||
fn mixing_g<E: Engine, CS: ConstraintSystem<E>, M>(
|
||||
fn mixing_g<E: ScalarEngine, CS: ConstraintSystem<E>, M>(
|
||||
mut cs: M,
|
||||
v: &mut [UInt32],
|
||||
a: usize,
|
||||
@@ -166,7 +164,7 @@ where
|
||||
END FUNCTION.
|
||||
*/
|
||||
|
||||
fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
|
||||
fn blake2s_compression<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
mut cs: CS,
|
||||
h: &mut [UInt32],
|
||||
m: &[UInt32],
|
||||
@@ -339,7 +337,7 @@ fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
|
||||
END FUNCTION.
|
||||
*/
|
||||
|
||||
pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
|
||||
pub fn blake2s<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
mut cs: CS,
|
||||
input: &[Boolean],
|
||||
personalization: &[u8],
|
||||
@@ -382,7 +380,7 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
|
||||
blocks.push(this_block);
|
||||
}
|
||||
|
||||
if blocks.len() == 0 {
|
||||
if blocks.is_empty() {
|
||||
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
|
||||
}
|
||||
|
||||
@@ -404,12 +402,13 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(h.iter().flat_map(|b| b.into_bits()).collect())
|
||||
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;
|
||||
@@ -433,7 +432,7 @@ mod test {
|
||||
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
|
||||
|
||||
let mut out = out.into_iter();
|
||||
for b in expected.into_iter() {
|
||||
for b in expected.iter() {
|
||||
for i in 0..8 {
|
||||
let c = out.next().unwrap().get_value().unwrap();
|
||||
|
||||
@@ -554,4 +553,145 @@ mod test {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use ff::{BitIterator, Field, PrimeField};
|
||||
use pairing::Engine;
|
||||
//! Gadgets for allocating bits in the circuit and performing boolean logic.
|
||||
|
||||
use ff::{BitIterator, Field, PrimeField, ScalarEngine};
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
@@ -31,7 +32,7 @@ impl AllocatedBit {
|
||||
must_be_false: &AllocatedBit,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let var = cs.alloc(
|
||||
@@ -60,7 +61,7 @@ impl AllocatedBit {
|
||||
|
||||
Ok(AllocatedBit {
|
||||
variable: var,
|
||||
value: value,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,7 +69,7 @@ impl AllocatedBit {
|
||||
/// boolean value.
|
||||
pub fn alloc<E, CS>(mut cs: CS, value: Option<bool>) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let var = cs.alloc(
|
||||
@@ -93,7 +94,7 @@ impl AllocatedBit {
|
||||
|
||||
Ok(AllocatedBit {
|
||||
variable: var,
|
||||
value: value,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ impl AllocatedBit {
|
||||
/// an `AllocatedBit`.
|
||||
pub fn xor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let mut result_value = None;
|
||||
@@ -153,7 +154,7 @@ impl AllocatedBit {
|
||||
/// an `AllocatedBit`.
|
||||
pub fn and<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let mut result_value = None;
|
||||
@@ -191,7 +192,7 @@ impl AllocatedBit {
|
||||
/// Calculates `a AND (NOT b)`.
|
||||
pub fn and_not<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let mut result_value = None;
|
||||
@@ -229,7 +230,7 @@ impl AllocatedBit {
|
||||
/// Calculates `(NOT a) AND (NOT b)`.
|
||||
pub fn nor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let mut result_value = None;
|
||||
@@ -265,7 +266,7 @@ impl AllocatedBit {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn u64_into_boolean_vec_le<E: Engine, CS: ConstraintSystem<E>>(
|
||||
pub fn u64_into_boolean_vec_le<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
mut cs: CS,
|
||||
value: Option<u64>,
|
||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
@@ -296,16 +297,16 @@ pub fn u64_into_boolean_vec_le<E: Engine, CS: ConstraintSystem<E>>(
|
||||
Ok(bits)
|
||||
}
|
||||
|
||||
pub fn field_into_boolean_vec_le<E: Engine, CS: ConstraintSystem<E>, F: PrimeField>(
|
||||
pub fn field_into_boolean_vec_le<E: ScalarEngine, CS: ConstraintSystem<E>, F: PrimeField>(
|
||||
cs: CS,
|
||||
value: Option<F>,
|
||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
let v = field_into_allocated_bits_le::<E, CS, F>(cs, value)?;
|
||||
|
||||
Ok(v.into_iter().map(|e| Boolean::from(e)).collect())
|
||||
Ok(v.into_iter().map(Boolean::from).collect())
|
||||
}
|
||||
|
||||
pub fn field_into_allocated_bits_le<E: Engine, CS: ConstraintSystem<E>, F: PrimeField>(
|
||||
pub fn field_into_allocated_bits_le<E: ScalarEngine, CS: ConstraintSystem<E>, F: PrimeField>(
|
||||
mut cs: CS,
|
||||
value: Option<F>,
|
||||
) -> Result<Vec<AllocatedBit>, SynthesisError> {
|
||||
@@ -367,7 +368,7 @@ impl Boolean {
|
||||
|
||||
pub fn enforce_equal<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
match (a, b) {
|
||||
@@ -412,24 +413,24 @@ impl Boolean {
|
||||
}
|
||||
|
||||
pub fn get_value(&self) -> Option<bool> {
|
||||
match self {
|
||||
&Boolean::Constant(c) => Some(c),
|
||||
&Boolean::Is(ref v) => v.get_value(),
|
||||
&Boolean::Not(ref v) => v.get_value().map(|b| !b),
|
||||
match *self {
|
||||
Boolean::Constant(c) => Some(c),
|
||||
Boolean::Is(ref v) => v.get_value(),
|
||||
Boolean::Not(ref v) => v.get_value().map(|b| !b),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lc<E: Engine>(&self, one: Variable, coeff: E::Fr) -> LinearCombination<E> {
|
||||
match self {
|
||||
&Boolean::Constant(c) => {
|
||||
pub fn lc<E: ScalarEngine>(&self, one: Variable, coeff: E::Fr) -> LinearCombination<E> {
|
||||
match *self {
|
||||
Boolean::Constant(c) => {
|
||||
if c {
|
||||
LinearCombination::<E>::zero() + (coeff, one)
|
||||
} else {
|
||||
LinearCombination::<E>::zero()
|
||||
}
|
||||
}
|
||||
&Boolean::Is(ref v) => LinearCombination::<E>::zero() + (coeff, v.get_variable()),
|
||||
&Boolean::Not(ref v) => {
|
||||
Boolean::Is(ref v) => LinearCombination::<E>::zero() + (coeff, v.get_variable()),
|
||||
Boolean::Not(ref v) => {
|
||||
LinearCombination::<E>::zero() + (coeff, one) - (coeff, v.get_variable())
|
||||
}
|
||||
}
|
||||
@@ -442,17 +443,17 @@ impl Boolean {
|
||||
|
||||
/// Return a negated interpretation of this boolean.
|
||||
pub fn not(&self) -> Self {
|
||||
match self {
|
||||
&Boolean::Constant(c) => Boolean::Constant(!c),
|
||||
&Boolean::Is(ref v) => Boolean::Not(v.clone()),
|
||||
&Boolean::Not(ref v) => Boolean::Is(v.clone()),
|
||||
match *self {
|
||||
Boolean::Constant(c) => Boolean::Constant(!c),
|
||||
Boolean::Is(ref v) => Boolean::Not(v.clone()),
|
||||
Boolean::Not(ref v) => Boolean::Is(v.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform XOR over two boolean operands
|
||||
pub fn xor<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
match (a, b) {
|
||||
@@ -474,7 +475,7 @@ impl Boolean {
|
||||
/// Perform AND over two boolean operands
|
||||
pub fn and<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
match (a, b) {
|
||||
@@ -508,7 +509,7 @@ impl Boolean {
|
||||
c: &'a Self,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let ch_value = match (a.get_value(), b.get_value(), c.get_value()) {
|
||||
@@ -615,7 +616,7 @@ impl Boolean {
|
||||
c: &'a Self,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let maj_value = match (a.get_value(), b.get_value(), c.get_value()) {
|
||||
@@ -1740,4 +1741,72 @@ mod test {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alloc_conditionally() {
|
||||
{
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||
let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap();
|
||||
|
||||
let value = None;
|
||||
// if value is none, fail with SynthesisError
|
||||
let is_err = AllocatedBit::alloc_conditionally(
|
||||
cs.namespace(|| "alloc_conditionally"),
|
||||
value,
|
||||
&b,
|
||||
)
|
||||
.is_err();
|
||||
assert!(is_err);
|
||||
}
|
||||
|
||||
{
|
||||
// since value is true, b must be false, so it should succeed
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||
|
||||
let value = Some(true);
|
||||
let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap();
|
||||
let allocated_value = AllocatedBit::alloc_conditionally(
|
||||
cs.namespace(|| "alloc_conditionally"),
|
||||
value,
|
||||
&b,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(allocated_value.get_value().unwrap(), true);
|
||||
assert!(cs.is_satisfied());
|
||||
}
|
||||
|
||||
{
|
||||
// since value is true, b must be false, so it should fail
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||
|
||||
let value = Some(true);
|
||||
let b = AllocatedBit::alloc(&mut cs, Some(true)).unwrap();
|
||||
AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b)
|
||||
.unwrap();
|
||||
|
||||
assert!(!cs.is_satisfied());
|
||||
}
|
||||
|
||||
{
|
||||
// since value is false, we don't care about the value of the bit
|
||||
|
||||
let value = Some(false);
|
||||
//check with false bit
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||
let b1 = AllocatedBit::alloc(&mut cs, Some(false)).unwrap();
|
||||
AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b1)
|
||||
.unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
//check with true bit
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||
let b2 = AllocatedBit::alloc(&mut cs, Some(true)).unwrap();
|
||||
AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b2)
|
||||
.unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use ff::Field;
|
||||
use pairing::Engine;
|
||||
//! Window table lookup gadgets.
|
||||
|
||||
use ff::{Field, ScalarEngine};
|
||||
|
||||
use super::boolean::Boolean;
|
||||
use super::num::{AllocatedNum, Num};
|
||||
@@ -7,7 +8,7 @@ use super::*;
|
||||
use crate::ConstraintSystem;
|
||||
|
||||
// Synthesize the constants for each base pattern.
|
||||
fn synth<'a, E: Engine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
|
||||
fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
|
||||
where
|
||||
I: IntoIterator<Item = &'a E::Fr>,
|
||||
{
|
||||
@@ -28,7 +29,7 @@ where
|
||||
|
||||
/// Performs a 3-bit window table lookup. `bits` is in
|
||||
/// little-endian order.
|
||||
pub fn lookup3_xy<E: Engine, CS>(
|
||||
pub fn lookup3_xy<E: ScalarEngine, CS>(
|
||||
mut cs: CS,
|
||||
bits: &[Boolean],
|
||||
coords: &[(E::Fr, E::Fr)],
|
||||
@@ -118,7 +119,7 @@ where
|
||||
|
||||
/// Performs a 3-bit window table lookup, where
|
||||
/// one of the bits is a sign bit.
|
||||
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
|
||||
pub fn lookup3_xy_with_conditional_negation<E: ScalarEngine, CS>(
|
||||
mut cs: CS,
|
||||
bits: &[Boolean],
|
||||
coords: &[(E::Fr, E::Fr)],
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use ff::{Field, PrimeField};
|
||||
use pairing::Engine;
|
||||
use ff::{Field, PrimeField, ScalarEngine};
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>> {
|
||||
pub struct MultiEq<E: ScalarEngine, CS: ConstraintSystem<E>> {
|
||||
cs: CS,
|
||||
ops: usize,
|
||||
bits_used: usize,
|
||||
@@ -11,10 +10,10 @@ pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>> {
|
||||
rhs: LinearCombination<E>,
|
||||
}
|
||||
|
||||
impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
||||
impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
||||
pub fn new(cs: CS) -> Self {
|
||||
MultiEq {
|
||||
cs: cs,
|
||||
cs,
|
||||
ops: 0,
|
||||
bits_used: 0,
|
||||
lhs: LinearCombination::zero(),
|
||||
@@ -58,7 +57,7 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
|
||||
impl<E: ScalarEngine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
|
||||
fn drop(&mut self) {
|
||||
if self.bits_used > 0 {
|
||||
self.accumulate();
|
||||
@@ -66,7 +65,7 @@ impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> {
|
||||
impl<E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> {
|
||||
type Root = Self;
|
||||
|
||||
fn one() -> Variable {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
//! 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};
|
||||
use pairing::Engine;
|
||||
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: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() {
|
||||
@@ -49,7 +50,7 @@ pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn compute_multipacking<E: Engine>(bits: &[bool]) -> Vec<E::Fr> {
|
||||
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) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr};
|
||||
use pairing::Engine;
|
||||
//! Gadgets representing numbers in the scalar field of the underlying curve.
|
||||
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
@@ -7,12 +8,12 @@ use super::Assignment;
|
||||
|
||||
use super::boolean::{self, AllocatedBit, Boolean};
|
||||
|
||||
pub struct AllocatedNum<E: Engine> {
|
||||
pub struct AllocatedNum<E: ScalarEngine> {
|
||||
value: Option<E::Fr>,
|
||||
variable: Variable,
|
||||
}
|
||||
|
||||
impl<E: Engine> Clone for AllocatedNum<E> {
|
||||
impl<E: ScalarEngine> Clone for AllocatedNum<E> {
|
||||
fn clone(&self) -> Self {
|
||||
AllocatedNum {
|
||||
value: self.value,
|
||||
@@ -21,7 +22,7 @@ impl<E: Engine> Clone for AllocatedNum<E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> AllocatedNum<E> {
|
||||
impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<E>,
|
||||
@@ -66,7 +67,7 @@ impl<E: Engine> AllocatedNum<E> {
|
||||
/// order, requiring that the representation
|
||||
/// strictly exists "in the field" (i.e., a
|
||||
/// congruency is not allowed.)
|
||||
pub fn into_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
@@ -75,10 +76,10 @@ impl<E: Engine> AllocatedNum<E> {
|
||||
v: &[AllocatedBit],
|
||||
) -> Result<AllocatedBit, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
assert!(v.len() > 0);
|
||||
assert!(!v.is_empty());
|
||||
|
||||
// Let's keep this simple for now and just AND them all
|
||||
// manually
|
||||
@@ -132,7 +133,7 @@ impl<E: Engine> AllocatedNum<E> {
|
||||
current_run.push(a_bit.clone());
|
||||
result.push(a_bit);
|
||||
} else {
|
||||
if current_run.len() > 0 {
|
||||
if !current_run.is_empty() {
|
||||
// This is the start of a run of zeros, but we need
|
||||
// to k-ary AND against `last_run` first.
|
||||
|
||||
@@ -183,13 +184,13 @@ impl<E: Engine> AllocatedNum<E> {
|
||||
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
||||
// Convert into booleans, and reverse for little-endian bit order
|
||||
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect())
|
||||
Ok(result.into_iter().map(Boolean::from).rev().collect())
|
||||
}
|
||||
|
||||
/// Convert the allocated number into its little-endian representation.
|
||||
/// Note that this does not strongly enforce that the commitment is
|
||||
/// "in the field."
|
||||
pub fn into_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
pub fn to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
@@ -208,7 +209,7 @@ impl<E: Engine> AllocatedNum<E> {
|
||||
|
||||
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
||||
Ok(bits.into_iter().map(|b| Boolean::from(b)).collect())
|
||||
Ok(bits.into_iter().map(Boolean::from).collect())
|
||||
}
|
||||
|
||||
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||
@@ -238,7 +239,7 @@ impl<E: Engine> AllocatedNum<E> {
|
||||
);
|
||||
|
||||
Ok(AllocatedNum {
|
||||
value: value,
|
||||
value,
|
||||
variable: var,
|
||||
})
|
||||
}
|
||||
@@ -270,7 +271,7 @@ impl<E: Engine> AllocatedNum<E> {
|
||||
);
|
||||
|
||||
Ok(AllocatedNum {
|
||||
value: value,
|
||||
value,
|
||||
variable: var,
|
||||
})
|
||||
}
|
||||
@@ -359,12 +360,12 @@ impl<E: Engine> AllocatedNum<E> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Num<E: Engine> {
|
||||
pub struct Num<E: ScalarEngine> {
|
||||
value: Option<E::Fr>,
|
||||
lc: LinearCombination<E>,
|
||||
}
|
||||
|
||||
impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
|
||||
impl<E: ScalarEngine> From<AllocatedNum<E>> for Num<E> {
|
||||
fn from(num: AllocatedNum<E>) -> Num<E> {
|
||||
Num {
|
||||
value: num.value,
|
||||
@@ -373,7 +374,7 @@ impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Num<E> {
|
||||
impl<E: ScalarEngine> Num<E> {
|
||||
pub fn zero() -> Self {
|
||||
Num {
|
||||
value: Some(E::Fr::zero()),
|
||||
@@ -522,7 +523,7 @@ mod test {
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
|
||||
n.into_bits_le_strict(&mut cs).unwrap();
|
||||
n.to_bits_le_strict(&mut cs).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
@@ -550,9 +551,9 @@ mod test {
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
|
||||
|
||||
let bits = if i % 2 == 0 {
|
||||
n.into_bits_le(&mut cs).unwrap()
|
||||
n.to_bits_le(&mut cs).unwrap()
|
||||
} else {
|
||||
n.into_bits_le_strict(&mut cs).unwrap()
|
||||
n.to_bits_le_strict(&mut cs).unwrap()
|
||||
};
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
//! Circuits for the [SHA-256] hash function and its internal compression
|
||||
//! function.
|
||||
//!
|
||||
//! [SHA-256]: https://tools.ietf.org/html/rfc6234
|
||||
|
||||
use super::boolean::Boolean;
|
||||
use super::multieq::MultiEq;
|
||||
use super::uint32::UInt32;
|
||||
use crate::{ConstraintSystem, SynthesisError};
|
||||
use pairing::Engine;
|
||||
use ff::ScalarEngine;
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
const ROUND_CONSTANTS: [u32; 64] = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
@@ -15,6 +21,7 @@ const ROUND_CONSTANTS: [u32; 64] = [
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||
];
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
const IV: [u32; 8] = [
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||
];
|
||||
@@ -24,7 +31,7 @@ pub fn sha256_block_no_padding<E, CS>(
|
||||
input: &[Boolean],
|
||||
) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
assert_eq!(input.len(), 512);
|
||||
@@ -39,7 +46,7 @@ where
|
||||
|
||||
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
assert!(input.len() % 8 == 0);
|
||||
@@ -76,7 +83,7 @@ fn sha256_compression_function<E, CS>(
|
||||
current_hash_value: &[UInt32],
|
||||
) -> Result<Vec<UInt32>, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
assert_eq!(input.len(), 512);
|
||||
@@ -123,14 +130,14 @@ where
|
||||
impl Maybe {
|
||||
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||
{
|
||||
Ok(match self {
|
||||
Maybe::Concrete(ref v) => return Ok(v.clone()),
|
||||
Maybe::Deferred(mut v) => {
|
||||
v.extend(others.into_iter().cloned());
|
||||
v.extend(others.iter().cloned());
|
||||
UInt32::addmany(cs, &v)?
|
||||
}
|
||||
})
|
||||
@@ -266,6 +273,7 @@ mod test {
|
||||
use super::*;
|
||||
use crate::gadgets::boolean::AllocatedBit;
|
||||
use crate::gadgets::test::TestConstraintSystem;
|
||||
use hex_literal::hex;
|
||||
use pairing::bls12_381::Bls12;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
@@ -286,7 +294,7 @@ mod test {
|
||||
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
||||
|
||||
let mut out = out_bits.into_iter();
|
||||
for b in expected.into_iter() {
|
||||
for b in expected.iter() {
|
||||
for i in (0..8).rev() {
|
||||
let c = out.next().unwrap().get_value().unwrap();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
||||
use pairing::Engine;
|
||||
//! Helpers for testing circuit implementations.
|
||||
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||
|
||||
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
@@ -20,7 +21,7 @@ enum NamedObject {
|
||||
}
|
||||
|
||||
/// Constraint system for testing purposes.
|
||||
pub struct TestConstraintSystem<E: Engine> {
|
||||
pub struct TestConstraintSystem<E: ScalarEngine> {
|
||||
named_objects: HashMap<String, NamedObject>,
|
||||
current_namespace: Vec<String>,
|
||||
constraints: Vec<(
|
||||
@@ -62,11 +63,11 @@ impl Ord for OrderedVariable {
|
||||
}
|
||||
}
|
||||
|
||||
fn proc_lc<E: Engine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
|
||||
fn proc_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
|
||||
let mut map = BTreeMap::new();
|
||||
for &(var, coeff) in terms {
|
||||
map.entry(OrderedVariable(var))
|
||||
.or_insert(E::Fr::zero())
|
||||
.or_insert_with(E::Fr::zero)
|
||||
.add_assign(&coeff);
|
||||
}
|
||||
|
||||
@@ -85,7 +86,7 @@ fn proc_lc<E: Engine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable,
|
||||
map
|
||||
}
|
||||
|
||||
fn hash_lc<E: Engine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
|
||||
fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
|
||||
let map = proc_lc::<E>(terms);
|
||||
|
||||
let mut buf = [0u8; 9 + 32];
|
||||
@@ -110,7 +111,7 @@ fn hash_lc<E: Engine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_lc<E: Engine>(
|
||||
fn eval_lc<E: ScalarEngine>(
|
||||
terms: &[(Variable, E::Fr)],
|
||||
inputs: &[(E::Fr, String)],
|
||||
aux: &[(E::Fr, String)],
|
||||
@@ -130,7 +131,7 @@ fn eval_lc<E: Engine>(
|
||||
acc
|
||||
}
|
||||
|
||||
impl<E: Engine> TestConstraintSystem<E> {
|
||||
impl<E: ScalarEngine> TestConstraintSystem<E> {
|
||||
pub fn new() -> TestConstraintSystem<E> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(
|
||||
@@ -157,7 +158,7 @@ impl<E: Engine> TestConstraintSystem<E> {
|
||||
};
|
||||
|
||||
let powers_of_two = (0..E::Fr::NUM_BITS)
|
||||
.map(|i| E::Fr::from_str("2").unwrap().pow(&[i as u64]))
|
||||
.map(|i| E::Fr::from_str("2").unwrap().pow(&[u64::from(i)]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pp = |s: &mut String, lc: &LinearCombination<E>| {
|
||||
@@ -286,7 +287,7 @@ impl<E: Engine> TestConstraintSystem<E> {
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn num_inputs(&self) -> usize {
|
||||
@@ -344,7 +345,7 @@ fn compute_path(ns: &[String], this: String) -> String {
|
||||
name
|
||||
}
|
||||
|
||||
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
||||
impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
||||
type Root = Self;
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use ff::{Field, PrimeField};
|
||||
use pairing::Engine;
|
||||
//! Circuit representation of a [`u32`], with helpers for the [`sha256`]
|
||||
//! gadgets.
|
||||
//!
|
||||
//! [`sha256`]: crate::gadgets::sha256
|
||||
|
||||
use ff::{Field, PrimeField, ScalarEngine};
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError};
|
||||
|
||||
@@ -33,7 +37,7 @@ impl UInt32 {
|
||||
}
|
||||
|
||||
UInt32 {
|
||||
bits: bits,
|
||||
bits,
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
@@ -41,7 +45,7 @@ impl UInt32 {
|
||||
/// Allocate a `UInt32` in the constraint system
|
||||
pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let values = match value {
|
||||
@@ -69,14 +73,13 @@ impl UInt32 {
|
||||
})
|
||||
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
||||
|
||||
Ok(UInt32 {
|
||||
bits: bits,
|
||||
value: value,
|
||||
})
|
||||
Ok(UInt32 { bits, value })
|
||||
}
|
||||
|
||||
pub fn into_bits_be(&self) -> Vec<Boolean> {
|
||||
self.bits.iter().rev().cloned().collect()
|
||||
pub fn into_bits_be(self) -> Vec<Boolean> {
|
||||
let mut ret = self.bits;
|
||||
ret.reverse();
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn from_bits_be(bits: &[Boolean]) -> Self {
|
||||
@@ -98,14 +101,14 @@ impl UInt32 {
|
||||
}
|
||||
|
||||
UInt32 {
|
||||
value: value,
|
||||
value,
|
||||
bits: bits.iter().rev().cloned().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns this `UInt32` into its little-endian byte order representation.
|
||||
pub fn into_bits(&self) -> Vec<Boolean> {
|
||||
self.bits.clone()
|
||||
pub fn into_bits(self) -> Vec<Boolean> {
|
||||
self.bits
|
||||
}
|
||||
|
||||
/// Converts a little-endian byte order representation of bits into a
|
||||
@@ -119,20 +122,20 @@ impl UInt32 {
|
||||
for b in new_bits.iter().rev() {
|
||||
value.as_mut().map(|v| *v <<= 1);
|
||||
|
||||
match b {
|
||||
&Boolean::Constant(b) => {
|
||||
match *b {
|
||||
Boolean::Constant(b) => {
|
||||
if b {
|
||||
value.as_mut().map(|v| *v |= 1);
|
||||
}
|
||||
}
|
||||
&Boolean::Is(ref b) => match b.get_value() {
|
||||
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() {
|
||||
Boolean::Not(ref b) => match b.get_value() {
|
||||
Some(false) => {
|
||||
value.as_mut().map(|v| *v |= 1);
|
||||
}
|
||||
@@ -143,7 +146,7 @@ impl UInt32 {
|
||||
}
|
||||
|
||||
UInt32 {
|
||||
value: value,
|
||||
value,
|
||||
bits: new_bits,
|
||||
}
|
||||
}
|
||||
@@ -195,7 +198,7 @@ impl UInt32 {
|
||||
circuit_fn: U,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
F: Fn(u32, u32, u32) -> u32,
|
||||
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
|
||||
@@ -215,7 +218,7 @@ impl UInt32 {
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(UInt32 {
|
||||
bits: bits,
|
||||
bits,
|
||||
value: new_value,
|
||||
})
|
||||
}
|
||||
@@ -224,7 +227,7 @@ impl UInt32 {
|
||||
/// during SHA256.
|
||||
pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
Self::triop(
|
||||
@@ -241,7 +244,7 @@ impl UInt32 {
|
||||
/// during SHA256.
|
||||
pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
Self::triop(
|
||||
@@ -257,7 +260,7 @@ impl UInt32 {
|
||||
/// XOR this `UInt32` with another `UInt32`
|
||||
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let new_value = match (self.value, other.value) {
|
||||
@@ -274,7 +277,7 @@ impl UInt32 {
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(UInt32 {
|
||||
bits: bits,
|
||||
bits,
|
||||
value: new_value,
|
||||
})
|
||||
}
|
||||
@@ -282,7 +285,7 @@ impl UInt32 {
|
||||
/// Perform modular addition of several `UInt32` objects.
|
||||
pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: Engine,
|
||||
E: ScalarEngine,
|
||||
CS: ConstraintSystem<E>,
|
||||
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||
{
|
||||
@@ -294,7 +297,7 @@ impl UInt32 {
|
||||
|
||||
// Compute the maximum value of the sum so we allocate enough bits for
|
||||
// the result
|
||||
let mut max_value = (operands.len() as u64) * (u32::max_value() as u64);
|
||||
let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value()));
|
||||
|
||||
// Keep track of the resulting value
|
||||
let mut result_value = Some(0u64);
|
||||
@@ -310,7 +313,7 @@ impl UInt32 {
|
||||
// Accumulate the value
|
||||
match op.value {
|
||||
Some(val) => {
|
||||
result_value.as_mut().map(|v| *v += val as u64);
|
||||
result_value.as_mut().map(|v| *v += u64::from(val));
|
||||
}
|
||||
None => {
|
||||
// If any of our operands have unknown value, we won't
|
||||
@@ -401,15 +404,15 @@ mod test {
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut v = (0..32)
|
||||
let v = (0..32)
|
||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let b = UInt32::from_bits_be(&v);
|
||||
|
||||
for (i, bit) in b.bits.iter().enumerate() {
|
||||
match bit {
|
||||
&Boolean::Constant(bit) => {
|
||||
match *bit {
|
||||
Boolean::Constant(bit) => {
|
||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -436,15 +439,15 @@ mod test {
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut v = (0..32)
|
||||
let v = (0..32)
|
||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let b = UInt32::from_bits(&v);
|
||||
|
||||
for (i, bit) in b.bits.iter().enumerate() {
|
||||
match bit {
|
||||
&Boolean::Constant(bit) => {
|
||||
match *bit {
|
||||
Boolean::Constant(bit) => {
|
||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -491,14 +494,14 @@ mod test {
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match b {
|
||||
&Boolean::Is(ref b) => {
|
||||
match *b {
|
||||
Boolean::Is(ref b) => {
|
||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
&Boolean::Not(ref b) => {
|
||||
Boolean::Not(ref b) => {
|
||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
&Boolean::Constant(b) => {
|
||||
Boolean::Constant(b) => {
|
||||
assert!(b == (expected & 1 == 1));
|
||||
}
|
||||
}
|
||||
@@ -538,10 +541,10 @@ mod test {
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match b {
|
||||
&Boolean::Is(_) => panic!(),
|
||||
&Boolean::Not(_) => panic!(),
|
||||
&Boolean::Constant(b) => {
|
||||
match *b {
|
||||
Boolean::Is(_) => panic!(),
|
||||
Boolean::Not(_) => panic!(),
|
||||
Boolean::Constant(b) => {
|
||||
assert!(b == (expected & 1 == 1));
|
||||
}
|
||||
}
|
||||
@@ -576,8 +579,7 @@ mod test {
|
||||
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
|
||||
let r = {
|
||||
let mut cs = MultiEq::new(&mut cs);
|
||||
let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap();
|
||||
r
|
||||
UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap()
|
||||
};
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
@@ -585,14 +587,14 @@ mod test {
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match b {
|
||||
&Boolean::Is(ref b) => {
|
||||
match *b {
|
||||
Boolean::Is(ref b) => {
|
||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
&Boolean::Not(ref b) => {
|
||||
Boolean::Not(ref b) => {
|
||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
&Boolean::Constant(_) => unreachable!(),
|
||||
Boolean::Constant(_) => unreachable!(),
|
||||
}
|
||||
|
||||
expected >>= 1;
|
||||
@@ -628,8 +630,8 @@ mod test {
|
||||
|
||||
let mut tmp = num;
|
||||
for b in &b.bits {
|
||||
match b {
|
||||
&Boolean::Constant(b) => {
|
||||
match *b {
|
||||
Boolean::Constant(b) => {
|
||||
assert_eq!(b, tmp & 1 == 1);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
||||
@@ -8,11 +8,11 @@ use pairing::Engine;
|
||||
|
||||
use super::{Parameters, VerifyingKey};
|
||||
|
||||
use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
use domain::{EvaluationDomain, Scalar};
|
||||
use crate::domain::{EvaluationDomain, Scalar};
|
||||
|
||||
use multicore::Worker;
|
||||
use crate::multicore::Worker;
|
||||
|
||||
/// Generates a random common reference string for
|
||||
/// a circuit.
|
||||
@@ -227,7 +227,7 @@ where
|
||||
let powers_of_tau = powers_of_tau.as_mut();
|
||||
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
||||
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move || {
|
||||
scope.spawn(move |_scope| {
|
||||
let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
|
||||
|
||||
for p in powers_of_tau {
|
||||
@@ -251,7 +251,7 @@ where
|
||||
{
|
||||
let mut g1_wnaf = g1_wnaf.shared();
|
||||
|
||||
scope.spawn(move || {
|
||||
scope.spawn(move |_scope| {
|
||||
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
||||
for (h, p) in h.iter_mut().zip(p.iter()) {
|
||||
// Compute final exponent
|
||||
@@ -330,7 +330,7 @@ where
|
||||
let mut g1_wnaf = g1_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()
|
||||
.zip(b_g1.iter_mut())
|
||||
@@ -451,7 +451,7 @@ where
|
||||
};
|
||||
|
||||
Ok(Parameters {
|
||||
vk: vk,
|
||||
vk,
|
||||
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
|
||||
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
//! The [Groth16] proving system.
|
||||
//!
|
||||
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||
|
||||
use group::{CurveAffine, EncodedPoint};
|
||||
use pairing::{Engine, PairingCurveAffine};
|
||||
|
||||
use SynthesisError;
|
||||
use crate::SynthesisError;
|
||||
|
||||
use crate::multiexp::SourceBuilder;
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use multiexp::SourceBuilder;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -90,7 +94,7 @@ impl<E: Engine> Proof<E> {
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Proof { a: a, b: b, c: c })
|
||||
Ok(Proof { a, b, c })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,13 +212,13 @@ impl<E: Engine> VerifyingKey<E> {
|
||||
}
|
||||
|
||||
Ok(VerifyingKey {
|
||||
alpha_g1: alpha_g1,
|
||||
beta_g1: beta_g1,
|
||||
beta_g2: beta_g2,
|
||||
gamma_g2: gamma_g2,
|
||||
delta_g1: delta_g1,
|
||||
delta_g2: delta_g2,
|
||||
ic: ic,
|
||||
alpha_g1,
|
||||
beta_g1,
|
||||
beta_g2,
|
||||
gamma_g2,
|
||||
delta_g1,
|
||||
delta_g2,
|
||||
ic,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -376,7 +380,7 @@ impl<E: Engine> Parameters<E> {
|
||||
}
|
||||
|
||||
Ok(Parameters {
|
||||
vk: vk,
|
||||
vk,
|
||||
h: Arc::new(h),
|
||||
l: Arc::new(l),
|
||||
a: Arc::new(a),
|
||||
@@ -465,7 +469,7 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
|
||||
#[cfg(test)]
|
||||
mod test_with_bls12_381 {
|
||||
use super::*;
|
||||
use {Circuit, ConstraintSystem, SynthesisError};
|
||||
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||
|
||||
use ff::Field;
|
||||
use pairing::bls12_381::{Bls12, Fr};
|
||||
|
||||
@@ -10,13 +10,13 @@ use pairing::Engine;
|
||||
|
||||
use super::{ParameterSource, Proof};
|
||||
|
||||
use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
use domain::{EvaluationDomain, Scalar};
|
||||
use crate::domain::{EvaluationDomain, Scalar};
|
||||
|
||||
use multiexp::{multiexp, DensityTracker, FullDensity};
|
||||
use crate::multiexp::{multiexp, DensityTracker, FullDensity};
|
||||
|
||||
use multicore::Worker;
|
||||
use crate::multicore::Worker;
|
||||
|
||||
fn eval<E: Engine>(
|
||||
lc: &LinearCombination<E>,
|
||||
|
||||
@@ -16,13 +16,13 @@ const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
||||
pub struct Fr(Wrapping<u32>);
|
||||
|
||||
impl fmt::Display for Fr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}", (self.0).0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fr {
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
Fr(Wrapping(rng.next_u32()) % MODULUS_R)
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ impl PartialOrd 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])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use self::dummy_engine::*;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use {Circuit, ConstraintSystem, SynthesisError};
|
||||
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||
|
||||
use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use pairing::{Engine, PairingCurveAffine};
|
||||
|
||||
use super::{PreparedVerifyingKey, Proof, VerifyingKey};
|
||||
|
||||
use SynthesisError;
|
||||
use crate::SynthesisError;
|
||||
|
||||
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
|
||||
let mut gamma = vk.gamma_g2;
|
||||
@@ -49,7 +49,7 @@ pub fn verify_proof<'a, E: Engine>(
|
||||
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
|
||||
(&proof.c.prepare(), &pvk.neg_delta_g2),
|
||||
]
|
||||
.into_iter(),
|
||||
.iter(),
|
||||
))
|
||||
.unwrap()
|
||||
== pvk.alpha_g1_beta_g2)
|
||||
|
||||
@@ -1,33 +1,139 @@
|
||||
extern crate ff;
|
||||
extern crate group;
|
||||
#[cfg(feature = "pairing")]
|
||||
extern crate pairing;
|
||||
extern crate rand_core;
|
||||
//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
|
||||
//! traits and and primitive structures, as well as basic gadget implementations
|
||||
//! such as booleans and number abstractions.
|
||||
//!
|
||||
//! # Example circuit
|
||||
//!
|
||||
//! Say we want to write a circuit that proves we know the preimage to some hash
|
||||
//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a
|
||||
//! fixed length known in advance (because the circuit parameters will depend on
|
||||
//! it), but can otherwise have any value. We take the following strategy:
|
||||
//!
|
||||
//! - Witness each bit of the preimage.
|
||||
//! - Compute `hash = SHA-256d(preimage)` inside the circuit.
|
||||
//! - Expose `hash` as a public input using multiscalar packing.
|
||||
//!
|
||||
//! ```
|
||||
//! use bellman::{
|
||||
//! gadgets::{
|
||||
//! boolean::{AllocatedBit, Boolean},
|
||||
//! multipack,
|
||||
//! sha256::sha256,
|
||||
//! },
|
||||
//! groth16, Circuit, ConstraintSystem, SynthesisError,
|
||||
//! };
|
||||
//! use pairing::{bls12_381::Bls12, Engine};
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use sha2::{Digest, Sha256};
|
||||
//!
|
||||
//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order.
|
||||
//! fn sha256d<E: Engine, CS: ConstraintSystem<E>>(
|
||||
//! mut cs: CS,
|
||||
//! data: &[Boolean],
|
||||
//! ) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
//! // Flip endianness of each input byte
|
||||
//! let input: Vec<_> = data
|
||||
//! .chunks(8)
|
||||
//! .map(|c| c.iter().rev())
|
||||
//! .flatten()
|
||||
//! .cloned()
|
||||
//! .collect();
|
||||
//!
|
||||
//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
|
||||
//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;
|
||||
//!
|
||||
//! // Flip endianness of each output byte
|
||||
//! Ok(res
|
||||
//! .chunks(8)
|
||||
//! .map(|c| c.iter().rev())
|
||||
//! .flatten()
|
||||
//! .cloned()
|
||||
//! .collect())
|
||||
//! }
|
||||
//!
|
||||
//! struct MyCircuit {
|
||||
//! /// The input to SHA-256d we are proving that we know. Set to `None` when we
|
||||
//! /// are verifying a proof (and do not have the witness data).
|
||||
//! preimage: Option<[u8; 80]>,
|
||||
//! }
|
||||
//!
|
||||
//! impl<E: Engine> Circuit<E> for MyCircuit {
|
||||
//! fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||
//! // Compute the values for the bits of the preimage. If we are verifying a proof,
|
||||
//! // we still need to create the same constraints, so we return an equivalent-size
|
||||
//! // Vec of None (indicating that the value of each bit is unknown).
|
||||
//! let bit_values = if let Some(preimage) = self.preimage {
|
||||
//! preimage
|
||||
//! .into_iter()
|
||||
//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
|
||||
//! .flatten()
|
||||
//! .map(|b| Some(b))
|
||||
//! .collect()
|
||||
//! } else {
|
||||
//! vec![None; 80 * 8]
|
||||
//! };
|
||||
//! assert_eq!(bit_values.len(), 80 * 8);
|
||||
//!
|
||||
//! // Witness the bits of the preimage.
|
||||
//! let preimage_bits = bit_values
|
||||
//! .into_iter()
|
||||
//! .enumerate()
|
||||
//! // Allocate each bit.
|
||||
//! .map(|(i, b)| {
|
||||
//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b)
|
||||
//! })
|
||||
//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
|
||||
//! .map(|b| b.map(Boolean::from))
|
||||
//! .collect::<Result<Vec<_>, _>>()?;
|
||||
//!
|
||||
//! // Compute hash = SHA-256d(preimage).
|
||||
//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;
|
||||
//!
|
||||
//! // Expose the vector of 32 boolean variables as compact public inputs.
|
||||
//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Create parameters for our circuit. In a production deployment these would
|
||||
//! // be generated securely using a multiparty computation.
|
||||
//! let params = {
|
||||
//! let c = MyCircuit { preimage: None };
|
||||
//! groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
|
||||
//! };
|
||||
//!
|
||||
//! // Prepare the verification key (for proof verification).
|
||||
//! let pvk = groth16::prepare_verifying_key(¶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 bit_vec;
|
||||
extern crate blake2s_simd;
|
||||
extern crate byteorder;
|
||||
extern crate futures;
|
||||
|
||||
#[cfg(feature = "multicore")]
|
||||
extern crate crossbeam;
|
||||
#[cfg(feature = "multicore")]
|
||||
extern crate futures_cpupool;
|
||||
#[cfg(feature = "multicore")]
|
||||
extern crate num_cpus;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand_xorshift;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate sha2;
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
|
||||
pub mod domain;
|
||||
pub mod gadgets;
|
||||
@@ -109,6 +215,7 @@ impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
|
||||
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
|
||||
type Output = LinearCombination<E>;
|
||||
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||
coeff.negate();
|
||||
|
||||
@@ -230,8 +337,8 @@ impl Error for SynthesisError {
|
||||
}
|
||||
|
||||
impl fmt::Display for SynthesisError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
if let &SynthesisError::IoError(ref e) = self {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
if let SynthesisError::IoError(ref e) = *self {
|
||||
write!(f, "I/O error: ")?;
|
||||
e.fmt(f)
|
||||
} else {
|
||||
@@ -296,7 +403,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
|
||||
fn get_root(&mut self) -> &mut Self::Root;
|
||||
|
||||
/// Begin a namespace for this constraint system.
|
||||
fn namespace<'a, NR, N>(&'a mut self, name_fn: N) -> Namespace<'a, E, Self::Root>
|
||||
fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root>
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
@@ -309,7 +416,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
|
||||
|
||||
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
|
||||
/// a namespace context) and, when dropped, pops out of the namespace context.
|
||||
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>);
|
||||
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E>>(&'a mut CS, PhantomData<E>);
|
||||
|
||||
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
|
||||
type Root = CS::Root;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
//! This is an interface for dealing with the kinds of
|
||||
//! parallel computations involved in bellman. It's
|
||||
//! currently just a thin wrapper around CpuPool and
|
||||
//! crossbeam but may be extended in the future to
|
||||
//! allow for various parallelism strategies.
|
||||
//! An interface for dealing with the kinds of parallel computations involved in
|
||||
//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and
|
||||
//! [`crossbeam`] but may be extended in the future to allow for various
|
||||
//! parallelism strategies.
|
||||
//!
|
||||
//! [`CpuPool`]: futures_cpupool::CpuPool
|
||||
|
||||
#[cfg(feature = "multicore")]
|
||||
mod implementation {
|
||||
use crossbeam::{self, Scope};
|
||||
use crossbeam::{self, thread::Scope};
|
||||
use futures::{Future, IntoFuture, Poll};
|
||||
use futures_cpupool::{CpuFuture, CpuPool};
|
||||
use num_cpus;
|
||||
@@ -23,7 +24,7 @@ mod implementation {
|
||||
// CPUs configured.
|
||||
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
|
||||
Worker {
|
||||
cpus: cpus,
|
||||
cpus,
|
||||
pool: CpuPool::new(cpus),
|
||||
}
|
||||
}
|
||||
@@ -59,7 +60,9 @@ mod implementation {
|
||||
elements / self.cpus
|
||||
};
|
||||
|
||||
// TODO: Handle case where threads fail
|
||||
crossbeam::scope(|scope| f(scope, chunk_size))
|
||||
.expect("Threads aren't allowed to fail yet")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,8 +155,8 @@ mod implementation {
|
||||
pub struct DummyScope;
|
||||
|
||||
impl DummyScope {
|
||||
pub fn spawn<F: FnOnce()>(&self, f: F) {
|
||||
f();
|
||||
pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
|
||||
f(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ fn multiexp_inner<Q, D, G, S>(
|
||||
mut skip: u32,
|
||||
c: u32,
|
||||
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,
|
||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||
@@ -256,7 +256,7 @@ pub fn multiexp<Q, D, G, S>(
|
||||
bases: S,
|
||||
density_map: D,
|
||||
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
||||
) -> Box<Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||
where
|
||||
for<'a> &'a Q: QueryDensity,
|
||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
extern crate bellman;
|
||||
extern crate ff;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
|
||||
// For randomness (during paramgen and proof generation)
|
||||
use rand::thread_rng;
|
||||
|
||||
@@ -90,12 +85,12 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
|
||||
let cs = &mut cs.namespace(|| format!("round {}", i));
|
||||
|
||||
// tmp = (xL + Ci)^2
|
||||
let mut tmp_value = xl_value.map(|mut e| {
|
||||
let tmp_value = xl_value.map(|mut e| {
|
||||
e.add_assign(&self.constants[i]);
|
||||
e.square();
|
||||
e
|
||||
});
|
||||
let mut tmp = cs.alloc(
|
||||
let tmp = cs.alloc(
|
||||
|| "tmp",
|
||||
|| tmp_value.ok_or(SynthesisError::AssignmentMissing),
|
||||
)?;
|
||||
@@ -110,14 +105,14 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
|
||||
// new_xL = xR + (xL + Ci)^3
|
||||
// new_xL = xR + tmp * (xL + Ci)
|
||||
// new_xL - xR = tmp * (xL + Ci)
|
||||
let mut new_xl_value = xl_value.map(|mut e| {
|
||||
let new_xl_value = xl_value.map(|mut e| {
|
||||
e.add_assign(&self.constants[i]);
|
||||
e.mul_assign(&tmp_value.unwrap());
|
||||
e.add_assign(&xr_value.unwrap());
|
||||
e
|
||||
});
|
||||
|
||||
let mut new_xl = if i == (MIMC_ROUNDS - 1) {
|
||||
let new_xl = if i == (MIMC_ROUNDS - 1) {
|
||||
// This is the last round, xL is our image and so
|
||||
// we allocate a public input.
|
||||
cs.alloc_input(
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
[package]
|
||||
name = "ff"
|
||||
version = "0.4.0"
|
||||
version = "0.5.2"
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
description = "Library for building and interfacing with finite fields"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/ff/"
|
||||
homepage = "https://github.com/ebfull/ff"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/ebfull/ff"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1"
|
||||
ff_derive = { version = "0.3.0", path = "ff_derive", optional = true }
|
||||
ff_derive = { version = "^0.4.1", path = "ff_derive", optional = true }
|
||||
rand_core = "0.5"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
derive = ["ff_derive"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
17
ff/README.md
17
ff/README.md
@@ -12,14 +12,18 @@ Add the `ff` crate to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ff = "0.4"
|
||||
ff = "0.5"
|
||||
```
|
||||
|
||||
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. See the **[documentation](https://docs.rs/ff/0.4.0/ff/)** for more.
|
||||
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits.
|
||||
See the **[documentation](https://docs.rs/ff/)** for more.
|
||||
|
||||
### #![derive(PrimeField)]
|
||||
|
||||
If you need an implementation of a prime field, this library also provides a procedural macro that will expand into an efficient implementation of a prime field when supplied with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is also quadratic nonresidue.
|
||||
If you need an implementation of a prime field, this library also provides a procedural
|
||||
macro that will expand into an efficient implementation of a prime field when supplied
|
||||
with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is
|
||||
also quadratic nonresidue.
|
||||
|
||||
First, enable the `derive` crate feature:
|
||||
|
||||
@@ -41,13 +45,16 @@ extern crate ff;
|
||||
struct Fp(FpRepr);
|
||||
```
|
||||
|
||||
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement `SqrtField` if supported. The library implements `FpRepr` itself and derives `PrimeFieldRepr` for it.
|
||||
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement
|
||||
`SqrtField` if supported. The library implements `FpRepr` itself and derives
|
||||
`PrimeFieldRepr` for it.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
[package]
|
||||
name = "ff_derive"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
description = "Procedural macro library used to build custom prime field implementations"
|
||||
documentation = "https://docs.rs/ff/"
|
||||
homepage = "https://github.com/ebfull/ff"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/ebfull/ff"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
@@ -15,6 +16,9 @@ proc-macro = true
|
||||
num-bigint = "0.2"
|
||||
num-traits = "0.2"
|
||||
num-integer = "0.1"
|
||||
proc-macro2 = "0.4"
|
||||
quote = "0.6"
|
||||
syn = "0.14"
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = "1"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "passively-maintained" }
|
||||
|
||||
@@ -2,17 +2,11 @@
|
||||
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
extern crate num_bigint;
|
||||
extern crate num_integer;
|
||||
extern crate num_traits;
|
||||
|
||||
use num_bigint::BigUint;
|
||||
use num_integer::Integer;
|
||||
use num_traits::{One, ToPrimitive, Zero};
|
||||
use quote::quote;
|
||||
use quote::TokenStreamExt;
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -91,10 +85,10 @@ fn fetch_wrapped_ident(body: &syn::Data) -> Option<syn::Ident> {
|
||||
/// Fetch an attribute string from the derived struct.
|
||||
fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> {
|
||||
for attr in attrs {
|
||||
if let Some(meta) = attr.interpret_meta() {
|
||||
if let Ok(meta) = attr.parse_meta() {
|
||||
match meta {
|
||||
syn::Meta::NameValue(nv) => {
|
||||
if nv.ident.to_string() == name {
|
||||
if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) {
|
||||
match nv.lit {
|
||||
syn::Lit::Str(ref s) => return Some(s.value()),
|
||||
_ => {
|
||||
@@ -122,9 +116,9 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
impl ::std::fmt::Debug for #repr
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
try!(write!(f, "0x"));
|
||||
write!(f, "0x")?;
|
||||
for i in self.0.iter().rev() {
|
||||
try!(write!(f, "{:016x}", *i));
|
||||
write!(f, "{:016x}", *i)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -133,9 +127,9 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
|
||||
impl ::std::fmt::Display for #repr {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
try!(write!(f, "0x"));
|
||||
write!(f, "0x")?;
|
||||
for i in self.0.iter().rev() {
|
||||
try!(write!(f, "{:016x}", *i));
|
||||
write!(f, "{:016x}", *i)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -883,7 +877,7 @@ fn prime_field_impl(
|
||||
|
||||
impl ::ff::Field for #name {
|
||||
/// Computes a uniformly random element using rejection sampling.
|
||||
fn random<R: ::rand_core::RngCore>(rng: &mut R) -> Self {
|
||||
fn random<R: ::rand_core::RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
loop {
|
||||
let mut tmp = {
|
||||
let mut repr = [0u64; #limbs];
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
//! This crate provides traits for working with finite fields.
|
||||
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate rand_core;
|
||||
|
||||
#[cfg(feature = "derive")]
|
||||
#[macro_use]
|
||||
extern crate ff_derive;
|
||||
|
||||
#[cfg(feature = "derive")]
|
||||
pub use ff_derive::*;
|
||||
|
||||
@@ -20,7 +17,7 @@ pub trait Field:
|
||||
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
|
||||
{
|
||||
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self;
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self;
|
||||
|
||||
/// Returns the zero element of the field, the additive identity.
|
||||
fn zero() -> Self;
|
||||
@@ -210,7 +207,7 @@ impl Error for PrimeFieldDecodingError {
|
||||
}
|
||||
|
||||
impl fmt::Display for PrimeFieldDecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
PrimeFieldDecodingError::NotInField(ref repr) => {
|
||||
write!(f, "{} is not an element of the field", repr)
|
||||
@@ -266,7 +263,7 @@ pub trait PrimeField: Field {
|
||||
}
|
||||
|
||||
/// Convert this prime field element into a biginteger representation.
|
||||
fn from_repr(Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
|
||||
fn from_repr(_: Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
|
||||
|
||||
/// Convert a biginteger representation into a prime field element, if
|
||||
/// the number is an element of the field.
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
[package]
|
||||
name = "group"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <jack@z.cash>",
|
||||
]
|
||||
readme = "README.md"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
description = "Elliptic curve group traits and utilities"
|
||||
documentation = "https://docs.rs/group/"
|
||||
homepage = "https://github.com/ebfull/group"
|
||||
repository = "https://github.com/ebfull/group"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
ff = { path = "../ff" }
|
||||
ff = { version = "0.5.0", path = "../ff" }
|
||||
rand = "0.7"
|
||||
rand_xorshift = "0.2"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# group [](https://crates.io/crates/group) #
|
||||
|
||||
`group` is a crate for working with groups over elliptic curves.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
extern crate ff;
|
||||
extern crate rand;
|
||||
extern crate rand_xorshift;
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
|
||||
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
|
||||
use rand::RngCore;
|
||||
@@ -180,7 +179,7 @@ impl Error for GroupDecodingError {
|
||||
}
|
||||
|
||||
impl fmt::Display for GroupDecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
|
||||
write!(f, "{} decoding error: {}", description, err)
|
||||
|
||||
@@ -2,7 +2,7 @@ use ff::{Field, PrimeField};
|
||||
use rand::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use {CurveAffine, CurveProjective, EncodedPoint};
|
||||
use crate::{CurveAffine, CurveProjective, EncodedPoint};
|
||||
|
||||
pub fn curve_tests<G: CurveProjective>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@@ -71,7 +71,7 @@ pub fn curve_tests<G: CurveProjective>() {
|
||||
}
|
||||
|
||||
fn random_wnaf_tests<G: CurveProjective>() {
|
||||
use wnaf::*;
|
||||
use crate::wnaf::*;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
[package]
|
||||
name = "librustzcash"
|
||||
version = "0.1.0"
|
||||
description = "Rust FFI used by the zcashd binary. Not an official API."
|
||||
version = "0.2.0"
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <jack@z.cash>",
|
||||
"Jay Graber <jay@z.cash>",
|
||||
"Simon Liu <simon@z.cash>"
|
||||
]
|
||||
]
|
||||
homepage = "https://github.com/zcash/librustzcash"
|
||||
repository = "https://github.com/zcash/librustzcash"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "rustzcash"
|
||||
@@ -14,15 +20,17 @@ path = "src/rustzcash.rs"
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
bellman = { path = "../bellman" }
|
||||
bellman = { version = "0.2.0", path = "../bellman" }
|
||||
blake2b_simd = "0.5"
|
||||
blake2s_simd = "0.5"
|
||||
ff = { path = "../ff" }
|
||||
ff = { version = "0.5.0", path = "../ff" }
|
||||
libc = "0.2"
|
||||
pairing = { path = "../pairing" }
|
||||
pairing = { version = "0.15.0", path = "../pairing" }
|
||||
lazy_static = "1"
|
||||
byteorder = "1"
|
||||
rand_core = "0.5"
|
||||
rand_os = "0.2"
|
||||
zcash_primitives = { path = "../zcash_primitives" }
|
||||
zcash_proofs = { path = "../zcash_proofs" }
|
||||
rand_core = "0.5.1"
|
||||
zcash_history = { version = "0.0.1", path = "../zcash_history" }
|
||||
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||
zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "deprecated" }
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
# librustzcash
|
||||
|
||||
This repository contains librustzcash, a static library for Zcash code assets written in Rust.
|
||||
`librustzcash` is an FFI library crate that exposes the Zcash Rust components to
|
||||
the `zcashd` full node.
|
||||
|
||||
The FFI API does not have any stability guarantees, and will change as required
|
||||
by `zcashd`.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -112,8 +112,7 @@ extern "C" {
|
||||
bool librustzcash_sapling_output_proof(
|
||||
void *ctx,
|
||||
const unsigned char *esk,
|
||||
const unsigned char *diversifier,
|
||||
const unsigned char *pk_d,
|
||||
const unsigned char *payment_address,
|
||||
const unsigned char *rcm,
|
||||
const uint64_t value,
|
||||
unsigned char *cv,
|
||||
@@ -308,6 +307,33 @@ extern "C" {
|
||||
unsigned char *j_ret,
|
||||
unsigned char *addr_ret
|
||||
);
|
||||
|
||||
uint32_t librustzcash_mmr_append(
|
||||
uint32_t cbranch,
|
||||
uint32_t t_len,
|
||||
const uint32_t *ni_ptr,
|
||||
const unsigned char *n_ptr,
|
||||
size_t p_len,
|
||||
const unsigned char *nn_ptr,
|
||||
unsigned char *rt_ret,
|
||||
unsigned char *buf_ret
|
||||
);
|
||||
|
||||
uint32_t librustzcash_mmr_delete(
|
||||
uint32_t cbranch,
|
||||
uint32_t t_len,
|
||||
const uint32_t *ni_ptr,
|
||||
const unsigned char *n_ptr,
|
||||
size_t p_len,
|
||||
size_t e_len,
|
||||
unsigned char *rt_ret
|
||||
);
|
||||
|
||||
uint32_t librustzcash_mmr_hash_node(
|
||||
uint32_t cbranch,
|
||||
const unsigned char *n_ptr,
|
||||
unsigned char *h_ret
|
||||
);
|
||||
}
|
||||
|
||||
#endif // LIBRUSTZCASH_INCLUDE_H_
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,10 @@
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use pairing::bls12_381::Bls12;
|
||||
use rand_core::RngCore;
|
||||
use rand_os::OsRng;
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use zcash_primitives::jubjub::{edwards, JubjubBls12};
|
||||
use zcash_primitives::primitives::{Diversifier, ViewingKey};
|
||||
|
||||
use {
|
||||
use crate::{
|
||||
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
|
||||
librustzcash_sapling_ka_derivepublic,
|
||||
};
|
||||
@@ -25,7 +24,7 @@ fn test_key_agreement() {
|
||||
let addr = loop {
|
||||
let mut d = [0; 11];
|
||||
rng.fill_bytes(&mut d);
|
||||
match vk.into_payment_address(Diversifier(d), ¶ms) {
|
||||
match vk.to_payment_address(Diversifier(d), ¶ms) {
|
||||
Some(a) => break a,
|
||||
None => {}
|
||||
}
|
||||
@@ -47,7 +46,7 @@ fn test_key_agreement() {
|
||||
|
||||
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
||||
let mut addr_pk_d = [0u8; 32];
|
||||
addr.pk_d.write(&mut addr_pk_d[..]).unwrap();
|
||||
addr.pk_d().write(&mut addr_pk_d[..]).unwrap();
|
||||
|
||||
assert!(librustzcash_sapling_ka_agree(
|
||||
&addr_pk_d,
|
||||
@@ -59,7 +58,7 @@ fn test_key_agreement() {
|
||||
// using the diversifier and esk.
|
||||
let mut epk = [0u8; 32];
|
||||
assert!(librustzcash_sapling_ka_derivepublic(
|
||||
&addr.diversifier.0,
|
||||
&addr.diversifier().0,
|
||||
&esk,
|
||||
&mut epk
|
||||
));
|
||||
|
||||
@@ -7,7 +7,7 @@ use zcash_primitives::{
|
||||
|
||||
use super::JUBJUB;
|
||||
|
||||
use {
|
||||
use crate::{
|
||||
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
|
||||
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
|
||||
};
|
||||
@@ -678,7 +678,7 @@ fn key_components() {
|
||||
}
|
||||
|
||||
let pgk = ProofGenerationKey { ak, nsk };
|
||||
let fvk = pgk.into_viewing_key(&JUBJUB);
|
||||
let fvk = pgk.to_viewing_key(&JUBJUB);
|
||||
{
|
||||
let mut vec = Vec::new();
|
||||
fvk.nk.write(&mut vec).unwrap();
|
||||
@@ -704,10 +704,10 @@ fn key_components() {
|
||||
let diversifier = Diversifier(tv.default_d);
|
||||
assert!(librustzcash_check_diversifier(&tv.default_d));
|
||||
|
||||
let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap();
|
||||
let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
|
||||
{
|
||||
let mut vec = Vec::new();
|
||||
addr.pk_d.write(&mut vec).unwrap();
|
||||
addr.pk_d().write(&mut vec).unwrap();
|
||||
assert_eq!(&vec, &tv.default_pk_d);
|
||||
}
|
||||
{
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ use super::JUBJUB;
|
||||
|
||||
mod key_agreement;
|
||||
mod key_components;
|
||||
mod mmr;
|
||||
mod notes;
|
||||
mod signatures;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use librustzcash_sapling_compute_cm;
|
||||
use librustzcash_sapling_compute_nf;
|
||||
use crate::librustzcash_sapling_compute_cm;
|
||||
use crate::librustzcash_sapling_compute_nf;
|
||||
|
||||
#[test]
|
||||
fn notes() {
|
||||
|
||||
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.
@@ -2,22 +2,24 @@
|
||||
name = "pairing"
|
||||
|
||||
# Remember to change version string in README.md.
|
||||
version = "0.14.2"
|
||||
version = "0.15.1"
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <jack@z.cash>",
|
||||
]
|
||||
readme = "README.md"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
description = "Pairing-friendly elliptic curve library"
|
||||
documentation = "https://docs.rs/pairing/"
|
||||
homepage = "https://github.com/ebfull/pairing"
|
||||
repository = "https://github.com/ebfull/pairing"
|
||||
edition ="2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1"
|
||||
ff = { path = "../ff", features = ["derive"] }
|
||||
group = { path = "../group" }
|
||||
ff = { version = "^0.5.2", path = "../ff", features = ["derive"] }
|
||||
group = { version = "0.2.0", path = "../group" }
|
||||
rand_core = "0.5"
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -27,3 +29,6 @@ rand_xorshift = "0.2"
|
||||
unstable-features = ["expose-arith"]
|
||||
expose-arith = []
|
||||
default = []
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
# 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/)
|
||||
|
||||
@@ -8,13 +18,15 @@ Bring the `pairing` crate into your project just as you normally would.
|
||||
|
||||
## Security Warnings
|
||||
|
||||
This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks.
|
||||
This library does not make any guarantees about constant-time operations, memory
|
||||
access patterns, or resistance to side-channel attacks.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
mod g1 {
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use ff::Field;
|
||||
use group::CurveProjective;
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
@@ -8,10 +10,13 @@ mod g1 {
|
||||
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(G1, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (G1::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.map(|_| (G1::random(&mut rng), Fr::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -27,10 +32,13 @@ mod g1 {
|
||||
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(G1, G1)> = (0..SAMPLES)
|
||||
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng)))
|
||||
.map(|_| (G1::random(&mut rng), G1::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -46,10 +54,13 @@ mod g1 {
|
||||
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(G1, G1Affine)> = (0..SAMPLES)
|
||||
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng).into()))
|
||||
.map(|_| (G1::random(&mut rng), G1::random(&mut rng).into()))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -63,8 +74,10 @@ mod g1 {
|
||||
}
|
||||
|
||||
mod g2 {
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use ff::Field;
|
||||
use group::CurveProjective;
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
@@ -72,10 +85,13 @@ mod g2 {
|
||||
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(G2, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (G2::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.map(|_| (G2::random(&mut rng), Fr::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -91,10 +107,13 @@ mod g2 {
|
||||
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(G2, G2)> = (0..SAMPLES)
|
||||
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng)))
|
||||
.map(|_| (G2::random(&mut rng), G2::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -110,10 +129,13 @@ mod g2 {
|
||||
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(G2, G2Affine)> = (0..SAMPLES)
|
||||
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng).into()))
|
||||
.map(|_| (G2::random(&mut rng), G2::random(&mut rng).into()))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use pairing::bls12_381::*;
|
||||
@@ -7,12 +8,15 @@ use pairing::bls12_381::*;
|
||||
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp1 = FqRepr::rand(&mut rng);
|
||||
let mut tmp2 = FqRepr::rand(&mut rng);
|
||||
let mut tmp1 = Fq::random(&mut rng).into_repr();
|
||||
let mut tmp2 = Fq::random(&mut rng).into_repr();
|
||||
// Shave a few bits off to avoid overflow.
|
||||
for _ in 0..3 {
|
||||
tmp1.div2();
|
||||
@@ -35,11 +39,14 @@ fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let tmp1 = FqRepr::rand(&mut rng);
|
||||
let tmp1 = Fq::random(&mut rng).into_repr();
|
||||
let mut tmp2 = tmp1;
|
||||
// Ensure tmp2 is smaller than tmp1.
|
||||
for _ in 0..10 {
|
||||
@@ -62,9 +69,14 @@ fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||
.map(|_| Fq::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -78,9 +90,14 @@ fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||
.map(|_| Fq::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -95,9 +112,14 @@ fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||
.map(|_| Fq::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -112,10 +134,13 @@ fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
||||
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -131,10 +156,13 @@ fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
||||
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -150,10 +178,13 @@ fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
||||
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -169,9 +200,12 @@ fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -186,9 +220,12 @@ fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -201,9 +238,12 @@ fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -218,11 +258,14 @@ fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp = Fq::rand(&mut rng);
|
||||
let mut tmp = Fq::random(&mut rng);
|
||||
tmp.square();
|
||||
tmp
|
||||
})
|
||||
@@ -239,9 +282,12 @@ fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -254,10 +300,13 @@ fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||
.map(|_| Fq::rand(&mut rng).into_repr())
|
||||
.map(|_| Fq::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use ff::Field;
|
||||
use pairing::bls12_381::*;
|
||||
@@ -7,10 +8,13 @@ use pairing::bls12_381::*;
|
||||
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
||||
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -26,10 +30,13 @@ fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
||||
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -45,10 +52,13 @@ fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
||||
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -64,9 +74,12 @@ fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -81,9 +94,12 @@ fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use ff::{Field, SqrtField};
|
||||
use pairing::bls12_381::*;
|
||||
@@ -7,10 +8,13 @@ use pairing::bls12_381::*;
|
||||
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
||||
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -26,10 +30,13 @@ fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
||||
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -45,10 +52,13 @@ fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
||||
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -64,9 +74,12 @@ fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -81,9 +94,12 @@ fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -97,9 +113,12 @@ fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use pairing::bls12_381::*;
|
||||
@@ -7,12 +8,15 @@ use pairing::bls12_381::*;
|
||||
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp1 = FrRepr::rand(&mut rng);
|
||||
let mut tmp2 = FrRepr::rand(&mut rng);
|
||||
let mut tmp1 = Fr::random(&mut rng).into_repr();
|
||||
let mut tmp2 = Fr::random(&mut rng).into_repr();
|
||||
// Shave a few bits off to avoid overflow.
|
||||
for _ in 0..3 {
|
||||
tmp1.div2();
|
||||
@@ -35,11 +39,14 @@ fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let tmp1 = FrRepr::rand(&mut rng);
|
||||
let tmp1 = Fr::random(&mut rng).into_repr();
|
||||
let mut tmp2 = tmp1;
|
||||
// Ensure tmp2 is smaller than tmp1.
|
||||
for _ in 0..10 {
|
||||
@@ -62,9 +69,14 @@ fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||
.map(|_| Fr::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -78,9 +90,14 @@ fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||
.map(|_| Fr::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -95,9 +112,14 @@ fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||
.map(|_| Fr::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -112,10 +134,13 @@ fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -131,10 +156,13 @@ fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -150,10 +178,13 @@ fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -169,9 +200,12 @@ fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -186,9 +220,12 @@ fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -201,9 +238,12 @@ fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -218,11 +258,14 @@ fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp = Fr::rand(&mut rng);
|
||||
let mut tmp = Fr::random(&mut rng);
|
||||
tmp.square();
|
||||
tmp
|
||||
})
|
||||
@@ -239,9 +282,12 @@ fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -254,10 +300,13 @@ fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||
.map(|_| Fr::rand(&mut rng).into_repr())
|
||||
.map(|_| Fr::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
@@ -4,8 +4,10 @@ mod fq12;
|
||||
mod fq2;
|
||||
mod fr;
|
||||
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use group::CurveProjective;
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::{Engine, PairingCurveAffine};
|
||||
|
||||
@@ -13,9 +15,12 @@ use pairing::{Engine, PairingCurveAffine};
|
||||
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::rand(&mut rng)).collect();
|
||||
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -29,9 +34,12 @@ fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::rand(&mut rng)).collect();
|
||||
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -45,13 +53,16 @@ fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
(
|
||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
||||
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@@ -68,13 +79,16 @@ fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<Fq12> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
(
|
||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
||||
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||
)
|
||||
})
|
||||
.map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)]))
|
||||
@@ -92,10 +106,13 @@ fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_full(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let v: Vec<(G1, G2)> = (0..SAMPLES)
|
||||
.map(|_| (G1::rand(&mut rng), G2::rand(&mut rng)))
|
||||
.map(|_| (G1::random(&mut rng), G2::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
extern crate ff;
|
||||
extern crate group;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
extern crate rand_core;
|
||||
extern crate rand_xorshift;
|
||||
extern crate test;
|
||||
|
||||
mod bls12_381;
|
||||
|
||||
@@ -18,7 +18,7 @@ macro_rules! curve_impl {
|
||||
}
|
||||
|
||||
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 {
|
||||
write!(f, "{}(Infinity)", $name)
|
||||
} else {
|
||||
@@ -35,7 +35,7 @@ macro_rules! curve_impl {
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -200,7 +200,7 @@ macro_rules! curve_impl {
|
||||
type Base = $basefield;
|
||||
type Affine = $affine;
|
||||
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
loop {
|
||||
let x = $basefield::random(rng);
|
||||
let greatest = rng.next_u32() % 2 != 0;
|
||||
@@ -622,11 +622,11 @@ macro_rules! curve_impl {
|
||||
pub mod g1 {
|
||||
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
|
||||
use super::g2::G2Affine;
|
||||
use crate::{Engine, PairingCurveAffine};
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||
use rand_core::RngCore;
|
||||
use std::fmt;
|
||||
use {Engine, PairingCurveAffine};
|
||||
|
||||
curve_impl!(
|
||||
"G1",
|
||||
@@ -656,7 +656,7 @@ pub mod g1 {
|
||||
}
|
||||
|
||||
impl fmt::Debug for G1Uncompressed {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
self.0[..].fmt(formatter)
|
||||
}
|
||||
}
|
||||
@@ -766,7 +766,7 @@ pub mod g1 {
|
||||
}
|
||||
|
||||
impl fmt::Debug for G1Compressed {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
self.0[..].fmt(formatter)
|
||||
}
|
||||
}
|
||||
@@ -934,7 +934,7 @@ pub mod g1 {
|
||||
|
||||
#[test]
|
||||
fn g1_generator() {
|
||||
use SqrtField;
|
||||
use crate::SqrtField;
|
||||
|
||||
let mut x = Fq::zero();
|
||||
let mut i = 0;
|
||||
@@ -1291,11 +1291,11 @@ pub mod g1 {
|
||||
pub mod g2 {
|
||||
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
|
||||
use super::g1::G1Affine;
|
||||
use crate::{Engine, PairingCurveAffine};
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||
use rand_core::RngCore;
|
||||
use std::fmt;
|
||||
use {Engine, PairingCurveAffine};
|
||||
|
||||
curve_impl!(
|
||||
"G2",
|
||||
@@ -1325,7 +1325,7 @@ pub mod g2 {
|
||||
}
|
||||
|
||||
impl fmt::Debug for G2Uncompressed {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
self.0[..].fmt(formatter)
|
||||
}
|
||||
}
|
||||
@@ -1451,7 +1451,7 @@ pub mod g2 {
|
||||
}
|
||||
|
||||
impl fmt::Debug for G2Compressed {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
self.0[..].fmt(formatter)
|
||||
}
|
||||
}
|
||||
@@ -1640,7 +1640,7 @@ pub mod g2 {
|
||||
|
||||
#[test]
|
||||
fn g2_generator() {
|
||||
use SqrtField;
|
||||
use crate::SqrtField;
|
||||
|
||||
let mut x = Fq2::zero();
|
||||
let mut i = 0;
|
||||
|
||||
@@ -2225,10 +2225,10 @@ fn test_fq_root_of_unity() {
|
||||
|
||||
#[test]
|
||||
fn fq_field_tests() {
|
||||
::tests::field::random_field_tests::<Fq>();
|
||||
::tests::field::random_sqrt_tests::<Fq>();
|
||||
::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
|
||||
::tests::field::from_str_tests::<Fq>();
|
||||
crate::tests::field::random_field_tests::<Fq>();
|
||||
crate::tests::field::random_sqrt_tests::<Fq>();
|
||||
crate::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
|
||||
crate::tests::field::from_str_tests::<Fq>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2244,7 +2244,7 @@ fn test_fq_ordering() {
|
||||
|
||||
#[test]
|
||||
fn fq_repr_tests() {
|
||||
::tests::repr::random_repr_tests::<Fq>();
|
||||
crate::tests::repr::random_repr_tests::<Fq>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct Fq12 {
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq12 {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ impl Fq12 {
|
||||
}
|
||||
|
||||
impl Field for Fq12 {
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
Fq12 {
|
||||
c0: Fq6::random(rng),
|
||||
c1: Fq6::random(rng),
|
||||
@@ -187,6 +187,6 @@ fn test_fq12_mul_by_014() {
|
||||
fn fq12_field_tests() {
|
||||
use ff::PrimeField;
|
||||
|
||||
::tests::field::random_field_tests::<Fq12>();
|
||||
::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
||||
crate::tests::field::random_field_tests::<Fq12>();
|
||||
crate::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct Fq2 {
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq2 {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ impl Fq2 {
|
||||
}
|
||||
|
||||
impl Field for Fq2 {
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
Fq2 {
|
||||
c0: Fq::random(rng),
|
||||
c1: Fq::random(rng),
|
||||
@@ -958,7 +958,7 @@ fn test_fq2_mul_nonresidue() {
|
||||
fn fq2_field_tests() {
|
||||
use ff::PrimeField;
|
||||
|
||||
::tests::field::random_field_tests::<Fq2>();
|
||||
::tests::field::random_sqrt_tests::<Fq2>();
|
||||
::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
||||
crate::tests::field::random_field_tests::<Fq2>();
|
||||
crate::tests::field::random_sqrt_tests::<Fq2>();
|
||||
crate::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct Fq6 {
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq6 {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ impl Fq6 {
|
||||
}
|
||||
|
||||
impl Field for Fq6 {
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
Fq6 {
|
||||
c0: Fq2::random(rng),
|
||||
c1: Fq2::random(rng),
|
||||
@@ -378,6 +378,6 @@ fn test_fq6_mul_by_01() {
|
||||
fn fq6_field_tests() {
|
||||
use ff::PrimeField;
|
||||
|
||||
::tests::field::random_field_tests::<Fq6>();
|
||||
::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
||||
crate::tests::field::random_field_tests::<Fq6>();
|
||||
crate::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
||||
}
|
||||
|
||||
@@ -1015,13 +1015,13 @@ fn test_fr_root_of_unity() {
|
||||
|
||||
#[test]
|
||||
fn fr_field_tests() {
|
||||
::tests::field::random_field_tests::<Fr>();
|
||||
::tests::field::random_sqrt_tests::<Fr>();
|
||||
::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
|
||||
::tests::field::from_str_tests::<Fr>();
|
||||
crate::tests::field::random_field_tests::<Fr>();
|
||||
crate::tests::field::random_sqrt_tests::<Fr>();
|
||||
crate::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
|
||||
crate::tests::field::from_str_tests::<Fr>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fr_repr_tests() {
|
||||
::tests::repr::random_repr_tests::<Fr>();
|
||||
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 fq;
|
||||
mod fq12;
|
||||
@@ -366,5 +369,5 @@ impl G2Prepared {
|
||||
|
||||
#[test]
|
||||
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 super::*;
|
||||
use *;
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn test_pairing_result_against_relic() {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! A library for working with pairing-friendly curves.
|
||||
|
||||
// `clippy` is a code linting tool for improving code quality by catching
|
||||
// common mistakes or strange code patterns. If the `cargo-clippy` feature
|
||||
// is provided, all compiler warnings are prohibited.
|
||||
@@ -8,17 +10,11 @@
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))]
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
// Force public structures to implement Debug
|
||||
#![deny(missing_debug_implementations)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate ff;
|
||||
extern crate group;
|
||||
extern crate rand_core;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand_xorshift;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
||||
@@ -87,7 +83,7 @@ pub trait Engine: ScalarEngine {
|
||||
>;
|
||||
|
||||
/// Perform final exponentiation of the result of a miller loop.
|
||||
fn final_exponentiation(&Self::Fqk) -> Option<Self::Fqk>;
|
||||
fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk>;
|
||||
|
||||
/// Performs a complete pairing operation `(p, q)`.
|
||||
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
|
||||
|
||||
@@ -2,7 +2,7 @@ use group::{CurveAffine, CurveProjective};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use {Engine, Field, PairingCurveAffine, PrimeField};
|
||||
use crate::{Engine, Field, PairingCurveAffine, PrimeField};
|
||||
|
||||
pub fn engine_tests<E: Engine>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
|
||||
2
zcash_client_backend/.gitignore
vendored
Normal file
2
zcash_client_backend/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Protobufs
|
||||
src/proto/
|
||||
@@ -1,16 +1,31 @@
|
||||
[package]
|
||||
name = "zcash_client_backend"
|
||||
version = "0.0.0"
|
||||
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"
|
||||
pairing = { path = "../pairing" }
|
||||
zcash_primitives = { path = "../zcash_primitives" }
|
||||
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_client_backend
|
||||
|
||||
This library contains Rust structs and traits for creating shielded Zcash light clients.
|
||||
This library contains Rust structs and traits for creating shielded Zcash light
|
||||
clients.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Zcash global and per-network constants.
|
||||
|
||||
pub mod mainnet;
|
||||
pub mod regtest;
|
||||
pub mod testnet;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 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
|
||||
@@ -23,6 +25,6 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews";
|
||||
///
|
||||
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
|
||||
///
|
||||
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress
|
||||
/// [`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];
|
||||
@@ -1,3 +1,5 @@
|
||||
//! 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
|
||||
@@ -23,6 +25,6 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling";
|
||||
///
|
||||
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
|
||||
///
|
||||
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress
|
||||
/// [`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
|
||||
}
|
||||
@@ -2,15 +2,14 @@
|
||||
//!
|
||||
//! 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::{
|
||||
jubjub::edwards,
|
||||
primitives::{Diversifier, PaymentAddress},
|
||||
};
|
||||
use zcash_primitives::{
|
||||
primitives::PaymentAddress,
|
||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||
JUBJUB,
|
||||
};
|
||||
@@ -113,10 +112,11 @@ pub fn decode_extended_full_viewing_key(
|
||||
/// 0xbc, 0xe5,
|
||||
/// ]);
|
||||
///
|
||||
/// let pa = PaymentAddress {
|
||||
/// diversifier: Diversifier([0u8; 11]),
|
||||
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
/// };
|
||||
/// 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),
|
||||
@@ -124,10 +124,7 @@ pub fn decode_extended_full_viewing_key(
|
||||
/// );
|
||||
/// ```
|
||||
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
|
||||
bech32_encode(hrp, |w| {
|
||||
w.write_all(&addr.diversifier.0)?;
|
||||
addr.pk_d.write(w)
|
||||
})
|
||||
bech32_encode(hrp, |w| w.write_all(&addr.to_bytes()))
|
||||
}
|
||||
|
||||
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
|
||||
@@ -153,10 +150,11 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
|
||||
/// 0xbc, 0xe5,
|
||||
/// ]);
|
||||
///
|
||||
/// let pa = PaymentAddress {
|
||||
/// diversifier: Diversifier([0u8; 11]),
|
||||
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
/// };
|
||||
/// let pa = PaymentAddress::from_parts(
|
||||
/// Diversifier([0u8; 11]),
|
||||
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
/// )
|
||||
/// .unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// decode_payment_address(
|
||||
@@ -168,17 +166,13 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
|
||||
/// ```
|
||||
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
|
||||
bech32_decode(hrp, s, |data| {
|
||||
let mut diversifier = Diversifier([0; 11]);
|
||||
diversifier.0.copy_from_slice(&data[0..11]);
|
||||
// Check that the diversifier is valid
|
||||
if diversifier.g_d::<Bls12>(&JUBJUB).is_none() {
|
||||
if data.len() != 43 {
|
||||
return None;
|
||||
}
|
||||
|
||||
edwards::Point::<Bls12, _>::read(&data[11..], &JUBJUB)
|
||||
.ok()?
|
||||
.as_prime_order(&JUBJUB)
|
||||
.map(|pk_d| PaymentAddress { pk_d, diversifier })
|
||||
let mut bytes = [0; 43];
|
||||
bytes.copy_from_slice(&data);
|
||||
PaymentAddress::<Bls12>::from_bytes(&bytes, &JUBJUB)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -191,11 +185,95 @@ mod tests {
|
||||
use zcash_primitives::{
|
||||
jubjub::edwards,
|
||||
primitives::{Diversifier, PaymentAddress},
|
||||
zip32::ExtendedSpendingKey,
|
||||
};
|
||||
|
||||
use super::{decode_payment_address, encode_payment_address};
|
||||
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([
|
||||
@@ -203,10 +281,11 @@ mod tests {
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let addr = PaymentAddress {
|
||||
diversifier: Diversifier([0u8; 11]),
|
||||
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
};
|
||||
let addr = PaymentAddress::from_parts(
|
||||
Diversifier([0u8; 11]),
|
||||
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let encoded_main =
|
||||
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
|
||||
@@ -247,10 +326,11 @@ mod tests {
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let addr = PaymentAddress {
|
||||
diversifier: Diversifier([1u8; 11]),
|
||||
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
};
|
||||
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);
|
||||
|
||||
@@ -5,6 +5,10 @@ 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
|
||||
///
|
||||
/// ```
|
||||
@@ -13,6 +17,10 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey};
|
||||
/// 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),
|
||||
&[
|
||||
@@ -22,3 +30,14 @@ pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendi
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
//! `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);
|
||||
}
|
||||
}
|
||||
4
zcash_history/.gitignore
vendored
Normal file
4
zcash_history/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
.idea
|
||||
14
zcash_history/COPYRIGHT
Normal file
14
zcash_history/COPYRIGHT
Normal file
@@ -0,0 +1,14 @@
|
||||
Copyrights in the "zcash_history" library are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the "zcash_history" library.
|
||||
|
||||
The "zcash_history" 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.
|
||||
17
zcash_history/Cargo.toml
Normal file
17
zcash_history/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "zcash_history"
|
||||
version = "0.0.1"
|
||||
authors = ["NikVolf <nikvolf@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT/Apache-2.0"
|
||||
documentation = "https://docs.rs/zcash_history/"
|
||||
description = "Library for Zcash blockchain history tools"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.3.0"
|
||||
quickcheck = "0.8"
|
||||
|
||||
[dependencies]
|
||||
bigint = "4"
|
||||
byteorder = "1"
|
||||
blake2 = { package = "blake2b_simd", version = "0.5" }
|
||||
201
zcash_history/LICENSE-APACHE
Normal file
201
zcash_history/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
25
zcash_history/LICENSE-MIT
Normal file
25
zcash_history/LICENSE-MIT
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2019 Nikolay Volf
|
||||
|
||||
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.
|
||||
26
zcash_history/README.md
Normal file
26
zcash_history/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# zcash_history
|
||||
|
||||
Special implementation of Merkle mountain ranges (MMR) for Zcash!
|
||||
|
||||
[](https://travis-ci.org/NikVolf/zcash-mmr)
|
||||
|
||||
The main design goals of this MMR implementation are
|
||||
|
||||
- Allow zero-cache and avoid db callbacks. As it is implemented, calling side must just smartly pre-load MMR nodes from the database (about log2(tree length) for append, twice as much for deletion).
|
||||
|
||||
- Reuse as much logic between rust and c++ clients and place it here and librustzcash.
|
||||
|
||||
- Close to zero memory consumption.
|
||||
|
||||
# License
|
||||
|
||||
`zcash_history` is distributed under the terms of both the MIT
|
||||
license and the Apache License (Version 2.0), at your choice.
|
||||
|
||||
See LICENSE-APACHE, and LICENSE-MIT for details.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in `zcash_history` by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
2
zcash_history/examples/lib/main.rs
Normal file
2
zcash_history/examples/lib/main.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
// dummy example
|
||||
pub fn main() {}
|
||||
86
zcash_history/examples/lib/shared.rs
Normal file
86
zcash_history/examples/lib/shared.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use zcash_history::{Entry, EntryLink, NodeData, Tree};
|
||||
|
||||
pub struct NodeDataIterator {
|
||||
return_stack: Vec<NodeData>,
|
||||
tree: Tree,
|
||||
cursor: usize,
|
||||
leaf_cursor: usize,
|
||||
}
|
||||
|
||||
impl Iterator for NodeDataIterator {
|
||||
type Item = NodeData;
|
||||
|
||||
fn next(&mut self) -> Option<NodeData> {
|
||||
let result = if self.cursor == 1 {
|
||||
self.leaf_cursor = 2;
|
||||
Some(leaf(1))
|
||||
} else if self.cursor == 2 {
|
||||
self.leaf_cursor = 3;
|
||||
Some(leaf(2))
|
||||
} else if self.cursor == 3 {
|
||||
Some(self.tree.root_node().expect("always exists").data().clone())
|
||||
} else if self.return_stack.len() > 0 {
|
||||
self.return_stack.pop()
|
||||
} else {
|
||||
for n_append in self
|
||||
.tree
|
||||
.append_leaf(leaf(self.leaf_cursor as u32))
|
||||
.expect("full tree cannot fail")
|
||||
.into_iter()
|
||||
.rev()
|
||||
{
|
||||
self.return_stack.push(
|
||||
self.tree
|
||||
.resolve_link(n_append)
|
||||
.expect("just pushed")
|
||||
.data()
|
||||
.clone(),
|
||||
)
|
||||
}
|
||||
self.leaf_cursor += 1;
|
||||
self.return_stack.pop()
|
||||
};
|
||||
|
||||
self.cursor += 1;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeDataIterator {
|
||||
pub fn new() -> Self {
|
||||
let root = Entry::new(
|
||||
NodeData::combine(&leaf(1), &leaf(2)),
|
||||
EntryLink::Stored(0),
|
||||
EntryLink::Stored(1),
|
||||
);
|
||||
let tree = Tree::new(
|
||||
3,
|
||||
vec![(2, root)],
|
||||
vec![(0, leaf(1).into()), (1, leaf(2).into())],
|
||||
);
|
||||
|
||||
NodeDataIterator {
|
||||
return_stack: Vec::new(),
|
||||
tree,
|
||||
cursor: 1,
|
||||
leaf_cursor: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn leaf(height: u32) -> NodeData {
|
||||
NodeData {
|
||||
consensus_branch_id: 0,
|
||||
subtree_commitment: [0u8; 32],
|
||||
start_time: height * 10 + 1,
|
||||
end_time: (height + 1) * 10,
|
||||
start_target: 100 + height * 10,
|
||||
end_target: 100 + (height + 1) * 10,
|
||||
start_sapling_root: [0u8; 32],
|
||||
end_sapling_root: [0u8; 32],
|
||||
subtree_total_work: 0.into(),
|
||||
start_height: height as u64,
|
||||
end_height: height as u64,
|
||||
sapling_tx: 5 + height as u64,
|
||||
}
|
||||
}
|
||||
113
zcash_history/examples/long.rs
Normal file
113
zcash_history/examples/long.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use zcash_history::{Entry, EntryLink, NodeData, Tree};
|
||||
|
||||
#[path = "lib/shared.rs"]
|
||||
mod share;
|
||||
|
||||
fn draft(into: &mut Vec<(u32, Entry)>, vec: &Vec<NodeData>, peak_pos: usize, h: u32) {
|
||||
let node_data = vec[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),
|
||||
),
|
||||
};
|
||||
|
||||
println!("Entry #{}: {}", into.len(), peak);
|
||||
|
||||
into.push(((peak_pos - 1) as u32, peak));
|
||||
}
|
||||
|
||||
fn prepare_tree(vec: &Vec<NodeData>) -> Tree {
|
||||
assert!(vec.len() > 0);
|
||||
|
||||
// integer log2 of (vec.len()+1), -1
|
||||
let mut h = (32 - ((vec.len() + 1) as u32).leading_zeros() - 1) - 1;
|
||||
let mut peak_pos = (1 << (h + 1)) - 1;
|
||||
let mut nodes = Vec::new();
|
||||
|
||||
// used later
|
||||
let mut last_peak_pos = 0;
|
||||
let mut last_peak_h = 0;
|
||||
|
||||
loop {
|
||||
if peak_pos > vec.len() {
|
||||
// left child, -2^h
|
||||
peak_pos = peak_pos - (1 << h);
|
||||
h = h - 1;
|
||||
}
|
||||
|
||||
if peak_pos <= vec.len() {
|
||||
draft(&mut nodes, vec, peak_pos, h);
|
||||
|
||||
// save to be used in next loop
|
||||
last_peak_pos = peak_pos;
|
||||
last_peak_h = h;
|
||||
|
||||
// right sibling
|
||||
peak_pos = 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 = h - 1;
|
||||
|
||||
// drafting left child
|
||||
draft(&mut extra, vec, left_pos, h);
|
||||
|
||||
// drafting right child
|
||||
draft(&mut extra, vec, right_pos, h);
|
||||
|
||||
// continuing on right slope
|
||||
peak_pos = right_pos;
|
||||
}
|
||||
|
||||
println!("Total extra of {} required for deletion!", extra.len());
|
||||
|
||||
Tree::new(vec.len() as u32, nodes, extra)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let number = match std::env::args().skip(1).next() {
|
||||
None => {
|
||||
eprintln!("writer <number of nodes> [<out_file>]");
|
||||
std::process::exit(1);
|
||||
}
|
||||
Some(number) => number.parse::<usize>().expect("invalid number"),
|
||||
};
|
||||
|
||||
let long_vec = share::NodeDataIterator::new()
|
||||
.take(number)
|
||||
.collect::<Vec<NodeData>>();
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
let tree = prepare_tree(&long_vec);
|
||||
let elapsed = now.elapsed();
|
||||
|
||||
println!(
|
||||
"Tree final root: {}-{}",
|
||||
tree.root_node().expect("root").data().start_height,
|
||||
tree.root_node().expect("root").data().end_height,
|
||||
);
|
||||
|
||||
println!(
|
||||
"Prepare tree of {} length: {} ns / {} mcs / {} ms",
|
||||
number,
|
||||
elapsed.as_nanos(),
|
||||
elapsed.as_micros(),
|
||||
elapsed.as_millis()
|
||||
);
|
||||
}
|
||||
44
zcash_history/examples/write.rs
Normal file
44
zcash_history/examples/write.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
#[path = "lib/shared.rs"]
|
||||
mod share;
|
||||
|
||||
// Test data generator
|
||||
// $ cargo run --example writer -- 16 nodes.dat
|
||||
// or
|
||||
// $ cargo run --example writer -- 16
|
||||
// to preview
|
||||
|
||||
fn main() {
|
||||
let mut args = std::env::args().skip(1);
|
||||
|
||||
let (number, out_file) = match args.next() {
|
||||
None => {
|
||||
eprintln!("writer <number of nodes> [<out_file>]");
|
||||
std::process::exit(1);
|
||||
}
|
||||
Some(number) => (
|
||||
number.parse::<usize>().expect("invalid number"),
|
||||
args.next(),
|
||||
),
|
||||
};
|
||||
|
||||
let iterator = share::NodeDataIterator::new().take(number);
|
||||
|
||||
if let Some(out_file_path) = out_file {
|
||||
use std::io::Write;
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
for node in iterator {
|
||||
node.write(&mut buf).expect("Failed to write data");
|
||||
}
|
||||
|
||||
let mut file = std::fs::File::create(&out_file_path).expect("Failed to create output file");
|
||||
|
||||
file.write_all(&buf[..])
|
||||
.expect("Failed to write data to file");
|
||||
} else {
|
||||
for n in iterator {
|
||||
println!("{:?}", n);
|
||||
}
|
||||
}
|
||||
}
|
||||
123
zcash_history/src/entry.rs
Normal file
123
zcash_history/src/entry.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use crate::{EntryKind, EntryLink, Error, NodeData, MAX_NODE_DATA_SIZE};
|
||||
|
||||
/// Max serialized length of entry data.
|
||||
pub const MAX_ENTRY_SIZE: usize = MAX_NODE_DATA_SIZE + 9;
|
||||
|
||||
/// MMR Entry.
|
||||
#[derive(Debug)]
|
||||
pub struct Entry {
|
||||
pub(crate) kind: EntryKind,
|
||||
pub(crate) data: NodeData,
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
/// New entry of type node.
|
||||
pub fn new(data: NodeData, left: EntryLink, right: EntryLink) -> Self {
|
||||
Entry {
|
||||
kind: EntryKind::Node(left, right),
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns if is this node complete (has total of 2^N leaves)
|
||||
pub fn complete(&self) -> bool {
|
||||
let leaves = self.leaf_count();
|
||||
leaves & (leaves - 1) == 0
|
||||
}
|
||||
|
||||
/// Number of leaves under this node.
|
||||
pub fn leaf_count(&self) -> u64 {
|
||||
self.data.end_height - (self.data.start_height - 1)
|
||||
}
|
||||
|
||||
/// Is this node a leaf.
|
||||
pub fn leaf(&self) -> bool {
|
||||
if let EntryKind::Leaf = self.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Left child
|
||||
pub fn left(&self) -> Result<EntryLink, Error> {
|
||||
match self.kind {
|
||||
EntryKind::Leaf => Err(Error::node_expected()),
|
||||
EntryKind::Node(left, _) => Ok(left),
|
||||
}
|
||||
}
|
||||
|
||||
/// Right child.
|
||||
pub fn right(&self) -> Result<EntryLink, Error> {
|
||||
match self.kind {
|
||||
EntryKind::Leaf => Err(Error::node_expected()),
|
||||
EntryKind::Node(_, right) => Ok(right),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read from byte representation.
|
||||
pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> {
|
||||
let kind = {
|
||||
match r.read_u8()? {
|
||||
0 => {
|
||||
let left = r.read_u32::<LittleEndian>()?;
|
||||
let right = r.read_u32::<LittleEndian>()?;
|
||||
EntryKind::Node(EntryLink::Stored(left), EntryLink::Stored(right))
|
||||
}
|
||||
1 => EntryKind::Leaf,
|
||||
_ => return Err(std::io::Error::from(std::io::ErrorKind::InvalidData)),
|
||||
}
|
||||
};
|
||||
|
||||
let data = NodeData::read(consensus_branch_id, r)?;
|
||||
|
||||
Ok(Entry { kind, data })
|
||||
}
|
||||
|
||||
/// Write to byte representation.
|
||||
pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||
match self.kind {
|
||||
EntryKind::Node(EntryLink::Stored(left), EntryLink::Stored(right)) => {
|
||||
w.write_u8(0)?;
|
||||
w.write_u32::<LittleEndian>(left)?;
|
||||
w.write_u32::<LittleEndian>(right)?;
|
||||
}
|
||||
EntryKind::Leaf => {
|
||||
w.write_u8(1)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(std::io::Error::from(std::io::ErrorKind::InvalidData));
|
||||
}
|
||||
}
|
||||
|
||||
self.data.write(w)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Convert from byte representation.
|
||||
pub fn from_bytes<T: AsRef<[u8]>>(consensus_branch_id: u32, buf: T) -> std::io::Result<Self> {
|
||||
let mut cursor = std::io::Cursor::new(buf);
|
||||
Self::read(consensus_branch_id, &mut cursor)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NodeData> for Entry {
|
||||
fn from(s: NodeData) -> Self {
|
||||
Entry {
|
||||
kind: EntryKind::Leaf,
|
||||
data: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Entry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.kind {
|
||||
EntryKind::Node(l, r) => write!(f, "node({}, {}, ..)", l, r),
|
||||
EntryKind::Leaf => write!(f, "leaf(..)"),
|
||||
}
|
||||
}
|
||||
}
|
||||
79
zcash_history/src/lib.rs
Normal file
79
zcash_history/src/lib.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
//! Chain history library for Zcash
|
||||
//!
|
||||
//! To be used in zebra and via FFI bindings in zcashd
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod entry;
|
||||
mod node_data;
|
||||
mod tree;
|
||||
|
||||
pub use entry::{Entry, MAX_ENTRY_SIZE};
|
||||
pub use node_data::{NodeData, MAX_NODE_DATA_SIZE};
|
||||
pub use tree::Tree;
|
||||
|
||||
/// Crate-level error type
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Entry expected to be presented in the tree view while it was not.
|
||||
ExpectedInMemory(EntryLink),
|
||||
/// Entry expected to be a node (specifying for which link this is not true).
|
||||
ExpectedNode(Option<EntryLink>),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match *self {
|
||||
Self::ExpectedInMemory(l) => write!(f, "Node/leaf expected to be in memory: {}", l),
|
||||
Self::ExpectedNode(None) => write!(f, "Node expected"),
|
||||
Self::ExpectedNode(Some(l)) => write!(f, "Node expected, not leaf: {}", l),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference to to the tree node.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum EntryLink {
|
||||
/// Reference to the stored (in the array representation) leaf/node.
|
||||
Stored(u32),
|
||||
/// Reference to the generated leaf/node.
|
||||
Generated(u32),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EntryLink {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match *self {
|
||||
Self::Stored(v) => write!(f, "stored({})", v),
|
||||
Self::Generated(v) => write!(f, "generated({})", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// MMR Node. It is leaf when `left`, `right` are `None` and node when they are not.
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub enum EntryKind {
|
||||
/// Leaf entry.
|
||||
Leaf,
|
||||
/// Node entry with children links.
|
||||
Node(EntryLink, EntryLink),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Entry expected to be a node (specifying for which link this is not true).
|
||||
pub fn link_node_expected(link: EntryLink) -> Self {
|
||||
Self::ExpectedNode(Some(link))
|
||||
}
|
||||
|
||||
/// Some entry is expected to be node
|
||||
pub fn node_expected() -> Self {
|
||||
Self::ExpectedNode(None)
|
||||
}
|
||||
|
||||
pub(crate) fn augment(self, link: EntryLink) -> Self {
|
||||
match self {
|
||||
Error::ExpectedNode(_) => Error::ExpectedNode(Some(link)),
|
||||
val => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
238
zcash_history/src/node_data.rs
Normal file
238
zcash_history/src/node_data.rs
Normal file
@@ -0,0 +1,238 @@
|
||||
use bigint::U256;
|
||||
use blake2::Params as Blake2Params;
|
||||
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
/// Maximum serialized size of the node metadata.
|
||||
pub const MAX_NODE_DATA_SIZE: usize = 32 + // subtree commitment
|
||||
4 + // start time
|
||||
4 + // end time
|
||||
4 + // start target
|
||||
4 + // end target
|
||||
32 + // start sapling tree root
|
||||
32 + // end sapling tree root
|
||||
32 + // subtree total work
|
||||
9 + // start height (compact uint)
|
||||
9 + // end height (compact uint)
|
||||
9; // Sapling tx count (compact uint)
|
||||
// = total of 171
|
||||
|
||||
/// Node metadata.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct NodeData {
|
||||
/// Consensus branch id, should be provided by deserializing node.
|
||||
pub consensus_branch_id: u32,
|
||||
/// Subtree commitment - either block hash for leaves or hashsum of children for nodes.
|
||||
pub subtree_commitment: [u8; 32],
|
||||
/// Start time.
|
||||
pub start_time: u32,
|
||||
/// End time.
|
||||
pub end_time: u32,
|
||||
/// Start target.
|
||||
pub start_target: u32,
|
||||
/// End target.
|
||||
pub end_target: u32,
|
||||
/// Start sapling tree root.
|
||||
pub start_sapling_root: [u8; 32],
|
||||
/// End sapling tree root.
|
||||
pub end_sapling_root: [u8; 32],
|
||||
/// Part of tree total work.
|
||||
pub subtree_total_work: U256,
|
||||
/// Start height.
|
||||
pub start_height: u64,
|
||||
/// End height
|
||||
pub end_height: u64,
|
||||
/// Number of Sapling transactions.
|
||||
pub sapling_tx: u64,
|
||||
}
|
||||
|
||||
fn blake2b_personal(personalization: &[u8], input: &[u8]) -> [u8; 32] {
|
||||
let hash_result = Blake2Params::new()
|
||||
.hash_length(32)
|
||||
.personal(personalization)
|
||||
.to_state()
|
||||
.update(input)
|
||||
.finalize();
|
||||
let mut result = [0u8; 32];
|
||||
result.copy_from_slice(hash_result.as_bytes());
|
||||
result
|
||||
}
|
||||
|
||||
fn personalization(branch_id: u32) -> [u8; 16] {
|
||||
let mut result = [0u8; 16];
|
||||
result[..12].copy_from_slice(b"ZcashHistory");
|
||||
LittleEndian::write_u32(&mut result[12..], branch_id);
|
||||
result
|
||||
}
|
||||
|
||||
impl NodeData {
|
||||
/// Combine two nodes metadata.
|
||||
pub fn combine(left: &NodeData, right: &NodeData) -> NodeData {
|
||||
assert_eq!(left.consensus_branch_id, right.consensus_branch_id);
|
||||
|
||||
let mut hash_buf = [0u8; MAX_NODE_DATA_SIZE * 2];
|
||||
let size = {
|
||||
let mut cursor = ::std::io::Cursor::new(&mut hash_buf[..]);
|
||||
left.write(&mut cursor)
|
||||
.expect("Writing to memory buf with enough length cannot fail; qed");
|
||||
right
|
||||
.write(&mut cursor)
|
||||
.expect("Writing to memory buf with enough length cannot fail; qed");
|
||||
cursor.position() as usize
|
||||
};
|
||||
|
||||
let hash = blake2b_personal(
|
||||
&personalization(left.consensus_branch_id),
|
||||
&hash_buf[..size],
|
||||
);
|
||||
|
||||
NodeData {
|
||||
consensus_branch_id: left.consensus_branch_id,
|
||||
subtree_commitment: hash,
|
||||
start_time: left.start_time,
|
||||
end_time: right.end_time,
|
||||
start_target: left.start_target,
|
||||
end_target: right.end_target,
|
||||
start_sapling_root: left.start_sapling_root,
|
||||
end_sapling_root: right.end_sapling_root,
|
||||
subtree_total_work: left.subtree_total_work + right.subtree_total_work,
|
||||
start_height: left.start_height,
|
||||
end_height: right.end_height,
|
||||
sapling_tx: left.sapling_tx + right.sapling_tx,
|
||||
}
|
||||
}
|
||||
|
||||
fn write_compact<W: std::io::Write>(w: &mut W, compact: u64) -> std::io::Result<()> {
|
||||
match compact {
|
||||
0..=0xfc => w.write_all(&[compact as u8])?,
|
||||
0xfd..=0xffff => {
|
||||
w.write_all(&[0xfd])?;
|
||||
w.write_u16::<LittleEndian>(compact as u16)?;
|
||||
}
|
||||
0x10000..=0xffff_ffff => {
|
||||
w.write_all(&[0xfe])?;
|
||||
w.write_u32::<LittleEndian>(compact as u32)?;
|
||||
}
|
||||
_ => {
|
||||
w.write_all(&[0xff])?;
|
||||
w.write_u64::<LittleEndian>(compact)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_compact<R: std::io::Read>(reader: &mut R) -> std::io::Result<u64> {
|
||||
let result = match reader.read_u8()? {
|
||||
i @ 0..=0xfc => i.into(),
|
||||
0xfd => reader.read_u16::<LittleEndian>()?.into(),
|
||||
0xfe => reader.read_u32::<LittleEndian>()?.into(),
|
||||
_ => reader.read_u64::<LittleEndian>()?,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Write to the byte representation.
|
||||
pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||
w.write_all(&self.subtree_commitment)?;
|
||||
w.write_u32::<LittleEndian>(self.start_time)?;
|
||||
w.write_u32::<LittleEndian>(self.end_time)?;
|
||||
w.write_u32::<LittleEndian>(self.start_target)?;
|
||||
w.write_u32::<LittleEndian>(self.end_target)?;
|
||||
w.write_all(&self.start_sapling_root)?;
|
||||
w.write_all(&self.end_sapling_root)?;
|
||||
|
||||
let mut work_buf = [0u8; 32];
|
||||
self.subtree_total_work.to_little_endian(&mut work_buf[..]);
|
||||
w.write_all(&work_buf)?;
|
||||
|
||||
Self::write_compact(w, self.start_height)?;
|
||||
Self::write_compact(w, self.end_height)?;
|
||||
Self::write_compact(w, self.sapling_tx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read from the byte representation.
|
||||
pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> {
|
||||
let mut data = Self::default();
|
||||
data.consensus_branch_id = consensus_branch_id;
|
||||
r.read_exact(&mut data.subtree_commitment)?;
|
||||
data.start_time = r.read_u32::<LittleEndian>()?;
|
||||
data.end_time = r.read_u32::<LittleEndian>()?;
|
||||
data.start_target = r.read_u32::<LittleEndian>()?;
|
||||
data.end_target = r.read_u32::<LittleEndian>()?;
|
||||
r.read_exact(&mut data.start_sapling_root)?;
|
||||
r.read_exact(&mut data.end_sapling_root)?;
|
||||
|
||||
let mut work_buf = [0u8; 32];
|
||||
r.read_exact(&mut work_buf)?;
|
||||
data.subtree_total_work = U256::from_little_endian(&work_buf);
|
||||
|
||||
data.start_height = Self::read_compact(r)?;
|
||||
data.end_height = Self::read_compact(r)?;
|
||||
data.sapling_tx = Self::read_compact(r)?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// Convert to byte representation.
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut buf = [0u8; MAX_NODE_DATA_SIZE];
|
||||
let pos = {
|
||||
let mut cursor = std::io::Cursor::new(&mut buf[..]);
|
||||
self.write(&mut cursor).expect("Cursor cannot fail");
|
||||
cursor.position() as usize
|
||||
};
|
||||
|
||||
buf[0..pos].to_vec()
|
||||
}
|
||||
|
||||
/// Convert from byte representation.
|
||||
pub fn from_bytes<T: AsRef<[u8]>>(consensus_branch_id: u32, buf: T) -> std::io::Result<Self> {
|
||||
let mut cursor = std::io::Cursor::new(buf);
|
||||
Self::read(consensus_branch_id, &mut cursor)
|
||||
}
|
||||
|
||||
/// Hash node metadata
|
||||
pub fn hash(&self) -> [u8; 32] {
|
||||
let bytes = self.to_bytes();
|
||||
|
||||
blake2b_personal(&personalization(self.consensus_branch_id), &bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl quickcheck::Arbitrary for NodeData {
|
||||
fn arbitrary<G: quickcheck::Gen>(gen: &mut G) -> Self {
|
||||
let mut node_data = NodeData::default();
|
||||
node_data.consensus_branch_id = 0;
|
||||
gen.fill_bytes(&mut node_data.subtree_commitment[..]);
|
||||
node_data.start_time = gen.next_u32();
|
||||
node_data.end_time = gen.next_u32();
|
||||
node_data.start_target = gen.next_u32();
|
||||
node_data.end_target = gen.next_u32();
|
||||
gen.fill_bytes(&mut node_data.start_sapling_root[..]);
|
||||
gen.fill_bytes(&mut node_data.end_sapling_root[..]);
|
||||
let mut number = [0u8; 32];
|
||||
gen.fill_bytes(&mut number[..]);
|
||||
node_data.subtree_total_work = U256::from_little_endian(&number[..]);
|
||||
node_data.start_height = gen.next_u64();
|
||||
node_data.end_height = gen.next_u64();
|
||||
node_data.sapling_tx = gen.next_u64();
|
||||
|
||||
node_data
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::NodeData;
|
||||
use quickcheck::{quickcheck, TestResult};
|
||||
|
||||
quickcheck! {
|
||||
fn serialization_round_trip(node_data: NodeData) -> TestResult {
|
||||
TestResult::from_bool(NodeData::from_bytes(0, &node_data.to_bytes()).unwrap() == node_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
741
zcash_history/src/tree.rs
Normal file
741
zcash_history/src/tree.rs
Normal file
@@ -0,0 +1,741 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{Entry, EntryKind, EntryLink, Error, NodeData};
|
||||
|
||||
/// Represents partially loaded tree.
|
||||
///
|
||||
/// Some kind of "view" into the array representation of the MMR tree.
|
||||
/// With only some of the leaves/nodes pre-loaded / pre-generated.
|
||||
/// Exact amount of the loaded data can be calculated by the constructing party,
|
||||
/// depending on the length of the tree and maximum amount of operations that are going
|
||||
/// to happen after construction. `Tree` should not be used as self-contained data structure,
|
||||
/// since it's internal state can grow indefinitely after serial operations.
|
||||
/// Intended use of this `Tree` is to instantiate it based on partially loaded data (see example
|
||||
/// how to pick right nodes from the array representation of MMR Tree), perform several operations
|
||||
/// (append-s/delete-s) and then drop it.
|
||||
pub struct Tree {
|
||||
stored: HashMap<u32, Entry>,
|
||||
|
||||
// This can grow indefinitely if `Tree` is misused as a self-contained data structure
|
||||
generated: Vec<Entry>,
|
||||
|
||||
// number of persistent(!) tree entries
|
||||
stored_count: u32,
|
||||
|
||||
root: EntryLink,
|
||||
}
|
||||
|
||||
impl Tree {
|
||||
/// Resolve link originated from this tree
|
||||
pub fn resolve_link(&self, link: EntryLink) -> Result<IndexedNode, Error> {
|
||||
match link {
|
||||
EntryLink::Generated(index) => self.generated.get(index as usize),
|
||||
EntryLink::Stored(index) => self.stored.get(&index),
|
||||
}
|
||||
.map(|node| IndexedNode { node, link })
|
||||
.ok_or(Error::ExpectedInMemory(link))
|
||||
}
|
||||
|
||||
fn push(&mut self, data: Entry) -> EntryLink {
|
||||
let idx = self.stored_count;
|
||||
self.stored_count += 1;
|
||||
self.stored.insert(idx, data);
|
||||
EntryLink::Stored(idx)
|
||||
}
|
||||
|
||||
fn push_generated(&mut self, data: Entry) -> EntryLink {
|
||||
self.generated.push(data);
|
||||
EntryLink::Generated(self.generated.len() as u32 - 1)
|
||||
}
|
||||
|
||||
/// Populate tree with plain list of the leaves/nodes. For now, only for tests,
|
||||
/// since this `Tree` structure is for partially loaded tree (but it might change)
|
||||
#[cfg(test)]
|
||||
pub fn populate(loaded: Vec<Entry>, root: EntryLink) -> Self {
|
||||
let mut result = Tree::invalid();
|
||||
result.stored_count = loaded.len() as u32;
|
||||
for (idx, item) in loaded.into_iter().enumerate() {
|
||||
result.stored.insert(idx as u32, item);
|
||||
}
|
||||
result.root = root;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// Empty tree with invalid root
|
||||
fn invalid() -> Self {
|
||||
Tree {
|
||||
root: EntryLink::Generated(0),
|
||||
generated: Default::default(),
|
||||
stored: Default::default(),
|
||||
stored_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// New view into the the tree array representation
|
||||
///
|
||||
/// `length` is total length of the array representation (is generally not a sum of
|
||||
/// peaks.len + extra.len)
|
||||
/// `peaks` is peaks of the mmr tree
|
||||
/// `extra` is some extra nodes that calculated to be required during next one or more
|
||||
/// operations on the tree.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if `peaks` is empty.
|
||||
pub fn new(length: u32, peaks: Vec<(u32, Entry)>, extra: Vec<(u32, Entry)>) -> Self {
|
||||
assert!(peaks.len() > 0);
|
||||
|
||||
let mut result = Tree::invalid();
|
||||
|
||||
result.stored_count = length;
|
||||
|
||||
let mut gen = 0;
|
||||
let mut root = EntryLink::Stored(peaks[0].0);
|
||||
for (idx, node) in peaks.into_iter() {
|
||||
result.stored.insert(idx, node);
|
||||
if gen != 0 {
|
||||
let next_generated = combine_nodes(
|
||||
result
|
||||
.resolve_link(root)
|
||||
.expect("Inserted before, cannot fail; qed"),
|
||||
result
|
||||
.resolve_link(EntryLink::Stored(idx))
|
||||
.expect("Inserted before, cannot fail; qed"),
|
||||
);
|
||||
root = result.push_generated(next_generated);
|
||||
}
|
||||
gen += 1;
|
||||
}
|
||||
|
||||
for (idx, node) in extra {
|
||||
result.stored.insert(idx, node);
|
||||
}
|
||||
|
||||
result.root = root;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn get_peaks(&self, root: EntryLink, target: &mut Vec<EntryLink>) -> Result<(), Error> {
|
||||
let (left_child_link, right_child_link) = {
|
||||
let root = self.resolve_link(root)?;
|
||||
if root.node.complete() {
|
||||
target.push(root.link);
|
||||
return Ok(());
|
||||
}
|
||||
(root.left()?, root.right()?)
|
||||
};
|
||||
|
||||
self.get_peaks(left_child_link, target)?;
|
||||
self.get_peaks(right_child_link, target)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Append one leaf to the tree.
|
||||
///
|
||||
/// Returns links to actual nodes that has to be persisted as the result of the append.
|
||||
/// If completed without error, at least one link to the appended
|
||||
/// node (with metadata provided in `new_leaf`) will be returned.
|
||||
pub fn append_leaf(&mut self, new_leaf: NodeData) -> Result<Vec<EntryLink>, Error> {
|
||||
let root = self.root;
|
||||
let new_leaf_link = self.push(new_leaf.into());
|
||||
let mut appended = Vec::new();
|
||||
appended.push(new_leaf_link);
|
||||
|
||||
let mut peaks = Vec::new();
|
||||
self.get_peaks(root, &mut peaks)?;
|
||||
|
||||
let mut merge_stack = Vec::new();
|
||||
merge_stack.push(new_leaf_link);
|
||||
|
||||
// Scan the peaks right-to-left, merging together equal-sized adjacent
|
||||
// complete subtrees. After this, merge_stack only contains peaks of
|
||||
// unequal-sized subtrees.
|
||||
while let Some(next_peak) = peaks.pop() {
|
||||
let next_merge = merge_stack
|
||||
.pop()
|
||||
.expect("there should be at least one, initial or re-pushed");
|
||||
|
||||
if let Some(stored) = {
|
||||
let peak = self.resolve_link(next_peak)?;
|
||||
let m = self.resolve_link(next_merge)?;
|
||||
if peak.node.leaf_count() == m.node.leaf_count() {
|
||||
Some(combine_nodes(peak, m))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} {
|
||||
let link = self.push(stored);
|
||||
merge_stack.push(link);
|
||||
appended.push(link);
|
||||
continue;
|
||||
} else {
|
||||
merge_stack.push(next_merge);
|
||||
merge_stack.push(next_peak);
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_root = merge_stack
|
||||
.pop()
|
||||
.expect("Loop above cannot reduce the merge_stack");
|
||||
// Scan the peaks left-to-right, producing new generated nodes that
|
||||
// connect the subtrees
|
||||
while let Some(next_child) = merge_stack.pop() {
|
||||
new_root = self.push_generated(combine_nodes(
|
||||
self.resolve_link(new_root)?,
|
||||
self.resolve_link(next_child)?,
|
||||
))
|
||||
}
|
||||
|
||||
self.root = new_root;
|
||||
|
||||
Ok(appended)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn for_children<F: Fn(EntryLink, EntryLink)>(&self, node: EntryLink, f: F) {
|
||||
let (left, right) = {
|
||||
let link = self
|
||||
.resolve_link(node)
|
||||
.expect("Failed to resolve link in test");
|
||||
(
|
||||
link.left().expect("Failed to find node in test"),
|
||||
link.right().expect("Failed to find node in test"),
|
||||
)
|
||||
};
|
||||
f(left, right);
|
||||
}
|
||||
|
||||
fn pop(&mut self) {
|
||||
self.stored.remove(&(self.stored_count - 1));
|
||||
self.stored_count = self.stored_count - 1;
|
||||
}
|
||||
|
||||
/// Truncate one leaf from the end of the tree.
|
||||
///
|
||||
/// Returns actual number of nodes that should be removed by the caller
|
||||
/// from the end of the array representation.
|
||||
pub fn truncate_leaf(&mut self) -> Result<u32, Error> {
|
||||
let root = {
|
||||
let (leaves, root_left_child) = {
|
||||
let n = self.resolve_link(self.root)?;
|
||||
(n.node.leaf_count(), n.node.left()?)
|
||||
};
|
||||
if leaves & 1 != 0 {
|
||||
self.pop();
|
||||
self.root = root_left_child;
|
||||
return Ok(1);
|
||||
} else {
|
||||
self.resolve_link(self.root)?
|
||||
}
|
||||
};
|
||||
|
||||
let mut peaks = vec![root.left()?];
|
||||
let mut subtree_root_link = root.right()?;
|
||||
let mut truncated = 1;
|
||||
|
||||
loop {
|
||||
let left_link = self.resolve_link(subtree_root_link)?.node;
|
||||
if let EntryKind::Node(left, right) = left_link.kind {
|
||||
peaks.push(left);
|
||||
subtree_root_link = right;
|
||||
truncated += 1;
|
||||
} else {
|
||||
if root.node.complete() {
|
||||
truncated += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_root = *peaks.get(0).expect("At lest 1 elements in peaks");
|
||||
|
||||
for next_peak in peaks.into_iter().skip(1) {
|
||||
new_root = self.push_generated(combine_nodes(
|
||||
self.resolve_link(new_root)?,
|
||||
self.resolve_link(next_peak)?,
|
||||
));
|
||||
}
|
||||
|
||||
for _ in 0..truncated {
|
||||
self.pop();
|
||||
}
|
||||
|
||||
self.root = new_root;
|
||||
|
||||
Ok(truncated)
|
||||
}
|
||||
|
||||
/// Length of array representation of the tree.
|
||||
pub fn len(&self) -> u32 {
|
||||
self.stored_count
|
||||
}
|
||||
|
||||
/// Link to the root node
|
||||
pub fn root(&self) -> EntryLink {
|
||||
self.root
|
||||
}
|
||||
|
||||
/// Reference to the root node.
|
||||
pub fn root_node(&self) -> Result<IndexedNode, Error> {
|
||||
self.resolve_link(self.root)
|
||||
}
|
||||
|
||||
/// If this tree is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.stored_count == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference to the node with link attached.
|
||||
#[derive(Debug)]
|
||||
pub struct IndexedNode<'a> {
|
||||
node: &'a Entry,
|
||||
link: EntryLink,
|
||||
}
|
||||
|
||||
impl<'a> IndexedNode<'a> {
|
||||
fn left(&self) -> Result<EntryLink, Error> {
|
||||
self.node.left().map_err(|e| e.augment(self.link))
|
||||
}
|
||||
|
||||
fn right(&self) -> Result<EntryLink, Error> {
|
||||
self.node.right().map_err(|e| e.augment(self.link))
|
||||
}
|
||||
|
||||
/// Reference to the entry struct.
|
||||
pub fn node(&self) -> &Entry {
|
||||
self.node
|
||||
}
|
||||
|
||||
/// Reference to the entry metadata.
|
||||
pub fn data(&self) -> &NodeData {
|
||||
&self.node.data
|
||||
}
|
||||
|
||||
/// Actual link by what this node was resolved.
|
||||
pub fn link(&self) -> EntryLink {
|
||||
self.link
|
||||
}
|
||||
}
|
||||
|
||||
fn combine_nodes<'a>(left: IndexedNode<'a>, right: IndexedNode<'a>) -> Entry {
|
||||
Entry {
|
||||
kind: EntryKind::Node(left.link, right.link),
|
||||
data: NodeData::combine(&left.node.data, &right.node.data),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::{Entry, EntryKind, EntryLink, NodeData, Tree};
|
||||
use assert_matches::assert_matches;
|
||||
use quickcheck::{quickcheck, TestResult};
|
||||
|
||||
fn leaf(height: u32) -> NodeData {
|
||||
NodeData {
|
||||
consensus_branch_id: 1,
|
||||
subtree_commitment: [0u8; 32],
|
||||
start_time: 0,
|
||||
end_time: 0,
|
||||
start_target: 0,
|
||||
end_target: 0,
|
||||
start_sapling_root: [0u8; 32],
|
||||
end_sapling_root: [0u8; 32],
|
||||
subtree_total_work: 0.into(),
|
||||
start_height: height as u64,
|
||||
end_height: height as u64,
|
||||
sapling_tx: 7,
|
||||
}
|
||||
}
|
||||
|
||||
fn initial() -> Tree {
|
||||
let node1: Entry = leaf(1).into();
|
||||
let node2: Entry = leaf(2).into();
|
||||
|
||||
let node3 = Entry {
|
||||
data: NodeData::combine(&node1.data, &node2.data),
|
||||
kind: EntryKind::Leaf,
|
||||
};
|
||||
|
||||
Tree::populate(vec![node1, node2, node3], EntryLink::Stored(2))
|
||||
}
|
||||
|
||||
// returns tree with specified number of leafs and it's root
|
||||
fn generated(length: u32) -> Tree {
|
||||
assert!(length >= 3);
|
||||
let mut tree = initial();
|
||||
for i in 2..length {
|
||||
tree.append_leaf(leaf(i + 1).into())
|
||||
.expect("Failed to append");
|
||||
}
|
||||
|
||||
tree
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discrete_append() {
|
||||
let mut tree = initial();
|
||||
|
||||
// ** APPEND 3 **
|
||||
let appended = tree.append_leaf(leaf(3)).expect("Failed to append");
|
||||
let new_root = tree.root_node().expect("Failed to resolve root").node;
|
||||
|
||||
// initial tree: (2)
|
||||
// / \
|
||||
// (0) (1)
|
||||
//
|
||||
// new tree:
|
||||
// (4g)
|
||||
// / \
|
||||
// (2) \
|
||||
// / \ \
|
||||
// (0) (1) (3)
|
||||
//
|
||||
// so only (3) is added as real leaf
|
||||
// while new root, (4g) is generated one
|
||||
assert_eq!(new_root.data.end_height, 3);
|
||||
assert_eq!(appended.len(), 1);
|
||||
|
||||
// ** APPEND 4 **
|
||||
let appended = tree.append_leaf(leaf(4)).expect("Failed to append");
|
||||
|
||||
let new_root = tree.root_node().expect("Failed to resolve root").node;
|
||||
|
||||
// intermediate tree:
|
||||
// (4g)
|
||||
// / \
|
||||
// (2) \
|
||||
// / \ \
|
||||
// (0) (1) (3)
|
||||
//
|
||||
// new tree:
|
||||
// ( 6 )
|
||||
// / \
|
||||
// (2) (5)
|
||||
// / \ / \
|
||||
// (0) (1) (3) (4)
|
||||
//
|
||||
// so (4), (5), (6) are added as real leaves
|
||||
// and new root, (6) is stored one
|
||||
assert_eq!(new_root.data.end_height, 4);
|
||||
assert_eq!(appended.len(), 3);
|
||||
assert_matches!(tree.root(), EntryLink::Stored(6));
|
||||
|
||||
// ** APPEND 5 **
|
||||
|
||||
let appended = tree.append_leaf(leaf(5)).expect("Failed to append");
|
||||
let new_root = tree.root_node().expect("Failed to resolve root").node;
|
||||
|
||||
// intermediate tree:
|
||||
// ( 6 )
|
||||
// / \
|
||||
// (2) (5)
|
||||
// / \ / \
|
||||
// (0) (1) (3) (4)
|
||||
//
|
||||
// new tree:
|
||||
// ( 8g )
|
||||
// / \
|
||||
// ( 6 ) \
|
||||
// / \ \
|
||||
// (2) (5) \
|
||||
// / \ / \ \
|
||||
// (0) (1) (3) (4) (7)
|
||||
//
|
||||
// so (7) is added as real leaf
|
||||
// and new root, (8g) is generated one
|
||||
assert_eq!(new_root.data.end_height, 5);
|
||||
assert_eq!(appended.len(), 1);
|
||||
assert_matches!(tree.root(), EntryLink::Generated(_));
|
||||
tree.for_children(tree.root(), |l, r| {
|
||||
assert_matches!(l, EntryLink::Stored(6));
|
||||
assert_matches!(r, EntryLink::Stored(7));
|
||||
});
|
||||
|
||||
// *** APPEND #6 ***
|
||||
let appended = tree.append_leaf(leaf(6)).expect("Failed to append");
|
||||
let new_root = tree.root_node().expect("Failed to resolve root").node;
|
||||
|
||||
// intermediate tree:
|
||||
// ( 8g )
|
||||
// / \
|
||||
// ( 6 ) \
|
||||
// / \ \
|
||||
// (2) (5) \
|
||||
// / \ / \ \
|
||||
// (0) (1) (3) (4) (7)
|
||||
//
|
||||
// new tree:
|
||||
// (---10g--)
|
||||
// / \
|
||||
// ( 6 ) \
|
||||
// / \ \
|
||||
// (2) (5) (9)
|
||||
// / \ / \ / \
|
||||
// (0) (1) (3) (4) (7) (8)
|
||||
//
|
||||
// so (7) is added as real leaf
|
||||
// and new root, (10g) is generated one
|
||||
assert_eq!(new_root.data.end_height, 6);
|
||||
assert_eq!(appended.len(), 2);
|
||||
assert_matches!(tree.root(), EntryLink::Generated(_));
|
||||
tree.for_children(tree.root(), |l, r| {
|
||||
assert_matches!(l, EntryLink::Stored(6));
|
||||
assert_matches!(r, EntryLink::Stored(9));
|
||||
});
|
||||
|
||||
// *** APPEND #7 ***
|
||||
|
||||
let appended = tree.append_leaf(leaf(7)).expect("Failed to append");
|
||||
let new_root = tree.root_node().expect("Failed to resolve root").node;
|
||||
|
||||
// intermediate tree:
|
||||
// (---8g---)
|
||||
// / \
|
||||
// ( 6 ) \
|
||||
// / \ \
|
||||
// (2) (5) (9)
|
||||
// / \ / \ / \
|
||||
// (0) (1) (3) (4) (7) (8)
|
||||
//
|
||||
// new tree:
|
||||
// (---12g--)
|
||||
// / \
|
||||
// (---11g---) \
|
||||
// / \ \
|
||||
// ( 6 ) \ \
|
||||
// / \ \ \
|
||||
// (2) (5) (9) \
|
||||
// / \ / \ / \ \
|
||||
// (0) (1) (3) (4) (7) (8) (10)
|
||||
//
|
||||
// so (10) is added as real leaf
|
||||
// and new root, (12g) is generated one
|
||||
assert_eq!(new_root.data.end_height, 7);
|
||||
assert_eq!(appended.len(), 1);
|
||||
assert_matches!(tree.root(), EntryLink::Generated(_));
|
||||
tree.for_children(tree.root(), |l, r| {
|
||||
assert_matches!(l, EntryLink::Generated(_));
|
||||
tree.for_children(l, |l, r| {
|
||||
assert_matches!((l, r), (EntryLink::Stored(6), EntryLink::Stored(9)))
|
||||
});
|
||||
assert_matches!(r, EntryLink::Stored(10));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncate_simple() {
|
||||
let mut tree = generated(9);
|
||||
let total_truncated = tree.truncate_leaf().expect("Failed to truncate");
|
||||
|
||||
// initial tree:
|
||||
//
|
||||
// (-------16g------)
|
||||
// / \
|
||||
// (--------14-------) \
|
||||
// / \ \
|
||||
// ( 6 ) ( 13 ) \
|
||||
// / \ / \ \
|
||||
// (2) (5) (9) (12) \
|
||||
// / \ / \ / \ / \ \
|
||||
// (0) (1) (3) (4) (7) (8) (10) (11) (15)
|
||||
//
|
||||
// new tree:
|
||||
// (--------14-------)
|
||||
// / \
|
||||
// ( 6 ) ( 13 )
|
||||
// / \ / \
|
||||
// (2) (5) (9) (12)
|
||||
// / \ / \ / \ / \
|
||||
// (0) (1) (3) (4) (7) (8) (10) (11)
|
||||
//
|
||||
// so (15) is truncated
|
||||
// and new root, (14) is a stored one now
|
||||
|
||||
assert_matches!(tree.root(), EntryLink::Stored(14));
|
||||
assert_eq!(total_truncated, 1);
|
||||
assert_eq!(tree.len(), 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncate_generated() {
|
||||
let mut tree = generated(10);
|
||||
let deleted = tree.truncate_leaf().expect("Failed to truncate");
|
||||
|
||||
// initial tree:
|
||||
//
|
||||
// (--------18g--------)
|
||||
// / \
|
||||
// (--------14-------) \
|
||||
// / \ \
|
||||
// ( 6 ) ( 13 ) \
|
||||
// / \ / \ \
|
||||
// (2) (5) (9) (12) (17)
|
||||
// / \ / \ / \ / \ / \
|
||||
// (0) (1) (3) (4) (7) (8) (10) (11) (15) (16)
|
||||
//
|
||||
// new tree:
|
||||
// (-------16g------)
|
||||
// / \
|
||||
// (--------14-------) \
|
||||
// / \ \
|
||||
// ( 6 ) ( 13 ) \
|
||||
// / \ / \ \
|
||||
// (2) (5) (9) (12) \
|
||||
// / \ / \ / \ / \ \
|
||||
// (0) (1) (3) (4) (7) (8) (10) (11) (15)
|
||||
|
||||
// new root is generated
|
||||
|
||||
assert_matches!(tree.root(), EntryLink::Generated(_));
|
||||
|
||||
tree.for_children(tree.root(), |left, right| {
|
||||
assert_matches!(
|
||||
(left, right),
|
||||
(EntryLink::Stored(14), EntryLink::Stored(15))
|
||||
)
|
||||
});
|
||||
|
||||
// two stored nodes should leave us (leaf 16 and no longer needed node 17)
|
||||
assert_eq!(deleted, 2);
|
||||
assert_eq!(tree.len(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_len() {
|
||||
let mut tree = initial();
|
||||
|
||||
assert_eq!(tree.len(), 3);
|
||||
|
||||
for i in 0..2 {
|
||||
tree.append_leaf(leaf(i + 3)).expect("Failed to append");
|
||||
}
|
||||
assert_eq!(tree.len(), 7);
|
||||
|
||||
tree.truncate_leaf().expect("Failed to truncate");
|
||||
|
||||
assert_eq!(tree.len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_len_long() {
|
||||
let mut tree = initial();
|
||||
|
||||
assert_eq!(tree.len(), 3);
|
||||
|
||||
for i in 0..4094 {
|
||||
tree.append_leaf(leaf(i + 3)).expect("Failed to append");
|
||||
}
|
||||
assert_eq!(tree.len(), 8191); // 4096*2-1 (full tree)
|
||||
|
||||
for _ in 0..2049 {
|
||||
tree.truncate_leaf().expect("Failed to truncate");
|
||||
}
|
||||
|
||||
assert_eq!(tree.len(), 4083); // 4095 - log2(4096)
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn there_and_back(number: u32) -> TestResult {
|
||||
if number > 1024*1024 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
let mut tree = initial();
|
||||
for i in 0..number {
|
||||
tree.append_leaf(leaf(i+3)).expect("Failed to append");
|
||||
}
|
||||
for _ in 0..number {
|
||||
tree.truncate_leaf().expect("Failed to truncate");
|
||||
}
|
||||
|
||||
TestResult::from_bool(if let EntryLink::Stored(2) = tree.root() { true } else { false })
|
||||
}
|
||||
}
|
||||
|
||||
fn leaf_count(number: u32) -> TestResult {
|
||||
if number > 1024 * 1024 || number < 3 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
let mut tree = initial();
|
||||
for i in 1..(number-1) {
|
||||
tree.append_leaf(leaf(i+2)).expect("Failed to append");
|
||||
}
|
||||
|
||||
TestResult::from_bool(
|
||||
tree.root_node().expect("no root").node.leaf_count() == number as u64
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn parity(number: u32) -> TestResult {
|
||||
if number > 2048 * 2048 || number < 3 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
let mut tree = initial();
|
||||
for i in 1..(number-1) {
|
||||
tree.append_leaf(leaf(i+2)).expect("Failed to append");
|
||||
}
|
||||
|
||||
TestResult::from_bool(
|
||||
if number & (number - 1) == 0 {
|
||||
if let EntryLink::Stored(_) = tree.root() { true }
|
||||
else { false }
|
||||
} else {
|
||||
if let EntryLink::Generated(_) = tree.root() { true }
|
||||
else { false }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn parity_with_truncate(add: u32, delete: u32) -> TestResult {
|
||||
// First we add `add` number of leaves, then delete `delete` number of leaves
|
||||
// What is left should be consistent with generated-stored structure
|
||||
if add > 2048 * 2048 || add < delete {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
let mut tree = initial();
|
||||
for i in 0..add {
|
||||
tree.append_leaf(leaf(i+3)).expect("Failed to append");
|
||||
}
|
||||
for _ in 0..delete {
|
||||
tree.truncate_leaf().expect("Failed to truncate");
|
||||
}
|
||||
|
||||
let total = add - delete + 2;
|
||||
|
||||
TestResult::from_bool(
|
||||
if total & total - 1 == 0 {
|
||||
if let EntryLink::Stored(_) = tree.root() { true }
|
||||
else { false }
|
||||
} else {
|
||||
if let EntryLink::Generated(_) = tree.root() { true }
|
||||
else { false }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Length of tree is always less than number of leaves squared
|
||||
fn stored_length(add: u32, delete: u32) -> TestResult {
|
||||
if add > 2048 * 2048 || add < delete {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
let mut tree = initial();
|
||||
for i in 0..add {
|
||||
tree.append_leaf(leaf(i+3)).expect("Failed to append");
|
||||
}
|
||||
for _ in 0..delete {
|
||||
tree.truncate_leaf().expect("Failed to truncate");
|
||||
}
|
||||
|
||||
let total = add - delete + 2;
|
||||
|
||||
TestResult::from_bool(total * total > tree.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
[package]
|
||||
name = "zcash_primitives"
|
||||
version = "0.0.0"
|
||||
description = "Rust implementations of the Zcash primitives"
|
||||
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]
|
||||
aes = "0.3"
|
||||
@@ -11,16 +17,24 @@ blake2b_simd = "0.5"
|
||||
blake2s_simd = "0.5"
|
||||
byteorder = "1"
|
||||
crypto_api_chachapoly = "0.2.1"
|
||||
ff = { path = "../ff" }
|
||||
ff = { version = "0.5.0", path = "../ff" }
|
||||
fpe = "0.2"
|
||||
hex = "0.3"
|
||||
lazy_static = "1"
|
||||
pairing = { path = "../pairing" }
|
||||
log = "0.4"
|
||||
pairing = { version = "0.15.0", path = "../pairing" }
|
||||
rand = "0.7"
|
||||
rand_core = "0.5"
|
||||
rand_os = "0.2"
|
||||
rand_core = "0.5.1"
|
||||
ripemd160 = { version = "0.8", optional = true }
|
||||
secp256k1 = { version = "=0.15.0", optional = true }
|
||||
sha2 = "0.8"
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.1"
|
||||
hex-literal = "0.2"
|
||||
rand_xorshift = "0.2"
|
||||
|
||||
[features]
|
||||
transparent-inputs = ["ripemd160", "secp256k1"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -6,7 +6,8 @@ This library contains Rust implementations of the Zcash primitives.
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
|
||||
extern crate pairing;
|
||||
extern crate rand_core;
|
||||
extern crate rand_os;
|
||||
extern crate test;
|
||||
extern crate zcash_primitives;
|
||||
|
||||
use pairing::bls12_381::Bls12;
|
||||
use rand_core::RngCore;
|
||||
use rand_os::OsRng;
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use zcash_primitives::jubjub::JubjubBls12;
|
||||
use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization};
|
||||
|
||||
|
||||
@@ -1,30 +1,52 @@
|
||||
//! Structs and methods for handling Zcash block headers.
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use hex;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::fmt;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::ops::Deref;
|
||||
|
||||
use serialize::Vector;
|
||||
use crate::serialize::Vector;
|
||||
|
||||
pub mod equihash;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct BlockHash(pub [u8; 32]);
|
||||
|
||||
impl fmt::Display for BlockHash {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut data = self.0.clone();
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut data = self.0;
|
||||
data.reverse();
|
||||
formatter.write_str(&hex::encode(data))
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHash {
|
||||
/// Constructs a [`BlockHash`] from the given slice.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the slice is not exactly 32 bytes.
|
||||
pub fn from_slice(bytes: &[u8]) -> Self {
|
||||
assert_eq!(bytes.len(), 32);
|
||||
let mut hash = [0; 32];
|
||||
hash.copy_from_slice(&bytes);
|
||||
BlockHash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// A Zcash block header.
|
||||
pub struct BlockHeader(BlockHeaderData);
|
||||
pub struct BlockHeader {
|
||||
hash: BlockHash,
|
||||
data: BlockHeaderData,
|
||||
}
|
||||
|
||||
impl Deref for BlockHeader {
|
||||
type Target = BlockHeaderData;
|
||||
|
||||
fn deref(&self) -> &BlockHeaderData {
|
||||
&self.0
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +62,31 @@ pub struct BlockHeaderData {
|
||||
}
|
||||
|
||||
impl BlockHeaderData {
|
||||
pub fn freeze(self) -> BlockHeader {
|
||||
BlockHeader(self)
|
||||
pub fn freeze(self) -> io::Result<BlockHeader> {
|
||||
BlockHeader::from_data(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHeader {
|
||||
fn from_data(data: BlockHeaderData) -> io::Result<Self> {
|
||||
let mut header = BlockHeader {
|
||||
hash: BlockHash([0; 32]),
|
||||
data,
|
||||
};
|
||||
let mut raw = vec![];
|
||||
header.write(&mut raw)?;
|
||||
header
|
||||
.hash
|
||||
.0
|
||||
.copy_from_slice(&Sha256::digest(&Sha256::digest(&raw)));
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
/// Returns the hash of this header.
|
||||
pub fn hash(&self) -> BlockHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||
let version = reader.read_i32::<LittleEndian>()?;
|
||||
|
||||
@@ -66,7 +107,7 @@ impl BlockHeader {
|
||||
|
||||
let solution = Vector::read(&mut reader, |r| r.read_u8())?;
|
||||
|
||||
Ok(BlockHeader(BlockHeaderData {
|
||||
BlockHeader::from_data(BlockHeaderData {
|
||||
version,
|
||||
prev_block,
|
||||
merkle_root,
|
||||
@@ -75,7 +116,7 @@ impl BlockHeader {
|
||||
bits,
|
||||
nonce,
|
||||
solution,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
@@ -202,6 +243,10 @@ mod tests {
|
||||
#[test]
|
||||
fn header_read_write() {
|
||||
let header = BlockHeader::read(&HEADER_MAINNET_415000[..]).unwrap();
|
||||
assert_eq!(
|
||||
format!("{}", header.hash()),
|
||||
"0000000001ab37793ce771262b2ffa082519aa3fe891250a1adb43baaf856168"
|
||||
);
|
||||
let mut encoded = Vec::with_capacity(HEADER_MAINNET_415000.len());
|
||||
header.write(&mut encoded).unwrap();
|
||||
assert_eq!(&HEADER_MAINNET_415000[..], &encoded[..]);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user