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
|
language: rust
|
||||||
rust:
|
rust:
|
||||||
- 1.36.0
|
- 1.37.0
|
||||||
|
|
||||||
cache: cargo
|
cache: cargo
|
||||||
|
|
||||||
@@ -11,3 +11,9 @@ script:
|
|||||||
- cargo build --verbose --release --all
|
- cargo build --verbose --release --all
|
||||||
- cargo fmt --all -- --check
|
- cargo fmt --all -- --check
|
||||||
- cargo test --verbose --release --all
|
- cargo test --verbose --release --all
|
||||||
|
- cargo test --verbose --release --all -- --ignored
|
||||||
|
|
||||||
|
before_cache:
|
||||||
|
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
|
||||||
|
- cargo install cargo-update || echo "cargo-update already installed"
|
||||||
|
- cargo install-update -a # update outdated cached binaries
|
||||||
|
|||||||
608
Cargo.lock
generated
608
Cargo.lock
generated
@@ -29,6 +29,14 @@ dependencies = [
|
|||||||
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "arrayref"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@@ -42,6 +50,11 @@ dependencies = [
|
|||||||
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assert_matches"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@@ -54,25 +67,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bellman"
|
name = "bellman"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ff 0.4.0",
|
"ff 0.5.2",
|
||||||
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"group 0.1.0",
|
"group 0.2.0",
|
||||||
"hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pairing 0.14.2",
|
"pairing 0.15.1",
|
||||||
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bigint"
|
||||||
|
version = "4.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-vec"
|
name = "bit-vec"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
@@ -80,7 +102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.1.0"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -130,14 +152,6 @@ dependencies = [
|
|||||||
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bs58"
|
|
||||||
version = "0.2.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byte-tools"
|
name = "byte-tools"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -159,7 +173,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.42"
|
version = "1.0.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -167,6 +181,14 @@ name = "cfg-if"
|
|||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cloudabi"
|
||||||
|
version = "0.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "constant_time_eq"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -174,7 +196,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam"
|
name = "crossbeam"
|
||||||
version = "0.3.2"
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-queue"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -207,40 +289,39 @@ dependencies = [
|
|||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fake-simd"
|
name = "fake-simd"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fallible-iterator"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fallible-streaming-iterator"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
version = "0.4.0"
|
version = "0.5.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ff_derive 0.3.0",
|
"ff_derive 0.4.1",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ff_derive"
|
name = "ff_derive"
|
||||||
version = "0.3.0"
|
version = "0.4.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -255,6 +336,11 @@ dependencies = [
|
|||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.1.29"
|
version = "0.1.29"
|
||||||
@@ -279,19 +365,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.11"
|
version = "0.1.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wasi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "group"
|
name = "group"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ff 0.4.0",
|
"ff 0.5.2",
|
||||||
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@@ -303,19 +389,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex-literal"
|
name = "hex-literal"
|
||||||
version = "0.1.4"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex-literal-impl"
|
name = "hex-literal-impl"
|
||||||
version = "0.1.2"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -330,50 +416,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librustzcash"
|
name = "librustzcash"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bellman 0.1.0",
|
"bellman 0.2.0",
|
||||||
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ff 0.5.2",
|
||||||
"ff 0.4.0",
|
|
||||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pairing 0.14.2",
|
"pairing 0.15.1",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zcash_history 0.0.1",
|
||||||
"zcash_primitives 0.0.0",
|
"zcash_primitives 0.1.0",
|
||||||
"zcash_proofs 0.0.0",
|
"zcash_proofs 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "log"
|
||||||
version = "0.16.0"
|
version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linked-hash-map"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lru-cache"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.2.1"
|
version = "2.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nodrop"
|
name = "nodrop"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@@ -421,20 +499,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pairing"
|
name = "pairing"
|
||||||
version = "0.14.2"
|
version = "0.15.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ff 0.4.0",
|
"ff 0.5.2",
|
||||||
"group 0.1.0",
|
"group 0.2.0",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pkg-config"
|
|
||||||
version = "0.3.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -442,53 +515,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.4.2"
|
version = "0.5.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-hack-impl"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "0.4.30"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "protobuf"
|
name = "protobuf"
|
||||||
version = "2.8.0"
|
version = "2.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "protobuf-codegen"
|
name = "protobuf-codegen"
|
||||||
version = "2.8.0"
|
version = "2.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "protobuf-codegen-pure"
|
name = "protobuf-codegen-pure"
|
||||||
version = "2.8.0"
|
version = "2.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"protobuf-codegen 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"protobuf-codegen 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quickcheck"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "0.6.13"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -496,13 +595,22 @@ name = "rand"
|
|||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_chacha"
|
name = "rand_chacha"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -512,12 +620,33 @@ dependencies = [
|
|||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -529,12 +658,51 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_os"
|
name = "rand_isaac"
|
||||||
version = "0.2.2"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_jitter"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_os"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_pcg"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xorshift"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -546,18 +714,29 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "rdrand"
|
||||||
version = "0.1.56"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "remove_dir_all"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ripemd160"
|
name = "ripemd160"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -569,27 +748,39 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusqlite"
|
name = "rustc_version"
|
||||||
version = "0.20.0"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "secp256k1"
|
name = "secp256k1"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -603,40 +794,25 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.1.1"
|
version = "2.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "0.14.9"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "thread_local"
|
||||||
version = "3.1.0"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "time"
|
|
||||||
version = "0.1.42"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
|
||||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -646,17 +822,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vcpkg"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.5.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -680,59 +851,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_client_backend"
|
name = "zcash_client_backend"
|
||||||
version = "0.0.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ff 0.5.2",
|
||||||
"ff 0.4.0",
|
|
||||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pairing 0.14.2",
|
"pairing 0.15.1",
|
||||||
"protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"protobuf-codegen-pure 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"protobuf-codegen-pure 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"zcash_primitives 0.0.0",
|
"zcash_primitives 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_client_sqlite"
|
name = "zcash_history"
|
||||||
version = "0.0.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ff 0.4.0",
|
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pairing 0.14.2",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"zcash_client_backend 0.0.0",
|
|
||||||
"zcash_primitives 0.0.0",
|
|
||||||
"zcash_proofs 0.0.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_primitives"
|
name = "zcash_primitives"
|
||||||
version = "0.0.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ff 0.4.0",
|
"ff 0.5.2",
|
||||||
"fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pairing 0.14.2",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pairing 0.15.1",
|
||||||
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"secp256k1 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"secp256k1 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -741,99 +902,116 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_proofs"
|
name = "zcash_proofs"
|
||||||
version = "0.0.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bellman 0.1.0",
|
"bellman 0.2.0",
|
||||||
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ff 0.4.0",
|
"ff 0.5.2",
|
||||||
"pairing 0.14.2",
|
"pairing 0.15.1",
|
||||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"zcash_primitives 0.0.0",
|
"zcash_primitives 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9"
|
"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9"
|
||||||
"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d"
|
"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d"
|
||||||
"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
|
"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
|
||||||
|
"checksum aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d5e63fd144e18ba274ae7095c0197a870a7b9468abc801dd62f190d80817d2ec"
|
||||||
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
|
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
|
||||||
"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba"
|
"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba"
|
||||||
|
"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5"
|
||||||
"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
|
"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
|
||||||
"checksum bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0089c35ab7c6f2bc55ab23f769913f0ac65b1023e7e74638a1f43128dd5df2"
|
"checksum bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0089c35ab7c6f2bc55ab23f769913f0ac65b1023e7e74638a1f43128dd5df2"
|
||||||
|
"checksum bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282"
|
||||||
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
|
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
|
||||||
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182"
|
"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182"
|
||||||
"checksum blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "979da0ce13c897d6be19e005ea77ac12b0fea0157aeeee7feb8c49f91386f0ea"
|
"checksum blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "979da0ce13c897d6be19e005ea77ac12b0fea0157aeeee7feb8c49f91386f0ea"
|
||||||
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||||
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
|
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
|
||||||
"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09"
|
"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09"
|
||||||
"checksum bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c95ee6bba9d950218b6cc910cf62bc9e0a171d0f4537e3627b0f54d08549b188"
|
|
||||||
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||||
"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101"
|
"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101"
|
||||||
"checksum cc 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "a61c7bce55cd2fae6ec8cb935ebd76256c2959a1f95790f6118a441c2cd5b406"
|
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
|
||||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||||
|
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"
|
"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"
|
||||||
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
|
"checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c"
|
||||||
|
"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa"
|
||||||
|
"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71"
|
||||||
|
"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
|
||||||
|
"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
|
||||||
|
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
|
||||||
|
"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
|
||||||
"checksum crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102"
|
"checksum crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102"
|
||||||
"checksum crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95b2ad7cab08fd71addba81df5077c49df208effdfb3118a1519f9cdeac5aaf2"
|
"checksum crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95b2ad7cab08fd71addba81df5077c49df208effdfb3118a1519f9cdeac5aaf2"
|
||||||
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||||
"checksum directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f"
|
"checksum directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f"
|
||||||
|
"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
|
||||||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||||
"checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
|
||||||
"checksum fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
|
||||||
"checksum fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21988a326139165b75e3196bc6962ca638e5fb0c95102fbf152a3743174b01e4"
|
"checksum fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21988a326139165b75e3196bc6962ca638e5fb0c95102fbf152a3743174b01e4"
|
||||||
|
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef"
|
"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef"
|
||||||
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
||||||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||||
"checksum getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fc344b02d3868feb131e8b5fe2b9b0a1cc42942679af493061fc13b853243872"
|
"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571"
|
||||||
"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
||||||
"checksum hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639"
|
"checksum hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0"
|
||||||
"checksum hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda"
|
"checksum hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d"
|
||||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
||||||
"checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25"
|
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||||
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
|
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||||
"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
|
||||||
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
|
||||||
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||||
"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a"
|
"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a"
|
||||||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||||
"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"
|
"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"
|
||||||
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||||
"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af"
|
|
||||||
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
|
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
|
||||||
"checksum proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b"
|
"checksum proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8"
|
||||||
"checksum proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842"
|
"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8"
|
||||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
"checksum protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40361836defdd5871ff7e84096c6f6444af7fc157f8ef1789f54f147687caa20"
|
||||||
"checksum protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8aefcec9f142b524d98fc81d07827743be89dd6586a1ba6ab21fa66a500b3fa5"
|
"checksum protobuf-codegen 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12c6abd78435445fc86898ebbd0521a68438063d4a73e23527b7134e6bf58b4a"
|
||||||
"checksum protobuf-codegen 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31539be8028d6b9e8e1b3b7c74e2fa3555302e27b2cc20dbaee6ffba648f75e2"
|
"checksum protobuf-codegen-pure 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c1646acda5319f5b28b0bff4a484324df43ddae2c0f5a3f3e63c0b26095cd600"
|
||||||
"checksum protobuf-codegen-pure 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00993dc5fbbfcf9d8a005f6b6c29fd29fd6d86deba3ae3f41fd20c624c414616"
|
"checksum quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9c35d9c36a562f37eca96e79f66d5fd56eefbc22560dacc4a864cabd2d277456"
|
||||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||||
|
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||||
"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c"
|
"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c"
|
||||||
|
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||||
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
|
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
|
||||||
|
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
|
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
||||||
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||||
"checksum rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a"
|
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
||||||
|
"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
||||||
|
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||||
|
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||||
|
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||||
"checksum rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8"
|
"checksum rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8"
|
||||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8"
|
||||||
|
"checksum regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1"
|
||||||
"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
|
"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
|
||||||
"checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051"
|
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
|
||||||
"checksum secp256k1 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e0344a794ff109f85547039536028e12f313178ac1545e49fdf16a530d900a7b"
|
"checksum secp256k1 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e0344a794ff109f85547039536028e12f313178ac1545e49fdf16a530d900a7b"
|
||||||
|
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
|
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
|
||||||
"checksum subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01f40907d9ffc762709e4ff3eb4a6f6b41b650375a3f09ac92b641942b7fb082"
|
"checksum subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3af2eb31c42e8f0ccf43548232556c42737e01a96db6e1777b0be108e79799"
|
||||||
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
|
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
|
||||||
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
||||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||||
"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95"
|
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
|
||||||
"checksum wasi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd5442abcac6525a045cc8c795aedb60da7a2e5e89c7bf18a0d5357849bb23c7"
|
|
||||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ members = [
|
|||||||
"librustzcash",
|
"librustzcash",
|
||||||
"pairing",
|
"pairing",
|
||||||
"zcash_client_backend",
|
"zcash_client_backend",
|
||||||
"zcash_client_sqlite",
|
"zcash_history",
|
||||||
"zcash_primitives",
|
"zcash_primitives",
|
||||||
"zcash_proofs",
|
"zcash_proofs",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,28 +1,29 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||||
description = "zk-SNARK library"
|
description = "zk-SNARK library"
|
||||||
documentation = "https://github.com/ebfull/bellman"
|
readme = "README.md"
|
||||||
homepage = "https://github.com/ebfull/bellman"
|
homepage = "https://github.com/ebfull/bellman"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
name = "bellman"
|
name = "bellman"
|
||||||
repository = "https://github.com/ebfull/bellman"
|
repository = "https://github.com/ebfull/bellman"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bit-vec = "0.4.4"
|
bit-vec = "0.4.4"
|
||||||
blake2s_simd = "0.5"
|
blake2s_simd = "0.5"
|
||||||
ff = { path = "../ff" }
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
futures-cpupool = { version = "0.1", optional = true }
|
futures-cpupool = { version = "0.1", optional = true }
|
||||||
group = { path = "../group" }
|
group = { version = "0.2.0", path = "../group" }
|
||||||
num_cpus = { version = "1", optional = true }
|
num_cpus = { version = "1", optional = true }
|
||||||
crossbeam = { version = "0.3", optional = true }
|
crossbeam = { version = "0.7", optional = true }
|
||||||
pairing = { path = "../pairing", optional = true }
|
pairing = { version = "0.15.0", path = "../pairing", optional = true }
|
||||||
rand_core = "0.5"
|
rand_core = "0.5"
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex-literal = "0.1"
|
hex-literal = "0.2"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
rand_xorshift = "0.2"
|
rand_xorshift = "0.2"
|
||||||
sha2 = "0.8"
|
sha2 = "0.8"
|
||||||
@@ -36,3 +37,6 @@ default = ["groth16", "multicore"]
|
|||||||
name = "mimc"
|
name = "mimc"
|
||||||
path = "tests/mimc.rs"
|
path = "tests/mimc.rs"
|
||||||
required-features = ["groth16"]
|
required-features = ["groth16"]
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
# bellman [](https://crates.io/crates/bellman) #
|
# bellman [](https://crates.io/crates/bellman) #
|
||||||
|
|
||||||
This is a research project being built for [Zcash](https://z.cash/).
|
`bellman` is a crate for building zk-SNARK circuits. It provides circuit traits
|
||||||
|
and primitive structures, as well as basic gadget implementations such as
|
||||||
|
booleans and number abstractions.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
`bellman` is being refactored into a generic proving library. Currently it is
|
||||||
|
pairing-specific, and different types of proving systems need to be implemented
|
||||||
|
as sub-modules. After the refactor, `bellman` will be generic using the `ff` and
|
||||||
|
`group` crates, while specific proving systems will be separate crates that pull
|
||||||
|
in the dependencies they require.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
//! This module contains an `EvaluationDomain` abstraction for
|
//! This module contains an [`EvaluationDomain`] abstraction for performing
|
||||||
//! performing various kinds of polynomial arithmetic on top of
|
//! various kinds of polynomial arithmetic on top of the scalar field.
|
||||||
//! the scalar field.
|
|
||||||
//!
|
//!
|
||||||
//! In pairing-based SNARKs like Groth16, we need to calculate
|
//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient
|
||||||
//! a quotient polynomial over a target polynomial with roots
|
//! polynomial over a target polynomial with roots at distinct points associated
|
||||||
//! at distinct points associated with each constraint of the
|
//! with each constraint of the constraint system. In order to be efficient, we
|
||||||
//! constraint system. In order to be efficient, we choose these
|
//! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the
|
||||||
//! roots to be the powers of a 2^n root of unity in the field.
|
//! field. This allows us to perform polynomial operations in O(n) by performing
|
||||||
//! This allows us to perform polynomial operations in O(n)
|
//! an O(n log n) FFT over such a domain.
|
||||||
//! by performing an O(n log n) FFT over such a domain.
|
//!
|
||||||
|
//! [`EvaluationDomain`]: crate::domain::EvaluationDomain
|
||||||
|
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||||
|
|
||||||
use ff::{Field, PrimeField, ScalarEngine};
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
use group::CurveProjective;
|
use group::CurveProjective;
|
||||||
@@ -26,15 +27,19 @@ pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
|
|||||||
minv: E::Fr,
|
minv: E::Fr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
impl<E: ScalarEngine, G: Group<E>> AsRef<[G]> for EvaluationDomain<E, G> {
|
||||||
pub fn as_ref(&self) -> &[G] {
|
fn as_ref(&self) -> &[G] {
|
||||||
&self.coeffs
|
&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
|
&mut self.coeffs
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||||
pub fn into_coeffs(self) -> Vec<G> {
|
pub fn into_coeffs(self) -> Vec<G> {
|
||||||
self.coeffs
|
self.coeffs
|
||||||
}
|
}
|
||||||
@@ -64,9 +69,9 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
coeffs.resize(m, G::group_zero());
|
coeffs.resize(m, G::group_zero());
|
||||||
|
|
||||||
Ok(EvaluationDomain {
|
Ok(EvaluationDomain {
|
||||||
coeffs: coeffs,
|
coeffs,
|
||||||
exp: exp,
|
exp,
|
||||||
omega: omega,
|
omega,
|
||||||
omegainv: omega.inverse().unwrap(),
|
omegainv: omega.inverse().unwrap(),
|
||||||
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
|
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
|
||||||
minv: E::Fr::from_str(&format!("{}", m))
|
minv: E::Fr::from_str(&format!("{}", m))
|
||||||
@@ -87,7 +92,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
let minv = self.minv;
|
let minv = self.minv;
|
||||||
|
|
||||||
for v in self.coeffs.chunks_mut(chunk) {
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
for v in v {
|
for v in v {
|
||||||
v.group_mul_assign(&minv);
|
v.group_mul_assign(&minv);
|
||||||
}
|
}
|
||||||
@@ -99,7 +104,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) {
|
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) {
|
||||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
let mut u = g.pow(&[(i * chunk) as u64]);
|
let mut u = g.pow(&[(i * chunk) as u64]);
|
||||||
for v in v.iter_mut() {
|
for v in v.iter_mut() {
|
||||||
v.group_mul_assign(&u);
|
v.group_mul_assign(&u);
|
||||||
@@ -142,7 +147,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
|
|
||||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||||
for v in self.coeffs.chunks_mut(chunk) {
|
for v in self.coeffs.chunks_mut(chunk) {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
for v in v {
|
for v in v {
|
||||||
v.group_mul_assign(&i);
|
v.group_mul_assign(&i);
|
||||||
}
|
}
|
||||||
@@ -161,7 +166,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
.chunks_mut(chunk)
|
.chunks_mut(chunk)
|
||||||
.zip(other.coeffs.chunks(chunk))
|
.zip(other.coeffs.chunks(chunk))
|
||||||
{
|
{
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
a.group_mul_assign(&b.0);
|
a.group_mul_assign(&b.0);
|
||||||
}
|
}
|
||||||
@@ -180,7 +185,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
|||||||
.chunks_mut(chunk)
|
.chunks_mut(chunk)
|
||||||
.zip(other.coeffs.chunks(chunk))
|
.zip(other.coeffs.chunks(chunk))
|
||||||
{
|
{
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||||
a.group_sub_assign(&b);
|
a.group_sub_assign(&b);
|
||||||
}
|
}
|
||||||
@@ -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;
|
let mut m = 1;
|
||||||
for _ in 0..log_n {
|
for _ in 0..log_n {
|
||||||
let w_m = omega.pow(&[(n / (2 * m)) as u64]);
|
let w_m = omega.pow(&[u64::from(n / (2 * m))]);
|
||||||
|
|
||||||
let mut k = 0;
|
let mut k = 0;
|
||||||
while k < n {
|
while k < n {
|
||||||
@@ -331,18 +336,18 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
|||||||
let a = &*a;
|
let a = &*a;
|
||||||
|
|
||||||
for (j, tmp) in tmp.iter_mut().enumerate() {
|
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
// Shuffle into a sub-FFT
|
// Shuffle into a sub-FFT
|
||||||
let omega_j = omega.pow(&[j as u64]);
|
let omega_j = omega.pow(&[j as u64]);
|
||||||
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
|
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
|
||||||
|
|
||||||
let mut elt = E::Fr::one();
|
let mut elt = E::Fr::one();
|
||||||
for i in 0..(1 << log_new_n) {
|
for (i, tmp) in tmp.iter_mut().enumerate() {
|
||||||
for s in 0..num_cpus {
|
for s in 0..num_cpus {
|
||||||
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
||||||
let mut t = a[idx];
|
let mut t = a[idx];
|
||||||
t.group_mul_assign(&elt);
|
t.group_mul_assign(&elt);
|
||||||
tmp[i].group_add_assign(&t);
|
tmp.group_add_assign(&t);
|
||||||
elt.mul_assign(&omega_step);
|
elt.mul_assign(&omega_step);
|
||||||
}
|
}
|
||||||
elt.mul_assign(&omega_j);
|
elt.mul_assign(&omega_j);
|
||||||
@@ -359,7 +364,7 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
|||||||
let tmp = &tmp;
|
let tmp = &tmp;
|
||||||
|
|
||||||
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
let mut idx = idx * chunk;
|
let mut idx = idx * chunk;
|
||||||
let mask = (1 << log_cpus) - 1;
|
let mask = (1 << log_cpus) - 1;
|
||||||
for a in a {
|
for a in a {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Self-contained sub-circuit implementations for various primitives.
|
||||||
|
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
pub mod blake2s;
|
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 crate::{ConstraintSystem, SynthesisError};
|
||||||
|
use ff::ScalarEngine;
|
||||||
use super::boolean::Boolean;
|
|
||||||
|
|
||||||
use super::uint32::UInt32;
|
|
||||||
|
|
||||||
use super::multieq::MultiEq;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
2.1. Parameters
|
2.1. Parameters
|
||||||
@@ -81,7 +79,7 @@ const SIGMA: [[usize; 16]; 10] = [
|
|||||||
END FUNCTION.
|
END FUNCTION.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn mixing_g<E: Engine, CS: ConstraintSystem<E>, M>(
|
fn mixing_g<E: ScalarEngine, CS: ConstraintSystem<E>, M>(
|
||||||
mut cs: M,
|
mut cs: M,
|
||||||
v: &mut [UInt32],
|
v: &mut [UInt32],
|
||||||
a: usize,
|
a: usize,
|
||||||
@@ -166,7 +164,7 @@ where
|
|||||||
END FUNCTION.
|
END FUNCTION.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
|
fn blake2s_compression<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
h: &mut [UInt32],
|
h: &mut [UInt32],
|
||||||
m: &[UInt32],
|
m: &[UInt32],
|
||||||
@@ -339,7 +337,7 @@ fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
|
|||||||
END FUNCTION.
|
END FUNCTION.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
|
pub fn blake2s<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
input: &[Boolean],
|
input: &[Boolean],
|
||||||
personalization: &[u8],
|
personalization: &[u8],
|
||||||
@@ -382,7 +380,7 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
|
|||||||
blocks.push(this_block);
|
blocks.push(this_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocks.len() == 0 {
|
if blocks.is_empty() {
|
||||||
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use blake2s_simd::Params as Blake2sParams;
|
use blake2s_simd::Params as Blake2sParams;
|
||||||
|
use hex_literal::hex;
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use rand_core::{RngCore, SeedableRng};
|
use rand_core::{RngCore, SeedableRng};
|
||||||
use rand_xorshift::XorShiftRng;
|
use rand_xorshift::XorShiftRng;
|
||||||
@@ -433,7 +432,7 @@ mod test {
|
|||||||
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
|
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
|
||||||
|
|
||||||
let mut out = out.into_iter();
|
let mut out = out.into_iter();
|
||||||
for b in expected.into_iter() {
|
for b in expected.iter() {
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
let c = out.next().unwrap().get_value().unwrap();
|
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};
|
//! Gadgets for allocating bits in the circuit and performing boolean logic.
|
||||||
use pairing::Engine;
|
|
||||||
|
use ff::{BitIterator, Field, PrimeField, ScalarEngine};
|
||||||
|
|
||||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ impl AllocatedBit {
|
|||||||
must_be_false: &AllocatedBit,
|
must_be_false: &AllocatedBit,
|
||||||
) -> Result<Self, SynthesisError>
|
) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let var = cs.alloc(
|
let var = cs.alloc(
|
||||||
@@ -60,7 +61,7 @@ impl AllocatedBit {
|
|||||||
|
|
||||||
Ok(AllocatedBit {
|
Ok(AllocatedBit {
|
||||||
variable: var,
|
variable: var,
|
||||||
value: value,
|
value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ impl AllocatedBit {
|
|||||||
/// boolean value.
|
/// boolean value.
|
||||||
pub fn alloc<E, CS>(mut cs: CS, value: Option<bool>) -> Result<Self, SynthesisError>
|
pub fn alloc<E, CS>(mut cs: CS, value: Option<bool>) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let var = cs.alloc(
|
let var = cs.alloc(
|
||||||
@@ -93,7 +94,7 @@ impl AllocatedBit {
|
|||||||
|
|
||||||
Ok(AllocatedBit {
|
Ok(AllocatedBit {
|
||||||
variable: var,
|
variable: var,
|
||||||
value: value,
|
value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ impl AllocatedBit {
|
|||||||
/// an `AllocatedBit`.
|
/// an `AllocatedBit`.
|
||||||
pub fn xor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
pub fn xor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let mut result_value = None;
|
let mut result_value = None;
|
||||||
@@ -153,7 +154,7 @@ impl AllocatedBit {
|
|||||||
/// an `AllocatedBit`.
|
/// an `AllocatedBit`.
|
||||||
pub fn and<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
pub fn and<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let mut result_value = None;
|
let mut result_value = None;
|
||||||
@@ -191,7 +192,7 @@ impl AllocatedBit {
|
|||||||
/// Calculates `a AND (NOT b)`.
|
/// Calculates `a AND (NOT b)`.
|
||||||
pub fn and_not<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
pub fn and_not<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let mut result_value = None;
|
let mut result_value = None;
|
||||||
@@ -229,7 +230,7 @@ impl AllocatedBit {
|
|||||||
/// Calculates `(NOT a) AND (NOT b)`.
|
/// Calculates `(NOT a) AND (NOT b)`.
|
||||||
pub fn nor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
pub fn nor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let mut result_value = None;
|
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,
|
mut cs: CS,
|
||||||
value: Option<u64>,
|
value: Option<u64>,
|
||||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||||
@@ -296,16 +297,16 @@ pub fn u64_into_boolean_vec_le<E: Engine, CS: ConstraintSystem<E>>(
|
|||||||
Ok(bits)
|
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,
|
cs: CS,
|
||||||
value: Option<F>,
|
value: Option<F>,
|
||||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||||
let v = field_into_allocated_bits_le::<E, CS, F>(cs, value)?;
|
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,
|
mut cs: CS,
|
||||||
value: Option<F>,
|
value: Option<F>,
|
||||||
) -> Result<Vec<AllocatedBit>, SynthesisError> {
|
) -> 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>
|
pub fn enforce_equal<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
@@ -412,24 +413,24 @@ impl Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_value(&self) -> Option<bool> {
|
pub fn get_value(&self) -> Option<bool> {
|
||||||
match self {
|
match *self {
|
||||||
&Boolean::Constant(c) => Some(c),
|
Boolean::Constant(c) => Some(c),
|
||||||
&Boolean::Is(ref v) => v.get_value(),
|
Boolean::Is(ref v) => v.get_value(),
|
||||||
&Boolean::Not(ref v) => v.get_value().map(|b| !b),
|
Boolean::Not(ref v) => v.get_value().map(|b| !b),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lc<E: Engine>(&self, one: Variable, coeff: E::Fr) -> LinearCombination<E> {
|
pub fn lc<E: ScalarEngine>(&self, one: Variable, coeff: E::Fr) -> LinearCombination<E> {
|
||||||
match self {
|
match *self {
|
||||||
&Boolean::Constant(c) => {
|
Boolean::Constant(c) => {
|
||||||
if c {
|
if c {
|
||||||
LinearCombination::<E>::zero() + (coeff, one)
|
LinearCombination::<E>::zero() + (coeff, one)
|
||||||
} else {
|
} else {
|
||||||
LinearCombination::<E>::zero()
|
LinearCombination::<E>::zero()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Boolean::Is(ref v) => LinearCombination::<E>::zero() + (coeff, v.get_variable()),
|
Boolean::Is(ref v) => LinearCombination::<E>::zero() + (coeff, v.get_variable()),
|
||||||
&Boolean::Not(ref v) => {
|
Boolean::Not(ref v) => {
|
||||||
LinearCombination::<E>::zero() + (coeff, one) - (coeff, v.get_variable())
|
LinearCombination::<E>::zero() + (coeff, one) - (coeff, v.get_variable())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,17 +443,17 @@ impl Boolean {
|
|||||||
|
|
||||||
/// Return a negated interpretation of this boolean.
|
/// Return a negated interpretation of this boolean.
|
||||||
pub fn not(&self) -> Self {
|
pub fn not(&self) -> Self {
|
||||||
match self {
|
match *self {
|
||||||
&Boolean::Constant(c) => Boolean::Constant(!c),
|
Boolean::Constant(c) => Boolean::Constant(!c),
|
||||||
&Boolean::Is(ref v) => Boolean::Not(v.clone()),
|
Boolean::Is(ref v) => Boolean::Not(v.clone()),
|
||||||
&Boolean::Not(ref v) => Boolean::Is(v.clone()),
|
Boolean::Not(ref v) => Boolean::Is(v.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform XOR over two boolean operands
|
/// Perform XOR over two boolean operands
|
||||||
pub fn xor<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
|
pub fn xor<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
@@ -474,7 +475,7 @@ impl Boolean {
|
|||||||
/// Perform AND over two boolean operands
|
/// Perform AND over two boolean operands
|
||||||
pub fn and<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
|
pub fn and<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
@@ -508,7 +509,7 @@ impl Boolean {
|
|||||||
c: &'a Self,
|
c: &'a Self,
|
||||||
) -> Result<Self, SynthesisError>
|
) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let ch_value = match (a.get_value(), b.get_value(), c.get_value()) {
|
let ch_value = match (a.get_value(), b.get_value(), c.get_value()) {
|
||||||
@@ -615,7 +616,7 @@ impl Boolean {
|
|||||||
c: &'a Self,
|
c: &'a Self,
|
||||||
) -> Result<Self, SynthesisError>
|
) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let maj_value = match (a.get_value(), b.get_value(), c.get_value()) {
|
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;
|
//! Window table lookup gadgets.
|
||||||
use pairing::Engine;
|
|
||||||
|
use ff::{Field, ScalarEngine};
|
||||||
|
|
||||||
use super::boolean::Boolean;
|
use super::boolean::Boolean;
|
||||||
use super::num::{AllocatedNum, Num};
|
use super::num::{AllocatedNum, Num};
|
||||||
@@ -7,7 +8,7 @@ use super::*;
|
|||||||
use crate::ConstraintSystem;
|
use crate::ConstraintSystem;
|
||||||
|
|
||||||
// Synthesize the constants for each base pattern.
|
// 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
|
where
|
||||||
I: IntoIterator<Item = &'a E::Fr>,
|
I: IntoIterator<Item = &'a E::Fr>,
|
||||||
{
|
{
|
||||||
@@ -28,7 +29,7 @@ where
|
|||||||
|
|
||||||
/// Performs a 3-bit window table lookup. `bits` is in
|
/// Performs a 3-bit window table lookup. `bits` is in
|
||||||
/// little-endian order.
|
/// little-endian order.
|
||||||
pub fn lookup3_xy<E: Engine, CS>(
|
pub fn lookup3_xy<E: ScalarEngine, CS>(
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
bits: &[Boolean],
|
bits: &[Boolean],
|
||||||
coords: &[(E::Fr, E::Fr)],
|
coords: &[(E::Fr, E::Fr)],
|
||||||
@@ -118,7 +119,7 @@ where
|
|||||||
|
|
||||||
/// Performs a 3-bit window table lookup, where
|
/// Performs a 3-bit window table lookup, where
|
||||||
/// one of the bits is a sign bit.
|
/// one of the bits is a sign bit.
|
||||||
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
|
pub fn lookup3_xy_with_conditional_negation<E: ScalarEngine, CS>(
|
||||||
mut cs: CS,
|
mut cs: CS,
|
||||||
bits: &[Boolean],
|
bits: &[Boolean],
|
||||||
coords: &[(E::Fr, E::Fr)],
|
coords: &[(E::Fr, E::Fr)],
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use ff::{Field, PrimeField};
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
use pairing::Engine;
|
|
||||||
|
|
||||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||||
|
|
||||||
pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>> {
|
pub struct MultiEq<E: ScalarEngine, CS: ConstraintSystem<E>> {
|
||||||
cs: CS,
|
cs: CS,
|
||||||
ops: usize,
|
ops: usize,
|
||||||
bits_used: usize,
|
bits_used: usize,
|
||||||
@@ -11,10 +10,10 @@ pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>> {
|
|||||||
rhs: LinearCombination<E>,
|
rhs: LinearCombination<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
||||||
pub fn new(cs: CS) -> Self {
|
pub fn new(cs: CS) -> Self {
|
||||||
MultiEq {
|
MultiEq {
|
||||||
cs: cs,
|
cs,
|
||||||
ops: 0,
|
ops: 0,
|
||||||
bits_used: 0,
|
bits_used: 0,
|
||||||
lhs: LinearCombination::zero(),
|
lhs: LinearCombination::zero(),
|
||||||
@@ -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) {
|
fn drop(&mut self) {
|
||||||
if self.bits_used > 0 {
|
if self.bits_used > 0 {
|
||||||
self.accumulate();
|
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;
|
type Root = Self;
|
||||||
|
|
||||||
fn one() -> Variable {
|
fn one() -> Variable {
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
|
//! Helpers for packing vectors of bits into scalar field elements.
|
||||||
|
|
||||||
use super::boolean::Boolean;
|
use super::boolean::Boolean;
|
||||||
use super::num::Num;
|
use super::num::Num;
|
||||||
use super::Assignment;
|
use super::Assignment;
|
||||||
use crate::{ConstraintSystem, SynthesisError};
|
use crate::{ConstraintSystem, SynthesisError};
|
||||||
use ff::{Field, PrimeField};
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
use pairing::Engine;
|
|
||||||
|
|
||||||
/// Takes a sequence of booleans and exposes them as compact
|
/// Takes a sequence of booleans and exposes them as compact
|
||||||
/// public inputs
|
/// public inputs
|
||||||
pub fn pack_into_inputs<E, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError>
|
pub fn pack_into_inputs<E, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() {
|
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() {
|
||||||
@@ -49,7 +50,7 @@ pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> {
|
|||||||
.collect()
|
.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![];
|
let mut result = vec![];
|
||||||
|
|
||||||
for bits in bits.chunks(E::Fr::CAPACITY as usize) {
|
for bits in bits.chunks(E::Fr::CAPACITY as usize) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr};
|
//! Gadgets representing numbers in the scalar field of the underlying curve.
|
||||||
use pairing::Engine;
|
|
||||||
|
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||||
|
|
||||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||||
|
|
||||||
@@ -7,12 +8,12 @@ use super::Assignment;
|
|||||||
|
|
||||||
use super::boolean::{self, AllocatedBit, Boolean};
|
use super::boolean::{self, AllocatedBit, Boolean};
|
||||||
|
|
||||||
pub struct AllocatedNum<E: Engine> {
|
pub struct AllocatedNum<E: ScalarEngine> {
|
||||||
value: Option<E::Fr>,
|
value: Option<E::Fr>,
|
||||||
variable: Variable,
|
variable: Variable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Clone for AllocatedNum<E> {
|
impl<E: ScalarEngine> Clone for AllocatedNum<E> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
AllocatedNum {
|
AllocatedNum {
|
||||||
value: self.value,
|
value: self.value,
|
||||||
@@ -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>
|
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
@@ -66,7 +67,7 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
/// order, requiring that the representation
|
/// order, requiring that the representation
|
||||||
/// strictly exists "in the field" (i.e., a
|
/// strictly exists "in the field" (i.e., a
|
||||||
/// congruency is not allowed.)
|
/// congruency is not allowed.)
|
||||||
pub fn into_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
where
|
where
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
@@ -75,10 +76,10 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
v: &[AllocatedBit],
|
v: &[AllocatedBit],
|
||||||
) -> Result<AllocatedBit, SynthesisError>
|
) -> Result<AllocatedBit, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
assert!(v.len() > 0);
|
assert!(!v.is_empty());
|
||||||
|
|
||||||
// Let's keep this simple for now and just AND them all
|
// Let's keep this simple for now and just AND them all
|
||||||
// manually
|
// manually
|
||||||
@@ -132,7 +133,7 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
current_run.push(a_bit.clone());
|
current_run.push(a_bit.clone());
|
||||||
result.push(a_bit);
|
result.push(a_bit);
|
||||||
} else {
|
} else {
|
||||||
if current_run.len() > 0 {
|
if !current_run.is_empty() {
|
||||||
// This is the start of a run of zeros, but we need
|
// This is the start of a run of zeros, but we need
|
||||||
// to k-ary AND against `last_run` first.
|
// to k-ary AND against `last_run` first.
|
||||||
|
|
||||||
@@ -183,13 +184,13 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||||
|
|
||||||
// Convert into booleans, and reverse for little-endian bit order
|
// Convert into booleans, and reverse for little-endian bit order
|
||||||
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect())
|
Ok(result.into_iter().map(Boolean::from).rev().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the allocated number into its little-endian representation.
|
/// Convert the allocated number into its little-endian representation.
|
||||||
/// Note that this does not strongly enforce that the commitment is
|
/// Note that this does not strongly enforce that the commitment is
|
||||||
/// "in the field."
|
/// "in the field."
|
||||||
pub fn into_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
pub fn to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
where
|
where
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
@@ -208,7 +209,7 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
|
|
||||||
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||||
|
|
||||||
Ok(bits.into_iter().map(|b| Boolean::from(b)).collect())
|
Ok(bits.into_iter().map(Boolean::from).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||||
@@ -238,7 +239,7 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Ok(AllocatedNum {
|
Ok(AllocatedNum {
|
||||||
value: value,
|
value,
|
||||||
variable: var,
|
variable: var,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -270,7 +271,7 @@ impl<E: Engine> AllocatedNum<E> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Ok(AllocatedNum {
|
Ok(AllocatedNum {
|
||||||
value: value,
|
value,
|
||||||
variable: var,
|
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>,
|
value: Option<E::Fr>,
|
||||||
lc: LinearCombination<E>,
|
lc: LinearCombination<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
|
impl<E: ScalarEngine> From<AllocatedNum<E>> for Num<E> {
|
||||||
fn from(num: AllocatedNum<E>) -> Num<E> {
|
fn from(num: AllocatedNum<E>) -> Num<E> {
|
||||||
Num {
|
Num {
|
||||||
value: num.value,
|
value: num.value,
|
||||||
@@ -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 {
|
pub fn zero() -> Self {
|
||||||
Num {
|
Num {
|
||||||
value: Some(E::Fr::zero()),
|
value: Some(E::Fr::zero()),
|
||||||
@@ -522,7 +523,7 @@ mod test {
|
|||||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||||
|
|
||||||
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
|
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
|
||||||
n.into_bits_le_strict(&mut cs).unwrap();
|
n.to_bits_le_strict(&mut cs).unwrap();
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
|
|
||||||
@@ -550,9 +551,9 @@ mod test {
|
|||||||
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
|
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
|
||||||
|
|
||||||
let bits = if i % 2 == 0 {
|
let bits = if i % 2 == 0 {
|
||||||
n.into_bits_le(&mut cs).unwrap()
|
n.to_bits_le(&mut cs).unwrap()
|
||||||
} else {
|
} else {
|
||||||
n.into_bits_le_strict(&mut cs).unwrap()
|
n.to_bits_le_strict(&mut cs).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
|
|||||||
@@ -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::boolean::Boolean;
|
||||||
use super::multieq::MultiEq;
|
use super::multieq::MultiEq;
|
||||||
use super::uint32::UInt32;
|
use super::uint32::UInt32;
|
||||||
use crate::{ConstraintSystem, SynthesisError};
|
use crate::{ConstraintSystem, SynthesisError};
|
||||||
use pairing::Engine;
|
use ff::ScalarEngine;
|
||||||
|
|
||||||
|
#[allow(clippy::unreadable_literal)]
|
||||||
const ROUND_CONSTANTS: [u32; 64] = [
|
const ROUND_CONSTANTS: [u32; 64] = [
|
||||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
@@ -15,6 +21,7 @@ const ROUND_CONSTANTS: [u32; 64] = [
|
|||||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[allow(clippy::unreadable_literal)]
|
||||||
const IV: [u32; 8] = [
|
const IV: [u32; 8] = [
|
||||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||||
];
|
];
|
||||||
@@ -24,7 +31,7 @@ pub fn sha256_block_no_padding<E, CS>(
|
|||||||
input: &[Boolean],
|
input: &[Boolean],
|
||||||
) -> Result<Vec<Boolean>, SynthesisError>
|
) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
assert_eq!(input.len(), 512);
|
assert_eq!(input.len(), 512);
|
||||||
@@ -39,7 +46,7 @@ where
|
|||||||
|
|
||||||
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
|
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
assert!(input.len() % 8 == 0);
|
assert!(input.len() % 8 == 0);
|
||||||
@@ -76,7 +83,7 @@ fn sha256_compression_function<E, CS>(
|
|||||||
current_hash_value: &[UInt32],
|
current_hash_value: &[UInt32],
|
||||||
) -> Result<Vec<UInt32>, SynthesisError>
|
) -> Result<Vec<UInt32>, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
assert_eq!(input.len(), 512);
|
assert_eq!(input.len(), 512);
|
||||||
@@ -123,14 +130,14 @@ where
|
|||||||
impl Maybe {
|
impl Maybe {
|
||||||
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
|
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||||
{
|
{
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Maybe::Concrete(ref v) => return Ok(v.clone()),
|
Maybe::Concrete(ref v) => return Ok(v.clone()),
|
||||||
Maybe::Deferred(mut v) => {
|
Maybe::Deferred(mut v) => {
|
||||||
v.extend(others.into_iter().cloned());
|
v.extend(others.iter().cloned());
|
||||||
UInt32::addmany(cs, &v)?
|
UInt32::addmany(cs, &v)?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -266,6 +273,7 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::gadgets::boolean::AllocatedBit;
|
use crate::gadgets::boolean::AllocatedBit;
|
||||||
use crate::gadgets::test::TestConstraintSystem;
|
use crate::gadgets::test::TestConstraintSystem;
|
||||||
|
use hex_literal::hex;
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use rand_core::{RngCore, SeedableRng};
|
use rand_core::{RngCore, SeedableRng};
|
||||||
use rand_xorshift::XorShiftRng;
|
use rand_xorshift::XorShiftRng;
|
||||||
@@ -286,7 +294,7 @@ mod test {
|
|||||||
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
||||||
|
|
||||||
let mut out = out_bits.into_iter();
|
let mut out = out_bits.into_iter();
|
||||||
for b in expected.into_iter() {
|
for b in expected.iter() {
|
||||||
for i in (0..8).rev() {
|
for i in (0..8).rev() {
|
||||||
let c = out.next().unwrap().get_value().unwrap();
|
let c = out.next().unwrap().get_value().unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
//! Helpers for testing circuit implementations.
|
||||||
use pairing::Engine;
|
|
||||||
|
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||||
|
|
||||||
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ enum NamedObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Constraint system for testing purposes.
|
/// Constraint system for testing purposes.
|
||||||
pub struct TestConstraintSystem<E: Engine> {
|
pub struct TestConstraintSystem<E: ScalarEngine> {
|
||||||
named_objects: HashMap<String, NamedObject>,
|
named_objects: HashMap<String, NamedObject>,
|
||||||
current_namespace: Vec<String>,
|
current_namespace: Vec<String>,
|
||||||
constraints: Vec<(
|
constraints: Vec<(
|
||||||
@@ -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();
|
let mut map = BTreeMap::new();
|
||||||
for &(var, coeff) in terms {
|
for &(var, coeff) in terms {
|
||||||
map.entry(OrderedVariable(var))
|
map.entry(OrderedVariable(var))
|
||||||
.or_insert(E::Fr::zero())
|
.or_insert_with(E::Fr::zero)
|
||||||
.add_assign(&coeff);
|
.add_assign(&coeff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ fn proc_lc<E: Engine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable,
|
|||||||
map
|
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 map = proc_lc::<E>(terms);
|
||||||
|
|
||||||
let mut buf = [0u8; 9 + 32];
|
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)],
|
terms: &[(Variable, E::Fr)],
|
||||||
inputs: &[(E::Fr, String)],
|
inputs: &[(E::Fr, String)],
|
||||||
aux: &[(E::Fr, String)],
|
aux: &[(E::Fr, String)],
|
||||||
@@ -130,7 +131,7 @@ fn eval_lc<E: Engine>(
|
|||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> TestConstraintSystem<E> {
|
impl<E: ScalarEngine> TestConstraintSystem<E> {
|
||||||
pub fn new() -> TestConstraintSystem<E> {
|
pub fn new() -> TestConstraintSystem<E> {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert(
|
map.insert(
|
||||||
@@ -157,7 +158,7 @@ impl<E: Engine> TestConstraintSystem<E> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let powers_of_two = (0..E::Fr::NUM_BITS)
|
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<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let pp = |s: &mut String, lc: &LinearCombination<E>| {
|
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 {
|
pub fn num_inputs(&self) -> usize {
|
||||||
@@ -344,7 +345,7 @@ fn compute_path(ns: &[String], this: String) -> String {
|
|||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
||||||
type Root = Self;
|
type Root = Self;
|
||||||
|
|
||||||
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
use ff::{Field, PrimeField};
|
//! Circuit representation of a [`u32`], with helpers for the [`sha256`]
|
||||||
use pairing::Engine;
|
//! gadgets.
|
||||||
|
//!
|
||||||
|
//! [`sha256`]: crate::gadgets::sha256
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField, ScalarEngine};
|
||||||
|
|
||||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError};
|
use crate::{ConstraintSystem, LinearCombination, SynthesisError};
|
||||||
|
|
||||||
@@ -33,7 +37,7 @@ impl UInt32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UInt32 {
|
UInt32 {
|
||||||
bits: bits,
|
bits,
|
||||||
value: Some(value),
|
value: Some(value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +45,7 @@ impl UInt32 {
|
|||||||
/// Allocate a `UInt32` in the constraint system
|
/// Allocate a `UInt32` in the constraint system
|
||||||
pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
|
pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let values = match value {
|
let values = match value {
|
||||||
@@ -69,14 +73,13 @@ impl UInt32 {
|
|||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
||||||
|
|
||||||
Ok(UInt32 {
|
Ok(UInt32 { bits, value })
|
||||||
bits: bits,
|
|
||||||
value: value,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_bits_be(&self) -> Vec<Boolean> {
|
pub fn into_bits_be(self) -> Vec<Boolean> {
|
||||||
self.bits.iter().rev().cloned().collect()
|
let mut ret = self.bits;
|
||||||
|
ret.reverse();
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bits_be(bits: &[Boolean]) -> Self {
|
pub fn from_bits_be(bits: &[Boolean]) -> Self {
|
||||||
@@ -98,14 +101,14 @@ impl UInt32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UInt32 {
|
UInt32 {
|
||||||
value: value,
|
value,
|
||||||
bits: bits.iter().rev().cloned().collect(),
|
bits: bits.iter().rev().cloned().collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turns this `UInt32` into its little-endian byte order representation.
|
/// Turns this `UInt32` into its little-endian byte order representation.
|
||||||
pub fn into_bits(&self) -> Vec<Boolean> {
|
pub fn into_bits(self) -> Vec<Boolean> {
|
||||||
self.bits.clone()
|
self.bits
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a little-endian byte order representation of bits into a
|
/// Converts a little-endian byte order representation of bits into a
|
||||||
@@ -119,20 +122,20 @@ impl UInt32 {
|
|||||||
for b in new_bits.iter().rev() {
|
for b in new_bits.iter().rev() {
|
||||||
value.as_mut().map(|v| *v <<= 1);
|
value.as_mut().map(|v| *v <<= 1);
|
||||||
|
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Constant(b) => {
|
Boolean::Constant(b) => {
|
||||||
if b {
|
if b {
|
||||||
value.as_mut().map(|v| *v |= 1);
|
value.as_mut().map(|v| *v |= 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Boolean::Is(ref b) => match b.get_value() {
|
Boolean::Is(ref b) => match b.get_value() {
|
||||||
Some(true) => {
|
Some(true) => {
|
||||||
value.as_mut().map(|v| *v |= 1);
|
value.as_mut().map(|v| *v |= 1);
|
||||||
}
|
}
|
||||||
Some(false) => {}
|
Some(false) => {}
|
||||||
None => value = None,
|
None => value = None,
|
||||||
},
|
},
|
||||||
&Boolean::Not(ref b) => match b.get_value() {
|
Boolean::Not(ref b) => match b.get_value() {
|
||||||
Some(false) => {
|
Some(false) => {
|
||||||
value.as_mut().map(|v| *v |= 1);
|
value.as_mut().map(|v| *v |= 1);
|
||||||
}
|
}
|
||||||
@@ -143,7 +146,7 @@ impl UInt32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UInt32 {
|
UInt32 {
|
||||||
value: value,
|
value,
|
||||||
bits: new_bits,
|
bits: new_bits,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,7 +198,7 @@ impl UInt32 {
|
|||||||
circuit_fn: U,
|
circuit_fn: U,
|
||||||
) -> Result<Self, SynthesisError>
|
) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
F: Fn(u32, u32, u32) -> u32,
|
F: Fn(u32, u32, u32) -> u32,
|
||||||
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
|
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
|
||||||
@@ -215,7 +218,7 @@ impl UInt32 {
|
|||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
Ok(UInt32 {
|
Ok(UInt32 {
|
||||||
bits: bits,
|
bits,
|
||||||
value: new_value,
|
value: new_value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -224,7 +227,7 @@ impl UInt32 {
|
|||||||
/// during SHA256.
|
/// during SHA256.
|
||||||
pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
Self::triop(
|
Self::triop(
|
||||||
@@ -241,7 +244,7 @@ impl UInt32 {
|
|||||||
/// during SHA256.
|
/// during SHA256.
|
||||||
pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
Self::triop(
|
Self::triop(
|
||||||
@@ -257,7 +260,7 @@ impl UInt32 {
|
|||||||
/// XOR this `UInt32` with another `UInt32`
|
/// XOR this `UInt32` with another `UInt32`
|
||||||
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
{
|
{
|
||||||
let new_value = match (self.value, other.value) {
|
let new_value = match (self.value, other.value) {
|
||||||
@@ -274,7 +277,7 @@ impl UInt32 {
|
|||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
Ok(UInt32 {
|
Ok(UInt32 {
|
||||||
bits: bits,
|
bits,
|
||||||
value: new_value,
|
value: new_value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -282,7 +285,7 @@ impl UInt32 {
|
|||||||
/// Perform modular addition of several `UInt32` objects.
|
/// Perform modular addition of several `UInt32` objects.
|
||||||
pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
|
pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
|
||||||
where
|
where
|
||||||
E: Engine,
|
E: ScalarEngine,
|
||||||
CS: ConstraintSystem<E>,
|
CS: ConstraintSystem<E>,
|
||||||
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
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
|
// Compute the maximum value of the sum so we allocate enough bits for
|
||||||
// the result
|
// the result
|
||||||
let mut max_value = (operands.len() as u64) * (u32::max_value() as u64);
|
let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value()));
|
||||||
|
|
||||||
// Keep track of the resulting value
|
// Keep track of the resulting value
|
||||||
let mut result_value = Some(0u64);
|
let mut result_value = Some(0u64);
|
||||||
@@ -310,7 +313,7 @@ impl UInt32 {
|
|||||||
// Accumulate the value
|
// Accumulate the value
|
||||||
match op.value {
|
match op.value {
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
result_value.as_mut().map(|v| *v += val as u64);
|
result_value.as_mut().map(|v| *v += u64::from(val));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// If any of our operands have unknown value, we won't
|
// If any of our operands have unknown value, we won't
|
||||||
@@ -401,15 +404,15 @@ mod test {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let mut v = (0..32)
|
let v = (0..32)
|
||||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let b = UInt32::from_bits_be(&v);
|
let b = UInt32::from_bits_be(&v);
|
||||||
|
|
||||||
for (i, bit) in b.bits.iter().enumerate() {
|
for (i, bit) in b.bits.iter().enumerate() {
|
||||||
match bit {
|
match *bit {
|
||||||
&Boolean::Constant(bit) => {
|
Boolean::Constant(bit) => {
|
||||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -436,15 +439,15 @@ mod test {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
let mut v = (0..32)
|
let v = (0..32)
|
||||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let b = UInt32::from_bits(&v);
|
let b = UInt32::from_bits(&v);
|
||||||
|
|
||||||
for (i, bit) in b.bits.iter().enumerate() {
|
for (i, bit) in b.bits.iter().enumerate() {
|
||||||
match bit {
|
match *bit {
|
||||||
&Boolean::Constant(bit) => {
|
Boolean::Constant(bit) => {
|
||||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -491,14 +494,14 @@ mod test {
|
|||||||
assert!(r.value == Some(expected));
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
for b in r.bits.iter() {
|
for b in r.bits.iter() {
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Is(ref b) => {
|
Boolean::Is(ref b) => {
|
||||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
&Boolean::Not(ref b) => {
|
Boolean::Not(ref b) => {
|
||||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
&Boolean::Constant(b) => {
|
Boolean::Constant(b) => {
|
||||||
assert!(b == (expected & 1 == 1));
|
assert!(b == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -538,10 +541,10 @@ mod test {
|
|||||||
assert!(r.value == Some(expected));
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
for b in r.bits.iter() {
|
for b in r.bits.iter() {
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Is(_) => panic!(),
|
Boolean::Is(_) => panic!(),
|
||||||
&Boolean::Not(_) => panic!(),
|
Boolean::Not(_) => panic!(),
|
||||||
&Boolean::Constant(b) => {
|
Boolean::Constant(b) => {
|
||||||
assert!(b == (expected & 1 == 1));
|
assert!(b == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -576,8 +579,7 @@ mod test {
|
|||||||
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
|
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
|
||||||
let r = {
|
let r = {
|
||||||
let mut cs = MultiEq::new(&mut cs);
|
let mut cs = MultiEq::new(&mut cs);
|
||||||
let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap();
|
UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap()
|
||||||
r
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied());
|
||||||
@@ -585,14 +587,14 @@ mod test {
|
|||||||
assert!(r.value == Some(expected));
|
assert!(r.value == Some(expected));
|
||||||
|
|
||||||
for b in r.bits.iter() {
|
for b in r.bits.iter() {
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Is(ref b) => {
|
Boolean::Is(ref b) => {
|
||||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
&Boolean::Not(ref b) => {
|
Boolean::Not(ref b) => {
|
||||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||||
}
|
}
|
||||||
&Boolean::Constant(_) => unreachable!(),
|
Boolean::Constant(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
expected >>= 1;
|
expected >>= 1;
|
||||||
@@ -628,8 +630,8 @@ mod test {
|
|||||||
|
|
||||||
let mut tmp = num;
|
let mut tmp = num;
|
||||||
for b in &b.bits {
|
for b in &b.bits {
|
||||||
match b {
|
match *b {
|
||||||
&Boolean::Constant(b) => {
|
Boolean::Constant(b) => {
|
||||||
assert_eq!(b, tmp & 1 == 1);
|
assert_eq!(b, tmp & 1 == 1);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ use pairing::Engine;
|
|||||||
|
|
||||||
use super::{Parameters, VerifyingKey};
|
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
|
/// Generates a random common reference string for
|
||||||
/// a circuit.
|
/// a circuit.
|
||||||
@@ -227,7 +227,7 @@ where
|
|||||||
let powers_of_tau = powers_of_tau.as_mut();
|
let powers_of_tau = powers_of_tau.as_mut();
|
||||||
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
||||||
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
|
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
|
let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
|
||||||
|
|
||||||
for p in powers_of_tau {
|
for p in powers_of_tau {
|
||||||
@@ -251,7 +251,7 @@ where
|
|||||||
{
|
{
|
||||||
let mut g1_wnaf = g1_wnaf.shared();
|
let mut g1_wnaf = g1_wnaf.shared();
|
||||||
|
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
||||||
for (h, p) in h.iter_mut().zip(p.iter()) {
|
for (h, p) in h.iter_mut().zip(p.iter()) {
|
||||||
// Compute final exponent
|
// Compute final exponent
|
||||||
@@ -330,7 +330,7 @@ where
|
|||||||
let mut g1_wnaf = g1_wnaf.shared();
|
let mut g1_wnaf = g1_wnaf.shared();
|
||||||
let mut g2_wnaf = g2_wnaf.shared();
|
let mut g2_wnaf = g2_wnaf.shared();
|
||||||
|
|
||||||
scope.spawn(move || {
|
scope.spawn(move |_scope| {
|
||||||
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
|
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(b_g1.iter_mut())
|
.zip(b_g1.iter_mut())
|
||||||
@@ -451,7 +451,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(Parameters {
|
Ok(Parameters {
|
||||||
vk: vk,
|
vk,
|
||||||
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
|
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
|
||||||
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
|
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
|
//! The [Groth16] proving system.
|
||||||
|
//!
|
||||||
|
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||||
|
|
||||||
use group::{CurveAffine, EncodedPoint};
|
use group::{CurveAffine, EncodedPoint};
|
||||||
use pairing::{Engine, PairingCurveAffine};
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
use SynthesisError;
|
use crate::SynthesisError;
|
||||||
|
|
||||||
|
use crate::multiexp::SourceBuilder;
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use multiexp::SourceBuilder;
|
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::sync::Arc;
|
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 {
|
Ok(VerifyingKey {
|
||||||
alpha_g1: alpha_g1,
|
alpha_g1,
|
||||||
beta_g1: beta_g1,
|
beta_g1,
|
||||||
beta_g2: beta_g2,
|
beta_g2,
|
||||||
gamma_g2: gamma_g2,
|
gamma_g2,
|
||||||
delta_g1: delta_g1,
|
delta_g1,
|
||||||
delta_g2: delta_g2,
|
delta_g2,
|
||||||
ic: ic,
|
ic,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,7 +380,7 @@ impl<E: Engine> Parameters<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(Parameters {
|
Ok(Parameters {
|
||||||
vk: vk,
|
vk,
|
||||||
h: Arc::new(h),
|
h: Arc::new(h),
|
||||||
l: Arc::new(l),
|
l: Arc::new(l),
|
||||||
a: Arc::new(a),
|
a: Arc::new(a),
|
||||||
@@ -465,7 +469,7 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_with_bls12_381 {
|
mod test_with_bls12_381 {
|
||||||
use super::*;
|
use super::*;
|
||||||
use {Circuit, ConstraintSystem, SynthesisError};
|
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use pairing::bls12_381::{Bls12, Fr};
|
use pairing::bls12_381::{Bls12, Fr};
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ use pairing::Engine;
|
|||||||
|
|
||||||
use super::{ParameterSource, Proof};
|
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>(
|
fn eval<E: Engine>(
|
||||||
lc: &LinearCombination<E>,
|
lc: &LinearCombination<E>,
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
|||||||
pub struct Fr(Wrapping<u32>);
|
pub struct Fr(Wrapping<u32>);
|
||||||
|
|
||||||
impl fmt::Display for Fr {
|
impl fmt::Display for Fr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
write!(f, "{}", (self.0).0)
|
write!(f, "{}", (self.0).0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Field for Fr {
|
impl 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)
|
Fr(Wrapping(rng.next_u32()) % MODULUS_R)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ impl PartialOrd for FrRepr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FrRepr {
|
impl fmt::Display for FrRepr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
write!(f, "{}", (self.0)[0])
|
write!(f, "{}", (self.0)[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use self::dummy_engine::*;
|
|||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use {Circuit, ConstraintSystem, SynthesisError};
|
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof};
|
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 super::{PreparedVerifyingKey, Proof, VerifyingKey};
|
||||||
|
|
||||||
use SynthesisError;
|
use crate::SynthesisError;
|
||||||
|
|
||||||
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
|
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
|
||||||
let mut gamma = vk.gamma_g2;
|
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),
|
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
|
||||||
(&proof.c.prepare(), &pvk.neg_delta_g2),
|
(&proof.c.prepare(), &pvk.neg_delta_g2),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.iter(),
|
||||||
))
|
))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
== pvk.alpha_g1_beta_g2)
|
== pvk.alpha_g1_beta_g2)
|
||||||
|
|||||||
@@ -1,33 +1,139 @@
|
|||||||
extern crate ff;
|
//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
|
||||||
extern crate group;
|
//! traits and and primitive structures, as well as basic gadget implementations
|
||||||
#[cfg(feature = "pairing")]
|
//! such as booleans and number abstractions.
|
||||||
extern crate pairing;
|
//!
|
||||||
extern crate rand_core;
|
//! # 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;
|
// Catch documentation errors caused by code changes.
|
||||||
extern crate blake2s_simd;
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
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;
|
|
||||||
|
|
||||||
pub mod domain;
|
pub mod domain;
|
||||||
pub mod gadgets;
|
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> {
|
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
|
||||||
type Output = LinearCombination<E>;
|
type Output = LinearCombination<E>;
|
||||||
|
|
||||||
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||||
coeff.negate();
|
coeff.negate();
|
||||||
|
|
||||||
@@ -230,8 +337,8 @@ impl Error for SynthesisError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SynthesisError {
|
impl fmt::Display for SynthesisError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
if let &SynthesisError::IoError(ref e) = self {
|
if let SynthesisError::IoError(ref e) = *self {
|
||||||
write!(f, "I/O error: ")?;
|
write!(f, "I/O error: ")?;
|
||||||
e.fmt(f)
|
e.fmt(f)
|
||||||
} else {
|
} else {
|
||||||
@@ -296,7 +403,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
|
|||||||
fn get_root(&mut self) -> &mut Self::Root;
|
fn get_root(&mut self) -> &mut Self::Root;
|
||||||
|
|
||||||
/// Begin a namespace for this constraint system.
|
/// Begin a namespace for this constraint system.
|
||||||
fn namespace<'a, NR, N>(&'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
|
where
|
||||||
NR: Into<String>,
|
NR: Into<String>,
|
||||||
N: FnOnce() -> NR,
|
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
|
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
|
||||||
/// a namespace context) and, when dropped, pops out of the namespace context.
|
/// a namespace context) and, when dropped, pops out of the namespace context.
|
||||||
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>);
|
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E>>(&'a mut CS, PhantomData<E>);
|
||||||
|
|
||||||
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
|
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
|
||||||
type Root = CS::Root;
|
type Root = CS::Root;
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
//! This is an interface for dealing with the kinds of
|
//! An interface for dealing with the kinds of parallel computations involved in
|
||||||
//! parallel computations involved in bellman. It's
|
//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and
|
||||||
//! currently just a thin wrapper around CpuPool and
|
//! [`crossbeam`] but may be extended in the future to allow for various
|
||||||
//! crossbeam but may be extended in the future to
|
//! parallelism strategies.
|
||||||
//! allow for various parallelism strategies.
|
//!
|
||||||
|
//! [`CpuPool`]: futures_cpupool::CpuPool
|
||||||
|
|
||||||
#[cfg(feature = "multicore")]
|
#[cfg(feature = "multicore")]
|
||||||
mod implementation {
|
mod implementation {
|
||||||
use crossbeam::{self, Scope};
|
use crossbeam::{self, thread::Scope};
|
||||||
use futures::{Future, IntoFuture, Poll};
|
use futures::{Future, IntoFuture, Poll};
|
||||||
use futures_cpupool::{CpuFuture, CpuPool};
|
use futures_cpupool::{CpuFuture, CpuPool};
|
||||||
use num_cpus;
|
use num_cpus;
|
||||||
@@ -23,7 +24,7 @@ mod implementation {
|
|||||||
// CPUs configured.
|
// CPUs configured.
|
||||||
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
|
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
|
||||||
Worker {
|
Worker {
|
||||||
cpus: cpus,
|
cpus,
|
||||||
pool: CpuPool::new(cpus),
|
pool: CpuPool::new(cpus),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,9 @@ mod implementation {
|
|||||||
elements / self.cpus
|
elements / self.cpus
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Handle case where threads fail
|
||||||
crossbeam::scope(|scope| f(scope, chunk_size))
|
crossbeam::scope(|scope| f(scope, chunk_size))
|
||||||
|
.expect("Threads aren't allowed to fail yet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,8 +155,8 @@ mod implementation {
|
|||||||
pub struct DummyScope;
|
pub struct DummyScope;
|
||||||
|
|
||||||
impl DummyScope {
|
impl DummyScope {
|
||||||
pub fn spawn<F: FnOnce()>(&self, f: F) {
|
pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
|
||||||
f();
|
f(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ fn multiexp_inner<Q, D, G, S>(
|
|||||||
mut skip: u32,
|
mut skip: u32,
|
||||||
c: u32,
|
c: u32,
|
||||||
handle_trivial: bool,
|
handle_trivial: bool,
|
||||||
) -> Box<Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||||
where
|
where
|
||||||
for<'a> &'a Q: QueryDensity,
|
for<'a> &'a Q: QueryDensity,
|
||||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
@@ -256,7 +256,7 @@ pub fn multiexp<Q, D, G, S>(
|
|||||||
bases: S,
|
bases: S,
|
||||||
density_map: D,
|
density_map: D,
|
||||||
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
||||||
) -> Box<Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||||
where
|
where
|
||||||
for<'a> &'a Q: QueryDensity,
|
for<'a> &'a Q: QueryDensity,
|
||||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
extern crate bellman;
|
|
||||||
extern crate ff;
|
|
||||||
extern crate pairing;
|
|
||||||
extern crate rand;
|
|
||||||
|
|
||||||
// For randomness (during paramgen and proof generation)
|
// For randomness (during paramgen and proof generation)
|
||||||
use rand::thread_rng;
|
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));
|
let cs = &mut cs.namespace(|| format!("round {}", i));
|
||||||
|
|
||||||
// tmp = (xL + Ci)^2
|
// tmp = (xL + Ci)^2
|
||||||
let mut tmp_value = xl_value.map(|mut e| {
|
let tmp_value = xl_value.map(|mut e| {
|
||||||
e.add_assign(&self.constants[i]);
|
e.add_assign(&self.constants[i]);
|
||||||
e.square();
|
e.square();
|
||||||
e
|
e
|
||||||
});
|
});
|
||||||
let mut tmp = cs.alloc(
|
let tmp = cs.alloc(
|
||||||
|| "tmp",
|
|| "tmp",
|
||||||
|| tmp_value.ok_or(SynthesisError::AssignmentMissing),
|
|| 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 + (xL + Ci)^3
|
||||||
// new_xL = xR + tmp * (xL + Ci)
|
// new_xL = xR + tmp * (xL + Ci)
|
||||||
// new_xL - xR = tmp * (xL + Ci)
|
// new_xL - xR = tmp * (xL + Ci)
|
||||||
let mut new_xl_value = xl_value.map(|mut e| {
|
let new_xl_value = xl_value.map(|mut e| {
|
||||||
e.add_assign(&self.constants[i]);
|
e.add_assign(&self.constants[i]);
|
||||||
e.mul_assign(&tmp_value.unwrap());
|
e.mul_assign(&tmp_value.unwrap());
|
||||||
e.add_assign(&xr_value.unwrap());
|
e.add_assign(&xr_value.unwrap());
|
||||||
e
|
e
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut new_xl = if i == (MIMC_ROUNDS - 1) {
|
let new_xl = if i == (MIMC_ROUNDS - 1) {
|
||||||
// This is the last round, xL is our image and so
|
// This is the last round, xL is our image and so
|
||||||
// we allocate a public input.
|
// we allocate a public input.
|
||||||
cs.alloc_input(
|
cs.alloc_input(
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
version = "0.4.0"
|
version = "0.5.2"
|
||||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||||
description = "Library for building and interfacing with finite fields"
|
description = "Library for building and interfacing with finite fields"
|
||||||
|
readme = "README.md"
|
||||||
documentation = "https://docs.rs/ff/"
|
documentation = "https://docs.rs/ff/"
|
||||||
homepage = "https://github.com/ebfull/ff"
|
homepage = "https://github.com/ebfull/ff"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
repository = "https://github.com/ebfull/ff"
|
repository = "https://github.com/ebfull/ff"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
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"
|
rand_core = "0.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
derive = ["ff_derive"]
|
derive = ["ff_derive"]
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
17
ff/README.md
17
ff/README.md
@@ -12,14 +12,18 @@ Add the `ff` crate to your `Cargo.toml`:
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ff = "0.4"
|
ff = "0.5"
|
||||||
```
|
```
|
||||||
|
|
||||||
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. See the **[documentation](https://docs.rs/ff/0.4.0/ff/)** for more.
|
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits.
|
||||||
|
See the **[documentation](https://docs.rs/ff/)** for more.
|
||||||
|
|
||||||
### #![derive(PrimeField)]
|
### #![derive(PrimeField)]
|
||||||
|
|
||||||
If you need an implementation of a prime field, this library also provides a procedural macro that will expand into an efficient implementation of a prime field when supplied with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is also quadratic nonresidue.
|
If you need an implementation of a prime field, this library also provides a procedural
|
||||||
|
macro that will expand into an efficient implementation of a prime field when supplied
|
||||||
|
with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is
|
||||||
|
also quadratic nonresidue.
|
||||||
|
|
||||||
First, enable the `derive` crate feature:
|
First, enable the `derive` crate feature:
|
||||||
|
|
||||||
@@ -41,13 +45,16 @@ extern crate ff;
|
|||||||
struct Fp(FpRepr);
|
struct Fp(FpRepr);
|
||||||
```
|
```
|
||||||
|
|
||||||
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement `SqrtField` if supported. The library implements `FpRepr` itself and derives `PrimeFieldRepr` for it.
|
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement
|
||||||
|
`SqrtField` if supported. The library implements `FpRepr` itself and derives
|
||||||
|
`PrimeFieldRepr` for it.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ff_derive"
|
name = "ff_derive"
|
||||||
version = "0.3.0"
|
version = "0.4.1"
|
||||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||||
description = "Procedural macro library used to build custom prime field implementations"
|
description = "Procedural macro library used to build custom prime field implementations"
|
||||||
documentation = "https://docs.rs/ff/"
|
documentation = "https://docs.rs/ff/"
|
||||||
homepage = "https://github.com/ebfull/ff"
|
homepage = "https://github.com/ebfull/ff"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
repository = "https://github.com/ebfull/ff"
|
repository = "https://github.com/ebfull/ff"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
@@ -15,6 +16,9 @@ proc-macro = true
|
|||||||
num-bigint = "0.2"
|
num-bigint = "0.2"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num-integer = "0.1"
|
num-integer = "0.1"
|
||||||
proc-macro2 = "0.4"
|
proc-macro2 = "1"
|
||||||
quote = "0.6"
|
quote = "1"
|
||||||
syn = "0.14"
|
syn = "1"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "passively-maintained" }
|
||||||
|
|||||||
@@ -2,17 +2,11 @@
|
|||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
extern crate proc_macro2;
|
extern crate proc_macro2;
|
||||||
extern crate syn;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate quote;
|
|
||||||
|
|
||||||
extern crate num_bigint;
|
|
||||||
extern crate num_integer;
|
|
||||||
extern crate num_traits;
|
|
||||||
|
|
||||||
use num_bigint::BigUint;
|
use num_bigint::BigUint;
|
||||||
use num_integer::Integer;
|
use num_integer::Integer;
|
||||||
use num_traits::{One, ToPrimitive, Zero};
|
use num_traits::{One, ToPrimitive, Zero};
|
||||||
|
use quote::quote;
|
||||||
use quote::TokenStreamExt;
|
use quote::TokenStreamExt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@@ -91,10 +85,10 @@ fn fetch_wrapped_ident(body: &syn::Data) -> Option<syn::Ident> {
|
|||||||
/// Fetch an attribute string from the derived struct.
|
/// Fetch an attribute string from the derived struct.
|
||||||
fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> {
|
fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> {
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if let Some(meta) = attr.interpret_meta() {
|
if let Ok(meta) = attr.parse_meta() {
|
||||||
match meta {
|
match meta {
|
||||||
syn::Meta::NameValue(nv) => {
|
syn::Meta::NameValue(nv) => {
|
||||||
if nv.ident.to_string() == name {
|
if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) {
|
||||||
match nv.lit {
|
match nv.lit {
|
||||||
syn::Lit::Str(ref s) => return Some(s.value()),
|
syn::Lit::Str(ref s) => return Some(s.value()),
|
||||||
_ => {
|
_ => {
|
||||||
@@ -122,9 +116,9 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
|||||||
impl ::std::fmt::Debug for #repr
|
impl ::std::fmt::Debug for #repr
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
try!(write!(f, "0x"));
|
write!(f, "0x")?;
|
||||||
for i in self.0.iter().rev() {
|
for i in self.0.iter().rev() {
|
||||||
try!(write!(f, "{:016x}", *i));
|
write!(f, "{:016x}", *i)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -133,9 +127,9 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
|||||||
|
|
||||||
impl ::std::fmt::Display for #repr {
|
impl ::std::fmt::Display for #repr {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
try!(write!(f, "0x"));
|
write!(f, "0x")?;
|
||||||
for i in self.0.iter().rev() {
|
for i in self.0.iter().rev() {
|
||||||
try!(write!(f, "{:016x}", *i));
|
write!(f, "{:016x}", *i)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -883,7 +877,7 @@ fn prime_field_impl(
|
|||||||
|
|
||||||
impl ::ff::Field for #name {
|
impl ::ff::Field for #name {
|
||||||
/// Computes a uniformly random element using rejection sampling.
|
/// 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 {
|
loop {
|
||||||
let mut tmp = {
|
let mut tmp = {
|
||||||
let mut repr = [0u64; #limbs];
|
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)]
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
extern crate byteorder;
|
|
||||||
extern crate rand_core;
|
|
||||||
|
|
||||||
#[cfg(feature = "derive")]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate ff_derive;
|
|
||||||
|
|
||||||
#[cfg(feature = "derive")]
|
#[cfg(feature = "derive")]
|
||||||
pub use ff_derive::*;
|
pub use ff_derive::*;
|
||||||
|
|
||||||
@@ -20,7 +17,7 @@ pub trait Field:
|
|||||||
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
|
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
|
||||||
{
|
{
|
||||||
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
/// 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.
|
/// Returns the zero element of the field, the additive identity.
|
||||||
fn zero() -> Self;
|
fn zero() -> Self;
|
||||||
@@ -210,7 +207,7 @@ impl Error for PrimeFieldDecodingError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for PrimeFieldDecodingError {
|
impl fmt::Display for PrimeFieldDecodingError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
PrimeFieldDecodingError::NotInField(ref repr) => {
|
PrimeFieldDecodingError::NotInField(ref repr) => {
|
||||||
write!(f, "{} is not an element of the field", repr)
|
write!(f, "{} is not an element of the field", repr)
|
||||||
@@ -266,7 +263,7 @@ pub trait PrimeField: Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this prime field element into a biginteger representation.
|
/// Convert this prime field element into a biginteger representation.
|
||||||
fn from_repr(Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
|
fn from_repr(_: Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
|
||||||
|
|
||||||
/// Convert a biginteger representation into a prime field element, if
|
/// Convert a biginteger representation into a prime field element, if
|
||||||
/// the number is an element of the field.
|
/// the number is an element of the field.
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "group"
|
name = "group"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Sean Bowe <ewillbefull@gmail.com>",
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
"Jack Grigg <jack@z.cash>",
|
"Jack Grigg <jack@z.cash>",
|
||||||
]
|
]
|
||||||
|
readme = "README.md"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
|
|
||||||
description = "Elliptic curve group traits and utilities"
|
description = "Elliptic curve group traits and utilities"
|
||||||
documentation = "https://docs.rs/group/"
|
documentation = "https://docs.rs/group/"
|
||||||
homepage = "https://github.com/ebfull/group"
|
homepage = "https://github.com/ebfull/group"
|
||||||
repository = "https://github.com/ebfull/group"
|
repository = "https://github.com/ebfull/group"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ff = { path = "../ff" }
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
rand_xorshift = "0.2"
|
rand_xorshift = "0.2"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
# group [](https://crates.io/crates/group) #
|
# group [](https://crates.io/crates/group) #
|
||||||
|
|
||||||
|
`group` is a crate for working with groups over elliptic curves.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
extern crate ff;
|
// Catch documentation errors caused by code changes.
|
||||||
extern crate rand;
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
extern crate rand_xorshift;
|
|
||||||
|
|
||||||
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
|
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
@@ -180,7 +179,7 @@ impl Error for GroupDecodingError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for GroupDecodingError {
|
impl fmt::Display for GroupDecodingError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
|
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
|
||||||
write!(f, "{} decoding error: {}", description, err)
|
write!(f, "{} decoding error: {}", description, err)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use ff::{Field, PrimeField};
|
|||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use rand_xorshift::XorShiftRng;
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use {CurveAffine, CurveProjective, EncodedPoint};
|
use crate::{CurveAffine, CurveProjective, EncodedPoint};
|
||||||
|
|
||||||
pub fn curve_tests<G: CurveProjective>() {
|
pub fn curve_tests<G: CurveProjective>() {
|
||||||
let mut rng = XorShiftRng::from_seed([
|
let mut rng = XorShiftRng::from_seed([
|
||||||
@@ -71,7 +71,7 @@ pub fn curve_tests<G: CurveProjective>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn random_wnaf_tests<G: CurveProjective>() {
|
fn random_wnaf_tests<G: CurveProjective>() {
|
||||||
use wnaf::*;
|
use crate::wnaf::*;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([
|
let mut rng = XorShiftRng::from_seed([
|
||||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "librustzcash"
|
name = "librustzcash"
|
||||||
version = "0.1.0"
|
description = "Rust FFI used by the zcashd binary. Not an official API."
|
||||||
|
version = "0.2.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Sean Bowe <ewillbefull@gmail.com>",
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
"Jack Grigg <jack@z.cash>",
|
"Jack Grigg <jack@z.cash>",
|
||||||
"Jay Graber <jay@z.cash>",
|
"Jay Graber <jay@z.cash>",
|
||||||
"Simon Liu <simon@z.cash>"
|
"Simon Liu <simon@z.cash>"
|
||||||
]
|
]
|
||||||
|
homepage = "https://github.com/zcash/librustzcash"
|
||||||
|
repository = "https://github.com/zcash/librustzcash"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "rustzcash"
|
name = "rustzcash"
|
||||||
@@ -14,15 +20,17 @@ path = "src/rustzcash.rs"
|
|||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bellman = { path = "../bellman" }
|
bellman = { version = "0.2.0", path = "../bellman" }
|
||||||
blake2b_simd = "0.5"
|
blake2b_simd = "0.5"
|
||||||
blake2s_simd = "0.5"
|
blake2s_simd = "0.5"
|
||||||
ff = { path = "../ff" }
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
pairing = { path = "../pairing" }
|
pairing = { version = "0.15.0", path = "../pairing" }
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
byteorder = "1"
|
rand_core = "0.5.1"
|
||||||
rand_core = "0.5"
|
zcash_history = { version = "0.0.1", path = "../zcash_history" }
|
||||||
rand_os = "0.2"
|
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||||
zcash_primitives = { path = "../zcash_primitives" }
|
zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
|
||||||
zcash_proofs = { path = "../zcash_proofs" }
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "deprecated" }
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
# librustzcash
|
# librustzcash
|
||||||
|
|
||||||
This repository contains librustzcash, a static library for Zcash code assets written in Rust.
|
`librustzcash` is an FFI library crate that exposes the Zcash Rust components to
|
||||||
|
the `zcashd` full node.
|
||||||
|
|
||||||
|
The FFI API does not have any stability guarantees, and will change as required
|
||||||
|
by `zcashd`.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -112,8 +112,7 @@ extern "C" {
|
|||||||
bool librustzcash_sapling_output_proof(
|
bool librustzcash_sapling_output_proof(
|
||||||
void *ctx,
|
void *ctx,
|
||||||
const unsigned char *esk,
|
const unsigned char *esk,
|
||||||
const unsigned char *diversifier,
|
const unsigned char *payment_address,
|
||||||
const unsigned char *pk_d,
|
|
||||||
const unsigned char *rcm,
|
const unsigned char *rcm,
|
||||||
const uint64_t value,
|
const uint64_t value,
|
||||||
unsigned char *cv,
|
unsigned char *cv,
|
||||||
@@ -308,6 +307,33 @@ extern "C" {
|
|||||||
unsigned char *j_ret,
|
unsigned char *j_ret,
|
||||||
unsigned char *addr_ret
|
unsigned char *addr_ret
|
||||||
);
|
);
|
||||||
|
|
||||||
|
uint32_t librustzcash_mmr_append(
|
||||||
|
uint32_t cbranch,
|
||||||
|
uint32_t t_len,
|
||||||
|
const uint32_t *ni_ptr,
|
||||||
|
const unsigned char *n_ptr,
|
||||||
|
size_t p_len,
|
||||||
|
const unsigned char *nn_ptr,
|
||||||
|
unsigned char *rt_ret,
|
||||||
|
unsigned char *buf_ret
|
||||||
|
);
|
||||||
|
|
||||||
|
uint32_t librustzcash_mmr_delete(
|
||||||
|
uint32_t cbranch,
|
||||||
|
uint32_t t_len,
|
||||||
|
const uint32_t *ni_ptr,
|
||||||
|
const unsigned char *n_ptr,
|
||||||
|
size_t p_len,
|
||||||
|
size_t e_len,
|
||||||
|
unsigned char *rt_ret
|
||||||
|
);
|
||||||
|
|
||||||
|
uint32_t librustzcash_mmr_hash_node(
|
||||||
|
uint32_t cbranch,
|
||||||
|
const unsigned char *n_ptr,
|
||||||
|
unsigned char *h_ret
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // LIBRUSTZCASH_INCLUDE_H_
|
#endif // LIBRUSTZCASH_INCLUDE_H_
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,10 @@
|
|||||||
use ff::{PrimeField, PrimeFieldRepr};
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use rand_core::RngCore;
|
use rand_core::{OsRng, RngCore};
|
||||||
use rand_os::OsRng;
|
|
||||||
use zcash_primitives::jubjub::{edwards, JubjubBls12};
|
use zcash_primitives::jubjub::{edwards, JubjubBls12};
|
||||||
use zcash_primitives::primitives::{Diversifier, ViewingKey};
|
use zcash_primitives::primitives::{Diversifier, ViewingKey};
|
||||||
|
|
||||||
use {
|
use crate::{
|
||||||
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
|
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
|
||||||
librustzcash_sapling_ka_derivepublic,
|
librustzcash_sapling_ka_derivepublic,
|
||||||
};
|
};
|
||||||
@@ -25,7 +24,7 @@ fn test_key_agreement() {
|
|||||||
let addr = loop {
|
let addr = loop {
|
||||||
let mut d = [0; 11];
|
let mut d = [0; 11];
|
||||||
rng.fill_bytes(&mut d);
|
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,
|
Some(a) => break a,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
@@ -47,7 +46,7 @@ fn test_key_agreement() {
|
|||||||
|
|
||||||
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
||||||
let mut addr_pk_d = [0u8; 32];
|
let mut addr_pk_d = [0u8; 32];
|
||||||
addr.pk_d.write(&mut addr_pk_d[..]).unwrap();
|
addr.pk_d().write(&mut addr_pk_d[..]).unwrap();
|
||||||
|
|
||||||
assert!(librustzcash_sapling_ka_agree(
|
assert!(librustzcash_sapling_ka_agree(
|
||||||
&addr_pk_d,
|
&addr_pk_d,
|
||||||
@@ -59,7 +58,7 @@ fn test_key_agreement() {
|
|||||||
// using the diversifier and esk.
|
// using the diversifier and esk.
|
||||||
let mut epk = [0u8; 32];
|
let mut epk = [0u8; 32];
|
||||||
assert!(librustzcash_sapling_ka_derivepublic(
|
assert!(librustzcash_sapling_ka_derivepublic(
|
||||||
&addr.diversifier.0,
|
&addr.diversifier().0,
|
||||||
&esk,
|
&esk,
|
||||||
&mut epk
|
&mut epk
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use zcash_primitives::{
|
|||||||
|
|
||||||
use super::JUBJUB;
|
use super::JUBJUB;
|
||||||
|
|
||||||
use {
|
use crate::{
|
||||||
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
|
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
|
||||||
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
|
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
|
||||||
};
|
};
|
||||||
@@ -678,7 +678,7 @@ fn key_components() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pgk = ProofGenerationKey { ak, nsk };
|
let pgk = ProofGenerationKey { ak, nsk };
|
||||||
let fvk = pgk.into_viewing_key(&JUBJUB);
|
let fvk = pgk.to_viewing_key(&JUBJUB);
|
||||||
{
|
{
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
fvk.nk.write(&mut vec).unwrap();
|
fvk.nk.write(&mut vec).unwrap();
|
||||||
@@ -704,10 +704,10 @@ fn key_components() {
|
|||||||
let diversifier = Diversifier(tv.default_d);
|
let diversifier = Diversifier(tv.default_d);
|
||||||
assert!(librustzcash_check_diversifier(&tv.default_d));
|
assert!(librustzcash_check_diversifier(&tv.default_d));
|
||||||
|
|
||||||
let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap();
|
let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
|
||||||
{
|
{
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
addr.pk_d.write(&mut vec).unwrap();
|
addr.pk_d().write(&mut vec).unwrap();
|
||||||
assert_eq!(&vec, &tv.default_pk_d);
|
assert_eq!(&vec, &tv.default_pk_d);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|||||||
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_agreement;
|
||||||
mod key_components;
|
mod key_components;
|
||||||
|
mod mmr;
|
||||||
mod notes;
|
mod notes;
|
||||||
mod signatures;
|
mod signatures;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use librustzcash_sapling_compute_cm;
|
use crate::librustzcash_sapling_compute_cm;
|
||||||
use librustzcash_sapling_compute_nf;
|
use crate::librustzcash_sapling_compute_nf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn notes() {
|
fn notes() {
|
||||||
|
|||||||
BIN
librustzcash/src/tests/res/tree1023.dat
Normal file
BIN
librustzcash/src/tests/res/tree1023.dat
Normal file
Binary file not shown.
BIN
librustzcash/src/tests/res/tree16.dat
Normal file
BIN
librustzcash/src/tests/res/tree16.dat
Normal file
Binary file not shown.
@@ -2,22 +2,24 @@
|
|||||||
name = "pairing"
|
name = "pairing"
|
||||||
|
|
||||||
# Remember to change version string in README.md.
|
# Remember to change version string in README.md.
|
||||||
version = "0.14.2"
|
version = "0.15.1"
|
||||||
authors = [
|
authors = [
|
||||||
"Sean Bowe <ewillbefull@gmail.com>",
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
"Jack Grigg <jack@z.cash>",
|
"Jack Grigg <jack@z.cash>",
|
||||||
]
|
]
|
||||||
|
readme = "README.md"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
|
|
||||||
description = "Pairing-friendly elliptic curve library"
|
description = "Pairing-friendly elliptic curve library"
|
||||||
documentation = "https://docs.rs/pairing/"
|
documentation = "https://docs.rs/pairing/"
|
||||||
homepage = "https://github.com/ebfull/pairing"
|
homepage = "https://github.com/ebfull/pairing"
|
||||||
repository = "https://github.com/ebfull/pairing"
|
repository = "https://github.com/ebfull/pairing"
|
||||||
|
edition ="2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
ff = { path = "../ff", features = ["derive"] }
|
ff = { version = "^0.5.2", path = "../ff", features = ["derive"] }
|
||||||
group = { path = "../group" }
|
group = { version = "0.2.0", path = "../group" }
|
||||||
rand_core = "0.5"
|
rand_core = "0.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
@@ -27,3 +29,6 @@ rand_xorshift = "0.2"
|
|||||||
unstable-features = ["expose-arith"]
|
unstable-features = ["expose-arith"]
|
||||||
expose-arith = []
|
expose-arith = []
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
# pairing [](https://crates.io/crates/pairing) #
|
# pairing [](https://crates.io/crates/pairing) #
|
||||||
|
|
||||||
This is a Rust crate for using pairing-friendly elliptic curves. Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) construction is implemented.
|
`pairing` is a crate for using pairing-friendly elliptic curves.
|
||||||
|
|
||||||
|
Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html)
|
||||||
|
construction is implemented.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
`pairing` is being refactored into a generic library for working with
|
||||||
|
pairing-friendly curves. After the refactor, `pairing` will provide basic traits
|
||||||
|
for pairing-friendly elliptic curve constructions, while specific curves will be
|
||||||
|
in separate crates.
|
||||||
|
|
||||||
## [Documentation](https://docs.rs/pairing/)
|
## [Documentation](https://docs.rs/pairing/)
|
||||||
|
|
||||||
@@ -8,13 +18,15 @@ Bring the `pairing` crate into your project just as you normally would.
|
|||||||
|
|
||||||
## Security Warnings
|
## Security Warnings
|
||||||
|
|
||||||
This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks.
|
This library does not make any guarantees about constant-time operations, memory
|
||||||
|
access patterns, or resistance to side-channel attacks.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
mod g1 {
|
mod g1 {
|
||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::Field;
|
||||||
use group::CurveProjective;
|
use group::CurveProjective;
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
@@ -8,10 +10,13 @@ mod g1 {
|
|||||||
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
|
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(G1, Fr)> = (0..SAMPLES)
|
let v: Vec<(G1, Fr)> = (0..SAMPLES)
|
||||||
.map(|_| (G1::rand(&mut rng), Fr::rand(&mut rng)))
|
.map(|_| (G1::random(&mut rng), Fr::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -27,10 +32,13 @@ mod g1 {
|
|||||||
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
|
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(G1, G1)> = (0..SAMPLES)
|
let v: Vec<(G1, G1)> = (0..SAMPLES)
|
||||||
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng)))
|
.map(|_| (G1::random(&mut rng), G1::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -46,10 +54,13 @@ mod g1 {
|
|||||||
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
|
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(G1, G1Affine)> = (0..SAMPLES)
|
let v: Vec<(G1, G1Affine)> = (0..SAMPLES)
|
||||||
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng).into()))
|
.map(|_| (G1::random(&mut rng), G1::random(&mut rng).into()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -63,8 +74,10 @@ mod g1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod g2 {
|
mod g2 {
|
||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use ff::Field;
|
||||||
use group::CurveProjective;
|
use group::CurveProjective;
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
|
|
||||||
@@ -72,10 +85,13 @@ mod g2 {
|
|||||||
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
|
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(G2, Fr)> = (0..SAMPLES)
|
let v: Vec<(G2, Fr)> = (0..SAMPLES)
|
||||||
.map(|_| (G2::rand(&mut rng), Fr::rand(&mut rng)))
|
.map(|_| (G2::random(&mut rng), Fr::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -91,10 +107,13 @@ mod g2 {
|
|||||||
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
|
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(G2, G2)> = (0..SAMPLES)
|
let v: Vec<(G2, G2)> = (0..SAMPLES)
|
||||||
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng)))
|
.map(|_| (G2::random(&mut rng), G2::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -110,10 +129,13 @@ mod g2 {
|
|||||||
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
|
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||||
|
0xbc, 0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(G2, G2Affine)> = (0..SAMPLES)
|
let v: Vec<(G2, G2Affine)> = (0..SAMPLES)
|
||||||
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng).into()))
|
.map(|_| (G2::random(&mut rng), G2::random(&mut rng).into()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
@@ -7,12 +8,15 @@ use pairing::bls12_381::*;
|
|||||||
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut tmp1 = FqRepr::rand(&mut rng);
|
let mut tmp1 = Fq::random(&mut rng).into_repr();
|
||||||
let mut tmp2 = FqRepr::rand(&mut rng);
|
let mut tmp2 = Fq::random(&mut rng).into_repr();
|
||||||
// Shave a few bits off to avoid overflow.
|
// Shave a few bits off to avoid overflow.
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
tmp1.div2();
|
tmp1.div2();
|
||||||
@@ -35,11 +39,14 @@ fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let tmp1 = FqRepr::rand(&mut rng);
|
let tmp1 = Fq::random(&mut rng).into_repr();
|
||||||
let mut tmp2 = tmp1;
|
let mut tmp2 = tmp1;
|
||||||
// Ensure tmp2 is smaller than tmp1.
|
// Ensure tmp2 is smaller than tmp1.
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
@@ -62,9 +69,14 @@ fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fq::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -78,9 +90,14 @@ fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fq::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -95,9 +112,14 @@ fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fq::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -112,10 +134,13 @@ fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -131,10 +156,13 @@ fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -150,10 +178,13 @@ fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -169,9 +200,12 @@ fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_square(b: &mut ::test::Bencher) {
|
fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -186,9 +220,12 @@ fn bench_fq_square(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -201,9 +238,12 @@ fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_negate(b: &mut ::test::Bencher) {
|
fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -218,11 +258,14 @@ fn bench_fq_negate(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq> = (0..SAMPLES)
|
let v: Vec<Fq> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut tmp = Fq::rand(&mut rng);
|
let mut tmp = Fq::random(&mut rng);
|
||||||
tmp.square();
|
tmp.square();
|
||||||
tmp
|
tmp
|
||||||
})
|
})
|
||||||
@@ -239,9 +282,12 @@ fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -254,10 +300,13 @@ fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||||
.map(|_| Fq::rand(&mut rng).into_repr())
|
.map(|_| Fq::random(&mut rng).into_repr())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
@@ -7,10 +8,13 @@ use pairing::bls12_381::*;
|
|||||||
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -26,10 +30,13 @@ fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -45,10 +52,13 @@ fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -64,9 +74,12 @@ fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
|
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -81,9 +94,12 @@ fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
|
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use ff::{Field, SqrtField};
|
use ff::{Field, SqrtField};
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
@@ -7,10 +8,13 @@ use pairing::bls12_381::*;
|
|||||||
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -26,10 +30,13 @@ fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -45,10 +52,13 @@ fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -64,9 +74,12 @@ fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -81,9 +94,12 @@ fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -97,9 +113,12 @@ fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
@@ -7,12 +8,15 @@ use pairing::bls12_381::*;
|
|||||||
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut tmp1 = FrRepr::rand(&mut rng);
|
let mut tmp1 = Fr::random(&mut rng).into_repr();
|
||||||
let mut tmp2 = FrRepr::rand(&mut rng);
|
let mut tmp2 = Fr::random(&mut rng).into_repr();
|
||||||
// Shave a few bits off to avoid overflow.
|
// Shave a few bits off to avoid overflow.
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
tmp1.div2();
|
tmp1.div2();
|
||||||
@@ -35,11 +39,14 @@ fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let tmp1 = FrRepr::rand(&mut rng);
|
let tmp1 = Fr::random(&mut rng).into_repr();
|
||||||
let mut tmp2 = tmp1;
|
let mut tmp2 = tmp1;
|
||||||
// Ensure tmp2 is smaller than tmp1.
|
// Ensure tmp2 is smaller than tmp1.
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
@@ -62,9 +69,14 @@ fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fr::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -78,9 +90,14 @@ fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fr::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -95,9 +112,14 @@ fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||||
|
.map(|_| Fr::random(&mut rng).into_repr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -112,10 +134,13 @@ fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -131,10 +156,13 @@ fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -150,10 +178,13 @@ fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@@ -169,9 +200,12 @@ fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_square(b: &mut ::test::Bencher) {
|
fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -186,9 +220,12 @@ fn bench_fr_square(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -201,9 +238,12 @@ fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_negate(b: &mut ::test::Bencher) {
|
fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -218,11 +258,14 @@ fn bench_fr_negate(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fr> = (0..SAMPLES)
|
let v: Vec<Fr> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let mut tmp = Fr::rand(&mut rng);
|
let mut tmp = Fr::random(&mut rng);
|
||||||
tmp.square();
|
tmp.square();
|
||||||
tmp
|
tmp
|
||||||
})
|
})
|
||||||
@@ -239,9 +282,12 @@ fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -254,10 +300,13 @@ fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
|||||||
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||||
.map(|_| Fr::rand(&mut rng).into_repr())
|
.map(|_| Fr::random(&mut rng).into_repr())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ mod fq12;
|
|||||||
mod fq2;
|
mod fq2;
|
||||||
mod fr;
|
mod fr;
|
||||||
|
|
||||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
use rand_core::SeedableRng;
|
||||||
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
|
use group::CurveProjective;
|
||||||
use pairing::bls12_381::*;
|
use pairing::bls12_381::*;
|
||||||
use pairing::{Engine, PairingCurveAffine};
|
use pairing::{Engine, PairingCurveAffine};
|
||||||
|
|
||||||
@@ -13,9 +15,12 @@ use pairing::{Engine, PairingCurveAffine};
|
|||||||
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::rand(&mut rng)).collect();
|
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -29,9 +34,12 @@ fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
|||||||
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::rand(&mut rng)).collect();
|
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -45,13 +53,16 @@ fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
|||||||
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES)
|
let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
(
|
(
|
||||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -68,13 +79,16 @@ fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
|||||||
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<Fq12> = (0..SAMPLES)
|
let v: Vec<Fq12> = (0..SAMPLES)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
(
|
(
|
||||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)]))
|
.map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)]))
|
||||||
@@ -92,10 +106,13 @@ fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
|||||||
fn bench_pairing_full(b: &mut ::test::Bencher) {
|
fn bench_pairing_full(b: &mut ::test::Bencher) {
|
||||||
const SAMPLES: usize = 1000;
|
const SAMPLES: usize = 1000;
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||||
|
0xe5,
|
||||||
|
]);
|
||||||
|
|
||||||
let v: Vec<(G1, G2)> = (0..SAMPLES)
|
let v: Vec<(G1, G2)> = (0..SAMPLES)
|
||||||
.map(|_| (G1::rand(&mut rng), G2::rand(&mut rng)))
|
.map(|_| (G1::random(&mut rng), G2::random(&mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
extern crate ff;
|
extern crate ff;
|
||||||
extern crate group;
|
extern crate group;
|
||||||
extern crate pairing;
|
extern crate pairing;
|
||||||
extern crate rand;
|
extern crate rand_core;
|
||||||
|
extern crate rand_xorshift;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
mod bls12_381;
|
mod bls12_381;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ macro_rules! curve_impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for $affine {
|
impl ::std::fmt::Display for $affine {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
if self.infinity {
|
if self.infinity {
|
||||||
write!(f, "{}(Infinity)", $name)
|
write!(f, "{}(Infinity)", $name)
|
||||||
} else {
|
} else {
|
||||||
@@ -35,7 +35,7 @@ macro_rules! curve_impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for $projective {
|
impl ::std::fmt::Display for $projective {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
write!(f, "{}", self.into_affine())
|
write!(f, "{}", self.into_affine())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +200,7 @@ macro_rules! curve_impl {
|
|||||||
type Base = $basefield;
|
type Base = $basefield;
|
||||||
type Affine = $affine;
|
type Affine = $affine;
|
||||||
|
|
||||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||||
loop {
|
loop {
|
||||||
let x = $basefield::random(rng);
|
let x = $basefield::random(rng);
|
||||||
let greatest = rng.next_u32() % 2 != 0;
|
let greatest = rng.next_u32() % 2 != 0;
|
||||||
@@ -622,11 +622,11 @@ macro_rules! curve_impl {
|
|||||||
pub mod g1 {
|
pub mod g1 {
|
||||||
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
|
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
|
||||||
use super::g2::G2Affine;
|
use super::g2::G2Affine;
|
||||||
|
use crate::{Engine, PairingCurveAffine};
|
||||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use {Engine, PairingCurveAffine};
|
|
||||||
|
|
||||||
curve_impl!(
|
curve_impl!(
|
||||||
"G1",
|
"G1",
|
||||||
@@ -656,7 +656,7 @@ pub mod g1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for G1Uncompressed {
|
impl fmt::Debug for G1Uncompressed {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
self.0[..].fmt(formatter)
|
self.0[..].fmt(formatter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -766,7 +766,7 @@ pub mod g1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for G1Compressed {
|
impl fmt::Debug for G1Compressed {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
self.0[..].fmt(formatter)
|
self.0[..].fmt(formatter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -934,7 +934,7 @@ pub mod g1 {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn g1_generator() {
|
fn g1_generator() {
|
||||||
use SqrtField;
|
use crate::SqrtField;
|
||||||
|
|
||||||
let mut x = Fq::zero();
|
let mut x = Fq::zero();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@@ -1291,11 +1291,11 @@ pub mod g1 {
|
|||||||
pub mod g2 {
|
pub mod g2 {
|
||||||
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
|
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
|
||||||
use super::g1::G1Affine;
|
use super::g1::G1Affine;
|
||||||
|
use crate::{Engine, PairingCurveAffine};
|
||||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use {Engine, PairingCurveAffine};
|
|
||||||
|
|
||||||
curve_impl!(
|
curve_impl!(
|
||||||
"G2",
|
"G2",
|
||||||
@@ -1325,7 +1325,7 @@ pub mod g2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for G2Uncompressed {
|
impl fmt::Debug for G2Uncompressed {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
self.0[..].fmt(formatter)
|
self.0[..].fmt(formatter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1451,7 +1451,7 @@ pub mod g2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for G2Compressed {
|
impl fmt::Debug for G2Compressed {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
self.0[..].fmt(formatter)
|
self.0[..].fmt(formatter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1640,7 +1640,7 @@ pub mod g2 {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn g2_generator() {
|
fn g2_generator() {
|
||||||
use SqrtField;
|
use crate::SqrtField;
|
||||||
|
|
||||||
let mut x = Fq2::zero();
|
let mut x = Fq2::zero();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
|||||||
@@ -2225,10 +2225,10 @@ fn test_fq_root_of_unity() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fq_field_tests() {
|
fn fq_field_tests() {
|
||||||
::tests::field::random_field_tests::<Fq>();
|
crate::tests::field::random_field_tests::<Fq>();
|
||||||
::tests::field::random_sqrt_tests::<Fq>();
|
crate::tests::field::random_sqrt_tests::<Fq>();
|
||||||
::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
|
||||||
::tests::field::from_str_tests::<Fq>();
|
crate::tests::field::from_str_tests::<Fq>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2244,7 +2244,7 @@ fn test_fq_ordering() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fq_repr_tests() {
|
fn fq_repr_tests() {
|
||||||
::tests::repr::random_repr_tests::<Fq>();
|
crate::tests::repr::random_repr_tests::<Fq>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub struct Fq12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for Fq12 {
|
impl ::std::fmt::Display for Fq12 {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
|
write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ impl Fq12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Field for 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 {
|
Fq12 {
|
||||||
c0: Fq6::random(rng),
|
c0: Fq6::random(rng),
|
||||||
c1: Fq6::random(rng),
|
c1: Fq6::random(rng),
|
||||||
@@ -187,6 +187,6 @@ fn test_fq12_mul_by_014() {
|
|||||||
fn fq12_field_tests() {
|
fn fq12_field_tests() {
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
|
|
||||||
::tests::field::random_field_tests::<Fq12>();
|
crate::tests::field::random_field_tests::<Fq12>();
|
||||||
::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub struct Fq2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for Fq2 {
|
impl ::std::fmt::Display for Fq2 {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
|
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ impl Fq2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Field for 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 {
|
Fq2 {
|
||||||
c0: Fq::random(rng),
|
c0: Fq::random(rng),
|
||||||
c1: Fq::random(rng),
|
c1: Fq::random(rng),
|
||||||
@@ -958,7 +958,7 @@ fn test_fq2_mul_nonresidue() {
|
|||||||
fn fq2_field_tests() {
|
fn fq2_field_tests() {
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
|
|
||||||
::tests::field::random_field_tests::<Fq2>();
|
crate::tests::field::random_field_tests::<Fq2>();
|
||||||
::tests::field::random_sqrt_tests::<Fq2>();
|
crate::tests::field::random_sqrt_tests::<Fq2>();
|
||||||
::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub struct Fq6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for Fq6 {
|
impl ::std::fmt::Display for Fq6 {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
|
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ impl Fq6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Field for 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 {
|
Fq6 {
|
||||||
c0: Fq2::random(rng),
|
c0: Fq2::random(rng),
|
||||||
c1: Fq2::random(rng),
|
c1: Fq2::random(rng),
|
||||||
@@ -378,6 +378,6 @@ fn test_fq6_mul_by_01() {
|
|||||||
fn fq6_field_tests() {
|
fn fq6_field_tests() {
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
|
|
||||||
::tests::field::random_field_tests::<Fq6>();
|
crate::tests::field::random_field_tests::<Fq6>();
|
||||||
::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1015,13 +1015,13 @@ fn test_fr_root_of_unity() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fr_field_tests() {
|
fn fr_field_tests() {
|
||||||
::tests::field::random_field_tests::<Fr>();
|
crate::tests::field::random_field_tests::<Fr>();
|
||||||
::tests::field::random_sqrt_tests::<Fr>();
|
crate::tests::field::random_sqrt_tests::<Fr>();
|
||||||
::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
|
crate::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
|
||||||
::tests::field::from_str_tests::<Fr>();
|
crate::tests::field::from_str_tests::<Fr>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fr_repr_tests() {
|
fn fr_repr_tests() {
|
||||||
::tests::repr::random_repr_tests::<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 ec;
|
||||||
mod fq;
|
mod fq;
|
||||||
mod fq12;
|
mod fq12;
|
||||||
@@ -366,5 +369,5 @@ impl G2Prepared {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bls12_engine_tests() {
|
fn bls12_engine_tests() {
|
||||||
::tests::engine::engine_tests::<Bls12>();
|
crate::tests::engine::engine_tests::<Bls12>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use ff::PrimeFieldRepr;
|
|||||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use *;
|
use crate::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pairing_result_against_relic() {
|
fn test_pairing_result_against_relic() {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! A library for working with pairing-friendly curves.
|
||||||
|
|
||||||
// `clippy` is a code linting tool for improving code quality by catching
|
// `clippy` is a code linting tool for improving code quality by catching
|
||||||
// common mistakes or strange code patterns. If the `cargo-clippy` feature
|
// common mistakes or strange code patterns. If the `cargo-clippy` feature
|
||||||
// is provided, all compiler warnings are prohibited.
|
// is provided, all compiler warnings are prohibited.
|
||||||
@@ -8,17 +10,11 @@
|
|||||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
|
#![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::new_without_default))]
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))]
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))]
|
||||||
|
// Catch documentation errors caused by code changes.
|
||||||
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
// Force public structures to implement Debug
|
// Force public structures to implement Debug
|
||||||
#![deny(missing_debug_implementations)]
|
#![deny(missing_debug_implementations)]
|
||||||
|
|
||||||
extern crate byteorder;
|
|
||||||
extern crate ff;
|
|
||||||
extern crate group;
|
|
||||||
extern crate rand_core;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
extern crate rand_xorshift;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
|
|
||||||
@@ -87,7 +83,7 @@ pub trait Engine: ScalarEngine {
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
/// Perform final exponentiation of the result of a miller loop.
|
/// Perform final exponentiation of the result of a miller loop.
|
||||||
fn final_exponentiation(&Self::Fqk) -> Option<Self::Fqk>;
|
fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk>;
|
||||||
|
|
||||||
/// Performs a complete pairing operation `(p, q)`.
|
/// Performs a complete pairing operation `(p, q)`.
|
||||||
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
|
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use group::{CurveAffine, CurveProjective};
|
|||||||
use rand_core::SeedableRng;
|
use rand_core::SeedableRng;
|
||||||
use rand_xorshift::XorShiftRng;
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use {Engine, Field, PairingCurveAffine, PrimeField};
|
use crate::{Engine, Field, PairingCurveAffine, PrimeField};
|
||||||
|
|
||||||
pub fn engine_tests<E: Engine>() {
|
pub fn engine_tests<E: Engine>() {
|
||||||
let mut rng = XorShiftRng::from_seed([
|
let mut rng = XorShiftRng::from_seed([
|
||||||
|
|||||||
@@ -1,25 +1,31 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "zcash_client_backend"
|
name = "zcash_client_backend"
|
||||||
version = "0.0.0"
|
description = "APIs for creating shielded Zcash light clients"
|
||||||
|
version = "0.1.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Jack Grigg <jack@z.cash>",
|
"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"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bech32 = "0.7"
|
bech32 = "0.7"
|
||||||
bs58 = { version = "0.2", features = ["check"] }
|
ff = { version = "0.5.0", path = "../ff" }
|
||||||
ff = { path = "../ff" }
|
|
||||||
hex = "0.3"
|
hex = "0.3"
|
||||||
pairing = { path = "../pairing" }
|
pairing = { version = "0.15.0", path = "../pairing" }
|
||||||
protobuf = "2"
|
protobuf = "2"
|
||||||
subtle = "2"
|
subtle = "2"
|
||||||
zcash_primitives = { path = "../zcash_primitives" }
|
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
protobuf-codegen-pure = "2"
|
protobuf-codegen-pure = "2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_core = "0.5"
|
rand_core = "0.5"
|
||||||
rand_os = "0.2"
|
|
||||||
rand_xorshift = "0.2"
|
rand_xorshift = "0.2"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
# zcash_client_backend
|
# 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
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
//! Zcash global and per-network constants.
|
//! Zcash global and per-network constants.
|
||||||
|
|
||||||
pub mod mainnet;
|
pub mod mainnet;
|
||||||
pub mod testnet;
|
|
||||||
pub mod regtest;
|
pub mod regtest;
|
||||||
|
pub mod testnet;
|
||||||
pub const SPROUT_CONSENSUS_BRANCH_ID: u32 = 0;
|
|
||||||
pub const OVERWINTER_CONSENSUS_BRANCH_ID: u32 = 0x5ba8_1b19;
|
|
||||||
pub const SAPLING_CONSENSUS_BRANCH_ID: u32 = 0x76b8_09bb;
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
//! Constants for the Zcash main network.
|
||||||
|
|
||||||
/// The mainnet coin type for ZEC, as defined by [SLIP 44].
|
/// The mainnet coin type for ZEC, as defined by [SLIP 44].
|
||||||
///
|
///
|
||||||
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||||
pub const COIN_TYPE: u32 = 141;
|
pub const COIN_TYPE: u32 = 133;
|
||||||
|
|
||||||
/// The HRP for a Bech32-encoded mainnet [`ExtendedSpendingKey`].
|
/// The HRP for a Bech32-encoded mainnet [`ExtendedSpendingKey`].
|
||||||
///
|
///
|
||||||
@@ -23,16 +25,6 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews";
|
|||||||
///
|
///
|
||||||
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
|
/// 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
|
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
|
||||||
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs";
|
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs";
|
||||||
|
|
||||||
/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::PublicKey`].
|
|
||||||
///
|
|
||||||
/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey
|
|
||||||
pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 1] = [0x3c];
|
|
||||||
|
|
||||||
/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::Script`].
|
|
||||||
///
|
|
||||||
/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script
|
|
||||||
pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 1] = [0x55];
|
|
||||||
|
|||||||
@@ -1,38 +1,45 @@
|
|||||||
/// The testnet coin type for ZEC, as defined by [SLIP 44].
|
//! # Regtest constants
|
||||||
///
|
//!
|
||||||
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
//! `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;
|
pub const COIN_TYPE: u32 = 1;
|
||||||
|
|
||||||
/// The HRP for a Bech32-encoded testnet [`ExtendedSpendingKey`].
|
/// The HRP for a Bech32-encoded regtest [`ExtendedSpendingKey`].
|
||||||
///
|
///
|
||||||
/// Defined in [ZIP 32].
|
/// It is defined in [the `zcashd` codebase].
|
||||||
///
|
///
|
||||||
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
|
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
|
||||||
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
|
/// [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";
|
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-regtest";
|
||||||
|
|
||||||
/// The HRP for a Bech32-encoded testnet [`ExtendedFullViewingKey`].
|
/// The HRP for a Bech32-encoded regtest [`ExtendedFullViewingKey`].
|
||||||
///
|
///
|
||||||
/// Defined in [ZIP 32].
|
/// It is defined in [the `zcashd` codebase].
|
||||||
///
|
///
|
||||||
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
|
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
|
||||||
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
|
/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L494
|
||||||
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewregtestsapling";
|
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewregtestsapling";
|
||||||
|
|
||||||
/// The HRP for a Bech32-encoded testnet [`PaymentAddress`].
|
/// The HRP for a Bech32-encoded regtest [`PaymentAddress`].
|
||||||
///
|
///
|
||||||
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
|
/// It is defined in [the `zcashd` codebase].
|
||||||
///
|
///
|
||||||
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress
|
/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
|
||||||
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
|
/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L493
|
||||||
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zregtestsapling";
|
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zregtestsapling";
|
||||||
|
|
||||||
/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::PublicKey`].
|
/// The prefix for a Base58Check-encoded regtest [`TransparentAddress::PublicKey`].
|
||||||
|
/// Same as the testnet prefix.
|
||||||
///
|
///
|
||||||
/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey
|
/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey
|
||||||
pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25];
|
pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25];
|
||||||
|
|
||||||
/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::Script`].
|
/// The prefix for a Base58Check-encoded regtest [`TransparentAddress::Script`].
|
||||||
|
/// Same as the testnet prefix.
|
||||||
///
|
///
|
||||||
/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script
|
/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script
|
||||||
pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xba];
|
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].
|
/// The testnet coin type for ZEC, as defined by [SLIP 44].
|
||||||
///
|
///
|
||||||
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||||
@@ -23,16 +25,6 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling";
|
|||||||
///
|
///
|
||||||
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
|
/// 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
|
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
|
||||||
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling";
|
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling";
|
||||||
|
|
||||||
/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::PublicKey`].
|
|
||||||
///
|
|
||||||
/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey
|
|
||||||
pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25];
|
|
||||||
|
|
||||||
/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::Script`].
|
|
||||||
///
|
|
||||||
/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script
|
|
||||||
pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xba];
|
|
||||||
|
|||||||
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,17 +2,14 @@
|
|||||||
//!
|
//!
|
||||||
//! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`]
|
//! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`]
|
||||||
//! module.
|
//! module.
|
||||||
|
//!
|
||||||
|
//! [`constants`]: crate::constants
|
||||||
|
|
||||||
use bech32::{self, Error, FromBase32, ToBase32};
|
use bech32::{self, Error, FromBase32, ToBase32};
|
||||||
use bs58::{self, decode::DecodeError};
|
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
jubjub::edwards,
|
primitives::PaymentAddress,
|
||||||
primitives::{Diversifier, PaymentAddress},
|
|
||||||
};
|
|
||||||
use zcash_primitives::{
|
|
||||||
legacy::TransparentAddress,
|
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||||
JUBJUB,
|
JUBJUB,
|
||||||
};
|
};
|
||||||
@@ -115,10 +112,11 @@ pub fn decode_extended_full_viewing_key(
|
|||||||
/// 0xbc, 0xe5,
|
/// 0xbc, 0xe5,
|
||||||
/// ]);
|
/// ]);
|
||||||
///
|
///
|
||||||
/// let pa = PaymentAddress {
|
/// let pa = PaymentAddress::from_parts(
|
||||||
/// diversifier: Diversifier([0u8; 11]),
|
/// Diversifier([0u8; 11]),
|
||||||
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||||
/// };
|
/// )
|
||||||
|
/// .unwrap();
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
|
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
|
||||||
@@ -126,10 +124,7 @@ pub fn decode_extended_full_viewing_key(
|
|||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
|
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
|
||||||
bech32_encode(hrp, |w| {
|
bech32_encode(hrp, |w| w.write_all(&addr.to_bytes()))
|
||||||
w.write_all(&addr.diversifier.0)?;
|
|
||||||
addr.pk_d.write(w)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
|
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
|
||||||
@@ -155,10 +150,11 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
|
|||||||
/// 0xbc, 0xe5,
|
/// 0xbc, 0xe5,
|
||||||
/// ]);
|
/// ]);
|
||||||
///
|
///
|
||||||
/// let pa = PaymentAddress {
|
/// let pa = PaymentAddress::from_parts(
|
||||||
/// diversifier: Diversifier([0u8; 11]),
|
/// Diversifier([0u8; 11]),
|
||||||
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||||
/// };
|
/// )
|
||||||
|
/// .unwrap();
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// decode_payment_address(
|
/// decode_payment_address(
|
||||||
@@ -170,127 +166,16 @@ 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> {
|
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
|
||||||
bech32_decode(hrp, s, |data| {
|
bech32_decode(hrp, s, |data| {
|
||||||
let mut diversifier = Diversifier([0; 11]);
|
if data.len() != 43 {
|
||||||
diversifier.0.copy_from_slice(&data[0..11]);
|
|
||||||
// Check that the diversifier is valid
|
|
||||||
if diversifier.g_d::<Bls12>(&JUBJUB).is_none() {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
edwards::Point::<Bls12, _>::read(&data[11..], &JUBJUB)
|
let mut bytes = [0; 43];
|
||||||
.ok()?
|
bytes.copy_from_slice(&data);
|
||||||
.as_prime_order(&JUBJUB)
|
PaymentAddress::<Bls12>::from_bytes(&bytes, &JUBJUB)
|
||||||
.map(|pk_d| PaymentAddress { pk_d, diversifier })
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes a [`TransparentAddress`] as a Base58Check-encoded string.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_backend::{
|
|
||||||
/// constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX},
|
|
||||||
/// encoding::encode_transparent_address,
|
|
||||||
/// };
|
|
||||||
/// use zcash_primitives::legacy::TransparentAddress;
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// encode_transparent_address(
|
|
||||||
/// &B58_PUBKEY_ADDRESS_PREFIX,
|
|
||||||
/// &B58_SCRIPT_ADDRESS_PREFIX,
|
|
||||||
/// &TransparentAddress::PublicKey([0; 20]),
|
|
||||||
/// ),
|
|
||||||
/// "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma",
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// encode_transparent_address(
|
|
||||||
/// &B58_PUBKEY_ADDRESS_PREFIX,
|
|
||||||
/// &B58_SCRIPT_ADDRESS_PREFIX,
|
|
||||||
/// &TransparentAddress::Script([0; 20]),
|
|
||||||
/// ),
|
|
||||||
/// "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2",
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn encode_transparent_address(
|
|
||||||
pubkey_version: &[u8],
|
|
||||||
script_version: &[u8],
|
|
||||||
addr: &TransparentAddress,
|
|
||||||
) -> String {
|
|
||||||
let decoded = match addr {
|
|
||||||
TransparentAddress::PublicKey(key_id) => {
|
|
||||||
let mut decoded = vec![0; pubkey_version.len() + 20];
|
|
||||||
decoded[..pubkey_version.len()].copy_from_slice(pubkey_version);
|
|
||||||
decoded[pubkey_version.len()..].copy_from_slice(key_id);
|
|
||||||
decoded
|
|
||||||
}
|
|
||||||
TransparentAddress::Script(script_id) => {
|
|
||||||
let mut decoded = vec![0; script_version.len() + 20];
|
|
||||||
decoded[..script_version.len()].copy_from_slice(script_version);
|
|
||||||
decoded[script_version.len()..].copy_from_slice(script_id);
|
|
||||||
decoded
|
|
||||||
}
|
|
||||||
};
|
|
||||||
bs58::encode(decoded).with_check().into_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decodes a [`TransparentAddress`] from a Base58Check-encoded string.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_backend::{
|
|
||||||
/// constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX},
|
|
||||||
/// encoding::decode_transparent_address,
|
|
||||||
/// };
|
|
||||||
/// use zcash_primitives::legacy::TransparentAddress;
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// decode_transparent_address(
|
|
||||||
/// &B58_PUBKEY_ADDRESS_PREFIX,
|
|
||||||
/// &B58_SCRIPT_ADDRESS_PREFIX,
|
|
||||||
/// "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma",
|
|
||||||
/// ),
|
|
||||||
/// Ok(Some(TransparentAddress::PublicKey([0; 20]))),
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// decode_transparent_address(
|
|
||||||
/// &B58_PUBKEY_ADDRESS_PREFIX,
|
|
||||||
/// &B58_SCRIPT_ADDRESS_PREFIX,
|
|
||||||
/// "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2",
|
|
||||||
/// ),
|
|
||||||
/// Ok(Some(TransparentAddress::Script([0; 20]))),
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn decode_transparent_address(
|
|
||||||
pubkey_version: &[u8],
|
|
||||||
script_version: &[u8],
|
|
||||||
s: &str,
|
|
||||||
) -> Result<Option<TransparentAddress>, DecodeError> {
|
|
||||||
let decoded = bs58::decode(s).with_check(None).into_vec()?;
|
|
||||||
if &decoded[..pubkey_version.len()] == pubkey_version {
|
|
||||||
if decoded.len() == pubkey_version.len() + 20 {
|
|
||||||
let mut data = [0; 20];
|
|
||||||
data.copy_from_slice(&decoded[pubkey_version.len()..]);
|
|
||||||
Ok(Some(TransparentAddress::PublicKey(data)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
} else if &decoded[..script_version.len()] == script_version {
|
|
||||||
if decoded.len() == script_version.len() + 20 {
|
|
||||||
let mut data = [0; 20];
|
|
||||||
data.copy_from_slice(&decoded[script_version.len()..]);
|
|
||||||
Ok(Some(TransparentAddress::Script(data)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
@@ -300,11 +185,95 @@ mod tests {
|
|||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
jubjub::edwards,
|
jubjub::edwards,
|
||||||
primitives::{Diversifier, PaymentAddress},
|
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;
|
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]
|
#[test]
|
||||||
fn payment_address() {
|
fn payment_address() {
|
||||||
let rng = &mut XorShiftRng::from_seed([
|
let rng = &mut XorShiftRng::from_seed([
|
||||||
@@ -312,10 +281,11 @@ mod tests {
|
|||||||
0xbc, 0xe5,
|
0xbc, 0xe5,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let addr = PaymentAddress {
|
let addr = PaymentAddress::from_parts(
|
||||||
diversifier: Diversifier([0u8; 11]),
|
Diversifier([0u8; 11]),
|
||||||
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||||
};
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let encoded_main =
|
let encoded_main =
|
||||||
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
|
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
|
||||||
@@ -356,10 +326,11 @@ mod tests {
|
|||||||
0xbc, 0xe5,
|
0xbc, 0xe5,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let addr = PaymentAddress {
|
let addr = PaymentAddress::from_parts(
|
||||||
diversifier: Diversifier([1u8; 11]),
|
Diversifier([1u8; 11]),
|
||||||
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||||
};
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let encoded_main =
|
let encoded_main =
|
||||||
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr);
|
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
|
/// Derives the ZIP 32 [`ExtendedSpendingKey`] for a given coin type and account from the
|
||||||
/// given seed.
|
/// given seed.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `seed` is shorter than 32 bytes.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@@ -13,6 +17,10 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey};
|
|||||||
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
|
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendingKey {
|
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::from_path(
|
||||||
&ExtendedSpendingKey::master(&seed),
|
&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,9 +3,15 @@
|
|||||||
//! `zcash_client_backend` contains Rust structs and traits for creating shielded Zcash
|
//! `zcash_client_backend` contains Rust structs and traits for creating shielded Zcash
|
||||||
//! light clients.
|
//! light clients.
|
||||||
|
|
||||||
|
// Catch documentation errors caused by code changes.
|
||||||
|
#![deny(intra_doc_link_resolution_failure)]
|
||||||
|
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
mod decrypt;
|
||||||
pub mod encoding;
|
pub mod encoding;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
pub mod welding_rig;
|
pub mod welding_rig;
|
||||||
|
|
||||||
|
pub use decrypt::{decrypt_transaction, DecryptedOutput};
|
||||||
|
|||||||
@@ -134,12 +134,11 @@ pub fn scan_block(
|
|||||||
// mutable references to wtxs for too long.
|
// mutable references to wtxs for too long.
|
||||||
let mut block_witnesses: Vec<_> = wtxs
|
let mut block_witnesses: Vec<_> = wtxs
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|tx| {
|
.flat_map(|tx| {
|
||||||
tx.shielded_outputs
|
tx.shielded_outputs
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|output| &mut output.witness)
|
.map(|output| &mut output.witness)
|
||||||
})
|
})
|
||||||
.flatten()
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for to_scan in tx.outputs.into_iter().enumerate() {
|
for to_scan in tx.outputs.into_iter().enumerate() {
|
||||||
@@ -186,8 +185,7 @@ pub fn scan_block(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
use ff::{Field, PrimeField, PrimeFieldRepr};
|
||||||
use pairing::bls12_381::{Bls12, Fr};
|
use pairing::bls12_381::{Bls12, Fr};
|
||||||
use rand_core::RngCore;
|
use rand_core::{OsRng, RngCore};
|
||||||
use rand_os::OsRng;
|
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform},
|
jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform},
|
||||||
merkle_tree::CommitmentTree,
|
merkle_tree::CommitmentTree,
|
||||||
@@ -254,8 +252,8 @@ mod tests {
|
|||||||
// Create a fake Note for the account
|
// Create a fake Note for the account
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let note = Note {
|
let note = Note {
|
||||||
g_d: to.diversifier.g_d::<Bls12>(&JUBJUB).unwrap(),
|
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
|
||||||
pk_d: to.pk_d.clone(),
|
pk_d: to.pk_d().clone(),
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
r: Fs::random(&mut rng),
|
r: Fs::random(&mut rng),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "zcash_client_sqlite"
|
|
||||||
version = "0.0.0"
|
|
||||||
authors = [
|
|
||||||
"Jack Grigg <jack@z.cash>",
|
|
||||||
]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bech32 = "0.7"
|
|
||||||
bs58 = { version = "0.2", features = ["check"] }
|
|
||||||
ff = { path = "../ff" }
|
|
||||||
pairing = { path = "../pairing" }
|
|
||||||
protobuf = "2"
|
|
||||||
rusqlite = { version = "0.20", features = ["bundled"] }
|
|
||||||
time = "0.1"
|
|
||||||
zcash_client_backend = { path = "../zcash_client_backend" }
|
|
||||||
zcash_primitives = { path = "../zcash_primitives" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
rand_core = "0.5"
|
|
||||||
rand_os = "0.2"
|
|
||||||
tempfile = "3"
|
|
||||||
zcash_proofs = { path = "../zcash_proofs" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
mainnet = []
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# Security Disclaimer
|
|
||||||
|
|
||||||
#### :warning: WARNING: This is an *early preview*
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
In the spirit of transparency, we provide this as a window into what we are actively
|
|
||||||
developing. This is an alpha build, not yet intended for 3rd party use. Please be advised
|
|
||||||
of the following:
|
|
||||||
|
|
||||||
* 🛑 This code currently is not audited. 🛑
|
|
||||||
* ❌ This is a public, active branch with **no support**.
|
|
||||||
* ❌ The code **does not have** documentation that is reviewed and approved by our Documentation team.
|
|
||||||
* ❌ The code **does not have** adequate unit tests, acceptance tests and stress tests.
|
|
||||||
* ❌ The code **does not have** automated tests that use the officially supported CI system.
|
|
||||||
* ❌ The code **has not been subjected to thorough review** by engineers at the Electric Coin Company.
|
|
||||||
* :warning: This library **is** compatible with the latest version of zcashd, but there **is no** automated testing of this.
|
|
||||||
* :heavy_check_mark: The library **is not** majorly broken in some way.
|
|
||||||
* :heavy_check_mark: The library **does run** on mainnet and testnet.
|
|
||||||
* ❌ We **are actively rebasing** this branch and adding features where/when needed.
|
|
||||||
* ❌ We **do not** undertake appropriate security coverage (threat models, review, response, etc.).
|
|
||||||
* :heavy_check_mark: There is a product manager for this library.
|
|
||||||
* :heavy_check_mark: Electric Coin Company maintains the library as we discover bugs and do network upgrades/minor releases.
|
|
||||||
* :heavy_check_mark: Users can expect to get a response within a few weeks after submitting an issue.
|
|
||||||
* ❌ The User Support team **has not yet been briefed** on the features provided to users and the functionality of the associated test-framework.
|
|
||||||
* ❌ The code is **not fully-documented**.
|
|
||||||
|
|
||||||
|
|
||||||
### 🛑 Use of this code may lead to a loss of funds 🛑
|
|
||||||
|
|
||||||
Use of this code in its current form or with modifications may lead to loss of funds, loss
|
|
||||||
of "expected" privacy, or denial of service for a large portion of users, or a bug which
|
|
||||||
could leverage any of those kinds of attacks (especially a "0 day" where we suspect few
|
|
||||||
people know about the vulnerability).
|
|
||||||
|
|
||||||
### :eyes: At this time, this is for preview purposes only. :eyes:
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
# zcash_client_sqlite
|
|
||||||
|
|
||||||
This library contains APIs that collectively implement a Zcash light client in
|
|
||||||
an SQLite database.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Licensed under either of
|
|
||||||
|
|
||||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
at your option.
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally
|
|
||||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
|
||||||
license, shall be dual licensed as above, without any additional terms or
|
|
||||||
conditions.
|
|
||||||
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
//! Structs for handling supported address types.
|
|
||||||
|
|
||||||
use pairing::bls12_381::Bls12;
|
|
||||||
use zcash_client_backend::encoding::{
|
|
||||||
decode_payment_address, decode_transparent_address, encode_payment_address,
|
|
||||||
encode_transparent_address,
|
|
||||||
};
|
|
||||||
use zcash_primitives::{legacy::TransparentAddress, primitives::PaymentAddress};
|
|
||||||
|
|
||||||
#[cfg(feature = "mainnet")]
|
|
||||||
use zcash_client_backend::constants::mainnet::{
|
|
||||||
B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "mainnet"))]
|
|
||||||
use zcash_client_backend::constants::testnet::{
|
|
||||||
B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An address that funds can be sent to.
|
|
||||||
pub enum RecipientAddress {
|
|
||||||
Shielded(PaymentAddress<Bls12>),
|
|
||||||
Transparent(TransparentAddress),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PaymentAddress<Bls12>> for RecipientAddress {
|
|
||||||
fn from(addr: PaymentAddress<Bls12>) -> Self {
|
|
||||||
RecipientAddress::Shielded(addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TransparentAddress> for RecipientAddress {
|
|
||||||
fn from(addr: TransparentAddress) -> Self {
|
|
||||||
RecipientAddress::Transparent(addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecipientAddress {
|
|
||||||
pub fn from_str(s: &str) -> Option<Self> {
|
|
||||||
if let Ok(Some(pa)) = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, s) {
|
|
||||||
Some(pa.into())
|
|
||||||
} else if let Ok(Some(addr)) =
|
|
||||||
decode_transparent_address(&B58_PUBKEY_ADDRESS_PREFIX, &B58_SCRIPT_ADDRESS_PREFIX, s)
|
|
||||||
{
|
|
||||||
Some(addr.into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
RecipientAddress::Shielded(pa) => {
|
|
||||||
encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, pa)
|
|
||||||
}
|
|
||||||
RecipientAddress::Transparent(addr) => encode_transparent_address(
|
|
||||||
&B58_PUBKEY_ADDRESS_PREFIX,
|
|
||||||
&B58_SCRIPT_ADDRESS_PREFIX,
|
|
||||||
addr,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,469 +0,0 @@
|
|||||||
//! Functions for enforcing chain validity and handling chain reorgs.
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! use zcash_client_sqlite::{
|
|
||||||
//! chain::{rewind_to_height, validate_combined_chain},
|
|
||||||
//! error::ErrorKind,
|
|
||||||
//! scan::scan_cached_blocks,
|
|
||||||
//! };
|
|
||||||
//!
|
|
||||||
//! let db_cache = "/path/to/cache.db";
|
|
||||||
//! let db_data = "/path/to/data.db";
|
|
||||||
//!
|
|
||||||
//! // 1) Download new CompactBlocks into db_cache.
|
|
||||||
//!
|
|
||||||
//! // 2) Run the chain validator on the received blocks.
|
|
||||||
//! //
|
|
||||||
//! // Given that we assume the server always gives us correct-at-the-time blocks, any
|
|
||||||
//! // errors are in the blocks we have previously cached or scanned.
|
|
||||||
//! if let Err(e) = validate_combined_chain(&db_cache, &db_data) {
|
|
||||||
//! match e.kind() {
|
|
||||||
//! ErrorKind::InvalidChain(upper_bound, _) => {
|
|
||||||
//! // a) Pick a height to rewind to.
|
|
||||||
//! //
|
|
||||||
//! // This might be informed by some external chain reorg information, or
|
|
||||||
//! // heuristics such as the platform, available bandwidth, size of recent
|
|
||||||
//! // CompactBlocks, etc.
|
|
||||||
//! let rewind_height = upper_bound - 10;
|
|
||||||
//!
|
|
||||||
//! // b) Rewind scanned block information.
|
|
||||||
//! rewind_to_height(&db_data, rewind_height);
|
|
||||||
//!
|
|
||||||
//! // c) Delete cached blocks from rewind_height onwards.
|
|
||||||
//! //
|
|
||||||
//! // This does imply that assumed-valid blocks will be re-downloaded, but it
|
|
||||||
//! // is also possible that in the intervening time, a chain reorg has
|
|
||||||
//! // occurred that orphaned some of those blocks.
|
|
||||||
//!
|
|
||||||
//! // d) If there is some separate thread or service downloading
|
|
||||||
//! // CompactBlocks, tell it to go back and download from rewind_height
|
|
||||||
//! // onwards.
|
|
||||||
//! }
|
|
||||||
//! _ => {
|
|
||||||
//! // Handle other errors.
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // 3) Scan (any remaining) cached blocks.
|
|
||||||
//! //
|
|
||||||
//! // At this point, the cache and scanned data are locally consistent (though not
|
|
||||||
//! // necessarily consistent with the latest chain tip - this would be discovered the
|
|
||||||
//! // next time this codepath is executed after new blocks are received).
|
|
||||||
//! scan_cached_blocks(&db_cache, &db_data);
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
use protobuf::parse_from_bytes;
|
|
||||||
use rusqlite::{Connection, NO_PARAMS};
|
|
||||||
use std::path::Path;
|
|
||||||
use zcash_client_backend::proto::compact_formats::CompactBlock;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::{Error, ErrorKind},
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ChainInvalidCause {
|
|
||||||
PrevHashMismatch,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CompactBlockRow {
|
|
||||||
height: i32,
|
|
||||||
data: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks that the scanned blocks in the data database, when combined with the recent
|
|
||||||
/// `CompactBlock`s in the cache database, form a valid chain.
|
|
||||||
///
|
|
||||||
/// This function is built on the core assumption that the information provided in the
|
|
||||||
/// cache database is more likely to be accurate than the previously-scanned information.
|
|
||||||
/// This follows from the design (and trust) assumption that the `lightwalletd` server
|
|
||||||
/// provides accurate block information as of the time it was requested.
|
|
||||||
///
|
|
||||||
/// Returns:
|
|
||||||
/// - `Ok(())` if the combined chain is valid.
|
|
||||||
/// - `Err(ErrorKind::InvalidChain(upper_bound, cause))` if the combined chain is invalid.
|
|
||||||
/// `upper_bound` is the height of the highest invalid block (on the assumption that the
|
|
||||||
/// highest block in the cache database is correct).
|
|
||||||
/// - `Err(e)` if there was an error during validation unrelated to chain validity.
|
|
||||||
///
|
|
||||||
/// This function does not mutate either of the databases.
|
|
||||||
pub fn validate_combined_chain<P: AsRef<Path>, Q: AsRef<Path>>(
|
|
||||||
db_cache: P,
|
|
||||||
db_data: Q,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let cache = Connection::open(db_cache)?;
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
// Recall where we synced up to previously.
|
|
||||||
// If we have never synced, use Sapling activation height to select all cached CompactBlocks.
|
|
||||||
let (have_scanned, last_scanned_height) =
|
|
||||||
data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| {
|
|
||||||
row.get(0)
|
|
||||||
.map(|h| (true, h))
|
|
||||||
.or(Ok((false, SAPLING_ACTIVATION_HEIGHT - 1)))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Fetch the CompactBlocks we need to validate
|
|
||||||
let mut stmt_blocks = cache
|
|
||||||
.prepare("SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height DESC")?;
|
|
||||||
let mut rows = stmt_blocks.query_map(&[last_scanned_height], |row| {
|
|
||||||
Ok(CompactBlockRow {
|
|
||||||
height: row.get(0)?,
|
|
||||||
data: row.get(1)?,
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Take the highest cached block as accurate.
|
|
||||||
let (mut last_height, mut last_prev_hash) = {
|
|
||||||
let assumed_correct = match rows.next() {
|
|
||||||
Some(row) => row?,
|
|
||||||
None => {
|
|
||||||
// No cached blocks, and we've already validated the blocks we've scanned,
|
|
||||||
// so there's nothing to validate.
|
|
||||||
// TODO: Maybe we still want to check if there are cached blocks that are
|
|
||||||
// at heights we previously scanned? Check scanning flow again.
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let block: CompactBlock = parse_from_bytes(&assumed_correct.data)?;
|
|
||||||
(block.height as i32, block.prev_hash())
|
|
||||||
};
|
|
||||||
|
|
||||||
for row in rows {
|
|
||||||
let row = row?;
|
|
||||||
|
|
||||||
// Scanned blocks MUST be height-sequential.
|
|
||||||
if row.height != (last_height - 1) {
|
|
||||||
return Err(Error(ErrorKind::InvalidHeight(last_height - 1, row.height)));
|
|
||||||
}
|
|
||||||
last_height = row.height;
|
|
||||||
|
|
||||||
let block: CompactBlock = parse_from_bytes(&row.data)?;
|
|
||||||
|
|
||||||
// Cached blocks MUST be hash-chained.
|
|
||||||
if block.hash() != last_prev_hash {
|
|
||||||
return Err(Error(ErrorKind::InvalidChain(
|
|
||||||
last_height,
|
|
||||||
ChainInvalidCause::PrevHashMismatch,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
last_prev_hash = block.prev_hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
if have_scanned {
|
|
||||||
// Cached blocks MUST hash-chain to the last scanned block.
|
|
||||||
let last_scanned_hash = data.query_row(
|
|
||||||
"SELECT hash FROM blocks WHERE height = ?",
|
|
||||||
&[last_scanned_height],
|
|
||||||
|row| row.get::<_, Vec<_>>(0),
|
|
||||||
)?;
|
|
||||||
if &last_scanned_hash[..] != &last_prev_hash.0[..] {
|
|
||||||
return Err(Error(ErrorKind::InvalidChain(
|
|
||||||
last_scanned_height,
|
|
||||||
ChainInvalidCause::PrevHashMismatch,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All good!
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rewinds the data database to the given height.
|
|
||||||
///
|
|
||||||
/// If the requested height is greater than or equal to the height of the last scanned
|
|
||||||
/// block, this function does nothing.
|
|
||||||
pub fn rewind_to_height<P: AsRef<Path>>(db_data: P, height: i32) -> Result<(), Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
// Recall where we synced up to previously.
|
|
||||||
// If we have never synced, use Sapling activation height.
|
|
||||||
let last_scanned_height =
|
|
||||||
data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| {
|
|
||||||
row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if height >= last_scanned_height {
|
|
||||||
// Nothing to do.
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start an SQL transaction for rewinding.
|
|
||||||
data.execute("BEGIN IMMEDIATE", NO_PARAMS)?;
|
|
||||||
|
|
||||||
// Decrement witnesses.
|
|
||||||
data.execute("DELETE FROM sapling_witnesses WHERE block > ?", &[height])?;
|
|
||||||
|
|
||||||
// Un-mine transactions.
|
|
||||||
data.execute(
|
|
||||||
"UPDATE transactions SET block = NULL, tx_index = NULL WHERE block > ?",
|
|
||||||
&[height],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Now that they aren't depended on, delete scanned blocks.
|
|
||||||
data.execute("DELETE FROM blocks WHERE height > ?", &[height])?;
|
|
||||||
|
|
||||||
// Commit the SQL transaction, rewinding atomically.
|
|
||||||
data.execute("COMMIT", NO_PARAMS)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use tempfile::NamedTempFile;
|
|
||||||
use zcash_primitives::{
|
|
||||||
block::BlockHash,
|
|
||||||
transaction::components::Amount,
|
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{rewind_to_height, validate_combined_chain};
|
|
||||||
use crate::{
|
|
||||||
error::ErrorKind,
|
|
||||||
init::{init_accounts_table, init_cache_database, init_data_database},
|
|
||||||
query::get_balance,
|
|
||||||
scan::scan_cached_blocks,
|
|
||||||
tests::{fake_compact_block, insert_into_cache},
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn valid_chain_states() {
|
|
||||||
let cache_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_cache = cache_file.path();
|
|
||||||
init_cache_database(&db_cache).unwrap();
|
|
||||||
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
||||||
init_accounts_table(&db_data, &[extfvk.clone()]).unwrap();
|
|
||||||
|
|
||||||
// Empty chain should be valid
|
|
||||||
validate_combined_chain(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Create a fake CompactBlock sending value to the address
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
BlockHash([0; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
Amount::from_u64(5).unwrap(),
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
|
|
||||||
// Cache-only chain should be valid
|
|
||||||
validate_combined_chain(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Scan the cache
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Data-only chain should be valid
|
|
||||||
validate_combined_chain(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Create a second fake CompactBlock sending more value to the address
|
|
||||||
let (cb2, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 1,
|
|
||||||
cb.hash(),
|
|
||||||
extfvk,
|
|
||||||
Amount::from_u64(7).unwrap(),
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb2);
|
|
||||||
|
|
||||||
// Data+cache chain should be valid
|
|
||||||
validate_combined_chain(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Scan the cache again
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Data-only chain should be valid
|
|
||||||
validate_combined_chain(db_cache, db_data).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn invalid_chain_cache_disconnected() {
|
|
||||||
let cache_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_cache = cache_file.path();
|
|
||||||
init_cache_database(&db_cache).unwrap();
|
|
||||||
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
||||||
init_accounts_table(&db_data, &[extfvk.clone()]).unwrap();
|
|
||||||
|
|
||||||
// Create some fake CompactBlocks
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
BlockHash([0; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
Amount::from_u64(5).unwrap(),
|
|
||||||
);
|
|
||||||
let (cb2, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 1,
|
|
||||||
cb.hash(),
|
|
||||||
extfvk.clone(),
|
|
||||||
Amount::from_u64(7).unwrap(),
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
insert_into_cache(db_cache, &cb2);
|
|
||||||
|
|
||||||
// Scan the cache
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Data-only chain should be valid
|
|
||||||
validate_combined_chain(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Create more fake CompactBlocks that don't connect to the scanned ones
|
|
||||||
let (cb3, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 2,
|
|
||||||
BlockHash([1; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
Amount::from_u64(8).unwrap(),
|
|
||||||
);
|
|
||||||
let (cb4, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 3,
|
|
||||||
cb3.hash(),
|
|
||||||
extfvk.clone(),
|
|
||||||
Amount::from_u64(3).unwrap(),
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb3);
|
|
||||||
insert_into_cache(db_cache, &cb4);
|
|
||||||
|
|
||||||
// Data+cache chain should be invalid at the data/cache boundary
|
|
||||||
match validate_combined_chain(db_cache, db_data) {
|
|
||||||
Err(e) => match e.kind() {
|
|
||||||
ErrorKind::InvalidChain(upper_bound, _) => {
|
|
||||||
assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 1)
|
|
||||||
}
|
|
||||||
_ => panic!(),
|
|
||||||
},
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn invalid_chain_cache_reorg() {
|
|
||||||
let cache_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_cache = cache_file.path();
|
|
||||||
init_cache_database(&db_cache).unwrap();
|
|
||||||
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
||||||
init_accounts_table(&db_data, &[extfvk.clone()]).unwrap();
|
|
||||||
|
|
||||||
// Create some fake CompactBlocks
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
BlockHash([0; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
Amount::from_u64(5).unwrap(),
|
|
||||||
);
|
|
||||||
let (cb2, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 1,
|
|
||||||
cb.hash(),
|
|
||||||
extfvk.clone(),
|
|
||||||
Amount::from_u64(7).unwrap(),
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
insert_into_cache(db_cache, &cb2);
|
|
||||||
|
|
||||||
// Scan the cache
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Data-only chain should be valid
|
|
||||||
validate_combined_chain(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Create more fake CompactBlocks that contain a reorg
|
|
||||||
let (cb3, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 2,
|
|
||||||
cb2.hash(),
|
|
||||||
extfvk.clone(),
|
|
||||||
Amount::from_u64(8).unwrap(),
|
|
||||||
);
|
|
||||||
let (cb4, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 3,
|
|
||||||
BlockHash([1; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
Amount::from_u64(3).unwrap(),
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb3);
|
|
||||||
insert_into_cache(db_cache, &cb4);
|
|
||||||
|
|
||||||
// Data+cache chain should be invalid inside the cache
|
|
||||||
match validate_combined_chain(db_cache, db_data) {
|
|
||||||
Err(e) => match e.kind() {
|
|
||||||
ErrorKind::InvalidChain(upper_bound, _) => {
|
|
||||||
assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 2)
|
|
||||||
}
|
|
||||||
_ => panic!(),
|
|
||||||
},
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn data_db_rewinding() {
|
|
||||||
let cache_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_cache = cache_file.path();
|
|
||||||
init_cache_database(&db_cache).unwrap();
|
|
||||||
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
||||||
init_accounts_table(&db_data, &[extfvk.clone()]).unwrap();
|
|
||||||
|
|
||||||
// Account balance should be zero
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero());
|
|
||||||
|
|
||||||
// Create fake CompactBlocks sending value to the address
|
|
||||||
let value = Amount::from_u64(5).unwrap();
|
|
||||||
let value2 = Amount::from_u64(7).unwrap();
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
BlockHash([0; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
insert_into_cache(db_cache, &cb2);
|
|
||||||
|
|
||||||
// Scan the cache
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Account balance should reflect both received notes
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value + value2);
|
|
||||||
|
|
||||||
// "Rewind" to height of last scanned block
|
|
||||||
rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT + 1).unwrap();
|
|
||||||
|
|
||||||
// Account balance should be unaltered
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value + value2);
|
|
||||||
|
|
||||||
// Rewind so that one block is dropped
|
|
||||||
rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT).unwrap();
|
|
||||||
|
|
||||||
// Account balance should only contain the first received note
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
use std::error;
|
|
||||||
use std::fmt;
|
|
||||||
use zcash_primitives::{
|
|
||||||
sapling::Node,
|
|
||||||
transaction::{builder, TxId},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ErrorKind {
|
|
||||||
CorruptedData(&'static str),
|
|
||||||
IncorrectHRPExtFVK,
|
|
||||||
InsufficientBalance(u64, u64),
|
|
||||||
InvalidChain(i32, crate::chain::ChainInvalidCause),
|
|
||||||
InvalidExtSK(u32),
|
|
||||||
InvalidHeight(i32, i32),
|
|
||||||
InvalidMemo(std::str::Utf8Error),
|
|
||||||
InvalidNewWitnessAnchor(usize, TxId, i32, Node),
|
|
||||||
InvalidNote,
|
|
||||||
InvalidWitnessAnchor(i64, i32),
|
|
||||||
ScanRequired,
|
|
||||||
TableNotEmpty,
|
|
||||||
Bech32(bech32::Error),
|
|
||||||
Base58(bs58::decode::DecodeError),
|
|
||||||
Builder(builder::Error),
|
|
||||||
Database(rusqlite::Error),
|
|
||||||
Io(std::io::Error),
|
|
||||||
Protobuf(protobuf::ProtobufError),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Error(pub(crate) ErrorKind);
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match &self.0 {
|
|
||||||
ErrorKind::CorruptedData(reason) => write!(f, "Data DB is corrupted: {}", reason),
|
|
||||||
ErrorKind::IncorrectHRPExtFVK => write!(f, "Incorrect HRP for extfvk"),
|
|
||||||
ErrorKind::InsufficientBalance(have, need) => write!(
|
|
||||||
f,
|
|
||||||
"Insufficient balance (have {}, need {} including fee)",
|
|
||||||
have, need
|
|
||||||
),
|
|
||||||
ErrorKind::InvalidChain(upper_bound, cause) => {
|
|
||||||
write!(f, "Invalid chain (upper bound: {}): {:?}", upper_bound, cause)
|
|
||||||
}
|
|
||||||
ErrorKind::InvalidExtSK(account) => {
|
|
||||||
write!(f, "Incorrect ExtendedSpendingKey for account {}", account)
|
|
||||||
}
|
|
||||||
ErrorKind::InvalidHeight(expected, actual) => write!(
|
|
||||||
f,
|
|
||||||
"Expected height of next CompactBlock to be {}, but was {}",
|
|
||||||
expected, actual
|
|
||||||
),
|
|
||||||
ErrorKind::InvalidMemo(e) => write!(f, "{}", e),
|
|
||||||
ErrorKind::InvalidNewWitnessAnchor(output, txid, last_height, anchor) => write!(
|
|
||||||
f,
|
|
||||||
"New witness for output {} in tx {} has incorrect anchor after scanning block {}: {:?}",
|
|
||||||
output, txid, last_height, anchor,
|
|
||||||
),
|
|
||||||
ErrorKind::InvalidNote => write!(f, "Invalid note"),
|
|
||||||
ErrorKind::InvalidWitnessAnchor(id_note, last_height) => write!(
|
|
||||||
f,
|
|
||||||
"Witness for note {} has incorrect anchor after scanning block {}",
|
|
||||||
id_note, last_height
|
|
||||||
),
|
|
||||||
ErrorKind::ScanRequired => write!(f, "Must scan blocks first"),
|
|
||||||
ErrorKind::TableNotEmpty => write!(f, "Table is not empty"),
|
|
||||||
ErrorKind::Bech32(e) => write!(f, "{}", e),
|
|
||||||
ErrorKind::Base58(e) => write!(f, "{}", e),
|
|
||||||
ErrorKind::Builder(e) => write!(f, "{:?}", e),
|
|
||||||
ErrorKind::Database(e) => write!(f, "{}", e),
|
|
||||||
ErrorKind::Io(e) => write!(f, "{}", e),
|
|
||||||
ErrorKind::Protobuf(e) => write!(f, "{}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for Error {
|
|
||||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
|
||||||
match &self.0 {
|
|
||||||
ErrorKind::InvalidMemo(e) => Some(e),
|
|
||||||
ErrorKind::Bech32(e) => Some(e),
|
|
||||||
ErrorKind::Builder(e) => Some(e),
|
|
||||||
ErrorKind::Database(e) => Some(e),
|
|
||||||
ErrorKind::Io(e) => Some(e),
|
|
||||||
ErrorKind::Protobuf(e) => Some(e),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bech32::Error> for Error {
|
|
||||||
fn from(e: bech32::Error) -> Self {
|
|
||||||
Error(ErrorKind::Bech32(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bs58::decode::DecodeError> for Error {
|
|
||||||
fn from(e: bs58::decode::DecodeError) -> Self {
|
|
||||||
Error(ErrorKind::Base58(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<builder::Error> for Error {
|
|
||||||
fn from(e: builder::Error) -> Self {
|
|
||||||
Error(ErrorKind::Builder(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<rusqlite::Error> for Error {
|
|
||||||
fn from(e: rusqlite::Error) -> Self {
|
|
||||||
Error(ErrorKind::Database(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
|
||||||
fn from(e: std::io::Error) -> Self {
|
|
||||||
Error(ErrorKind::Io(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<protobuf::ProtobufError> for Error {
|
|
||||||
fn from(e: protobuf::ProtobufError) -> Self {
|
|
||||||
Error(ErrorKind::Protobuf(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error {
|
|
||||||
pub fn kind(&self) -> &ErrorKind {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,303 +0,0 @@
|
|||||||
//! Functions for initializing the various databases.
|
|
||||||
|
|
||||||
use rusqlite::{types::ToSql, Connection, NO_PARAMS};
|
|
||||||
use std::path::Path;
|
|
||||||
use zcash_client_backend::encoding::encode_extended_full_viewing_key;
|
|
||||||
use zcash_primitives::{block::BlockHash, zip32::ExtendedFullViewingKey};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
address_from_extfvk,
|
|
||||||
error::{Error, ErrorKind},
|
|
||||||
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Sets up the internal structure of the cache database.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tempfile::NamedTempFile;
|
|
||||||
/// use zcash_client_sqlite::init::init_cache_database;
|
|
||||||
///
|
|
||||||
/// let data_file = NamedTempFile::new().unwrap();
|
|
||||||
/// let db_cache = data_file.path();
|
|
||||||
/// init_cache_database(&db_cache).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn init_cache_database<P: AsRef<Path>>(db_cache: P) -> Result<(), Error> {
|
|
||||||
let cache = Connection::open(db_cache)?;
|
|
||||||
cache.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS compactblocks (
|
|
||||||
height INTEGER PRIMARY KEY,
|
|
||||||
data BLOB NOT NULL
|
|
||||||
)",
|
|
||||||
NO_PARAMS,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets up the internal structure of the data database.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tempfile::NamedTempFile;
|
|
||||||
/// use zcash_client_sqlite::init::init_data_database;
|
|
||||||
///
|
|
||||||
/// let data_file = NamedTempFile::new().unwrap();
|
|
||||||
/// let db_data = data_file.path();
|
|
||||||
/// init_data_database(&db_data).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn init_data_database<P: AsRef<Path>>(db_data: P) -> Result<(), Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
data.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS accounts (
|
|
||||||
account INTEGER PRIMARY KEY,
|
|
||||||
extfvk TEXT NOT NULL,
|
|
||||||
address TEXT NOT NULL
|
|
||||||
)",
|
|
||||||
NO_PARAMS,
|
|
||||||
)?;
|
|
||||||
data.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS blocks (
|
|
||||||
height INTEGER PRIMARY KEY,
|
|
||||||
hash BLOB NOT NULL,
|
|
||||||
time INTEGER NOT NULL,
|
|
||||||
sapling_tree BLOB NOT NULL
|
|
||||||
)",
|
|
||||||
NO_PARAMS,
|
|
||||||
)?;
|
|
||||||
data.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS transactions (
|
|
||||||
id_tx INTEGER PRIMARY KEY,
|
|
||||||
txid BLOB NOT NULL UNIQUE,
|
|
||||||
created TEXT,
|
|
||||||
block INTEGER,
|
|
||||||
tx_index INTEGER,
|
|
||||||
expiry_height INTEGER,
|
|
||||||
raw BLOB,
|
|
||||||
FOREIGN KEY (block) REFERENCES blocks(height)
|
|
||||||
)",
|
|
||||||
NO_PARAMS,
|
|
||||||
)?;
|
|
||||||
data.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS received_notes (
|
|
||||||
id_note INTEGER PRIMARY KEY,
|
|
||||||
tx INTEGER NOT NULL,
|
|
||||||
output_index INTEGER NOT NULL,
|
|
||||||
account INTEGER NOT NULL,
|
|
||||||
diversifier BLOB NOT NULL,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
rcm BLOB NOT NULL,
|
|
||||||
nf BLOB NOT NULL UNIQUE,
|
|
||||||
is_change BOOLEAN NOT NULL,
|
|
||||||
memo BLOB,
|
|
||||||
spent INTEGER,
|
|
||||||
FOREIGN KEY (tx) REFERENCES transactions(id_tx),
|
|
||||||
FOREIGN KEY (account) REFERENCES accounts(account),
|
|
||||||
FOREIGN KEY (spent) REFERENCES transactions(id_tx),
|
|
||||||
CONSTRAINT tx_output UNIQUE (tx, output_index)
|
|
||||||
)",
|
|
||||||
NO_PARAMS,
|
|
||||||
)?;
|
|
||||||
data.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS sapling_witnesses (
|
|
||||||
id_witness INTEGER PRIMARY KEY,
|
|
||||||
note INTEGER NOT NULL,
|
|
||||||
block INTEGER NOT NULL,
|
|
||||||
witness BLOB NOT NULL,
|
|
||||||
FOREIGN KEY (note) REFERENCES received_notes(id_note),
|
|
||||||
FOREIGN KEY (block) REFERENCES blocks(height),
|
|
||||||
CONSTRAINT witness_height UNIQUE (note, block)
|
|
||||||
)",
|
|
||||||
NO_PARAMS,
|
|
||||||
)?;
|
|
||||||
data.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS sent_notes (
|
|
||||||
id_note INTEGER PRIMARY KEY,
|
|
||||||
tx INTEGER NOT NULL,
|
|
||||||
output_index INTEGER NOT NULL,
|
|
||||||
from_account INTEGER NOT NULL,
|
|
||||||
address TEXT NOT NULL,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
memo BLOB,
|
|
||||||
FOREIGN KEY (tx) REFERENCES transactions(id_tx),
|
|
||||||
FOREIGN KEY (from_account) REFERENCES accounts(account),
|
|
||||||
CONSTRAINT tx_output UNIQUE (tx, output_index)
|
|
||||||
)",
|
|
||||||
NO_PARAMS,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialises the data database with the given [`ExtendedFullViewingKey`]s.
|
|
||||||
///
|
|
||||||
/// The [`ExtendedFullViewingKey`]s are stored internally and used by other APIs such as
|
|
||||||
/// [`get_address`], [`scan_cached_blocks`], and [`create_to_address`]. `extfvks` **MUST**
|
|
||||||
/// be arranged in account-order; that is, the [`ExtendedFullViewingKey`] for ZIP 32
|
|
||||||
/// account `i` **MUST** be at `extfvks[i]`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tempfile::NamedTempFile;
|
|
||||||
/// use zcash_client_sqlite::init::{init_accounts_table, init_data_database};
|
|
||||||
/// use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey};
|
|
||||||
///
|
|
||||||
/// let data_file = NamedTempFile::new().unwrap();
|
|
||||||
/// let db_data = data_file.path();
|
|
||||||
/// init_data_database(&db_data).unwrap();
|
|
||||||
///
|
|
||||||
/// let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
/// let extfvks = [ExtendedFullViewingKey::from(&extsk)];
|
|
||||||
/// init_accounts_table(&db_data, &extfvks).unwrap();
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`get_address`]: crate::query::get_address
|
|
||||||
/// [`scan_cached_blocks`]: crate::scan::scan_cached_blocks
|
|
||||||
/// [`create_to_address`]: crate::transact::create_to_address
|
|
||||||
pub fn init_accounts_table<P: AsRef<Path>>(
|
|
||||||
db_data: P,
|
|
||||||
extfvks: &[ExtendedFullViewingKey],
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
let mut empty_check = data.prepare("SELECT * FROM accounts LIMIT 1")?;
|
|
||||||
if empty_check.exists(NO_PARAMS)? {
|
|
||||||
return Err(Error(ErrorKind::TableNotEmpty));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert accounts atomically
|
|
||||||
data.execute("BEGIN IMMEDIATE", NO_PARAMS)?;
|
|
||||||
for (account, extfvk) in extfvks.iter().enumerate() {
|
|
||||||
let address = address_from_extfvk(extfvk);
|
|
||||||
let extfvk =
|
|
||||||
encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, extfvk);
|
|
||||||
data.execute(
|
|
||||||
"INSERT INTO accounts (account, extfvk, address)
|
|
||||||
VALUES (?, ?, ?)",
|
|
||||||
&[
|
|
||||||
(account as u32).to_sql()?,
|
|
||||||
extfvk.to_sql()?,
|
|
||||||
address.to_sql()?,
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
data.execute("COMMIT", NO_PARAMS)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialises the data database with the given block.
|
|
||||||
///
|
|
||||||
/// This enables a newly-created database to be immediately-usable, without needing to
|
|
||||||
/// synchronise historic blocks.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_sqlite::init::init_blocks_table;
|
|
||||||
/// use zcash_primitives::block::BlockHash;
|
|
||||||
///
|
|
||||||
/// // The block height.
|
|
||||||
/// let height = 500_000;
|
|
||||||
/// // The hash of the block header.
|
|
||||||
/// let hash = BlockHash([0; 32]);
|
|
||||||
/// // The nTime field from the block header.
|
|
||||||
/// let time = 12_3456_7890;
|
|
||||||
/// // The serialized Sapling commitment tree as of this block.
|
|
||||||
/// // Pre-compute and hard-code, or obtain from a service.
|
|
||||||
/// let sapling_tree = &[];
|
|
||||||
///
|
|
||||||
/// init_blocks_table("/path/to/data.db", height, hash, time, sapling_tree);
|
|
||||||
/// ```
|
|
||||||
pub fn init_blocks_table<P: AsRef<Path>>(
|
|
||||||
db_data: P,
|
|
||||||
height: i32,
|
|
||||||
hash: BlockHash,
|
|
||||||
time: u32,
|
|
||||||
sapling_tree: &[u8],
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
let mut empty_check = data.prepare("SELECT * FROM blocks LIMIT 1")?;
|
|
||||||
if empty_check.exists(NO_PARAMS)? {
|
|
||||||
return Err(Error(ErrorKind::TableNotEmpty));
|
|
||||||
}
|
|
||||||
|
|
||||||
data.execute(
|
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree)
|
|
||||||
VALUES (?, ?, ?, ?)",
|
|
||||||
&[
|
|
||||||
height.to_sql()?,
|
|
||||||
hash.0.to_sql()?,
|
|
||||||
time.to_sql()?,
|
|
||||||
sapling_tree.to_sql()?,
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use tempfile::NamedTempFile;
|
|
||||||
use zcash_client_backend::encoding::decode_payment_address;
|
|
||||||
use zcash_primitives::{
|
|
||||||
block::BlockHash,
|
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{init_accounts_table, init_blocks_table, init_data_database};
|
|
||||||
use crate::{query::get_address, HRP_SAPLING_PAYMENT_ADDRESS};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn init_accounts_table_only_works_once() {
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// We can call the function as many times as we want with no data
|
|
||||||
init_accounts_table(&db_data, &[]).unwrap();
|
|
||||||
init_accounts_table(&db_data, &[]).unwrap();
|
|
||||||
|
|
||||||
// First call with data should initialise the accounts table
|
|
||||||
let extfvks = [ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(
|
|
||||||
&[],
|
|
||||||
))];
|
|
||||||
init_accounts_table(&db_data, &extfvks).unwrap();
|
|
||||||
|
|
||||||
// Subsequent calls should return an error
|
|
||||||
init_accounts_table(&db_data, &[]).unwrap_err();
|
|
||||||
init_accounts_table(&db_data, &extfvks).unwrap_err();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn init_blocks_table_only_works_once() {
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// First call with data should initialise the blocks table
|
|
||||||
init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap();
|
|
||||||
|
|
||||||
// Subsequent calls should return an error
|
|
||||||
init_blocks_table(&db_data, 2, BlockHash([2; 32]), 2, &[]).unwrap_err();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn init_accounts_table_stores_correct_address() {
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvks = [ExtendedFullViewingKey::from(&extsk)];
|
|
||||||
init_accounts_table(&db_data, &extfvks).unwrap();
|
|
||||||
|
|
||||||
// The account's address should be in the data DB
|
|
||||||
let addr = get_address(&db_data, 0).unwrap();
|
|
||||||
let pa = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr).unwrap();
|
|
||||||
assert_eq!(pa.unwrap(), extsk.default_address().unwrap().1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
//! *An SQLite-based Zcash light client.*
|
|
||||||
//!
|
|
||||||
//! `zcash_client_backend` contains a set of APIs that collectively implement an
|
|
||||||
//! SQLite-based light client for the Zcash network.
|
|
||||||
//!
|
|
||||||
//! # Design
|
|
||||||
//!
|
|
||||||
//! The light client is built around two SQLite databases:
|
|
||||||
//!
|
|
||||||
//! - A cache database, used to inform the light client about new [`CompactBlock`]s. It is
|
|
||||||
//! read-only within all light client APIs *except* for [`init_cache_database`] which
|
|
||||||
//! can be used to initialize the database.
|
|
||||||
//!
|
|
||||||
//! - A data database, where the light client's state is stored. It is read-write within
|
|
||||||
//! the light client APIs, and **assumed to be read-only outside these APIs**. Callers
|
|
||||||
//! **MUST NOT** write to the database without using these APIs. Callers **MAY** read
|
|
||||||
//! the database directly in order to extract information for display to users.
|
|
||||||
//!
|
|
||||||
//! # Features
|
|
||||||
//!
|
|
||||||
//! The `mainnet` feature configures the light client for use with the Zcash mainnet. By
|
|
||||||
//! default, the light client is configured for use with the Zcash testnet.
|
|
||||||
//!
|
|
||||||
//! [`CompactBlock`]: zcash_client_backend::proto::compact_formats::CompactBlock
|
|
||||||
//! [`init_cache_database`]: crate::init::init_cache_database
|
|
||||||
|
|
||||||
use rusqlite::{Connection, NO_PARAMS};
|
|
||||||
use std::cmp;
|
|
||||||
use zcash_client_backend::encoding::encode_payment_address;
|
|
||||||
use zcash_primitives::zip32::ExtendedFullViewingKey;
|
|
||||||
|
|
||||||
#[cfg(feature = "mainnet")]
|
|
||||||
use zcash_client_backend::constants::mainnet::{
|
|
||||||
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "mainnet"))]
|
|
||||||
use zcash_client_backend::constants::testnet::{
|
|
||||||
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod address;
|
|
||||||
pub mod chain;
|
|
||||||
pub mod error;
|
|
||||||
pub mod init;
|
|
||||||
pub mod query;
|
|
||||||
pub mod scan;
|
|
||||||
pub mod transact;
|
|
||||||
|
|
||||||
const ANCHOR_OFFSET: u32 = 10;
|
|
||||||
|
|
||||||
#[cfg(feature = "mainnet")]
|
|
||||||
const SAPLING_ACTIVATION_HEIGHT: i32 = 227_520;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "mainnet"))]
|
|
||||||
const SAPLING_ACTIVATION_HEIGHT: i32 = 280_000;
|
|
||||||
|
|
||||||
fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String {
|
|
||||||
let addr = extfvk.default_address().unwrap().1;
|
|
||||||
encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines the target height for a transaction, and the height from which to
|
|
||||||
/// select anchors, based on the current synchronised block chain.
|
|
||||||
fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error::Error> {
|
|
||||||
data.query_row_and_then(
|
|
||||||
"SELECT MIN(height), MAX(height) FROM blocks",
|
|
||||||
NO_PARAMS,
|
|
||||||
|row| match (row.get::<_, u32>(0), row.get::<_, u32>(1)) {
|
|
||||||
// If there are no blocks, the query returns NULL.
|
|
||||||
(Err(rusqlite::Error::InvalidColumnType(_, _, _)), _)
|
|
||||||
| (_, Err(rusqlite::Error::InvalidColumnType(_, _, _))) => {
|
|
||||||
Err(error::Error(error::ErrorKind::ScanRequired))
|
|
||||||
}
|
|
||||||
(Err(e), _) | (_, Err(e)) => Err(e.into()),
|
|
||||||
(Ok(min_height), Ok(max_height)) => {
|
|
||||||
let target_height = max_height + 1;
|
|
||||||
|
|
||||||
// Select an anchor ANCHOR_OFFSET back from the target block,
|
|
||||||
// unless that would be before the earliest block we have.
|
|
||||||
let anchor_height =
|
|
||||||
cmp::max(target_height.saturating_sub(ANCHOR_OFFSET), min_height);
|
|
||||||
|
|
||||||
Ok((target_height, anchor_height))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
|
||||||
use pairing::bls12_381::Bls12;
|
|
||||||
use protobuf::Message;
|
|
||||||
use rand_core::RngCore;
|
|
||||||
use rand_os::OsRng;
|
|
||||||
use rusqlite::{types::ToSql, Connection};
|
|
||||||
use std::path::Path;
|
|
||||||
use zcash_client_backend::proto::compact_formats::{
|
|
||||||
CompactBlock, CompactOutput, CompactSpend, CompactTx,
|
|
||||||
};
|
|
||||||
use zcash_primitives::{
|
|
||||||
block::BlockHash,
|
|
||||||
jubjub::fs::Fs,
|
|
||||||
note_encryption::{Memo, SaplingNoteEncryption},
|
|
||||||
primitives::{Note, PaymentAddress},
|
|
||||||
transaction::components::Amount,
|
|
||||||
zip32::ExtendedFullViewingKey,
|
|
||||||
JUBJUB,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Create a fake CompactBlock at the given height, containing a single output paying
|
|
||||||
/// the given address. Returns the CompactBlock and the nullifier for the new note.
|
|
||||||
pub(crate) fn fake_compact_block(
|
|
||||||
height: i32,
|
|
||||||
prev_hash: BlockHash,
|
|
||||||
extfvk: ExtendedFullViewingKey,
|
|
||||||
value: Amount,
|
|
||||||
) -> (CompactBlock, Vec<u8>) {
|
|
||||||
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 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.outputs.push(cout);
|
|
||||||
let mut cb = CompactBlock::new();
|
|
||||||
cb.set_height(height as u64);
|
|
||||||
cb.hash.resize(32, 0);
|
|
||||||
rng.fill_bytes(&mut cb.hash);
|
|
||||||
cb.prevHash.extend_from_slice(&prev_hash.0);
|
|
||||||
cb.vtx.push(ctx);
|
|
||||||
(cb, note.nf(&extfvk.fvk.vk, 0, &JUBJUB))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a fake CompactBlock at the given height, spending a single note from the
|
|
||||||
/// given address.
|
|
||||||
pub(crate) fn fake_compact_block_spending(
|
|
||||||
height: i32,
|
|
||||||
prev_hash: BlockHash,
|
|
||||||
(nf, in_value): (Vec<u8>, Amount),
|
|
||||||
extfvk: ExtendedFullViewingKey,
|
|
||||||
to: PaymentAddress<Bls12>,
|
|
||||||
value: Amount,
|
|
||||||
) -> CompactBlock {
|
|
||||||
let mut rng = OsRng;
|
|
||||||
|
|
||||||
// Create a fake CompactBlock containing the note
|
|
||||||
let mut cspend = CompactSpend::new();
|
|
||||||
cspend.set_nf(nf);
|
|
||||||
let mut ctx = CompactTx::new();
|
|
||||||
let mut txid = vec![0; 32];
|
|
||||||
rng.fill_bytes(&mut txid);
|
|
||||||
ctx.set_hash(txid);
|
|
||||||
ctx.spends.push(cspend);
|
|
||||||
|
|
||||||
// Create a fake Note for the payment
|
|
||||||
ctx.outputs.push({
|
|
||||||
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,
|
|
||||||
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();
|
|
||||||
|
|
||||||
let mut cout = CompactOutput::new();
|
|
||||||
cout.set_cmu(cmu);
|
|
||||||
cout.set_epk(epk);
|
|
||||||
cout.set_ciphertext(enc_ciphertext[..52].to_vec());
|
|
||||||
cout
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a fake Note for the change
|
|
||||||
ctx.outputs.push({
|
|
||||||
let change_addr = extfvk.default_address().unwrap().1;
|
|
||||||
let note = Note {
|
|
||||||
g_d: change_addr.diversifier.g_d::<Bls12>(&JUBJUB).unwrap(),
|
|
||||||
pk_d: change_addr.pk_d.clone(),
|
|
||||||
value: (in_value - value).into(),
|
|
||||||
r: Fs::random(&mut rng),
|
|
||||||
};
|
|
||||||
let encryptor = SaplingNoteEncryption::new(
|
|
||||||
extfvk.fvk.ovk,
|
|
||||||
note.clone(),
|
|
||||||
change_addr,
|
|
||||||
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();
|
|
||||||
|
|
||||||
let mut cout = CompactOutput::new();
|
|
||||||
cout.set_cmu(cmu);
|
|
||||||
cout.set_epk(epk);
|
|
||||||
cout.set_ciphertext(enc_ciphertext[..52].to_vec());
|
|
||||||
cout
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut cb = CompactBlock::new();
|
|
||||||
cb.set_height(height as u64);
|
|
||||||
cb.hash.resize(32, 0);
|
|
||||||
rng.fill_bytes(&mut cb.hash);
|
|
||||||
cb.prevHash.extend_from_slice(&prev_hash.0);
|
|
||||||
cb.vtx.push(ctx);
|
|
||||||
cb
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Insert a fake CompactBlock into the cache DB.
|
|
||||||
pub(crate) fn insert_into_cache<P: AsRef<Path>>(db_cache: P, cb: &CompactBlock) {
|
|
||||||
let cb_bytes = cb.write_to_bytes().unwrap();
|
|
||||||
let cache = Connection::open(&db_cache).unwrap();
|
|
||||||
cache
|
|
||||||
.prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)")
|
|
||||||
.unwrap()
|
|
||||||
.execute(&[
|
|
||||||
(cb.height as i32).to_sql().unwrap(),
|
|
||||||
cb_bytes.to_sql().unwrap(),
|
|
||||||
])
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
//! Functions for querying information in the data database.
|
|
||||||
|
|
||||||
use rusqlite::Connection;
|
|
||||||
use std::path::Path;
|
|
||||||
use zcash_primitives::{note_encryption::Memo, transaction::components::Amount};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::{Error, ErrorKind},
|
|
||||||
get_target_and_anchor_heights,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Returns the address for the account.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_sqlite::query::get_address;
|
|
||||||
///
|
|
||||||
/// let addr = get_address("/path/to/data.db", 0);
|
|
||||||
/// ```
|
|
||||||
pub fn get_address<P: AsRef<Path>>(db_data: P, account: u32) -> Result<String, Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
let addr = data.query_row(
|
|
||||||
"SELECT address FROM accounts
|
|
||||||
WHERE account = ?",
|
|
||||||
&[account],
|
|
||||||
|row| row.get(0),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the balance for the account, including all mined unspent notes that we know
|
|
||||||
/// about.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_sqlite::query::get_balance;
|
|
||||||
///
|
|
||||||
/// let addr = get_balance("/path/to/data.db", 0);
|
|
||||||
/// ```
|
|
||||||
pub fn get_balance<P: AsRef<Path>>(db_data: P, account: u32) -> Result<Amount, Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
let balance = data.query_row(
|
|
||||||
"SELECT SUM(value) FROM received_notes
|
|
||||||
INNER JOIN transactions ON transactions.id_tx = received_notes.tx
|
|
||||||
WHERE account = ? AND spent IS NULL AND transactions.block IS NOT NULL",
|
|
||||||
&[account],
|
|
||||||
|row| row.get(0).or(Ok(0)),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
match Amount::from_i64(balance) {
|
|
||||||
Ok(amount) if !amount.is_negative() => Ok(amount),
|
|
||||||
_ => Err(Error(ErrorKind::CorruptedData(
|
|
||||||
"Sum of values in received_notes is out of range",
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the verified balance for the account, which ignores notes that have been
|
|
||||||
/// received too recently and are not yet deemed spendable.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_sqlite::query::get_verified_balance;
|
|
||||||
///
|
|
||||||
/// let addr = get_verified_balance("/path/to/data.db", 0);
|
|
||||||
/// ```
|
|
||||||
pub fn get_verified_balance<P: AsRef<Path>>(db_data: P, account: u32) -> Result<Amount, Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
let (_, anchor_height) = get_target_and_anchor_heights(&data)?;
|
|
||||||
|
|
||||||
let balance = data.query_row(
|
|
||||||
"SELECT SUM(value) FROM received_notes
|
|
||||||
INNER JOIN transactions ON transactions.id_tx = received_notes.tx
|
|
||||||
WHERE account = ? AND spent IS NULL AND transactions.block <= ?",
|
|
||||||
&[account, anchor_height],
|
|
||||||
|row| row.get(0).or(Ok(0)),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
match Amount::from_i64(balance) {
|
|
||||||
Ok(amount) if !amount.is_negative() => Ok(amount),
|
|
||||||
_ => Err(Error(ErrorKind::CorruptedData(
|
|
||||||
"Sum of values in received_notes is out of range",
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the memo for a received note, if it is known and a valid UTF-8 string.
|
|
||||||
///
|
|
||||||
/// The note is identified by its row index in the `received_notes` table within the data
|
|
||||||
/// database.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_sqlite::query::get_received_memo_as_utf8;
|
|
||||||
///
|
|
||||||
/// let memo = get_received_memo_as_utf8("/path/to/data.db", 27);
|
|
||||||
pub fn get_received_memo_as_utf8<P: AsRef<Path>>(
|
|
||||||
db_data: P,
|
|
||||||
id_note: i64,
|
|
||||||
) -> Result<Option<String>, Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
let memo: Vec<_> = data.query_row(
|
|
||||||
"SELECT memo FROM received_notes
|
|
||||||
WHERE id_note = ?",
|
|
||||||
&[id_note],
|
|
||||||
|row| row.get(0),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
match Memo::from_bytes(&memo) {
|
|
||||||
Some(memo) => match memo.to_utf8() {
|
|
||||||
Some(Ok(res)) => Ok(Some(res)),
|
|
||||||
Some(Err(e)) => Err(Error(ErrorKind::InvalidMemo(e))),
|
|
||||||
None => Ok(None),
|
|
||||||
},
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the memo for a sent note, if it is known and a valid UTF-8 string.
|
|
||||||
///
|
|
||||||
/// The note is identified by its row index in the `sent_notes` table within the data
|
|
||||||
/// database.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_sqlite::query::get_sent_memo_as_utf8;
|
|
||||||
///
|
|
||||||
/// let memo = get_sent_memo_as_utf8("/path/to/data.db", 12);
|
|
||||||
pub fn get_sent_memo_as_utf8<P: AsRef<Path>>(
|
|
||||||
db_data: P,
|
|
||||||
id_note: i64,
|
|
||||||
) -> Result<Option<String>, Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
let memo: Vec<_> = data.query_row(
|
|
||||||
"SELECT memo FROM sent_notes
|
|
||||||
WHERE id_note = ?",
|
|
||||||
&[id_note],
|
|
||||||
|row| row.get(0),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
match Memo::from_bytes(&memo) {
|
|
||||||
Some(memo) => match memo.to_utf8() {
|
|
||||||
Some(Ok(res)) => Ok(Some(res)),
|
|
||||||
Some(Err(e)) => Err(Error(ErrorKind::InvalidMemo(e))),
|
|
||||||
None => Ok(None),
|
|
||||||
},
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use tempfile::NamedTempFile;
|
|
||||||
use zcash_primitives::{
|
|
||||||
transaction::components::Amount,
|
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{get_address, get_balance, get_verified_balance};
|
|
||||||
use crate::{
|
|
||||||
error::ErrorKind,
|
|
||||||
init::{init_accounts_table, init_data_database},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_database_has_no_balance() {
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvks = [ExtendedFullViewingKey::from(&extsk)];
|
|
||||||
init_accounts_table(&db_data, &extfvks).unwrap();
|
|
||||||
|
|
||||||
// The account should be empty
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero());
|
|
||||||
|
|
||||||
// The account should have no verified balance, as we haven't scanned any blocks
|
|
||||||
let e = get_verified_balance(db_data, 0).unwrap_err();
|
|
||||||
match e.kind() {
|
|
||||||
ErrorKind::ScanRequired => (),
|
|
||||||
_ => panic!("Unexpected error: {:?}", e),
|
|
||||||
}
|
|
||||||
|
|
||||||
// An invalid account has zero balance
|
|
||||||
assert!(get_address(db_data, 1).is_err());
|
|
||||||
assert_eq!(get_balance(db_data, 1).unwrap(), Amount::zero());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,500 +0,0 @@
|
|||||||
//! Functions for scanning the chain and extracting relevant information.
|
|
||||||
|
|
||||||
use ff::{PrimeField, PrimeFieldRepr};
|
|
||||||
use protobuf::parse_from_bytes;
|
|
||||||
use rusqlite::{types::ToSql, Connection, NO_PARAMS};
|
|
||||||
use std::path::Path;
|
|
||||||
use zcash_client_backend::{
|
|
||||||
encoding::decode_extended_full_viewing_key, proto::compact_formats::CompactBlock,
|
|
||||||
welding_rig::scan_block,
|
|
||||||
};
|
|
||||||
use zcash_primitives::{
|
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
|
||||||
sapling::Node,
|
|
||||||
JUBJUB,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::{Error, ErrorKind},
|
|
||||||
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompactBlockRow {
|
|
||||||
height: i32,
|
|
||||||
data: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct WitnessRow {
|
|
||||||
id_note: i64,
|
|
||||||
witness: IncrementalWitness<Node>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Scans new blocks added to the cache for any transactions received by the tracked
|
|
||||||
/// accounts.
|
|
||||||
///
|
|
||||||
/// This function pays attention only to cached blocks with heights greater than the
|
|
||||||
/// highest scanned block in `db_data`. Cached blocks with lower heights are not verified
|
|
||||||
/// against previously-scanned blocks. In particular, this function **assumes** that the
|
|
||||||
/// caller is handling rollbacks.
|
|
||||||
///
|
|
||||||
/// For brand-new light client databases, this function starts scanning from the Sapling
|
|
||||||
/// activation height. This height can be fast-forwarded to a more recent block by calling
|
|
||||||
/// [`init_blocks_table`] before this function.
|
|
||||||
///
|
|
||||||
/// Scanned blocks are required to be height-sequential. If a block is missing from the
|
|
||||||
/// cache, an error will be returned with kind [`ErrorKind::InvalidHeight`].
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_sqlite::scan::scan_cached_blocks;
|
|
||||||
///
|
|
||||||
/// scan_cached_blocks("/path/to/cache.db", "/path/to/data.db");
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`init_blocks_table`]: crate::init::init_blocks_table
|
|
||||||
pub fn scan_cached_blocks<P: AsRef<Path>, Q: AsRef<Path>>(
|
|
||||||
db_cache: P,
|
|
||||||
db_data: Q,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let cache = Connection::open(db_cache)?;
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
// Recall where we synced up to previously.
|
|
||||||
// If we have never synced, use sapling activation height to select all cached CompactBlocks.
|
|
||||||
let mut last_height = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| {
|
|
||||||
row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Fetch the CompactBlocks we need to scan
|
|
||||||
let mut stmt_blocks = cache
|
|
||||||
.prepare("SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height ASC")?;
|
|
||||||
let rows = stmt_blocks.query_map(&[last_height], |row| {
|
|
||||||
Ok(CompactBlockRow {
|
|
||||||
height: row.get(0)?,
|
|
||||||
data: row.get(1)?,
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Fetch the ExtendedFullViewingKeys we are tracking
|
|
||||||
let mut stmt_fetch_accounts =
|
|
||||||
data.prepare("SELECT extfvk FROM accounts ORDER BY account ASC")?;
|
|
||||||
let extfvks = stmt_fetch_accounts.query_map(NO_PARAMS, |row| {
|
|
||||||
row.get(0).map(|extfvk: String| {
|
|
||||||
decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk)
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
// Raise SQL errors from the query, IO errors from parsing, and incorrect HRP errors.
|
|
||||||
let extfvks: Vec<_> = extfvks
|
|
||||||
.collect::<Result<Result<Option<_>, _>, _>>()??
|
|
||||||
.ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?;
|
|
||||||
|
|
||||||
// Get the most recent CommitmentTree
|
|
||||||
let mut stmt_fetch_tree = data.prepare("SELECT sapling_tree FROM blocks WHERE height = ?")?;
|
|
||||||
let mut tree = stmt_fetch_tree
|
|
||||||
.query_row(&[last_height], |row| {
|
|
||||||
row.get(0).map(|data: Vec<_>| {
|
|
||||||
CommitmentTree::read(&data[..]).unwrap_or_else(|_| CommitmentTree::new())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|_| CommitmentTree::new());
|
|
||||||
|
|
||||||
// Get most recent incremental witnesses for the notes we are tracking
|
|
||||||
let mut stmt_fetch_witnesses =
|
|
||||||
data.prepare("SELECT note, witness FROM sapling_witnesses WHERE block = ?")?;
|
|
||||||
let witnesses = stmt_fetch_witnesses.query_map(&[last_height], |row| {
|
|
||||||
let id_note = row.get(0)?;
|
|
||||||
let data: Vec<_> = row.get(1)?;
|
|
||||||
Ok(IncrementalWitness::read(&data[..]).map(|witness| WitnessRow { id_note, witness }))
|
|
||||||
})?;
|
|
||||||
let mut witnesses: Vec<_> = witnesses.collect::<Result<Result<_, _>, _>>()??;
|
|
||||||
|
|
||||||
// Get the nullifiers for the notes we are tracking
|
|
||||||
let mut stmt_fetch_nullifiers =
|
|
||||||
data.prepare("SELECT id_note, nf, account FROM received_notes WHERE spent IS NULL")?;
|
|
||||||
let nullifiers = stmt_fetch_nullifiers.query_map(NO_PARAMS, |row| {
|
|
||||||
let nf: Vec<_> = row.get(1)?;
|
|
||||||
let account: i64 = row.get(2)?;
|
|
||||||
Ok((nf, account as usize))
|
|
||||||
})?;
|
|
||||||
let mut nullifiers: Vec<_> = nullifiers.collect::<Result<_, _>>()?;
|
|
||||||
|
|
||||||
// Prepare per-block SQL statements
|
|
||||||
let mut stmt_insert_block = data.prepare(
|
|
||||||
"INSERT INTO blocks (height, hash, time, sapling_tree)
|
|
||||||
VALUES (?, ?, ?, ?)",
|
|
||||||
)?;
|
|
||||||
let mut stmt_update_tx = data.prepare(
|
|
||||||
"UPDATE transactions
|
|
||||||
SET block = ?, tx_index = ? WHERE txid = ?",
|
|
||||||
)?;
|
|
||||||
let mut stmt_insert_tx = data.prepare(
|
|
||||||
"INSERT INTO transactions (txid, block, tx_index)
|
|
||||||
VALUES (?, ?, ?)",
|
|
||||||
)?;
|
|
||||||
let mut stmt_select_tx = data.prepare("SELECT id_tx FROM transactions WHERE txid = ?")?;
|
|
||||||
let mut stmt_mark_spent_note =
|
|
||||||
data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?;
|
|
||||||
let mut stmt_insert_note = data.prepare(
|
|
||||||
"INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
)?;
|
|
||||||
let mut stmt_insert_witness = data.prepare(
|
|
||||||
"INSERT INTO sapling_witnesses (note, block, witness)
|
|
||||||
VALUES (?, ?, ?)",
|
|
||||||
)?;
|
|
||||||
let mut stmt_prune_witnesses = data.prepare("DELETE FROM sapling_witnesses WHERE block < ?")?;
|
|
||||||
let mut stmt_update_expired = data.prepare(
|
|
||||||
"UPDATE received_notes SET spent = NULL WHERE EXISTS (
|
|
||||||
SELECT id_tx FROM transactions
|
|
||||||
WHERE id_tx = received_notes.spent AND block IS NULL AND expiry_height < ?
|
|
||||||
)",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for row in rows {
|
|
||||||
let row = row?;
|
|
||||||
|
|
||||||
// Start an SQL transaction for this block.
|
|
||||||
data.execute("BEGIN IMMEDIATE", NO_PARAMS)?;
|
|
||||||
|
|
||||||
// Scanned blocks MUST be height-sequential.
|
|
||||||
if row.height != (last_height + 1) {
|
|
||||||
return Err(Error(ErrorKind::InvalidHeight(last_height + 1, row.height)));
|
|
||||||
}
|
|
||||||
last_height = row.height;
|
|
||||||
|
|
||||||
let block: CompactBlock = parse_from_bytes(&row.data)?;
|
|
||||||
let block_hash = block.hash.clone();
|
|
||||||
let block_time = block.time;
|
|
||||||
|
|
||||||
let txs = {
|
|
||||||
let nf_refs: Vec<_> = nullifiers.iter().map(|(nf, acc)| (&nf[..], *acc)).collect();
|
|
||||||
let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.witness).collect();
|
|
||||||
scan_block(
|
|
||||||
block,
|
|
||||||
&extfvks[..],
|
|
||||||
&nf_refs,
|
|
||||||
&mut tree,
|
|
||||||
&mut witness_refs[..],
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Enforce that all roots match. This is slow, so only include in debug builds.
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
{
|
|
||||||
let cur_root = tree.root();
|
|
||||||
for row in &witnesses {
|
|
||||||
if row.witness.root() != cur_root {
|
|
||||||
return Err(Error(ErrorKind::InvalidWitnessAnchor(
|
|
||||||
row.id_note,
|
|
||||||
last_height,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for tx in &txs {
|
|
||||||
for output in tx.shielded_outputs.iter() {
|
|
||||||
if output.witness.root() != cur_root {
|
|
||||||
return Err(Error(ErrorKind::InvalidNewWitnessAnchor(
|
|
||||||
output.index,
|
|
||||||
tx.txid,
|
|
||||||
last_height,
|
|
||||||
output.witness.root(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the block into the database.
|
|
||||||
let mut encoded_tree = Vec::new();
|
|
||||||
tree.write(&mut encoded_tree)
|
|
||||||
.expect("Should be able to write to a Vec");
|
|
||||||
stmt_insert_block.execute(&[
|
|
||||||
row.height.to_sql()?,
|
|
||||||
block_hash.to_sql()?,
|
|
||||||
block_time.to_sql()?,
|
|
||||||
encoded_tree.to_sql()?,
|
|
||||||
])?;
|
|
||||||
|
|
||||||
for tx in txs {
|
|
||||||
// First try update an existing transaction in the database.
|
|
||||||
let txid = tx.txid.0.to_vec();
|
|
||||||
let tx_row = if stmt_update_tx.execute(&[
|
|
||||||
row.height.to_sql()?,
|
|
||||||
(tx.index as i64).to_sql()?,
|
|
||||||
txid.to_sql()?,
|
|
||||||
])? == 0
|
|
||||||
{
|
|
||||||
// It isn't there, so insert our transaction into the database.
|
|
||||||
stmt_insert_tx.execute(&[
|
|
||||||
txid.to_sql()?,
|
|
||||||
row.height.to_sql()?,
|
|
||||||
(tx.index as i64).to_sql()?,
|
|
||||||
])?;
|
|
||||||
data.last_insert_rowid()
|
|
||||||
} else {
|
|
||||||
// It was there, so grab its row number.
|
|
||||||
stmt_select_tx.query_row(&[txid], |row| row.get(0))?
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mark notes as spent and remove them from the scanning cache
|
|
||||||
for spend in &tx.shielded_spends {
|
|
||||||
stmt_mark_spent_note.execute(&[tx_row.to_sql()?, spend.nf.to_sql()?])?;
|
|
||||||
}
|
|
||||||
nullifiers = nullifiers
|
|
||||||
.into_iter()
|
|
||||||
.filter(|(nf, _acc)| {
|
|
||||||
tx.shielded_spends
|
|
||||||
.iter()
|
|
||||||
.find(|spend| &spend.nf == nf)
|
|
||||||
.is_none()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for output in tx.shielded_outputs {
|
|
||||||
let mut rcm = [0; 32];
|
|
||||||
output.note.r.into_repr().write_le(&mut rcm[..])?;
|
|
||||||
let nf = output.note.nf(
|
|
||||||
&extfvks[output.account].fvk.vk,
|
|
||||||
output.witness.position() as u64,
|
|
||||||
&JUBJUB,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Insert received note into the database.
|
|
||||||
// Assumptions:
|
|
||||||
// - A transaction will not contain more than 2^63 shielded outputs.
|
|
||||||
// - A note value will never exceed 2^63 zatoshis.
|
|
||||||
stmt_insert_note.execute(&[
|
|
||||||
tx_row.to_sql()?,
|
|
||||||
(output.index as i64).to_sql()?,
|
|
||||||
(output.account as i64).to_sql()?,
|
|
||||||
output.to.diversifier.0.to_sql()?,
|
|
||||||
(output.note.value as i64).to_sql()?,
|
|
||||||
rcm.to_sql()?,
|
|
||||||
nf.to_sql()?,
|
|
||||||
output.is_change.to_sql()?,
|
|
||||||
])?;
|
|
||||||
let note_row = data.last_insert_rowid();
|
|
||||||
|
|
||||||
// Save witness for note.
|
|
||||||
witnesses.push(WitnessRow {
|
|
||||||
id_note: note_row,
|
|
||||||
witness: output.witness,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Cache nullifier for note (to detect subsequent spends in this scan).
|
|
||||||
nullifiers.push((nf, output.account));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert current witnesses into the database.
|
|
||||||
let mut encoded = Vec::new();
|
|
||||||
for witness_row in witnesses.iter() {
|
|
||||||
encoded.clear();
|
|
||||||
witness_row
|
|
||||||
.witness
|
|
||||||
.write(&mut encoded)
|
|
||||||
.expect("Should be able to write to a Vec");
|
|
||||||
stmt_insert_witness.execute(&[
|
|
||||||
witness_row.id_note.to_sql()?,
|
|
||||||
last_height.to_sql()?,
|
|
||||||
encoded.to_sql()?,
|
|
||||||
])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune the stored witnesses (we only expect rollbacks of at most 100 blocks).
|
|
||||||
stmt_prune_witnesses.execute(&[last_height - 100])?;
|
|
||||||
|
|
||||||
// Update now-expired transactions that didn't get mined.
|
|
||||||
stmt_update_expired.execute(&[last_height])?;
|
|
||||||
|
|
||||||
// Commit the SQL transaction, writing this block's data atomically.
|
|
||||||
data.execute("COMMIT", NO_PARAMS)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use tempfile::NamedTempFile;
|
|
||||||
use zcash_primitives::{
|
|
||||||
block::BlockHash,
|
|
||||||
transaction::components::Amount,
|
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::scan_cached_blocks;
|
|
||||||
use crate::{
|
|
||||||
init::{init_accounts_table, init_cache_database, init_data_database},
|
|
||||||
query::get_balance,
|
|
||||||
tests::{fake_compact_block, fake_compact_block_spending, insert_into_cache},
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scan_cached_blocks_requires_sequential_blocks() {
|
|
||||||
let cache_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_cache = cache_file.path();
|
|
||||||
init_cache_database(&db_cache).unwrap();
|
|
||||||
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
||||||
init_accounts_table(&db_data, &[extfvk.clone()]).unwrap();
|
|
||||||
|
|
||||||
// Create a block with height SAPLING_ACTIVATION_HEIGHT
|
|
||||||
let value = Amount::from_u64(50000).unwrap();
|
|
||||||
let (cb1, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
BlockHash([0; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb1);
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value);
|
|
||||||
|
|
||||||
// We cannot scan a block of height SAPLING_ACTIVATION_HEIGHT + 2 next
|
|
||||||
let (cb2, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 1,
|
|
||||||
cb1.hash(),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
let (cb3, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 2,
|
|
||||||
cb2.hash(),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb3);
|
|
||||||
match scan_cached_blocks(db_cache, db_data) {
|
|
||||||
Ok(_) => panic!("Should have failed"),
|
|
||||||
Err(e) => assert_eq!(
|
|
||||||
e.to_string(),
|
|
||||||
format!(
|
|
||||||
"Expected height of next CompactBlock to be {}, but was {}",
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 1,
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 2
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we add a block of height SAPLING_ACTIVATION_HEIGHT + 1, we can now scan both
|
|
||||||
insert_into_cache(db_cache, &cb2);
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
get_balance(db_data, 0).unwrap(),
|
|
||||||
Amount::from_u64(150_000).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scan_cached_blocks_finds_received_notes() {
|
|
||||||
let cache_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_cache = cache_file.path();
|
|
||||||
init_cache_database(&db_cache).unwrap();
|
|
||||||
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
||||||
init_accounts_table(&db_data, &[extfvk.clone()]).unwrap();
|
|
||||||
|
|
||||||
// Account balance should be zero
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero());
|
|
||||||
|
|
||||||
// Create a fake CompactBlock sending value to the address
|
|
||||||
let value = Amount::from_u64(5).unwrap();
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
BlockHash([0; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
|
|
||||||
// Scan the cache
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Account balance should reflect the received note
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value);
|
|
||||||
|
|
||||||
// Create a second fake CompactBlock sending more value to the address
|
|
||||||
let value2 = Amount::from_u64(7).unwrap();
|
|
||||||
let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2);
|
|
||||||
insert_into_cache(db_cache, &cb2);
|
|
||||||
|
|
||||||
// Scan the cache again
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Account balance should reflect both received notes
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value + value2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scan_cached_blocks_finds_change_notes() {
|
|
||||||
let cache_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_cache = cache_file.path();
|
|
||||||
init_cache_database(&db_cache).unwrap();
|
|
||||||
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
||||||
init_accounts_table(&db_data, &[extfvk.clone()]).unwrap();
|
|
||||||
|
|
||||||
// Account balance should be zero
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero());
|
|
||||||
|
|
||||||
// Create a fake CompactBlock sending value to the address
|
|
||||||
let value = Amount::from_u64(5).unwrap();
|
|
||||||
let (cb, nf) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
BlockHash([0; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
|
|
||||||
// Scan the cache
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Account balance should reflect the received note
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value);
|
|
||||||
|
|
||||||
// Create a second fake CompactBlock spending value from the address
|
|
||||||
let extsk2 = ExtendedSpendingKey::master(&[0]);
|
|
||||||
let to2 = extsk2.default_address().unwrap().1;
|
|
||||||
let value2 = Amount::from_u64(2).unwrap();
|
|
||||||
insert_into_cache(
|
|
||||||
db_cache,
|
|
||||||
&fake_compact_block_spending(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 1,
|
|
||||||
cb.hash(),
|
|
||||||
(nf, value),
|
|
||||||
extfvk,
|
|
||||||
to2,
|
|
||||||
value2,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Scan the cache again
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Account balance should equal the change
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value - value2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,665 +0,0 @@
|
|||||||
//! Functions for creating transactions.
|
|
||||||
|
|
||||||
use ff::{PrimeField, PrimeFieldRepr};
|
|
||||||
use pairing::bls12_381::Bls12;
|
|
||||||
use rusqlite::{types::ToSql, Connection, NO_PARAMS};
|
|
||||||
use std::path::Path;
|
|
||||||
use zcash_client_backend::encoding::encode_extended_full_viewing_key;
|
|
||||||
use zcash_primitives::{
|
|
||||||
jubjub::fs::{Fs, FsRepr},
|
|
||||||
merkle_tree::IncrementalWitness,
|
|
||||||
note_encryption::Memo,
|
|
||||||
primitives::{Diversifier, Note},
|
|
||||||
prover::TxProver,
|
|
||||||
sapling::Node,
|
|
||||||
transaction::{
|
|
||||||
builder::Builder,
|
|
||||||
components::{amount::DEFAULT_FEE, Amount},
|
|
||||||
},
|
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
|
||||||
JUBJUB,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
address::RecipientAddress,
|
|
||||||
error::{Error, ErrorKind},
|
|
||||||
get_target_and_anchor_heights, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SelectedNoteRow {
|
|
||||||
diversifier: Diversifier,
|
|
||||||
note: Note<Bls12>,
|
|
||||||
witness: IncrementalWitness<Node>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a transaction paying the specified address from the given account.
|
|
||||||
///
|
|
||||||
/// Returns the row index of the newly-created transaction in the `transactions` table
|
|
||||||
/// within the data database. The caller can read the raw transaction bytes from the `raw`
|
|
||||||
/// column in order to broadcast the transaction to the network.
|
|
||||||
///
|
|
||||||
/// Do not call this multiple times in parallel, or you will generate transactions that
|
|
||||||
/// double-spend the same notes.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use zcash_client_backend::{
|
|
||||||
/// constants::{testnet::COIN_TYPE, SAPLING_CONSENSUS_BRANCH_ID},
|
|
||||||
/// keys::spending_key,
|
|
||||||
/// };
|
|
||||||
/// use zcash_client_sqlite::transact::create_to_address;
|
|
||||||
/// use zcash_primitives::transaction::components::Amount;
|
|
||||||
/// use zcash_proofs::prover::LocalTxProver;
|
|
||||||
///
|
|
||||||
/// let tx_prover = match LocalTxProver::with_default_location() {
|
|
||||||
/// Some(tx_prover) => tx_prover,
|
|
||||||
/// None => {
|
|
||||||
/// panic!("Cannot locate the Zcash parameters. Please run zcash-fetch-params or fetch-params.sh to download the parameters, and then re-run the tests.");
|
|
||||||
/// }
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let account = 0;
|
|
||||||
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, account);
|
|
||||||
/// let to = extsk.default_address().unwrap().1.into();
|
|
||||||
/// match create_to_address(
|
|
||||||
/// "/path/to/data.db",
|
|
||||||
/// SAPLING_CONSENSUS_BRANCH_ID,
|
|
||||||
/// tx_prover,
|
|
||||||
/// (account, &extsk),
|
|
||||||
/// &to,
|
|
||||||
/// Amount::from_u64(1).unwrap(),
|
|
||||||
/// None,
|
|
||||||
/// ) {
|
|
||||||
/// Ok(tx_row) => (),
|
|
||||||
/// Err(e) => (),
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn create_to_address<P: AsRef<Path>>(
|
|
||||||
db_data: P,
|
|
||||||
consensus_branch_id: u32,
|
|
||||||
prover: impl TxProver,
|
|
||||||
(account, extsk): (u32, &ExtendedSpendingKey),
|
|
||||||
to: &RecipientAddress,
|
|
||||||
value: Amount,
|
|
||||||
memo: Option<Memo>,
|
|
||||||
) -> Result<i64, Error> {
|
|
||||||
let data = Connection::open(db_data)?;
|
|
||||||
|
|
||||||
// Check that the ExtendedSpendingKey we have been given corresponds to the
|
|
||||||
// ExtendedFullViewingKey for the account we are spending from.
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(extsk);
|
|
||||||
if !data
|
|
||||||
.prepare("SELECT * FROM accounts WHERE account = ? AND extfvk = ?")?
|
|
||||||
.exists(&[
|
|
||||||
account.to_sql()?,
|
|
||||||
encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk)
|
|
||||||
.to_sql()?,
|
|
||||||
])?
|
|
||||||
{
|
|
||||||
return Err(Error(ErrorKind::InvalidExtSK(account)));
|
|
||||||
}
|
|
||||||
let ovk = extfvk.fvk.ovk;
|
|
||||||
|
|
||||||
// Target the next block, assuming we are up-to-date.
|
|
||||||
let (height, anchor_height) = {
|
|
||||||
let (target_height, anchor_height) = get_target_and_anchor_heights(&data)?;
|
|
||||||
(target_height, i64::from(anchor_height))
|
|
||||||
};
|
|
||||||
|
|
||||||
// The goal of this SQL statement is to select the oldest notes until the required
|
|
||||||
// value has been reached, and then fetch the witnesses at the desired height for the
|
|
||||||
// selected notes. This is achieved in several steps:
|
|
||||||
//
|
|
||||||
// 1) Use a window function to create a view of all notes, ordered from oldest to
|
|
||||||
// newest, with an additional column containing a running sum:
|
|
||||||
// - Unspent notes accumulate the values of all unspent notes in that note's
|
|
||||||
// account, up to itself.
|
|
||||||
// - Spent notes accumulate the values of all notes in the transaction they were
|
|
||||||
// spent in, up to itself.
|
|
||||||
//
|
|
||||||
// 2) Select all unspent notes in the desired account, along with their running sum.
|
|
||||||
//
|
|
||||||
// 3) Select all notes for which the running sum was less than the required value, as
|
|
||||||
// well as a single note for which the sum was greater than or equal to the
|
|
||||||
// required value, bringing the sum of all selected notes across the threshold.
|
|
||||||
//
|
|
||||||
// 4) Match the selected notes against the witnesses at the desired height.
|
|
||||||
let target_value = i64::from(value + DEFAULT_FEE);
|
|
||||||
let mut stmt_select_notes = data.prepare(
|
|
||||||
"WITH selected AS (
|
|
||||||
WITH eligible AS (
|
|
||||||
SELECT id_note, diversifier, value, rcm,
|
|
||||||
SUM(value) OVER
|
|
||||||
(PARTITION BY account, spent ORDER BY id_note) AS so_far
|
|
||||||
FROM received_notes
|
|
||||||
INNER JOIN transactions ON transactions.id_tx = received_notes.tx
|
|
||||||
WHERE account = ? AND spent IS NULL AND transactions.block <= ?
|
|
||||||
)
|
|
||||||
SELECT * FROM eligible WHERE so_far < ?
|
|
||||||
UNION
|
|
||||||
SELECT * FROM (SELECT * FROM eligible WHERE so_far >= ? LIMIT 1)
|
|
||||||
), witnesses AS (
|
|
||||||
SELECT note, witness FROM sapling_witnesses
|
|
||||||
WHERE block = ?
|
|
||||||
)
|
|
||||||
SELECT selected.diversifier, selected.value, selected.rcm, witnesses.witness
|
|
||||||
FROM selected
|
|
||||||
INNER JOIN witnesses ON selected.id_note = witnesses.note",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Select notes
|
|
||||||
let notes = stmt_select_notes.query_and_then::<_, Error, _, _>(
|
|
||||||
&[
|
|
||||||
i64::from(account),
|
|
||||||
anchor_height,
|
|
||||||
target_value,
|
|
||||||
target_value,
|
|
||||||
anchor_height,
|
|
||||||
],
|
|
||||||
|row| {
|
|
||||||
let diversifier = {
|
|
||||||
let d: Vec<_> = row.get(0)?;
|
|
||||||
if d.len() != 11 {
|
|
||||||
return Err(Error(ErrorKind::CorruptedData(
|
|
||||||
"Invalid diversifier length",
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
let mut tmp = [0; 11];
|
|
||||||
tmp.copy_from_slice(&d);
|
|
||||||
Diversifier(tmp)
|
|
||||||
};
|
|
||||||
|
|
||||||
let note_value: i64 = row.get(1)?;
|
|
||||||
|
|
||||||
let rcm = {
|
|
||||||
let d: Vec<_> = row.get(2)?;
|
|
||||||
let mut tmp = FsRepr::default();
|
|
||||||
tmp.read_le(&d[..])?;
|
|
||||||
Fs::from_repr(tmp).map_err(|_| Error(ErrorKind::InvalidNote))?
|
|
||||||
};
|
|
||||||
|
|
||||||
let from = extfvk
|
|
||||||
.fvk
|
|
||||||
.vk
|
|
||||||
.into_payment_address(diversifier, &JUBJUB)
|
|
||||||
.unwrap();
|
|
||||||
let note = from.create_note(note_value as u64, rcm, &JUBJUB).unwrap();
|
|
||||||
|
|
||||||
let witness = {
|
|
||||||
let d: Vec<_> = row.get(3)?;
|
|
||||||
IncrementalWitness::read(&d[..])?
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(SelectedNoteRow {
|
|
||||||
diversifier,
|
|
||||||
note,
|
|
||||||
witness,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
let notes: Vec<SelectedNoteRow> = notes.collect::<Result<_, _>>()?;
|
|
||||||
|
|
||||||
// Confirm we were able to select sufficient value
|
|
||||||
let selected_value = notes
|
|
||||||
.iter()
|
|
||||||
.fold(0, |acc, selected| acc + selected.note.value);
|
|
||||||
if selected_value < target_value as u64 {
|
|
||||||
return Err(Error(ErrorKind::InsufficientBalance(
|
|
||||||
selected_value,
|
|
||||||
target_value as u64,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the transaction
|
|
||||||
let mut builder = Builder::new(height);
|
|
||||||
for selected in notes {
|
|
||||||
builder.add_sapling_spend(
|
|
||||||
extsk.clone(),
|
|
||||||
selected.diversifier,
|
|
||||||
selected.note,
|
|
||||||
selected.witness,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
match to {
|
|
||||||
RecipientAddress::Shielded(to) => {
|
|
||||||
builder.add_sapling_output(ovk, to.clone(), value, memo.clone())
|
|
||||||
}
|
|
||||||
RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value),
|
|
||||||
}?;
|
|
||||||
let (tx, tx_metadata) = builder.build(consensus_branch_id, prover)?;
|
|
||||||
// We only called add_sapling_output() once.
|
|
||||||
let output_index = match tx_metadata.output_index(0) {
|
|
||||||
Some(idx) => idx as i64,
|
|
||||||
None => panic!("Output 0 should exist in the transaction"),
|
|
||||||
};
|
|
||||||
let created = time::get_time();
|
|
||||||
|
|
||||||
// Update the database atomically, to ensure the result is internally consistent.
|
|
||||||
data.execute("BEGIN IMMEDIATE", NO_PARAMS)?;
|
|
||||||
|
|
||||||
// Save the transaction in the database.
|
|
||||||
let mut raw_tx = vec![];
|
|
||||||
tx.write(&mut raw_tx)?;
|
|
||||||
let mut stmt_insert_tx = data.prepare(
|
|
||||||
"INSERT INTO transactions (txid, created, expiry_height, raw)
|
|
||||||
VALUES (?, ?, ?, ?)",
|
|
||||||
)?;
|
|
||||||
stmt_insert_tx.execute(&[
|
|
||||||
tx.txid().0.to_sql()?,
|
|
||||||
created.to_sql()?,
|
|
||||||
tx.expiry_height.to_sql()?,
|
|
||||||
raw_tx.to_sql()?,
|
|
||||||
])?;
|
|
||||||
let id_tx = data.last_insert_rowid();
|
|
||||||
|
|
||||||
// Mark notes as spent.
|
|
||||||
//
|
|
||||||
// This locks the notes so they aren't selected again by a subsequent call to
|
|
||||||
// create_to_address() before this transaction has been mined (at which point the notes
|
|
||||||
// get re-marked as spent).
|
|
||||||
//
|
|
||||||
// Assumes that create_to_address() will never be called in parallel, which is a
|
|
||||||
// reasonable assumption for a light client such as a mobile phone.
|
|
||||||
let mut stmt_mark_spent_note =
|
|
||||||
data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?;
|
|
||||||
for spend in &tx.shielded_spends {
|
|
||||||
stmt_mark_spent_note.execute(&[id_tx.to_sql()?, spend.nullifier.to_sql()?])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the sent note in the database.
|
|
||||||
// TODO: Decide how to save transparent output information.
|
|
||||||
let to_str = to.to_string();
|
|
||||||
if let Some(memo) = memo {
|
|
||||||
let mut stmt_insert_sent_note = data.prepare(
|
|
||||||
"INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?)",
|
|
||||||
)?;
|
|
||||||
stmt_insert_sent_note.execute(&[
|
|
||||||
id_tx.to_sql()?,
|
|
||||||
output_index.to_sql()?,
|
|
||||||
account.to_sql()?,
|
|
||||||
to_str.to_sql()?,
|
|
||||||
i64::from(value).to_sql()?,
|
|
||||||
memo.as_bytes().to_sql()?,
|
|
||||||
])?;
|
|
||||||
} else {
|
|
||||||
let mut stmt_insert_sent_note = data.prepare(
|
|
||||||
"INSERT INTO sent_notes (tx, output_index, from_account, address, value)
|
|
||||||
VALUES (?, ?, ?, ?, ?)",
|
|
||||||
)?;
|
|
||||||
stmt_insert_sent_note.execute(&[
|
|
||||||
id_tx.to_sql()?,
|
|
||||||
output_index.to_sql()?,
|
|
||||||
account.to_sql()?,
|
|
||||||
to_str.to_sql()?,
|
|
||||||
i64::from(value).to_sql()?,
|
|
||||||
])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.execute("COMMIT", NO_PARAMS)?;
|
|
||||||
|
|
||||||
// Return the row number of the transaction, so the caller can fetch it for sending.
|
|
||||||
Ok(id_tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use tempfile::NamedTempFile;
|
|
||||||
use zcash_primitives::{
|
|
||||||
block::BlockHash,
|
|
||||||
prover::TxProver,
|
|
||||||
transaction::components::Amount,
|
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
|
||||||
};
|
|
||||||
use zcash_proofs::prover::LocalTxProver;
|
|
||||||
|
|
||||||
use super::create_to_address;
|
|
||||||
use crate::{
|
|
||||||
init::{init_accounts_table, init_blocks_table, init_cache_database, init_data_database},
|
|
||||||
query::{get_balance, get_verified_balance},
|
|
||||||
scan::scan_cached_blocks,
|
|
||||||
tests::{fake_compact_block, insert_into_cache},
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn test_prover() -> impl TxProver {
|
|
||||||
match LocalTxProver::with_default_location() {
|
|
||||||
Some(tx_prover) => tx_prover,
|
|
||||||
None => {
|
|
||||||
panic!("Cannot locate the Zcash parameters. Please run zcash-fetch-params or fetch-params.sh to download the parameters, and then re-run the tests.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create_to_address_fails_on_incorrect_extsk() {
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add two accounts to the wallet
|
|
||||||
let extsk0 = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extsk1 = ExtendedSpendingKey::master(&[0]);
|
|
||||||
let extfvks = [
|
|
||||||
ExtendedFullViewingKey::from(&extsk0),
|
|
||||||
ExtendedFullViewingKey::from(&extsk1),
|
|
||||||
];
|
|
||||||
init_accounts_table(&db_data, &extfvks).unwrap();
|
|
||||||
let to = extsk0.default_address().unwrap().1.into();
|
|
||||||
|
|
||||||
// Invalid extsk for the given account should cause an error
|
|
||||||
match create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk1),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(1).unwrap(),
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(_) => panic!("Should have failed"),
|
|
||||||
Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 0"),
|
|
||||||
}
|
|
||||||
match create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(1, &extsk0),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(1).unwrap(),
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(_) => panic!("Should have failed"),
|
|
||||||
Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 1"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create_to_address_fails_with_no_blocks() {
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvks = [ExtendedFullViewingKey::from(&extsk)];
|
|
||||||
init_accounts_table(&db_data, &extfvks).unwrap();
|
|
||||||
let to = extsk.default_address().unwrap().1.into();
|
|
||||||
|
|
||||||
// We cannot do anything if we aren't synchronised
|
|
||||||
match create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(1).unwrap(),
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(_) => panic!("Should have failed"),
|
|
||||||
Err(e) => assert_eq!(e.to_string(), "Must scan blocks first"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create_to_address_fails_on_insufficient_balance() {
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvks = [ExtendedFullViewingKey::from(&extsk)];
|
|
||||||
init_accounts_table(&db_data, &extfvks).unwrap();
|
|
||||||
let to = extsk.default_address().unwrap().1.into();
|
|
||||||
|
|
||||||
// Account balance should be zero
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero());
|
|
||||||
|
|
||||||
// We cannot spend anything
|
|
||||||
match create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(1).unwrap(),
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(_) => panic!("Should have failed"),
|
|
||||||
Err(e) => assert_eq!(
|
|
||||||
e.to_string(),
|
|
||||||
"Insufficient balance (have 0, need 10001 including fee)"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create_to_address_fails_on_unverified_notes() {
|
|
||||||
let cache_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_cache = cache_file.path();
|
|
||||||
init_cache_database(&db_cache).unwrap();
|
|
||||||
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
||||||
init_accounts_table(&db_data, &[extfvk.clone()]).unwrap();
|
|
||||||
|
|
||||||
// Add funds to the wallet in a single note
|
|
||||||
let value = Amount::from_u64(50000).unwrap();
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
BlockHash([0; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Verified balance matches total balance
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value);
|
|
||||||
assert_eq!(get_verified_balance(db_data, 0).unwrap(), value);
|
|
||||||
|
|
||||||
// Add more funds to the wallet in a second note
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 1,
|
|
||||||
cb.hash(),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Verified balance does not include the second note
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value + value);
|
|
||||||
assert_eq!(get_verified_balance(db_data, 0).unwrap(), value);
|
|
||||||
|
|
||||||
// Spend fails because there are insufficient verified notes
|
|
||||||
let extsk2 = ExtendedSpendingKey::master(&[]);
|
|
||||||
let to = extsk2.default_address().unwrap().1.into();
|
|
||||||
match create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(70000).unwrap(),
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(_) => panic!("Should have failed"),
|
|
||||||
Err(e) => assert_eq!(
|
|
||||||
e.to_string(),
|
|
||||||
"Insufficient balance (have 50000, need 80000 including fee)"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mine blocks SAPLING_ACTIVATION_HEIGHT + 2 to 9 until just before the second
|
|
||||||
// note is verified
|
|
||||||
for i in 2..10 {
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + i,
|
|
||||||
cb.hash(),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
}
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Second spend still fails
|
|
||||||
match create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(70000).unwrap(),
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(_) => panic!("Should have failed"),
|
|
||||||
Err(e) => assert_eq!(
|
|
||||||
e.to_string(),
|
|
||||||
"Insufficient balance (have 50000, need 80000 including fee)"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mine block 11 so that the second note becomes verified
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 10,
|
|
||||||
cb.hash(),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Second spend should now succeed
|
|
||||||
create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(70000).unwrap(),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create_to_address_fails_on_locked_notes() {
|
|
||||||
let cache_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_cache = cache_file.path();
|
|
||||||
init_cache_database(&db_cache).unwrap();
|
|
||||||
|
|
||||||
let data_file = NamedTempFile::new().unwrap();
|
|
||||||
let db_data = data_file.path();
|
|
||||||
init_data_database(&db_data).unwrap();
|
|
||||||
|
|
||||||
// Add an account to the wallet
|
|
||||||
let extsk = ExtendedSpendingKey::master(&[]);
|
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
||||||
init_accounts_table(&db_data, &[extfvk.clone()]).unwrap();
|
|
||||||
|
|
||||||
// Add funds to the wallet in a single note
|
|
||||||
let value = Amount::from_u64(50000).unwrap();
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT,
|
|
||||||
BlockHash([0; 32]),
|
|
||||||
extfvk.clone(),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
assert_eq!(get_balance(db_data, 0).unwrap(), value);
|
|
||||||
|
|
||||||
// Send some of the funds to another address
|
|
||||||
let extsk2 = ExtendedSpendingKey::master(&[]);
|
|
||||||
let to = extsk2.default_address().unwrap().1.into();
|
|
||||||
create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(15000).unwrap(),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// A second spend fails because there are no usable notes
|
|
||||||
match create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(2000).unwrap(),
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(_) => panic!("Should have failed"),
|
|
||||||
Err(e) => assert_eq!(
|
|
||||||
e.to_string(),
|
|
||||||
"Insufficient balance (have 0, need 12000 including fee)"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 21 (that don't send us funds)
|
|
||||||
// until just before the first transaction expires
|
|
||||||
for i in 1..22 {
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + i,
|
|
||||||
cb.hash(),
|
|
||||||
ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
}
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Second spend still fails
|
|
||||||
match create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(2000).unwrap(),
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(_) => panic!("Should have failed"),
|
|
||||||
Err(e) => assert_eq!(
|
|
||||||
e.to_string(),
|
|
||||||
"Insufficient balance (have 0, need 12000 including fee)"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mine block SAPLING_ACTIVATION_HEIGHT + 22 so that the first transaction expires
|
|
||||||
let (cb, _) = fake_compact_block(
|
|
||||||
SAPLING_ACTIVATION_HEIGHT + 22,
|
|
||||||
cb.hash(),
|
|
||||||
ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[22])),
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
insert_into_cache(db_cache, &cb);
|
|
||||||
scan_cached_blocks(db_cache, db_data).unwrap();
|
|
||||||
|
|
||||||
// Second spend should now succeed
|
|
||||||
create_to_address(
|
|
||||||
db_data,
|
|
||||||
1,
|
|
||||||
test_prover(),
|
|
||||||
(0, &extsk),
|
|
||||||
&to,
|
|
||||||
Amount::from_u64(2000).unwrap(),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user