Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acff1444ec | ||
|
|
368b59f937 | ||
|
|
1a0204113d | ||
|
|
caaee693c4 | ||
|
|
b51e7c055b | ||
|
|
44a1c3981d | ||
|
|
7510890cf3 | ||
|
|
188537ea02 | ||
|
|
24691f2d50 | ||
|
|
0743dadcd0 | ||
|
|
37e47981b1 | ||
|
|
2e22139130 | ||
|
|
90b38d0eb1 | ||
|
|
3c9b29e47b | ||
|
|
5f274e70d0 | ||
|
|
323182c4a5 | ||
|
|
3ceefdf81c | ||
|
|
f8d01215a0 | ||
|
|
5e233620bd | ||
|
|
3ee778de59 | ||
|
|
1056db3bea | ||
|
|
99d0f78636 | ||
|
|
be84cd8e05 | ||
|
|
7948bd1bf7 | ||
|
|
f60d7d331b | ||
|
|
1cbeac9d59 | ||
|
|
1c60a79ec1 | ||
|
|
601e88c633 | ||
|
|
98db781931 | ||
|
|
2419c6648c | ||
|
|
cfaa0cf067 | ||
|
|
4c1237fa50 | ||
|
|
9a742d25ea | ||
|
|
68291090c6 | ||
|
|
0bf1fad0ed | ||
|
|
bee4d6a92b | ||
|
|
99aef05318 | ||
|
|
8cd6666e56 | ||
|
|
789e2ff216 | ||
|
|
2bafc688ff | ||
|
|
2774d2730f | ||
|
|
fd87121244 | ||
|
|
2e038207f0 | ||
|
|
36f1ef62de | ||
|
|
c1e6b1844c | ||
|
|
9c51f3426b | ||
|
|
8b353b3d55 | ||
|
|
c3a30b9597 | ||
|
|
5ec94b5db5 | ||
|
|
2dd2fc620e | ||
|
|
388a585515 | ||
|
|
2a8748582b | ||
|
|
ff5775418b | ||
|
|
3b7f4faa1b |
120
.github/workflows/ci.yml
vendored
120
.github/workflows/ci.yml
vendored
@@ -1,120 +0,0 @@
|
||||
name: CI checks
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.37.0
|
||||
override: true
|
||||
|
||||
# cargo fmt does not build the code, and running it in a fresh clone of
|
||||
# the codebase will fail because the protobuf code has not been generated.
|
||||
- name: cargo build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --all
|
||||
|
||||
# Ensure all code has been formatted with rustfmt
|
||||
- run: rustup component add rustfmt
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check --color always
|
||||
|
||||
test:
|
||||
name: Test on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.37.0
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
- name: Build tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --release --all --tests
|
||||
- name: Run tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --release --all
|
||||
- name: Run slow tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --release --all -- --ignored
|
||||
|
||||
codecov:
|
||||
name: Code coverage
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
# Use stable for this to ensure that cargo-tarpaulin can be built.
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Install cargo-tarpaulin
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: cargo-tarpaulin
|
||||
- name: Generate coverage report
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: tarpaulin
|
||||
args: --release --timeout 600 --out Xml --packages "librustzcash,zcash_client_backend,zcash_primitives,zcash_proofs"
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1.0.3
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
|
||||
doc-links:
|
||||
name: Nightly lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
|
||||
# Ensure intra-documentation links all resolve correctly
|
||||
# Requires #![deny(intra_doc_link_resolution_failure)] in crates.
|
||||
- name: Check intra-doc links
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --all --document-private-items
|
||||
|
||||
# Build benchmarks to prevent bitrot
|
||||
- name: Build benchmarks
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --all --benches
|
||||
@@ -1,6 +1,6 @@
|
||||
language: rust
|
||||
rust:
|
||||
- 1.37.0
|
||||
- 1.36.0
|
||||
|
||||
cache: cargo
|
||||
|
||||
@@ -11,9 +11,3 @@ script:
|
||||
- cargo build --verbose --release --all
|
||||
- cargo fmt --all -- --check
|
||||
- cargo test --verbose --release --all
|
||||
- cargo test --verbose --release --all -- --ignored
|
||||
|
||||
before_cache:
|
||||
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
|
||||
- cargo install cargo-update || echo "cargo-update already installed"
|
||||
- cargo install-update -a # update outdated cached binaries
|
||||
|
||||
600
Cargo.lock
generated
600
Cargo.lock
generated
@@ -29,14 +29,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "arrayref"
|
||||
version = "0.3.5"
|
||||
@@ -50,11 +42,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "autocfg"
|
||||
version = "0.1.6"
|
||||
@@ -67,34 +54,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bellman"
|
||||
version = "0.2.0"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"byteorder 1.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.5.2",
|
||||
"crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.4.0",
|
||||
"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)",
|
||||
"group 0.2.0",
|
||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"group 0.1.0",
|
||||
"hex-literal 0.1.4 (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.15.1",
|
||||
"pairing 0.14.2",
|
||||
"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_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)",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "bit-vec"
|
||||
version = "0.4.4"
|
||||
@@ -102,7 +80,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -152,6 +130,14 @@ dependencies = [
|
||||
"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]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
@@ -173,7 +159,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.45"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -181,14 +167,6 @@ name = "cfg-if"
|
||||
version = "0.1.9"
|
||||
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]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.4"
|
||||
@@ -196,67 +174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
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"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -289,39 +207,40 @@ dependencies = [
|
||||
"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]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
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]]
|
||||
name = "ff"
|
||||
version = "0.5.2"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff_derive 0.4.1",
|
||||
"ff_derive 0.3.0",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff_derive"
|
||||
version = "0.4.1"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"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-traits 0.2.8 (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)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -336,11 +255,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "futures"
|
||||
version = "0.1.29"
|
||||
@@ -365,19 +279,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.12"
|
||||
version = "0.1.11"
|
||||
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)",
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.2.0"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ff 0.5.2",
|
||||
"ff 0.4.0",
|
||||
"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)",
|
||||
]
|
||||
@@ -389,19 +303,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "hex-literal"
|
||||
version = "0.2.1"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex-literal-impl"
|
||||
version = "0.2.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -416,42 +330,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "librustzcash"
|
||||
version = "0.2.0"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bellman 0.2.0",
|
||||
"bellman 0.1.0",
|
||||
"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)",
|
||||
"ff 0.5.2",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.4.0",
|
||||
"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)",
|
||||
"pairing 0.15.1",
|
||||
"pairing 0.14.2",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zcash_history 0.0.1",
|
||||
"zcash_primitives 0.1.0",
|
||||
"zcash_proofs 0.1.0",
|
||||
"rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zcash_primitives 0.0.0",
|
||||
"zcash_proofs 0.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.16.0"
|
||||
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)",
|
||||
"cc 1.0.42 (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]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
version = "2.2.1"
|
||||
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]]
|
||||
name = "nodrop"
|
||||
version = "0.1.13"
|
||||
@@ -499,15 +421,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pairing"
|
||||
version = "0.15.1"
|
||||
version = "0.14.2"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.5.2",
|
||||
"group 0.2.0",
|
||||
"ff 0.4.0",
|
||||
"group 0.1.0",
|
||||
"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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.5"
|
||||
@@ -515,79 +442,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.9"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"proc-macro-hack-impl 0.4.2 (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]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.3"
|
||||
version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "2.8.1"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-codegen"
|
||||
version = "2.8.1"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-codegen-pure"
|
||||
version = "2.8.1"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"protobuf 2.8.1 (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)",
|
||||
"protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"protobuf-codegen 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.2"
|
||||
version = "0.6.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -595,22 +496,13 @@ name = "rand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"getrandom 0.1.11 (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_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)",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.1"
|
||||
@@ -620,33 +512,12 @@ dependencies = [
|
||||
"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]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -657,52 +528,13 @@ dependencies = [
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_isaac"
|
||||
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]]
|
||||
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"
|
||||
version = "0.2.2"
|
||||
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)",
|
||||
"getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -714,29 +546,18 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
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]]
|
||||
name = "regex"
|
||||
version = "1.3.4"
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.2"
|
||||
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)",
|
||||
"winapi 0.3.8 (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]]
|
||||
name = "ripemd160"
|
||||
version = "0.8.0"
|
||||
@@ -748,39 +569,27 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
name = "rusqlite"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.1.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]]
|
||||
name = "secp256k1"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.42 (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]]
|
||||
name = "sha2"
|
||||
version = "0.8.0"
|
||||
@@ -794,25 +603,40 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.2.1"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.5"
|
||||
version = "0.14.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
name = "tempfile"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (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)",
|
||||
"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]]
|
||||
@@ -822,12 +646,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
version = "0.1.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"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.7.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -851,49 +680,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "zcash_client_backend"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.5.2",
|
||||
"bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.4.0",
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pairing 0.15.1",
|
||||
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"protobuf-codegen-pure 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pairing 0.14.2",
|
||||
"protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"protobuf-codegen-pure 2.8.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_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)",
|
||||
"subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zcash_primitives 0.1.0",
|
||||
"subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zcash_primitives 0.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zcash_history"
|
||||
version = "0.0.1"
|
||||
name = "zcash_client_sqlite"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bigint 4.4.1 (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)",
|
||||
"quickcheck 0.8.5 (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.4.0",
|
||||
"pairing 0.14.2",
|
||||
"protobuf 2.8.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_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]]
|
||||
name = "zcash_primitives"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
"crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.5.2",
|
||||
"ff 0.4.0",
|
||||
"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-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pairing 0.15.1",
|
||||
"pairing 0.14.2",
|
||||
"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_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)",
|
||||
"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)",
|
||||
@@ -902,116 +741,99 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zcash_proofs"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bellman 0.2.0",
|
||||
"bellman 0.1.0",
|
||||
"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)",
|
||||
"directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.5.2",
|
||||
"pairing 0.15.1",
|
||||
"ff 0.4.0",
|
||||
"pairing 0.14.2",
|
||||
"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)",
|
||||
"zcash_primitives 0.1.0",
|
||||
"zcash_primitives 0.0.0",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"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 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 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 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 bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||
"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 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-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 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 cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
|
||||
"checksum cc 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "a61c7bce55cd2fae6ec8cb935ebd76256c2959a1f95790f6118a441c2cd5b406"
|
||||
"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 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 crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
|
||||
"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 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 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 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 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-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 getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571"
|
||||
"checksum getrandom 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fc344b02d3868feb131e8b5fe2b9b0a1cc42942679af493061fc13b853243872"
|
||||
"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
||||
"checksum hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0"
|
||||
"checksum hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d"
|
||||
"checksum hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639"
|
||||
"checksum hex-literal-impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda"
|
||||
"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 log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
|
||||
"checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25"
|
||||
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
|
||||
"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
||||
"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 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-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 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 proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8"
|
||||
"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8"
|
||||
"checksum protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40361836defdd5871ff7e84096c6f6444af7fc157f8ef1789f54f147687caa20"
|
||||
"checksum protobuf-codegen 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12c6abd78435445fc86898ebbd0521a68438063d4a73e23527b7134e6bf58b4a"
|
||||
"checksum protobuf-codegen-pure 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c1646acda5319f5b28b0bff4a484324df43ddae2c0f5a3f3e63c0b26095cd600"
|
||||
"checksum quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9c35d9c36a562f37eca96e79f66d5fd56eefbc22560dacc4a864cabd2d277456"
|
||||
"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 proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b"
|
||||
"checksum proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
"checksum protobuf 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8aefcec9f142b524d98fc81d07827743be89dd6586a1ba6ab21fa66a500b3fa5"
|
||||
"checksum protobuf-codegen 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31539be8028d6b9e8e1b3b7c74e2fa3555302e27b2cc20dbaee6ffba648f75e2"
|
||||
"checksum protobuf-codegen-pure 2.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "00993dc5fbbfcf9d8a005f6b6c29fd29fd6d86deba3ae3f41fd20c624c414616"
|
||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||
"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_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_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_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_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a"
|
||||
"checksum rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8"
|
||||
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
"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 redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||
"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
|
||||
"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 rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051"
|
||||
"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 subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3af2eb31c42e8f0ccf43548232556c42737e01a96db6e1777b0be108e79799"
|
||||
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
"checksum subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01f40907d9ffc762709e4ff3eb4a6f6b41b650375a3f09ac92b641942b7fb082"
|
||||
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
|
||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
"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 unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95"
|
||||
"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-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"
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
"librustzcash",
|
||||
"pairing",
|
||||
"zcash_client_backend",
|
||||
"zcash_history",
|
||||
"zcash_client_sqlite",
|
||||
"zcash_primitives",
|
||||
"zcash_proofs",
|
||||
]
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
[package]
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
description = "zk-SNARK library"
|
||||
readme = "README.md"
|
||||
documentation = "https://github.com/ebfull/bellman"
|
||||
homepage = "https://github.com/ebfull/bellman"
|
||||
license = "MIT/Apache-2.0"
|
||||
name = "bellman"
|
||||
repository = "https://github.com/ebfull/bellman"
|
||||
version = "0.2.0"
|
||||
edition = "2018"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
bit-vec = "0.4.4"
|
||||
blake2s_simd = "0.5"
|
||||
ff = { version = "0.5.0", path = "../ff" }
|
||||
ff = { path = "../ff" }
|
||||
futures = "0.1"
|
||||
futures-cpupool = { version = "0.1", optional = true }
|
||||
group = { version = "0.2.0", path = "../group" }
|
||||
group = { path = "../group" }
|
||||
num_cpus = { version = "1", optional = true }
|
||||
crossbeam = { version = "0.7", optional = true }
|
||||
pairing = { version = "0.15.0", path = "../pairing", optional = true }
|
||||
crossbeam = { version = "0.3", optional = true }
|
||||
pairing = { path = "../pairing", optional = true }
|
||||
rand_core = "0.5"
|
||||
byteorder = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.2"
|
||||
hex-literal = "0.1"
|
||||
rand = "0.7"
|
||||
rand_xorshift = "0.2"
|
||||
sha2 = "0.8"
|
||||
@@ -37,6 +36,3 @@ default = ["groth16", "multicore"]
|
||||
name = "mimc"
|
||||
path = "tests/mimc.rs"
|
||||
required-features = ["groth16"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
# bellman [](https://crates.io/crates/bellman) #
|
||||
|
||||
`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.
|
||||
This is a research project being built for [Zcash](https://z.cash/).
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
//! This module contains an [`EvaluationDomain`] abstraction for performing
|
||||
//! various kinds of polynomial arithmetic on top of the scalar field.
|
||||
//! This module contains an `EvaluationDomain` abstraction for
|
||||
//! performing various kinds of polynomial arithmetic on top of
|
||||
//! the scalar field.
|
||||
//!
|
||||
//! In pairing-based SNARKs like [Groth16], we need to calculate a quotient
|
||||
//! polynomial over a target polynomial with roots at distinct points associated
|
||||
//! with each constraint of the constraint system. In order to be efficient, we
|
||||
//! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the
|
||||
//! field. This allows us to perform polynomial operations in O(n) by performing
|
||||
//! an O(n log n) FFT over such a domain.
|
||||
//!
|
||||
//! [`EvaluationDomain`]: crate::domain::EvaluationDomain
|
||||
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||
//! In pairing-based SNARKs like Groth16, we need to calculate
|
||||
//! a quotient polynomial over a target polynomial with roots
|
||||
//! at distinct points associated with each constraint of the
|
||||
//! constraint system. In order to be efficient, we choose these
|
||||
//! roots to be the powers of a 2^n root of unity in the field.
|
||||
//! This allows us to perform polynomial operations in O(n)
|
||||
//! by performing an O(n log n) FFT over such a domain.
|
||||
|
||||
use ff::{Field, PrimeField, ScalarEngine};
|
||||
use group::CurveProjective;
|
||||
@@ -27,19 +26,15 @@ pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
|
||||
minv: E::Fr,
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine, G: Group<E>> AsRef<[G]> for EvaluationDomain<E, G> {
|
||||
fn as_ref(&self) -> &[G] {
|
||||
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
pub fn as_ref(&self) -> &[G] {
|
||||
&self.coeffs
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine, G: Group<E>> AsMut<[G]> for EvaluationDomain<E, G> {
|
||||
fn as_mut(&mut self) -> &mut [G] {
|
||||
pub fn as_mut(&mut self) -> &mut [G] {
|
||||
&mut self.coeffs
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
pub fn into_coeffs(self) -> Vec<G> {
|
||||
self.coeffs
|
||||
}
|
||||
@@ -69,9 +64,9 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
coeffs.resize(m, G::group_zero());
|
||||
|
||||
Ok(EvaluationDomain {
|
||||
coeffs,
|
||||
exp,
|
||||
omega,
|
||||
coeffs: coeffs,
|
||||
exp: exp,
|
||||
omega: omega,
|
||||
omegainv: omega.inverse().unwrap(),
|
||||
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
|
||||
minv: E::Fr::from_str(&format!("{}", m))
|
||||
@@ -92,7 +87,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
let minv = self.minv;
|
||||
|
||||
for v in self.coeffs.chunks_mut(chunk) {
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
for v in v {
|
||||
v.group_mul_assign(&minv);
|
||||
}
|
||||
@@ -104,7 +99,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) {
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
let mut u = g.pow(&[(i * chunk) as u64]);
|
||||
for v in v.iter_mut() {
|
||||
v.group_mul_assign(&u);
|
||||
@@ -147,7 +142,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for v in self.coeffs.chunks_mut(chunk) {
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
for v in v {
|
||||
v.group_mul_assign(&i);
|
||||
}
|
||||
@@ -166,7 +161,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
.chunks_mut(chunk)
|
||||
.zip(other.coeffs.chunks(chunk))
|
||||
{
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||
a.group_mul_assign(&b.0);
|
||||
}
|
||||
@@ -185,7 +180,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
.chunks_mut(chunk)
|
||||
.zip(other.coeffs.chunks(chunk))
|
||||
{
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
for (a, b) in a.iter_mut().zip(b.iter()) {
|
||||
a.group_sub_assign(&b);
|
||||
}
|
||||
@@ -296,7 +291,7 @@ fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u
|
||||
|
||||
let mut m = 1;
|
||||
for _ in 0..log_n {
|
||||
let w_m = omega.pow(&[u64::from(n / (2 * m))]);
|
||||
let w_m = omega.pow(&[(n / (2 * m)) as u64]);
|
||||
|
||||
let mut k = 0;
|
||||
while k < n {
|
||||
@@ -336,18 +331,18 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
||||
let a = &*a;
|
||||
|
||||
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
// Shuffle into a sub-FFT
|
||||
let omega_j = omega.pow(&[j as u64]);
|
||||
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
|
||||
|
||||
let mut elt = E::Fr::one();
|
||||
for (i, tmp) in tmp.iter_mut().enumerate() {
|
||||
for i in 0..(1 << log_new_n) {
|
||||
for s in 0..num_cpus {
|
||||
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
||||
let mut t = a[idx];
|
||||
t.group_mul_assign(&elt);
|
||||
tmp.group_add_assign(&t);
|
||||
tmp[i].group_add_assign(&t);
|
||||
elt.mul_assign(&omega_step);
|
||||
}
|
||||
elt.mul_assign(&omega_j);
|
||||
@@ -364,7 +359,7 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
||||
let tmp = &tmp;
|
||||
|
||||
for (idx, a) in a.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
let mut idx = idx * chunk;
|
||||
let mask = (1 << log_cpus) - 1;
|
||||
for a in a {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! Self-contained sub-circuit implementations for various primitives.
|
||||
|
||||
pub mod test;
|
||||
|
||||
pub mod blake2s;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
//! The [BLAKE2s] hash function with personalization support.
|
||||
//!
|
||||
//! [BLAKE2s]: https://tools.ietf.org/html/rfc7693
|
||||
use pairing::Engine;
|
||||
|
||||
use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32};
|
||||
use crate::{ConstraintSystem, SynthesisError};
|
||||
use ff::ScalarEngine;
|
||||
|
||||
use super::boolean::Boolean;
|
||||
|
||||
use super::uint32::UInt32;
|
||||
|
||||
use super::multieq::MultiEq;
|
||||
|
||||
/*
|
||||
2.1. Parameters
|
||||
@@ -79,7 +81,7 @@ const SIGMA: [[usize; 16]; 10] = [
|
||||
END FUNCTION.
|
||||
*/
|
||||
|
||||
fn mixing_g<E: ScalarEngine, CS: ConstraintSystem<E>, M>(
|
||||
fn mixing_g<E: Engine, CS: ConstraintSystem<E>, M>(
|
||||
mut cs: M,
|
||||
v: &mut [UInt32],
|
||||
a: usize,
|
||||
@@ -164,7 +166,7 @@ where
|
||||
END FUNCTION.
|
||||
*/
|
||||
|
||||
fn blake2s_compression<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
|
||||
mut cs: CS,
|
||||
h: &mut [UInt32],
|
||||
m: &[UInt32],
|
||||
@@ -337,7 +339,7 @@ fn blake2s_compression<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
END FUNCTION.
|
||||
*/
|
||||
|
||||
pub fn blake2s<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
|
||||
mut cs: CS,
|
||||
input: &[Boolean],
|
||||
personalization: &[u8],
|
||||
@@ -380,7 +382,7 @@ pub fn blake2s<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
blocks.push(this_block);
|
||||
}
|
||||
|
||||
if blocks.is_empty() {
|
||||
if blocks.len() == 0 {
|
||||
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
|
||||
}
|
||||
|
||||
@@ -402,13 +404,12 @@ pub fn blake2s<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(h.into_iter().flat_map(|b| b.into_bits()).collect())
|
||||
Ok(h.iter().flat_map(|b| b.into_bits()).collect())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use blake2s_simd::Params as Blake2sParams;
|
||||
use hex_literal::hex;
|
||||
use pairing::bls12_381::Bls12;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
@@ -432,7 +433,7 @@ mod test {
|
||||
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
|
||||
|
||||
let mut out = out.into_iter();
|
||||
for b in expected.iter() {
|
||||
for b in expected.into_iter() {
|
||||
for i in 0..8 {
|
||||
let c = out.next().unwrap().get_value().unwrap();
|
||||
|
||||
@@ -553,145 +554,4 @@ 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,6 +1,5 @@
|
||||
//! Gadgets for allocating bits in the circuit and performing boolean logic.
|
||||
|
||||
use ff::{BitIterator, Field, PrimeField, ScalarEngine};
|
||||
use ff::{BitIterator, Field, PrimeField};
|
||||
use pairing::Engine;
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
@@ -32,7 +31,7 @@ impl AllocatedBit {
|
||||
must_be_false: &AllocatedBit,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let var = cs.alloc(
|
||||
@@ -61,7 +60,7 @@ impl AllocatedBit {
|
||||
|
||||
Ok(AllocatedBit {
|
||||
variable: var,
|
||||
value,
|
||||
value: value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,7 +68,7 @@ impl AllocatedBit {
|
||||
/// boolean value.
|
||||
pub fn alloc<E, CS>(mut cs: CS, value: Option<bool>) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let var = cs.alloc(
|
||||
@@ -94,7 +93,7 @@ impl AllocatedBit {
|
||||
|
||||
Ok(AllocatedBit {
|
||||
variable: var,
|
||||
value,
|
||||
value: value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -102,7 +101,7 @@ impl AllocatedBit {
|
||||
/// an `AllocatedBit`.
|
||||
pub fn xor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let mut result_value = None;
|
||||
@@ -154,7 +153,7 @@ impl AllocatedBit {
|
||||
/// an `AllocatedBit`.
|
||||
pub fn and<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let mut result_value = None;
|
||||
@@ -192,7 +191,7 @@ impl AllocatedBit {
|
||||
/// Calculates `a AND (NOT b)`.
|
||||
pub fn and_not<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let mut result_value = None;
|
||||
@@ -230,7 +229,7 @@ impl AllocatedBit {
|
||||
/// Calculates `(NOT a) AND (NOT b)`.
|
||||
pub fn nor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let mut result_value = None;
|
||||
@@ -266,7 +265,7 @@ impl AllocatedBit {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn u64_into_boolean_vec_le<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
pub fn u64_into_boolean_vec_le<E: Engine, CS: ConstraintSystem<E>>(
|
||||
mut cs: CS,
|
||||
value: Option<u64>,
|
||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
@@ -297,16 +296,16 @@ pub fn u64_into_boolean_vec_le<E: ScalarEngine, CS: ConstraintSystem<E>>(
|
||||
Ok(bits)
|
||||
}
|
||||
|
||||
pub fn field_into_boolean_vec_le<E: ScalarEngine, CS: ConstraintSystem<E>, F: PrimeField>(
|
||||
pub fn field_into_boolean_vec_le<E: Engine, CS: ConstraintSystem<E>, F: PrimeField>(
|
||||
cs: CS,
|
||||
value: Option<F>,
|
||||
) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
let v = field_into_allocated_bits_le::<E, CS, F>(cs, value)?;
|
||||
|
||||
Ok(v.into_iter().map(Boolean::from).collect())
|
||||
Ok(v.into_iter().map(|e| Boolean::from(e)).collect())
|
||||
}
|
||||
|
||||
pub fn field_into_allocated_bits_le<E: ScalarEngine, CS: ConstraintSystem<E>, F: PrimeField>(
|
||||
pub fn field_into_allocated_bits_le<E: Engine, CS: ConstraintSystem<E>, F: PrimeField>(
|
||||
mut cs: CS,
|
||||
value: Option<F>,
|
||||
) -> Result<Vec<AllocatedBit>, SynthesisError> {
|
||||
@@ -368,7 +367,7 @@ impl Boolean {
|
||||
|
||||
pub fn enforce_equal<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
match (a, b) {
|
||||
@@ -413,24 +412,24 @@ impl Boolean {
|
||||
}
|
||||
|
||||
pub fn get_value(&self) -> Option<bool> {
|
||||
match *self {
|
||||
Boolean::Constant(c) => Some(c),
|
||||
Boolean::Is(ref v) => v.get_value(),
|
||||
Boolean::Not(ref v) => v.get_value().map(|b| !b),
|
||||
match self {
|
||||
&Boolean::Constant(c) => Some(c),
|
||||
&Boolean::Is(ref v) => v.get_value(),
|
||||
&Boolean::Not(ref v) => v.get_value().map(|b| !b),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lc<E: ScalarEngine>(&self, one: Variable, coeff: E::Fr) -> LinearCombination<E> {
|
||||
match *self {
|
||||
Boolean::Constant(c) => {
|
||||
pub fn lc<E: Engine>(&self, one: Variable, coeff: E::Fr) -> LinearCombination<E> {
|
||||
match self {
|
||||
&Boolean::Constant(c) => {
|
||||
if c {
|
||||
LinearCombination::<E>::zero() + (coeff, one)
|
||||
} else {
|
||||
LinearCombination::<E>::zero()
|
||||
}
|
||||
}
|
||||
Boolean::Is(ref v) => LinearCombination::<E>::zero() + (coeff, v.get_variable()),
|
||||
Boolean::Not(ref v) => {
|
||||
&Boolean::Is(ref v) => LinearCombination::<E>::zero() + (coeff, v.get_variable()),
|
||||
&Boolean::Not(ref v) => {
|
||||
LinearCombination::<E>::zero() + (coeff, one) - (coeff, v.get_variable())
|
||||
}
|
||||
}
|
||||
@@ -443,17 +442,17 @@ impl Boolean {
|
||||
|
||||
/// Return a negated interpretation of this boolean.
|
||||
pub fn not(&self) -> Self {
|
||||
match *self {
|
||||
Boolean::Constant(c) => Boolean::Constant(!c),
|
||||
Boolean::Is(ref v) => Boolean::Not(v.clone()),
|
||||
Boolean::Not(ref v) => Boolean::Is(v.clone()),
|
||||
match self {
|
||||
&Boolean::Constant(c) => Boolean::Constant(!c),
|
||||
&Boolean::Is(ref v) => Boolean::Not(v.clone()),
|
||||
&Boolean::Not(ref v) => Boolean::Is(v.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform XOR over two boolean operands
|
||||
pub fn xor<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
match (a, b) {
|
||||
@@ -475,7 +474,7 @@ impl Boolean {
|
||||
/// Perform AND over two boolean operands
|
||||
pub fn and<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
match (a, b) {
|
||||
@@ -509,7 +508,7 @@ impl Boolean {
|
||||
c: &'a Self,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let ch_value = match (a.get_value(), b.get_value(), c.get_value()) {
|
||||
@@ -616,7 +615,7 @@ impl Boolean {
|
||||
c: &'a Self,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let maj_value = match (a.get_value(), b.get_value(), c.get_value()) {
|
||||
@@ -1741,72 +1740,4 @@ 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,6 +1,5 @@
|
||||
//! Window table lookup gadgets.
|
||||
|
||||
use ff::{Field, ScalarEngine};
|
||||
use ff::Field;
|
||||
use pairing::Engine;
|
||||
|
||||
use super::boolean::Boolean;
|
||||
use super::num::{AllocatedNum, Num};
|
||||
@@ -8,7 +7,7 @@ use super::*;
|
||||
use crate::ConstraintSystem;
|
||||
|
||||
// Synthesize the constants for each base pattern.
|
||||
fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
|
||||
fn synth<'a, E: Engine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
|
||||
where
|
||||
I: IntoIterator<Item = &'a E::Fr>,
|
||||
{
|
||||
@@ -29,7 +28,7 @@ where
|
||||
|
||||
/// Performs a 3-bit window table lookup. `bits` is in
|
||||
/// little-endian order.
|
||||
pub fn lookup3_xy<E: ScalarEngine, CS>(
|
||||
pub fn lookup3_xy<E: Engine, CS>(
|
||||
mut cs: CS,
|
||||
bits: &[Boolean],
|
||||
coords: &[(E::Fr, E::Fr)],
|
||||
@@ -119,7 +118,7 @@ where
|
||||
|
||||
/// Performs a 3-bit window table lookup, where
|
||||
/// one of the bits is a sign bit.
|
||||
pub fn lookup3_xy_with_conditional_negation<E: ScalarEngine, CS>(
|
||||
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
|
||||
mut cs: CS,
|
||||
bits: &[Boolean],
|
||||
coords: &[(E::Fr, E::Fr)],
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use ff::{Field, PrimeField, ScalarEngine};
|
||||
use ff::{Field, PrimeField};
|
||||
use pairing::Engine;
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
pub struct MultiEq<E: ScalarEngine, CS: ConstraintSystem<E>> {
|
||||
pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>> {
|
||||
cs: CS,
|
||||
ops: usize,
|
||||
bits_used: usize,
|
||||
@@ -10,10 +11,10 @@ pub struct MultiEq<E: ScalarEngine, CS: ConstraintSystem<E>> {
|
||||
rhs: LinearCombination<E>,
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
||||
impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
||||
pub fn new(cs: CS) -> Self {
|
||||
MultiEq {
|
||||
cs,
|
||||
cs: cs,
|
||||
ops: 0,
|
||||
bits_used: 0,
|
||||
lhs: LinearCombination::zero(),
|
||||
@@ -57,7 +58,7 @@ impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
|
||||
impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
|
||||
fn drop(&mut self) {
|
||||
if self.bits_used > 0 {
|
||||
self.accumulate();
|
||||
@@ -65,7 +66,7 @@ impl<E: ScalarEngine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> {
|
||||
impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> {
|
||||
type Root = Self;
|
||||
|
||||
fn one() -> Variable {
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
//! Helpers for packing vectors of bits into scalar field elements.
|
||||
|
||||
use super::boolean::Boolean;
|
||||
use super::num::Num;
|
||||
use super::Assignment;
|
||||
use crate::{ConstraintSystem, SynthesisError};
|
||||
use ff::{Field, PrimeField, ScalarEngine};
|
||||
use ff::{Field, PrimeField};
|
||||
use pairing::Engine;
|
||||
|
||||
/// Takes a sequence of booleans and exposes them as compact
|
||||
/// public inputs
|
||||
pub fn pack_into_inputs<E, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() {
|
||||
@@ -50,7 +49,7 @@ pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn compute_multipacking<E: ScalarEngine>(bits: &[bool]) -> Vec<E::Fr> {
|
||||
pub fn compute_multipacking<E: Engine>(bits: &[bool]) -> Vec<E::Fr> {
|
||||
let mut result = vec![];
|
||||
|
||||
for bits in bits.chunks(E::Fr::CAPACITY as usize) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Gadgets representing numbers in the scalar field of the underlying curve.
|
||||
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr};
|
||||
use pairing::Engine;
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
@@ -8,12 +7,12 @@ use super::Assignment;
|
||||
|
||||
use super::boolean::{self, AllocatedBit, Boolean};
|
||||
|
||||
pub struct AllocatedNum<E: ScalarEngine> {
|
||||
pub struct AllocatedNum<E: Engine> {
|
||||
value: Option<E::Fr>,
|
||||
variable: Variable,
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine> Clone for AllocatedNum<E> {
|
||||
impl<E: Engine> Clone for AllocatedNum<E> {
|
||||
fn clone(&self) -> Self {
|
||||
AllocatedNum {
|
||||
value: self.value,
|
||||
@@ -22,7 +21,7 @@ impl<E: ScalarEngine> Clone for AllocatedNum<E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
impl<E: Engine> AllocatedNum<E> {
|
||||
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<E>,
|
||||
@@ -67,7 +66,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
/// order, requiring that the representation
|
||||
/// strictly exists "in the field" (i.e., a
|
||||
/// congruency is not allowed.)
|
||||
pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
pub fn into_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
@@ -76,10 +75,10 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
v: &[AllocatedBit],
|
||||
) -> Result<AllocatedBit, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
assert!(!v.is_empty());
|
||||
assert!(v.len() > 0);
|
||||
|
||||
// Let's keep this simple for now and just AND them all
|
||||
// manually
|
||||
@@ -133,7 +132,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
current_run.push(a_bit.clone());
|
||||
result.push(a_bit);
|
||||
} else {
|
||||
if !current_run.is_empty() {
|
||||
if current_run.len() > 0 {
|
||||
// This is the start of a run of zeros, but we need
|
||||
// to k-ary AND against `last_run` first.
|
||||
|
||||
@@ -184,13 +183,13 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
||||
// Convert into booleans, and reverse for little-endian bit order
|
||||
Ok(result.into_iter().map(Boolean::from).rev().collect())
|
||||
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect())
|
||||
}
|
||||
|
||||
/// Convert the allocated number into its little-endian representation.
|
||||
/// Note that this does not strongly enforce that the commitment is
|
||||
/// "in the field."
|
||||
pub fn to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
pub fn into_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
@@ -209,7 +208,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
|
||||
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|
||||
|
||||
Ok(bits.into_iter().map(Boolean::from).collect())
|
||||
Ok(bits.into_iter().map(|b| Boolean::from(b)).collect())
|
||||
}
|
||||
|
||||
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||
@@ -239,7 +238,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
);
|
||||
|
||||
Ok(AllocatedNum {
|
||||
value,
|
||||
value: value,
|
||||
variable: var,
|
||||
})
|
||||
}
|
||||
@@ -271,7 +270,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
);
|
||||
|
||||
Ok(AllocatedNum {
|
||||
value,
|
||||
value: value,
|
||||
variable: var,
|
||||
})
|
||||
}
|
||||
@@ -360,12 +359,12 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Num<E: ScalarEngine> {
|
||||
pub struct Num<E: Engine> {
|
||||
value: Option<E::Fr>,
|
||||
lc: LinearCombination<E>,
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine> From<AllocatedNum<E>> for Num<E> {
|
||||
impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
|
||||
fn from(num: AllocatedNum<E>) -> Num<E> {
|
||||
Num {
|
||||
value: num.value,
|
||||
@@ -374,7 +373,7 @@ impl<E: ScalarEngine> From<AllocatedNum<E>> for Num<E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine> Num<E> {
|
||||
impl<E: Engine> Num<E> {
|
||||
pub fn zero() -> Self {
|
||||
Num {
|
||||
value: Some(E::Fr::zero()),
|
||||
@@ -523,7 +522,7 @@ mod test {
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
|
||||
n.to_bits_le_strict(&mut cs).unwrap();
|
||||
n.into_bits_le_strict(&mut cs).unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
@@ -551,9 +550,9 @@ mod test {
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
|
||||
|
||||
let bits = if i % 2 == 0 {
|
||||
n.to_bits_le(&mut cs).unwrap()
|
||||
n.into_bits_le(&mut cs).unwrap()
|
||||
} else {
|
||||
n.to_bits_le_strict(&mut cs).unwrap()
|
||||
n.into_bits_le_strict(&mut cs).unwrap()
|
||||
};
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
//! Circuits for the [SHA-256] hash function and its internal compression
|
||||
//! function.
|
||||
//!
|
||||
//! [SHA-256]: https://tools.ietf.org/html/rfc6234
|
||||
|
||||
use super::boolean::Boolean;
|
||||
use super::multieq::MultiEq;
|
||||
use super::uint32::UInt32;
|
||||
use crate::{ConstraintSystem, SynthesisError};
|
||||
use ff::ScalarEngine;
|
||||
use pairing::Engine;
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
const ROUND_CONSTANTS: [u32; 64] = [
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
@@ -21,7 +15,6 @@ const ROUND_CONSTANTS: [u32; 64] = [
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||
];
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
const IV: [u32; 8] = [
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||
];
|
||||
@@ -31,7 +24,7 @@ pub fn sha256_block_no_padding<E, CS>(
|
||||
input: &[Boolean],
|
||||
) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
assert_eq!(input.len(), 512);
|
||||
@@ -46,7 +39,7 @@ where
|
||||
|
||||
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
assert!(input.len() % 8 == 0);
|
||||
@@ -83,7 +76,7 @@ fn sha256_compression_function<E, CS>(
|
||||
current_hash_value: &[UInt32],
|
||||
) -> Result<Vec<UInt32>, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
assert_eq!(input.len(), 512);
|
||||
@@ -130,14 +123,14 @@ where
|
||||
impl Maybe {
|
||||
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||
{
|
||||
Ok(match self {
|
||||
Maybe::Concrete(ref v) => return Ok(v.clone()),
|
||||
Maybe::Deferred(mut v) => {
|
||||
v.extend(others.iter().cloned());
|
||||
v.extend(others.into_iter().cloned());
|
||||
UInt32::addmany(cs, &v)?
|
||||
}
|
||||
})
|
||||
@@ -273,7 +266,6 @@ mod test {
|
||||
use super::*;
|
||||
use crate::gadgets::boolean::AllocatedBit;
|
||||
use crate::gadgets::test::TestConstraintSystem;
|
||||
use hex_literal::hex;
|
||||
use pairing::bls12_381::Bls12;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
@@ -294,7 +286,7 @@ mod test {
|
||||
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
||||
|
||||
let mut out = out_bits.into_iter();
|
||||
for b in expected.iter() {
|
||||
for b in expected.into_iter() {
|
||||
for i in (0..8).rev() {
|
||||
let c = out.next().unwrap().get_value().unwrap();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Helpers for testing circuit implementations.
|
||||
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
||||
use pairing::Engine;
|
||||
|
||||
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
@@ -21,7 +20,7 @@ enum NamedObject {
|
||||
}
|
||||
|
||||
/// Constraint system for testing purposes.
|
||||
pub struct TestConstraintSystem<E: ScalarEngine> {
|
||||
pub struct TestConstraintSystem<E: Engine> {
|
||||
named_objects: HashMap<String, NamedObject>,
|
||||
current_namespace: Vec<String>,
|
||||
constraints: Vec<(
|
||||
@@ -63,11 +62,11 @@ impl Ord for OrderedVariable {
|
||||
}
|
||||
}
|
||||
|
||||
fn proc_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
|
||||
fn proc_lc<E: Engine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
|
||||
let mut map = BTreeMap::new();
|
||||
for &(var, coeff) in terms {
|
||||
map.entry(OrderedVariable(var))
|
||||
.or_insert_with(E::Fr::zero)
|
||||
.or_insert(E::Fr::zero())
|
||||
.add_assign(&coeff);
|
||||
}
|
||||
|
||||
@@ -86,7 +85,7 @@ fn proc_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVari
|
||||
map
|
||||
}
|
||||
|
||||
fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
|
||||
fn hash_lc<E: Engine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
|
||||
let map = proc_lc::<E>(terms);
|
||||
|
||||
let mut buf = [0u8; 9 + 32];
|
||||
@@ -111,7 +110,7 @@ fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_lc<E: ScalarEngine>(
|
||||
fn eval_lc<E: Engine>(
|
||||
terms: &[(Variable, E::Fr)],
|
||||
inputs: &[(E::Fr, String)],
|
||||
aux: &[(E::Fr, String)],
|
||||
@@ -131,7 +130,7 @@ fn eval_lc<E: ScalarEngine>(
|
||||
acc
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine> TestConstraintSystem<E> {
|
||||
impl<E: Engine> TestConstraintSystem<E> {
|
||||
pub fn new() -> TestConstraintSystem<E> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(
|
||||
@@ -158,7 +157,7 @@ impl<E: ScalarEngine> TestConstraintSystem<E> {
|
||||
};
|
||||
|
||||
let powers_of_two = (0..E::Fr::NUM_BITS)
|
||||
.map(|i| E::Fr::from_str("2").unwrap().pow(&[u64::from(i)]))
|
||||
.map(|i| E::Fr::from_str("2").unwrap().pow(&[i as u64]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pp = |s: &mut String, lc: &LinearCombination<E>| {
|
||||
@@ -287,7 +286,7 @@ impl<E: ScalarEngine> TestConstraintSystem<E> {
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn num_inputs(&self) -> usize {
|
||||
@@ -345,7 +344,7 @@ fn compute_path(ns: &[String], this: String) -> String {
|
||||
name
|
||||
}
|
||||
|
||||
impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
||||
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
|
||||
type Root = Self;
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
//! Circuit representation of a [`u32`], with helpers for the [`sha256`]
|
||||
//! gadgets.
|
||||
//!
|
||||
//! [`sha256`]: crate::gadgets::sha256
|
||||
|
||||
use ff::{Field, PrimeField, ScalarEngine};
|
||||
use ff::{Field, PrimeField};
|
||||
use pairing::Engine;
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError};
|
||||
|
||||
@@ -37,7 +33,7 @@ impl UInt32 {
|
||||
}
|
||||
|
||||
UInt32 {
|
||||
bits,
|
||||
bits: bits,
|
||||
value: Some(value),
|
||||
}
|
||||
}
|
||||
@@ -45,7 +41,7 @@ impl UInt32 {
|
||||
/// Allocate a `UInt32` in the constraint system
|
||||
pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let values = match value {
|
||||
@@ -73,13 +69,14 @@ impl UInt32 {
|
||||
})
|
||||
.collect::<Result<Vec<_>, SynthesisError>>()?;
|
||||
|
||||
Ok(UInt32 { bits, value })
|
||||
Ok(UInt32 {
|
||||
bits: bits,
|
||||
value: value,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_bits_be(self) -> Vec<Boolean> {
|
||||
let mut ret = self.bits;
|
||||
ret.reverse();
|
||||
ret
|
||||
pub fn into_bits_be(&self) -> Vec<Boolean> {
|
||||
self.bits.iter().rev().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn from_bits_be(bits: &[Boolean]) -> Self {
|
||||
@@ -101,14 +98,14 @@ impl UInt32 {
|
||||
}
|
||||
|
||||
UInt32 {
|
||||
value,
|
||||
value: value,
|
||||
bits: bits.iter().rev().cloned().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns this `UInt32` into its little-endian byte order representation.
|
||||
pub fn into_bits(self) -> Vec<Boolean> {
|
||||
self.bits
|
||||
pub fn into_bits(&self) -> Vec<Boolean> {
|
||||
self.bits.clone()
|
||||
}
|
||||
|
||||
/// Converts a little-endian byte order representation of bits into a
|
||||
@@ -122,20 +119,20 @@ impl UInt32 {
|
||||
for b in new_bits.iter().rev() {
|
||||
value.as_mut().map(|v| *v <<= 1);
|
||||
|
||||
match *b {
|
||||
Boolean::Constant(b) => {
|
||||
match b {
|
||||
&Boolean::Constant(b) => {
|
||||
if b {
|
||||
value.as_mut().map(|v| *v |= 1);
|
||||
}
|
||||
}
|
||||
Boolean::Is(ref b) => match b.get_value() {
|
||||
&Boolean::Is(ref b) => match b.get_value() {
|
||||
Some(true) => {
|
||||
value.as_mut().map(|v| *v |= 1);
|
||||
}
|
||||
Some(false) => {}
|
||||
None => value = None,
|
||||
},
|
||||
Boolean::Not(ref b) => match b.get_value() {
|
||||
&Boolean::Not(ref b) => match b.get_value() {
|
||||
Some(false) => {
|
||||
value.as_mut().map(|v| *v |= 1);
|
||||
}
|
||||
@@ -146,7 +143,7 @@ impl UInt32 {
|
||||
}
|
||||
|
||||
UInt32 {
|
||||
value,
|
||||
value: value,
|
||||
bits: new_bits,
|
||||
}
|
||||
}
|
||||
@@ -198,7 +195,7 @@ impl UInt32 {
|
||||
circuit_fn: U,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
F: Fn(u32, u32, u32) -> u32,
|
||||
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
|
||||
@@ -218,7 +215,7 @@ impl UInt32 {
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(UInt32 {
|
||||
bits,
|
||||
bits: bits,
|
||||
value: new_value,
|
||||
})
|
||||
}
|
||||
@@ -227,7 +224,7 @@ impl UInt32 {
|
||||
/// during SHA256.
|
||||
pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
Self::triop(
|
||||
@@ -244,7 +241,7 @@ impl UInt32 {
|
||||
/// during SHA256.
|
||||
pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
Self::triop(
|
||||
@@ -260,7 +257,7 @@ impl UInt32 {
|
||||
/// XOR this `UInt32` with another `UInt32`
|
||||
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
{
|
||||
let new_value = match (self.value, other.value) {
|
||||
@@ -277,7 +274,7 @@ impl UInt32 {
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(UInt32 {
|
||||
bits,
|
||||
bits: bits,
|
||||
value: new_value,
|
||||
})
|
||||
}
|
||||
@@ -285,7 +282,7 @@ impl UInt32 {
|
||||
/// Perform modular addition of several `UInt32` objects.
|
||||
pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
|
||||
where
|
||||
E: ScalarEngine,
|
||||
E: Engine,
|
||||
CS: ConstraintSystem<E>,
|
||||
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
|
||||
{
|
||||
@@ -297,7 +294,7 @@ impl UInt32 {
|
||||
|
||||
// Compute the maximum value of the sum so we allocate enough bits for
|
||||
// the result
|
||||
let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value()));
|
||||
let mut max_value = (operands.len() as u64) * (u32::max_value() as u64);
|
||||
|
||||
// Keep track of the resulting value
|
||||
let mut result_value = Some(0u64);
|
||||
@@ -313,7 +310,7 @@ impl UInt32 {
|
||||
// Accumulate the value
|
||||
match op.value {
|
||||
Some(val) => {
|
||||
result_value.as_mut().map(|v| *v += u64::from(val));
|
||||
result_value.as_mut().map(|v| *v += val as u64);
|
||||
}
|
||||
None => {
|
||||
// If any of our operands have unknown value, we won't
|
||||
@@ -404,15 +401,15 @@ mod test {
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let v = (0..32)
|
||||
let mut v = (0..32)
|
||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let b = UInt32::from_bits_be(&v);
|
||||
|
||||
for (i, bit) in b.bits.iter().enumerate() {
|
||||
match *bit {
|
||||
Boolean::Constant(bit) => {
|
||||
match bit {
|
||||
&Boolean::Constant(bit) => {
|
||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -439,15 +436,15 @@ mod test {
|
||||
]);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let v = (0..32)
|
||||
let mut v = (0..32)
|
||||
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let b = UInt32::from_bits(&v);
|
||||
|
||||
for (i, bit) in b.bits.iter().enumerate() {
|
||||
match *bit {
|
||||
Boolean::Constant(bit) => {
|
||||
match bit {
|
||||
&Boolean::Constant(bit) => {
|
||||
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -494,14 +491,14 @@ mod test {
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match *b {
|
||||
Boolean::Is(ref b) => {
|
||||
match b {
|
||||
&Boolean::Is(ref b) => {
|
||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
Boolean::Not(ref b) => {
|
||||
&Boolean::Not(ref b) => {
|
||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
Boolean::Constant(b) => {
|
||||
&Boolean::Constant(b) => {
|
||||
assert!(b == (expected & 1 == 1));
|
||||
}
|
||||
}
|
||||
@@ -541,10 +538,10 @@ mod test {
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match *b {
|
||||
Boolean::Is(_) => panic!(),
|
||||
Boolean::Not(_) => panic!(),
|
||||
Boolean::Constant(b) => {
|
||||
match b {
|
||||
&Boolean::Is(_) => panic!(),
|
||||
&Boolean::Not(_) => panic!(),
|
||||
&Boolean::Constant(b) => {
|
||||
assert!(b == (expected & 1 == 1));
|
||||
}
|
||||
}
|
||||
@@ -579,7 +576,8 @@ mod test {
|
||||
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
|
||||
let r = {
|
||||
let mut cs = MultiEq::new(&mut cs);
|
||||
UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap()
|
||||
let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap();
|
||||
r
|
||||
};
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
@@ -587,14 +585,14 @@ mod test {
|
||||
assert!(r.value == Some(expected));
|
||||
|
||||
for b in r.bits.iter() {
|
||||
match *b {
|
||||
Boolean::Is(ref b) => {
|
||||
match b {
|
||||
&Boolean::Is(ref b) => {
|
||||
assert!(b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
Boolean::Not(ref b) => {
|
||||
&Boolean::Not(ref b) => {
|
||||
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
|
||||
}
|
||||
Boolean::Constant(_) => unreachable!(),
|
||||
&Boolean::Constant(_) => unreachable!(),
|
||||
}
|
||||
|
||||
expected >>= 1;
|
||||
@@ -630,8 +628,8 @@ mod test {
|
||||
|
||||
let mut tmp = num;
|
||||
for b in &b.bits {
|
||||
match *b {
|
||||
Boolean::Constant(b) => {
|
||||
match b {
|
||||
&Boolean::Constant(b) => {
|
||||
assert_eq!(b, tmp & 1 == 1);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
||||
@@ -8,11 +8,11 @@ use pairing::Engine;
|
||||
|
||||
use super::{Parameters, VerifyingKey};
|
||||
|
||||
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
use crate::domain::{EvaluationDomain, Scalar};
|
||||
use domain::{EvaluationDomain, Scalar};
|
||||
|
||||
use crate::multicore::Worker;
|
||||
use multicore::Worker;
|
||||
|
||||
/// Generates a random common reference string for
|
||||
/// a circuit.
|
||||
@@ -227,7 +227,7 @@ where
|
||||
let powers_of_tau = powers_of_tau.as_mut();
|
||||
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
||||
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
|
||||
|
||||
for p in powers_of_tau {
|
||||
@@ -251,7 +251,7 @@ where
|
||||
{
|
||||
let mut g1_wnaf = g1_wnaf.shared();
|
||||
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
|
||||
for (h, p) in h.iter_mut().zip(p.iter()) {
|
||||
// Compute final exponent
|
||||
@@ -330,7 +330,7 @@ where
|
||||
let mut g1_wnaf = g1_wnaf.shared();
|
||||
let mut g2_wnaf = g2_wnaf.shared();
|
||||
|
||||
scope.spawn(move |_scope| {
|
||||
scope.spawn(move || {
|
||||
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
|
||||
.iter_mut()
|
||||
.zip(b_g1.iter_mut())
|
||||
@@ -451,7 +451,7 @@ where
|
||||
};
|
||||
|
||||
Ok(Parameters {
|
||||
vk,
|
||||
vk: vk,
|
||||
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
|
||||
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
|
||||
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
//! The [Groth16] proving system.
|
||||
//!
|
||||
//! [Groth16]: https://eprint.iacr.org/2016/260
|
||||
|
||||
use group::{CurveAffine, EncodedPoint};
|
||||
use pairing::{Engine, PairingCurveAffine};
|
||||
|
||||
use crate::SynthesisError;
|
||||
use SynthesisError;
|
||||
|
||||
use crate::multiexp::SourceBuilder;
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use multiexp::SourceBuilder;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -94,7 +90,7 @@ impl<E: Engine> Proof<E> {
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Proof { a, b, c })
|
||||
Ok(Proof { a: a, b: b, c: c })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,13 +208,13 @@ impl<E: Engine> VerifyingKey<E> {
|
||||
}
|
||||
|
||||
Ok(VerifyingKey {
|
||||
alpha_g1,
|
||||
beta_g1,
|
||||
beta_g2,
|
||||
gamma_g2,
|
||||
delta_g1,
|
||||
delta_g2,
|
||||
ic,
|
||||
alpha_g1: alpha_g1,
|
||||
beta_g1: beta_g1,
|
||||
beta_g2: beta_g2,
|
||||
gamma_g2: gamma_g2,
|
||||
delta_g1: delta_g1,
|
||||
delta_g2: delta_g2,
|
||||
ic: ic,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -380,7 +376,7 @@ impl<E: Engine> Parameters<E> {
|
||||
}
|
||||
|
||||
Ok(Parameters {
|
||||
vk,
|
||||
vk: vk,
|
||||
h: Arc::new(h),
|
||||
l: Arc::new(l),
|
||||
a: Arc::new(a),
|
||||
@@ -469,7 +465,7 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
|
||||
#[cfg(test)]
|
||||
mod test_with_bls12_381 {
|
||||
use super::*;
|
||||
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||
use {Circuit, ConstraintSystem, SynthesisError};
|
||||
|
||||
use ff::Field;
|
||||
use pairing::bls12_381::{Bls12, Fr};
|
||||
|
||||
@@ -10,13 +10,13 @@ use pairing::Engine;
|
||||
|
||||
use super::{ParameterSource, Proof};
|
||||
|
||||
use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
use crate::domain::{EvaluationDomain, Scalar};
|
||||
use domain::{EvaluationDomain, Scalar};
|
||||
|
||||
use crate::multiexp::{multiexp, DensityTracker, FullDensity};
|
||||
use multiexp::{multiexp, DensityTracker, FullDensity};
|
||||
|
||||
use crate::multicore::Worker;
|
||||
use multicore::Worker;
|
||||
|
||||
fn eval<E: Engine>(
|
||||
lc: &LinearCombination<E>,
|
||||
|
||||
@@ -16,13 +16,13 @@ const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
||||
pub struct Fr(Wrapping<u32>);
|
||||
|
||||
impl fmt::Display for Fr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}", (self.0).0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fr {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
Fr(Wrapping(rng.next_u32()) % MODULUS_R)
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ impl PartialOrd for FrRepr {
|
||||
}
|
||||
|
||||
impl fmt::Display for FrRepr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}", (self.0)[0])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use self::dummy_engine::*;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||
use {Circuit, ConstraintSystem, SynthesisError};
|
||||
|
||||
use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use pairing::{Engine, PairingCurveAffine};
|
||||
|
||||
use super::{PreparedVerifyingKey, Proof, VerifyingKey};
|
||||
|
||||
use crate::SynthesisError;
|
||||
use SynthesisError;
|
||||
|
||||
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
|
||||
let mut gamma = vk.gamma_g2;
|
||||
@@ -49,7 +49,7 @@ pub fn verify_proof<'a, E: Engine>(
|
||||
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
|
||||
(&proof.c.prepare(), &pvk.neg_delta_g2),
|
||||
]
|
||||
.iter(),
|
||||
.into_iter(),
|
||||
))
|
||||
.unwrap()
|
||||
== pvk.alpha_g1_beta_g2)
|
||||
|
||||
@@ -1,139 +1,33 @@
|
||||
//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
|
||||
//! traits and and primitive structures, as well as basic gadget implementations
|
||||
//! such as booleans and number abstractions.
|
||||
//!
|
||||
//! # Example circuit
|
||||
//!
|
||||
//! Say we want to write a circuit that proves we know the preimage to some hash
|
||||
//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a
|
||||
//! fixed length known in advance (because the circuit parameters will depend on
|
||||
//! it), but can otherwise have any value. We take the following strategy:
|
||||
//!
|
||||
//! - Witness each bit of the preimage.
|
||||
//! - Compute `hash = SHA-256d(preimage)` inside the circuit.
|
||||
//! - Expose `hash` as a public input using multiscalar packing.
|
||||
//!
|
||||
//! ```
|
||||
//! use bellman::{
|
||||
//! gadgets::{
|
||||
//! boolean::{AllocatedBit, Boolean},
|
||||
//! multipack,
|
||||
//! sha256::sha256,
|
||||
//! },
|
||||
//! groth16, Circuit, ConstraintSystem, SynthesisError,
|
||||
//! };
|
||||
//! use pairing::{bls12_381::Bls12, Engine};
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use sha2::{Digest, Sha256};
|
||||
//!
|
||||
//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order.
|
||||
//! fn sha256d<E: Engine, CS: ConstraintSystem<E>>(
|
||||
//! mut cs: CS,
|
||||
//! data: &[Boolean],
|
||||
//! ) -> Result<Vec<Boolean>, SynthesisError> {
|
||||
//! // Flip endianness of each input byte
|
||||
//! let input: Vec<_> = data
|
||||
//! .chunks(8)
|
||||
//! .map(|c| c.iter().rev())
|
||||
//! .flatten()
|
||||
//! .cloned()
|
||||
//! .collect();
|
||||
//!
|
||||
//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
|
||||
//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;
|
||||
//!
|
||||
//! // Flip endianness of each output byte
|
||||
//! Ok(res
|
||||
//! .chunks(8)
|
||||
//! .map(|c| c.iter().rev())
|
||||
//! .flatten()
|
||||
//! .cloned()
|
||||
//! .collect())
|
||||
//! }
|
||||
//!
|
||||
//! struct MyCircuit {
|
||||
//! /// The input to SHA-256d we are proving that we know. Set to `None` when we
|
||||
//! /// are verifying a proof (and do not have the witness data).
|
||||
//! preimage: Option<[u8; 80]>,
|
||||
//! }
|
||||
//!
|
||||
//! impl<E: Engine> Circuit<E> for MyCircuit {
|
||||
//! fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||
//! // Compute the values for the bits of the preimage. If we are verifying a proof,
|
||||
//! // we still need to create the same constraints, so we return an equivalent-size
|
||||
//! // Vec of None (indicating that the value of each bit is unknown).
|
||||
//! let bit_values = if let Some(preimage) = self.preimage {
|
||||
//! preimage
|
||||
//! .into_iter()
|
||||
//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
|
||||
//! .flatten()
|
||||
//! .map(|b| Some(b))
|
||||
//! .collect()
|
||||
//! } else {
|
||||
//! vec![None; 80 * 8]
|
||||
//! };
|
||||
//! assert_eq!(bit_values.len(), 80 * 8);
|
||||
//!
|
||||
//! // Witness the bits of the preimage.
|
||||
//! let preimage_bits = bit_values
|
||||
//! .into_iter()
|
||||
//! .enumerate()
|
||||
//! // Allocate each bit.
|
||||
//! .map(|(i, b)| {
|
||||
//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b)
|
||||
//! })
|
||||
//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
|
||||
//! .map(|b| b.map(Boolean::from))
|
||||
//! .collect::<Result<Vec<_>, _>>()?;
|
||||
//!
|
||||
//! // Compute hash = SHA-256d(preimage).
|
||||
//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;
|
||||
//!
|
||||
//! // Expose the vector of 32 boolean variables as compact public inputs.
|
||||
//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Create parameters for our circuit. In a production deployment these would
|
||||
//! // be generated securely using a multiparty computation.
|
||||
//! let params = {
|
||||
//! let c = MyCircuit { preimage: None };
|
||||
//! groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
|
||||
//! };
|
||||
//!
|
||||
//! // Prepare the verification key (for proof verification).
|
||||
//! let pvk = groth16::prepare_verifying_key(¶ms.vk);
|
||||
//!
|
||||
//! // Pick a preimage and compute its hash.
|
||||
//! let preimage = [42; 80];
|
||||
//! let hash = Sha256::digest(&Sha256::digest(&preimage));
|
||||
//!
|
||||
//! // Create an instance of our circuit (with the preimage as a witness).
|
||||
//! let c = MyCircuit {
|
||||
//! preimage: Some(preimage),
|
||||
//! };
|
||||
//!
|
||||
//! // Create a Groth16 proof with our parameters.
|
||||
//! let proof = groth16::create_random_proof(c, ¶ms, &mut OsRng).unwrap();
|
||||
//!
|
||||
//! // Pack the hash as inputs for proof verification.
|
||||
//! let hash_bits = multipack::bytes_to_bits_le(&hash);
|
||||
//! let inputs = multipack::compute_multipacking::<Bls12>(&hash_bits);
|
||||
//!
|
||||
//! // Check the proof!
|
||||
//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap());
|
||||
//! ```
|
||||
//!
|
||||
//! # Roadmap
|
||||
//!
|
||||
//! `bellman` is being refactored into a generic proving library. Currently it
|
||||
//! is pairing-specific, and different types of proving systems need to be
|
||||
//! implemented as sub-modules. After the refactor, `bellman` will be generic
|
||||
//! using the [`ff`] and [`group`] crates, while specific proving systems will
|
||||
//! be separate crates that pull in the dependencies they require.
|
||||
extern crate ff;
|
||||
extern crate group;
|
||||
#[cfg(feature = "pairing")]
|
||||
extern crate pairing;
|
||||
extern crate rand_core;
|
||||
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
extern crate bit_vec;
|
||||
extern crate blake2s_simd;
|
||||
extern crate byteorder;
|
||||
extern crate futures;
|
||||
|
||||
#[cfg(feature = "multicore")]
|
||||
extern crate crossbeam;
|
||||
#[cfg(feature = "multicore")]
|
||||
extern crate futures_cpupool;
|
||||
#[cfg(feature = "multicore")]
|
||||
extern crate num_cpus;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand_xorshift;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate sha2;
|
||||
|
||||
pub mod domain;
|
||||
pub mod gadgets;
|
||||
@@ -215,7 +109,6 @@ impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
|
||||
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
|
||||
type Output = LinearCombination<E>;
|
||||
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||
coeff.negate();
|
||||
|
||||
@@ -337,8 +230,8 @@ impl Error for SynthesisError {
|
||||
}
|
||||
|
||||
impl fmt::Display for SynthesisError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
if let SynthesisError::IoError(ref e) = *self {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
if let &SynthesisError::IoError(ref e) = self {
|
||||
write!(f, "I/O error: ")?;
|
||||
e.fmt(f)
|
||||
} else {
|
||||
@@ -403,7 +296,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
|
||||
fn get_root(&mut self) -> &mut Self::Root;
|
||||
|
||||
/// Begin a namespace for this constraint system.
|
||||
fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root>
|
||||
fn namespace<'a, NR, N>(&'a mut self, name_fn: N) -> Namespace<'a, E, Self::Root>
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
@@ -416,7 +309,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
|
||||
|
||||
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
|
||||
/// a namespace context) and, when dropped, pops out of the namespace context.
|
||||
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E>>(&'a mut CS, PhantomData<E>);
|
||||
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>);
|
||||
|
||||
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
|
||||
type Root = CS::Root;
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
//! An interface for dealing with the kinds of parallel computations involved in
|
||||
//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and
|
||||
//! [`crossbeam`] but may be extended in the future to allow for various
|
||||
//! parallelism strategies.
|
||||
//!
|
||||
//! [`CpuPool`]: futures_cpupool::CpuPool
|
||||
//! This is an interface for dealing with the kinds of
|
||||
//! parallel computations involved in bellman. It's
|
||||
//! currently just a thin wrapper around CpuPool and
|
||||
//! crossbeam but may be extended in the future to
|
||||
//! allow for various parallelism strategies.
|
||||
|
||||
#[cfg(feature = "multicore")]
|
||||
mod implementation {
|
||||
use crossbeam::{self, thread::Scope};
|
||||
use crossbeam::{self, Scope};
|
||||
use futures::{Future, IntoFuture, Poll};
|
||||
use futures_cpupool::{CpuFuture, CpuPool};
|
||||
use num_cpus;
|
||||
@@ -24,7 +23,7 @@ mod implementation {
|
||||
// CPUs configured.
|
||||
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
|
||||
Worker {
|
||||
cpus,
|
||||
cpus: cpus,
|
||||
pool: CpuPool::new(cpus),
|
||||
}
|
||||
}
|
||||
@@ -60,9 +59,7 @@ mod implementation {
|
||||
elements / self.cpus
|
||||
};
|
||||
|
||||
// TODO: Handle case where threads fail
|
||||
crossbeam::scope(|scope| f(scope, chunk_size))
|
||||
.expect("Threads aren't allowed to fail yet")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +152,8 @@ mod implementation {
|
||||
pub struct DummyScope;
|
||||
|
||||
impl DummyScope {
|
||||
pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
|
||||
f(self);
|
||||
pub fn spawn<F: FnOnce()>(&self, f: F) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ fn multiexp_inner<Q, D, G, S>(
|
||||
mut skip: u32,
|
||||
c: u32,
|
||||
handle_trivial: bool,
|
||||
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||
) -> Box<Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||
where
|
||||
for<'a> &'a Q: QueryDensity,
|
||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||
@@ -256,7 +256,7 @@ pub fn multiexp<Q, D, G, S>(
|
||||
bases: S,
|
||||
density_map: D,
|
||||
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
||||
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||
) -> Box<Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||
where
|
||||
for<'a> &'a Q: QueryDensity,
|
||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
extern crate bellman;
|
||||
extern crate ff;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
|
||||
// For randomness (during paramgen and proof generation)
|
||||
use rand::thread_rng;
|
||||
|
||||
@@ -85,12 +90,12 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
|
||||
let cs = &mut cs.namespace(|| format!("round {}", i));
|
||||
|
||||
// tmp = (xL + Ci)^2
|
||||
let tmp_value = xl_value.map(|mut e| {
|
||||
let mut tmp_value = xl_value.map(|mut e| {
|
||||
e.add_assign(&self.constants[i]);
|
||||
e.square();
|
||||
e
|
||||
});
|
||||
let tmp = cs.alloc(
|
||||
let mut tmp = cs.alloc(
|
||||
|| "tmp",
|
||||
|| tmp_value.ok_or(SynthesisError::AssignmentMissing),
|
||||
)?;
|
||||
@@ -105,14 +110,14 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
|
||||
// new_xL = xR + (xL + Ci)^3
|
||||
// new_xL = xR + tmp * (xL + Ci)
|
||||
// new_xL - xR = tmp * (xL + Ci)
|
||||
let new_xl_value = xl_value.map(|mut e| {
|
||||
let mut new_xl_value = xl_value.map(|mut e| {
|
||||
e.add_assign(&self.constants[i]);
|
||||
e.mul_assign(&tmp_value.unwrap());
|
||||
e.add_assign(&xr_value.unwrap());
|
||||
e
|
||||
});
|
||||
|
||||
let new_xl = if i == (MIMC_ROUNDS - 1) {
|
||||
let mut new_xl = if i == (MIMC_ROUNDS - 1) {
|
||||
// This is the last round, xL is our image and so
|
||||
// we allocate a public input.
|
||||
cs.alloc_input(
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
[package]
|
||||
name = "ff"
|
||||
version = "0.5.2"
|
||||
version = "0.4.0"
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
description = "Library for building and interfacing with finite fields"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/ff/"
|
||||
homepage = "https://github.com/ebfull/ff"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/ebfull/ff"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1"
|
||||
ff_derive = { version = "^0.4.1", path = "ff_derive", optional = true }
|
||||
ff_derive = { version = "0.3.0", path = "ff_derive", optional = true }
|
||||
rand_core = "0.5"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
derive = ["ff_derive"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
17
ff/README.md
17
ff/README.md
@@ -12,18 +12,14 @@ Add the `ff` crate to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ff = "0.5"
|
||||
ff = "0.4"
|
||||
```
|
||||
|
||||
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits.
|
||||
See the **[documentation](https://docs.rs/ff/)** for more.
|
||||
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. See the **[documentation](https://docs.rs/ff/0.4.0/ff/)** for more.
|
||||
|
||||
### #![derive(PrimeField)]
|
||||
|
||||
If you need an implementation of a prime field, this library also provides a procedural
|
||||
macro that will expand into an efficient implementation of a prime field when supplied
|
||||
with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is
|
||||
also quadratic nonresidue.
|
||||
If you need an implementation of a prime field, this library also provides a procedural macro that will expand into an efficient implementation of a prime field when supplied with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is also quadratic nonresidue.
|
||||
|
||||
First, enable the `derive` crate feature:
|
||||
|
||||
@@ -45,16 +41,13 @@ extern crate ff;
|
||||
struct Fp(FpRepr);
|
||||
```
|
||||
|
||||
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement
|
||||
`SqrtField` if supported. The library implements `FpRepr` itself and derives
|
||||
`PrimeFieldRepr` for it.
|
||||
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement `SqrtField` if supported. The library implements `FpRepr` itself and derives `PrimeFieldRepr` for it.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
[package]
|
||||
name = "ff_derive"
|
||||
version = "0.4.1"
|
||||
version = "0.3.0"
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
description = "Procedural macro library used to build custom prime field implementations"
|
||||
documentation = "https://docs.rs/ff/"
|
||||
homepage = "https://github.com/ebfull/ff"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/ebfull/ff"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
@@ -16,9 +15,6 @@ proc-macro = true
|
||||
num-bigint = "0.2"
|
||||
num-traits = "0.2"
|
||||
num-integer = "0.1"
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = "1"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "passively-maintained" }
|
||||
proc-macro2 = "0.4"
|
||||
quote = "0.6"
|
||||
syn = "0.14"
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
extern crate num_bigint;
|
||||
extern crate num_integer;
|
||||
extern crate num_traits;
|
||||
|
||||
use num_bigint::BigUint;
|
||||
use num_integer::Integer;
|
||||
use num_traits::{One, ToPrimitive, Zero};
|
||||
use quote::quote;
|
||||
use quote::TokenStreamExt;
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -85,10 +91,10 @@ fn fetch_wrapped_ident(body: &syn::Data) -> Option<syn::Ident> {
|
||||
/// Fetch an attribute string from the derived struct.
|
||||
fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> {
|
||||
for attr in attrs {
|
||||
if let Ok(meta) = attr.parse_meta() {
|
||||
if let Some(meta) = attr.interpret_meta() {
|
||||
match meta {
|
||||
syn::Meta::NameValue(nv) => {
|
||||
if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) {
|
||||
if nv.ident.to_string() == name {
|
||||
match nv.lit {
|
||||
syn::Lit::Str(ref s) => return Some(s.value()),
|
||||
_ => {
|
||||
@@ -116,9 +122,9 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
impl ::std::fmt::Debug for #repr
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "0x")?;
|
||||
try!(write!(f, "0x"));
|
||||
for i in self.0.iter().rev() {
|
||||
write!(f, "{:016x}", *i)?;
|
||||
try!(write!(f, "{:016x}", *i));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -127,9 +133,9 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
|
||||
impl ::std::fmt::Display for #repr {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "0x")?;
|
||||
try!(write!(f, "0x"));
|
||||
for i in self.0.iter().rev() {
|
||||
write!(f, "{:016x}", *i)?;
|
||||
try!(write!(f, "{:016x}", *i));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -877,7 +883,7 @@ fn prime_field_impl(
|
||||
|
||||
impl ::ff::Field for #name {
|
||||
/// Computes a uniformly random element using rejection sampling.
|
||||
fn random<R: ::rand_core::RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
fn random<R: ::rand_core::RngCore>(rng: &mut R) -> Self {
|
||||
loop {
|
||||
let mut tmp = {
|
||||
let mut repr = [0u64; #limbs];
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
//! This crate provides traits for working with finite fields.
|
||||
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate rand_core;
|
||||
|
||||
#[cfg(feature = "derive")]
|
||||
#[macro_use]
|
||||
extern crate ff_derive;
|
||||
|
||||
#[cfg(feature = "derive")]
|
||||
pub use ff_derive::*;
|
||||
|
||||
@@ -17,7 +20,7 @@ pub trait Field:
|
||||
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
|
||||
{
|
||||
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self;
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self;
|
||||
|
||||
/// Returns the zero element of the field, the additive identity.
|
||||
fn zero() -> Self;
|
||||
@@ -207,7 +210,7 @@ impl Error for PrimeFieldDecodingError {
|
||||
}
|
||||
|
||||
impl fmt::Display for PrimeFieldDecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
PrimeFieldDecodingError::NotInField(ref repr) => {
|
||||
write!(f, "{} is not an element of the field", repr)
|
||||
@@ -263,7 +266,7 @@ pub trait PrimeField: Field {
|
||||
}
|
||||
|
||||
/// Convert this prime field element into a biginteger representation.
|
||||
fn from_repr(_: Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
|
||||
fn from_repr(Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
|
||||
|
||||
/// Convert a biginteger representation into a prime field element, if
|
||||
/// the number is an element of the field.
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
[package]
|
||||
name = "group"
|
||||
version = "0.2.0"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <jack@z.cash>",
|
||||
]
|
||||
readme = "README.md"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
description = "Elliptic curve group traits and utilities"
|
||||
documentation = "https://docs.rs/group/"
|
||||
homepage = "https://github.com/ebfull/group"
|
||||
repository = "https://github.com/ebfull/group"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
ff = { version = "0.5.0", path = "../ff" }
|
||||
ff = { path = "../ff" }
|
||||
rand = "0.7"
|
||||
rand_xorshift = "0.2"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
# group [](https://crates.io/crates/group) #
|
||||
|
||||
`group` is a crate for working with groups over elliptic curves.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
extern crate ff;
|
||||
extern crate rand;
|
||||
extern crate rand_xorshift;
|
||||
|
||||
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
|
||||
use rand::RngCore;
|
||||
@@ -179,7 +180,7 @@ impl Error for GroupDecodingError {
|
||||
}
|
||||
|
||||
impl fmt::Display for GroupDecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
|
||||
write!(f, "{} decoding error: {}", description, err)
|
||||
|
||||
@@ -2,7 +2,7 @@ use ff::{Field, PrimeField};
|
||||
use rand::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use crate::{CurveAffine, CurveProjective, EncodedPoint};
|
||||
use {CurveAffine, CurveProjective, EncodedPoint};
|
||||
|
||||
pub fn curve_tests<G: CurveProjective>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@@ -71,7 +71,7 @@ pub fn curve_tests<G: CurveProjective>() {
|
||||
}
|
||||
|
||||
fn random_wnaf_tests<G: CurveProjective>() {
|
||||
use crate::wnaf::*;
|
||||
use wnaf::*;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
[package]
|
||||
name = "librustzcash"
|
||||
description = "Rust FFI used by the zcashd binary. Not an official API."
|
||||
version = "0.2.0"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <jack@z.cash>",
|
||||
"Jay Graber <jay@z.cash>",
|
||||
"Simon Liu <simon@z.cash>"
|
||||
]
|
||||
homepage = "https://github.com/zcash/librustzcash"
|
||||
repository = "https://github.com/zcash/librustzcash"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2018"
|
||||
]
|
||||
|
||||
[lib]
|
||||
name = "rustzcash"
|
||||
@@ -20,17 +14,15 @@ path = "src/rustzcash.rs"
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
bellman = { version = "0.2.0", path = "../bellman" }
|
||||
bellman = { path = "../bellman" }
|
||||
blake2b_simd = "0.5"
|
||||
blake2s_simd = "0.5"
|
||||
ff = { version = "0.5.0", path = "../ff" }
|
||||
ff = { path = "../ff" }
|
||||
libc = "0.2"
|
||||
pairing = { version = "0.15.0", path = "../pairing" }
|
||||
pairing = { path = "../pairing" }
|
||||
lazy_static = "1"
|
||||
rand_core = "0.5.1"
|
||||
zcash_history = { version = "0.0.1", path = "../zcash_history" }
|
||||
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||
zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "deprecated" }
|
||||
byteorder = "1"
|
||||
rand_core = "0.5"
|
||||
rand_os = "0.2"
|
||||
zcash_primitives = { path = "../zcash_primitives" }
|
||||
zcash_proofs = { path = "../zcash_proofs" }
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
# librustzcash
|
||||
|
||||
`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`.
|
||||
This repository contains librustzcash, a static library for Zcash code assets written in Rust.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -112,7 +112,8 @@ extern "C" {
|
||||
bool librustzcash_sapling_output_proof(
|
||||
void *ctx,
|
||||
const unsigned char *esk,
|
||||
const unsigned char *payment_address,
|
||||
const unsigned char *diversifier,
|
||||
const unsigned char *pk_d,
|
||||
const unsigned char *rcm,
|
||||
const uint64_t value,
|
||||
unsigned char *cv,
|
||||
@@ -307,33 +308,6 @@ extern "C" {
|
||||
unsigned char *j_ret,
|
||||
unsigned char *addr_ret
|
||||
);
|
||||
|
||||
uint32_t librustzcash_mmr_append(
|
||||
uint32_t cbranch,
|
||||
uint32_t t_len,
|
||||
const uint32_t *ni_ptr,
|
||||
const unsigned char *n_ptr,
|
||||
size_t p_len,
|
||||
const unsigned char *nn_ptr,
|
||||
unsigned char *rt_ret,
|
||||
unsigned char *buf_ret
|
||||
);
|
||||
|
||||
uint32_t librustzcash_mmr_delete(
|
||||
uint32_t cbranch,
|
||||
uint32_t t_len,
|
||||
const uint32_t *ni_ptr,
|
||||
const unsigned char *n_ptr,
|
||||
size_t p_len,
|
||||
size_t e_len,
|
||||
unsigned char *rt_ret
|
||||
);
|
||||
|
||||
uint32_t librustzcash_mmr_hash_node(
|
||||
uint32_t cbranch,
|
||||
const unsigned char *n_ptr,
|
||||
unsigned char *h_ret
|
||||
);
|
||||
}
|
||||
|
||||
#endif // LIBRUSTZCASH_INCLUDE_H_
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
//! Verification functions for the [Equihash] proof-of-work algorithm.
|
||||
//!
|
||||
//! [Equihash]: https://zips.z.cash/protocol/protocol.pdf#equihash
|
||||
|
||||
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams, State as Blake2bState};
|
||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use log::error;
|
||||
use std::io::Cursor;
|
||||
use std::mem::size_of;
|
||||
|
||||
@@ -65,7 +60,10 @@ impl Node {
|
||||
indices.extend(a.indices.iter());
|
||||
indices
|
||||
};
|
||||
Node { hash, indices }
|
||||
Node {
|
||||
hash: hash,
|
||||
indices: indices,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self {
|
||||
@@ -84,7 +82,10 @@ impl Node {
|
||||
indices.extend(b.indices.iter());
|
||||
indices.extend(a.indices.iter());
|
||||
}
|
||||
Node { hash, indices }
|
||||
Node {
|
||||
hash: hash,
|
||||
indices: indices,
|
||||
}
|
||||
}
|
||||
|
||||
fn indices_before(&self, other: &Node) -> bool {
|
||||
@@ -140,7 +141,7 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec<u8> {
|
||||
|
||||
let mut j = 0;
|
||||
for b in vin {
|
||||
acc_value = (acc_value << 8) | u32::from(*b);
|
||||
acc_value = (acc_value << 8) | *b as u32;
|
||||
acc_bits += 8;
|
||||
|
||||
// When we have bit_len or more bits in the accumulator, write the next
|
||||
@@ -196,18 +197,18 @@ fn distinct_indices(a: &Node, b: &Node) -> bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
return true;
|
||||
}
|
||||
|
||||
fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> bool {
|
||||
if !has_collision(a, b, p.collision_byte_length()) {
|
||||
error!("Invalid solution: invalid collision length between StepRows");
|
||||
// error!("Invalid solution: invalid collision length between StepRows");
|
||||
false
|
||||
} else if b.indices_before(a) {
|
||||
error!("Invalid solution: Index tree incorrectly ordered");
|
||||
// error!("Invalid solution: Index tree incorrectly ordered");
|
||||
false
|
||||
} else if !distinct_indices(a, b) {
|
||||
error!("Invalid solution: duplicate indices");
|
||||
// error!("Invalid solution: duplicate indices");
|
||||
false
|
||||
} else {
|
||||
true
|
||||
@@ -221,7 +222,7 @@ pub fn is_valid_solution_iterative(
|
||||
nonce: &[u8],
|
||||
indices: &[u32],
|
||||
) -> bool {
|
||||
let p = Params { n, k };
|
||||
let p = Params { n: n, k: k };
|
||||
|
||||
let mut state = initialise_state(p.n, p.k, p.hash_output());
|
||||
state.update(input);
|
||||
@@ -248,25 +249,25 @@ pub fn is_valid_solution_iterative(
|
||||
}
|
||||
|
||||
assert!(rows.len() == 1);
|
||||
rows[0].is_zero(hash_len)
|
||||
return rows[0].is_zero(hash_len);
|
||||
}
|
||||
|
||||
fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Option<Node> {
|
||||
if indices.len() > 1 {
|
||||
let end = indices.len();
|
||||
let mid = end / 2;
|
||||
match (
|
||||
tree_validator(p, state, &indices[0..mid]),
|
||||
tree_validator(p, state, &indices[mid..end]),
|
||||
) {
|
||||
(Some(a), Some(b)) => {
|
||||
match tree_validator(p, state, &indices[0..mid]) {
|
||||
Some(a) => match tree_validator(p, state, &indices[mid..end]) {
|
||||
Some(b) => {
|
||||
if validate_subtrees(p, &a, &b) {
|
||||
Some(Node::from_children(a, b, p.collision_byte_length()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
None => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
Some(Node::new(&p, &state, indices[0]))
|
||||
@@ -280,7 +281,7 @@ pub fn is_valid_solution_recursive(
|
||||
nonce: &[u8],
|
||||
indices: &[u32],
|
||||
) -> bool {
|
||||
let p = Params { n, k };
|
||||
let p = Params { n: n, k: k };
|
||||
|
||||
let mut state = initialise_state(p.n, p.k, p.hash_output());
|
||||
state.update(input);
|
||||
@@ -296,7 +297,7 @@ pub fn is_valid_solution_recursive(
|
||||
}
|
||||
|
||||
pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool {
|
||||
let p = Params { n, k };
|
||||
let p = Params { n: n, k: k };
|
||||
let indices = indices_from_minimal(soln, p.collision_bit_length());
|
||||
|
||||
// Recursive validation is faster
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,11 @@
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use pairing::bls12_381::Bls12;
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use rand_core::RngCore;
|
||||
use rand_os::OsRng;
|
||||
use zcash_primitives::jubjub::{edwards, JubjubBls12};
|
||||
use zcash_primitives::primitives::{Diversifier, ViewingKey};
|
||||
|
||||
use crate::{
|
||||
use {
|
||||
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
|
||||
librustzcash_sapling_ka_derivepublic,
|
||||
};
|
||||
@@ -24,7 +25,7 @@ fn test_key_agreement() {
|
||||
let addr = loop {
|
||||
let mut d = [0; 11];
|
||||
rng.fill_bytes(&mut d);
|
||||
match vk.to_payment_address(Diversifier(d), ¶ms) {
|
||||
match vk.into_payment_address(Diversifier(d), ¶ms) {
|
||||
Some(a) => break a,
|
||||
None => {}
|
||||
}
|
||||
@@ -46,7 +47,7 @@ fn test_key_agreement() {
|
||||
|
||||
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
||||
let mut addr_pk_d = [0u8; 32];
|
||||
addr.pk_d().write(&mut addr_pk_d[..]).unwrap();
|
||||
addr.pk_d.write(&mut addr_pk_d[..]).unwrap();
|
||||
|
||||
assert!(librustzcash_sapling_ka_agree(
|
||||
&addr_pk_d,
|
||||
@@ -58,7 +59,7 @@ fn test_key_agreement() {
|
||||
// using the diversifier and esk.
|
||||
let mut epk = [0u8; 32];
|
||||
assert!(librustzcash_sapling_ka_derivepublic(
|
||||
&addr.diversifier().0,
|
||||
&addr.diversifier.0,
|
||||
&esk,
|
||||
&mut epk
|
||||
));
|
||||
|
||||
@@ -7,7 +7,7 @@ use zcash_primitives::{
|
||||
|
||||
use super::JUBJUB;
|
||||
|
||||
use crate::{
|
||||
use {
|
||||
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
|
||||
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
|
||||
};
|
||||
@@ -678,7 +678,7 @@ fn key_components() {
|
||||
}
|
||||
|
||||
let pgk = ProofGenerationKey { ak, nsk };
|
||||
let fvk = pgk.to_viewing_key(&JUBJUB);
|
||||
let fvk = pgk.into_viewing_key(&JUBJUB);
|
||||
{
|
||||
let mut vec = Vec::new();
|
||||
fvk.nk.write(&mut vec).unwrap();
|
||||
@@ -704,10 +704,10 @@ fn key_components() {
|
||||
let diversifier = Diversifier(tv.default_d);
|
||||
assert!(librustzcash_check_diversifier(&tv.default_d));
|
||||
|
||||
let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
|
||||
let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap();
|
||||
{
|
||||
let mut vec = Vec::new();
|
||||
addr.pk_d().write(&mut vec).unwrap();
|
||||
addr.pk_d.write(&mut vec).unwrap();
|
||||
assert_eq!(&vec, &tv.default_pk_d);
|
||||
}
|
||||
{
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
use zcash_history::{Entry, EntryLink, NodeData};
|
||||
|
||||
use crate::{librustzcash_mmr_append, librustzcash_mmr_delete};
|
||||
|
||||
const NODE_DATA_16L: &[u8] = include_bytes!("./res/tree16.dat");
|
||||
const NODE_DATA_1023L: &[u8] = include_bytes!("./res/tree1023.dat");
|
||||
|
||||
struct TreeView {
|
||||
peaks: Vec<(u32, Entry)>,
|
||||
extra: Vec<(u32, Entry)>,
|
||||
}
|
||||
|
||||
fn draft(into: &mut Vec<(u32, Entry)>, nodes: &[NodeData], peak_pos: usize, h: u32) {
|
||||
let node_data = nodes[peak_pos - 1].clone();
|
||||
let peak: Entry = match h {
|
||||
0 => node_data.into(),
|
||||
_ => Entry::new(
|
||||
node_data,
|
||||
EntryLink::Stored((peak_pos - (1 << h) - 1) as u32),
|
||||
EntryLink::Stored((peak_pos - 2) as u32),
|
||||
),
|
||||
};
|
||||
|
||||
into.push(((peak_pos - 1) as u32, peak));
|
||||
}
|
||||
|
||||
fn prepare_tree(nodes: &[NodeData]) -> TreeView {
|
||||
assert!(!nodes.is_empty());
|
||||
|
||||
// integer log2 of (nodes.len()+1), -1
|
||||
let mut h = (32 - ((nodes.len() + 1) as u32).leading_zeros() - 1) - 1;
|
||||
let mut peak_pos = (1 << (h + 1)) - 1;
|
||||
let mut peaks = Vec::new();
|
||||
|
||||
// used later
|
||||
let mut last_peak_pos = 0;
|
||||
let mut last_peak_h = 0;
|
||||
|
||||
loop {
|
||||
if peak_pos > nodes.len() {
|
||||
// left child, -2^h
|
||||
peak_pos -= 1 << h;
|
||||
h -= 1;
|
||||
}
|
||||
|
||||
if peak_pos <= nodes.len() {
|
||||
draft(&mut peaks, nodes, peak_pos, h);
|
||||
|
||||
// save to be used in next loop
|
||||
last_peak_pos = peak_pos;
|
||||
last_peak_h = h;
|
||||
|
||||
// right sibling
|
||||
peak_pos += (1 << (h + 1)) - 1;
|
||||
}
|
||||
|
||||
if h == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// for deletion, everything on the right slope of the last peak should be pre-loaded
|
||||
let mut extra = Vec::new();
|
||||
let mut h = last_peak_h;
|
||||
let mut peak_pos = last_peak_pos;
|
||||
|
||||
while h > 0 {
|
||||
let left_pos = peak_pos - (1 << h);
|
||||
let right_pos = peak_pos - 1;
|
||||
h -= 1;
|
||||
|
||||
// drafting left child
|
||||
draft(&mut extra, nodes, left_pos, h);
|
||||
|
||||
// drafting right child
|
||||
draft(&mut extra, nodes, right_pos, h);
|
||||
|
||||
// continuing on right slope
|
||||
peak_pos = right_pos;
|
||||
}
|
||||
|
||||
TreeView { peaks, extra }
|
||||
}
|
||||
|
||||
fn preload_tree_append(nodes: &[NodeData]) -> (Vec<u32>, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>) {
|
||||
assert!(!nodes.is_empty());
|
||||
|
||||
let tree_view = prepare_tree(nodes);
|
||||
|
||||
let mut indices = Vec::new();
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
for (idx, entry) in tree_view.peaks.into_iter() {
|
||||
let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE];
|
||||
entry
|
||||
.write(&mut &mut buf[..])
|
||||
.expect("Cannot fail if enough buffer length");
|
||||
indices.push(idx);
|
||||
bytes.push(buf);
|
||||
}
|
||||
|
||||
(indices, bytes)
|
||||
}
|
||||
|
||||
// also returns number of peaks
|
||||
fn preload_tree_delete(
|
||||
nodes: &[NodeData],
|
||||
) -> (Vec<u32>, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>, usize) {
|
||||
assert!(!nodes.is_empty());
|
||||
|
||||
let tree_view = prepare_tree(nodes);
|
||||
|
||||
let mut indices = Vec::new();
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
let peak_count = tree_view.peaks.len();
|
||||
|
||||
for (idx, entry) in tree_view
|
||||
.peaks
|
||||
.into_iter()
|
||||
.chain(tree_view.extra.into_iter())
|
||||
{
|
||||
let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE];
|
||||
entry
|
||||
.write(&mut &mut buf[..])
|
||||
.expect("Cannot fail if enough buffer length");
|
||||
indices.push(idx);
|
||||
bytes.push(buf);
|
||||
}
|
||||
|
||||
(indices, bytes, peak_count)
|
||||
}
|
||||
|
||||
fn load_nodes(bytes: &'static [u8]) -> Vec<NodeData> {
|
||||
let mut res = Vec::new();
|
||||
let mut cursor = std::io::Cursor::new(bytes);
|
||||
while (cursor.position() as usize) < bytes.len() {
|
||||
let node_data = zcash_history::NodeData::read(0, &mut cursor)
|
||||
.expect("Statically checked to be correct");
|
||||
res.push(node_data);
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append() {
|
||||
let nodes = load_nodes(NODE_DATA_16L);
|
||||
let (indices, peaks) = preload_tree_append(&nodes);
|
||||
|
||||
let mut rt_ret = [0u8; 32];
|
||||
|
||||
let mut buf_ret = Vec::<[u8; zcash_history::MAX_NODE_DATA_SIZE]>::with_capacity(32);
|
||||
|
||||
let mut new_node_data = [0u8; zcash_history::MAX_NODE_DATA_SIZE];
|
||||
let new_node = NodeData {
|
||||
consensus_branch_id: 0,
|
||||
subtree_commitment: [0u8; 32],
|
||||
start_time: 101,
|
||||
end_time: 110,
|
||||
start_target: 190,
|
||||
end_target: 200,
|
||||
start_sapling_root: [0u8; 32],
|
||||
end_sapling_root: [0u8; 32],
|
||||
subtree_total_work: Default::default(),
|
||||
start_height: 10,
|
||||
end_height: 10,
|
||||
sapling_tx: 13,
|
||||
};
|
||||
new_node
|
||||
.write(&mut &mut new_node_data[..])
|
||||
.expect("Failed to write node data");
|
||||
|
||||
let result = librustzcash_mmr_append(
|
||||
0,
|
||||
nodes.len() as u32,
|
||||
indices.as_ptr(),
|
||||
peaks.as_ptr(),
|
||||
peaks.len(),
|
||||
&new_node_data,
|
||||
&mut rt_ret,
|
||||
buf_ret.as_mut_ptr(),
|
||||
);
|
||||
|
||||
unsafe {
|
||||
buf_ret.set_len(result as usize);
|
||||
}
|
||||
|
||||
assert_eq!(result, 2);
|
||||
|
||||
let new_node_1 =
|
||||
NodeData::from_bytes(0, &buf_ret[0][..]).expect("Failed to reconstruct return node #1");
|
||||
|
||||
let new_node_2 =
|
||||
NodeData::from_bytes(0, &buf_ret[1][..]).expect("Failed to reconstruct return node #2");
|
||||
|
||||
assert_eq!(new_node_1.start_height, 10);
|
||||
assert_eq!(new_node_1.end_height, 10);
|
||||
|
||||
// this is combined new node (which is `new_node_1`) + the one which was there before (for block #9)
|
||||
assert_eq!(new_node_2.start_height, 9);
|
||||
assert_eq!(new_node_2.end_height, 10);
|
||||
assert_eq!(new_node_2.sapling_tx, 27);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete() {
|
||||
let nodes = load_nodes(NODE_DATA_1023L);
|
||||
let (indices, nodes, peak_count) = preload_tree_delete(&nodes);
|
||||
|
||||
let mut rt_ret = [0u8; 32];
|
||||
|
||||
let result = librustzcash_mmr_delete(
|
||||
0,
|
||||
nodes.len() as u32,
|
||||
indices.as_ptr(),
|
||||
nodes.as_ptr(),
|
||||
peak_count,
|
||||
indices.len() - peak_count,
|
||||
&mut rt_ret,
|
||||
);
|
||||
|
||||
// Deleting from full tree of 9 height would result in cascade deleting of 10 nodes
|
||||
assert_eq!(result, 10);
|
||||
}
|
||||
@@ -4,7 +4,6 @@ use super::JUBJUB;
|
||||
|
||||
mod key_agreement;
|
||||
mod key_components;
|
||||
mod mmr;
|
||||
mod notes;
|
||||
mod signatures;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::librustzcash_sapling_compute_cm;
|
||||
use crate::librustzcash_sapling_compute_nf;
|
||||
use librustzcash_sapling_compute_cm;
|
||||
use librustzcash_sapling_compute_nf;
|
||||
|
||||
#[test]
|
||||
fn notes() {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -2,24 +2,22 @@
|
||||
name = "pairing"
|
||||
|
||||
# Remember to change version string in README.md.
|
||||
version = "0.15.1"
|
||||
version = "0.14.2"
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Jack Grigg <jack@z.cash>",
|
||||
]
|
||||
readme = "README.md"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
description = "Pairing-friendly elliptic curve library"
|
||||
documentation = "https://docs.rs/pairing/"
|
||||
homepage = "https://github.com/ebfull/pairing"
|
||||
repository = "https://github.com/ebfull/pairing"
|
||||
edition ="2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1"
|
||||
ff = { version = "^0.5.2", path = "../ff", features = ["derive"] }
|
||||
group = { version = "0.2.0", path = "../group" }
|
||||
ff = { path = "../ff", features = ["derive"] }
|
||||
group = { path = "../group" }
|
||||
rand_core = "0.5"
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -29,6 +27,3 @@ rand_xorshift = "0.2"
|
||||
unstable-features = ["expose-arith"]
|
||||
expose-arith = []
|
||||
default = []
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
# pairing [](https://crates.io/crates/pairing) #
|
||||
|
||||
`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.
|
||||
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.
|
||||
|
||||
## [Documentation](https://docs.rs/pairing/)
|
||||
|
||||
@@ -18,15 +8,13 @@ Bring the `pairing` crate into your project just as you normally would.
|
||||
|
||||
## Security Warnings
|
||||
|
||||
This library does not make any guarantees about constant-time operations, memory
|
||||
access patterns, or resistance to side-channel attacks.
|
||||
This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
mod g1 {
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use ff::Field;
|
||||
use group::CurveProjective;
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
@@ -10,13 +8,10 @@ mod g1 {
|
||||
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (G1::random(&mut rng), Fr::random(&mut rng)))
|
||||
.map(|_| (G1::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -32,13 +27,10 @@ mod g1 {
|
||||
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1, G1)> = (0..SAMPLES)
|
||||
.map(|_| (G1::random(&mut rng), G1::random(&mut rng)))
|
||||
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -54,13 +46,10 @@ mod g1 {
|
||||
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1, G1Affine)> = (0..SAMPLES)
|
||||
.map(|_| (G1::random(&mut rng), G1::random(&mut rng).into()))
|
||||
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng).into()))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -74,10 +63,8 @@ mod g1 {
|
||||
}
|
||||
|
||||
mod g2 {
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use ff::Field;
|
||||
use group::CurveProjective;
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
@@ -85,13 +72,10 @@ mod g2 {
|
||||
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G2, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (G2::random(&mut rng), Fr::random(&mut rng)))
|
||||
.map(|_| (G2::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -107,13 +91,10 @@ mod g2 {
|
||||
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G2, G2)> = (0..SAMPLES)
|
||||
.map(|_| (G2::random(&mut rng), G2::random(&mut rng)))
|
||||
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -129,13 +110,10 @@ mod g2 {
|
||||
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G2, G2Affine)> = (0..SAMPLES)
|
||||
.map(|_| (G2::random(&mut rng), G2::random(&mut rng).into()))
|
||||
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng).into()))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use pairing::bls12_381::*;
|
||||
@@ -8,15 +7,12 @@ use pairing::bls12_381::*;
|
||||
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp1 = Fq::random(&mut rng).into_repr();
|
||||
let mut tmp2 = Fq::random(&mut rng).into_repr();
|
||||
let mut tmp1 = FqRepr::rand(&mut rng);
|
||||
let mut tmp2 = FqRepr::rand(&mut rng);
|
||||
// Shave a few bits off to avoid overflow.
|
||||
for _ in 0..3 {
|
||||
tmp1.div2();
|
||||
@@ -39,14 +35,11 @@ fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let tmp1 = Fq::random(&mut rng).into_repr();
|
||||
let tmp1 = FqRepr::rand(&mut rng);
|
||||
let mut tmp2 = tmp1;
|
||||
// Ensure tmp2 is smaller than tmp1.
|
||||
for _ in 0..10 {
|
||||
@@ -69,14 +62,9 @@ fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||
.map(|_| Fq::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -90,14 +78,9 @@ fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||
.map(|_| Fq::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -112,14 +95,9 @@ fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||
.map(|_| Fq::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -134,13 +112,10 @@ fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -156,13 +131,10 @@ fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -178,13 +150,10 @@ fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
|
||||
.map(|_| (Fq::random(&mut rng), Fq::random(&mut rng)))
|
||||
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -200,12 +169,9 @@ fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -220,12 +186,9 @@ fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -238,12 +201,9 @@ fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -258,14 +218,11 @@ fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp = Fq::random(&mut rng);
|
||||
let mut tmp = Fq::rand(&mut rng);
|
||||
tmp.square();
|
||||
tmp
|
||||
})
|
||||
@@ -282,12 +239,9 @@ fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -300,13 +254,10 @@ fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FqRepr> = (0..SAMPLES)
|
||||
.map(|_| Fq::random(&mut rng).into_repr())
|
||||
.map(|_| Fq::rand(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use ff::Field;
|
||||
use pairing::bls12_381::*;
|
||||
@@ -8,13 +7,10 @@ use pairing::bls12_381::*;
|
||||
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -30,13 +26,10 @@ fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -52,13 +45,10 @@ fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
|
||||
.map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng)))
|
||||
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -74,12 +64,9 @@ fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -94,12 +81,9 @@ fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use ff::{Field, SqrtField};
|
||||
use pairing::bls12_381::*;
|
||||
@@ -8,13 +7,10 @@ use pairing::bls12_381::*;
|
||||
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -30,13 +26,10 @@ fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -52,13 +45,10 @@ fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
|
||||
.map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng)))
|
||||
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -74,12 +64,9 @@ fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -94,12 +81,9 @@ fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -113,12 +97,9 @@ fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use pairing::bls12_381::*;
|
||||
@@ -8,15 +7,12 @@ use pairing::bls12_381::*;
|
||||
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp1 = Fr::random(&mut rng).into_repr();
|
||||
let mut tmp2 = Fr::random(&mut rng).into_repr();
|
||||
let mut tmp1 = FrRepr::rand(&mut rng);
|
||||
let mut tmp2 = FrRepr::rand(&mut rng);
|
||||
// Shave a few bits off to avoid overflow.
|
||||
for _ in 0..3 {
|
||||
tmp1.div2();
|
||||
@@ -39,14 +35,11 @@ fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let tmp1 = Fr::random(&mut rng).into_repr();
|
||||
let tmp1 = FrRepr::rand(&mut rng);
|
||||
let mut tmp2 = tmp1;
|
||||
// Ensure tmp2 is smaller than tmp1.
|
||||
for _ in 0..10 {
|
||||
@@ -69,14 +62,9 @@ fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||
.map(|_| Fr::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -90,14 +78,9 @@ fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||
.map(|_| Fr::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -112,14 +95,9 @@ fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||
.map(|_| Fr::random(&mut rng).into_repr())
|
||||
.collect();
|
||||
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -134,13 +112,10 @@ fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -156,13 +131,10 @@ fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -178,13 +150,10 @@ fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
|
||||
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
|
||||
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
@@ -200,12 +169,9 @@ fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -220,12 +186,9 @@ fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -238,12 +201,9 @@ fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -258,14 +218,11 @@ fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp = Fr::random(&mut rng);
|
||||
let mut tmp = Fr::rand(&mut rng);
|
||||
tmp.square();
|
||||
tmp
|
||||
})
|
||||
@@ -282,12 +239,9 @@ fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -300,13 +254,10 @@ fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<FrRepr> = (0..SAMPLES)
|
||||
.map(|_| Fr::random(&mut rng).into_repr())
|
||||
.map(|_| Fr::rand(&mut rng).into_repr())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
@@ -4,10 +4,8 @@ mod fq12;
|
||||
mod fq2;
|
||||
mod fr;
|
||||
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use rand::{Rand, SeedableRng, XorShiftRng};
|
||||
|
||||
use group::CurveProjective;
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::{Engine, PairingCurveAffine};
|
||||
|
||||
@@ -15,12 +13,9 @@ use pairing::{Engine, PairingCurveAffine};
|
||||
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect();
|
||||
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -34,12 +29,9 @@ fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect();
|
||||
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::rand(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
@@ -53,16 +45,13 @@ fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
(
|
||||
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@@ -79,16 +68,13 @@ fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<Fq12> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
(
|
||||
G1Affine::from(G1::random(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::random(&mut rng)).prepare(),
|
||||
G1Affine::from(G1::rand(&mut rng)).prepare(),
|
||||
G2Affine::from(G2::rand(&mut rng)).prepare(),
|
||||
)
|
||||
})
|
||||
.map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)]))
|
||||
@@ -106,13 +92,10 @@ fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_full(b: &mut ::test::Bencher) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
|
||||
let v: Vec<(G1, G2)> = (0..SAMPLES)
|
||||
.map(|_| (G1::random(&mut rng), G2::random(&mut rng)))
|
||||
.map(|_| (G1::rand(&mut rng), G2::rand(&mut rng)))
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
extern crate ff;
|
||||
extern crate group;
|
||||
extern crate pairing;
|
||||
extern crate rand_core;
|
||||
extern crate rand_xorshift;
|
||||
extern crate rand;
|
||||
extern crate test;
|
||||
|
||||
mod bls12_381;
|
||||
|
||||
@@ -18,7 +18,7 @@ macro_rules! curve_impl {
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for $affine {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
if self.infinity {
|
||||
write!(f, "{}(Infinity)", $name)
|
||||
} else {
|
||||
@@ -35,7 +35,7 @@ macro_rules! curve_impl {
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for $projective {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "{}", self.into_affine())
|
||||
}
|
||||
}
|
||||
@@ -200,7 +200,7 @@ macro_rules! curve_impl {
|
||||
type Base = $basefield;
|
||||
type Affine = $affine;
|
||||
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
loop {
|
||||
let x = $basefield::random(rng);
|
||||
let greatest = rng.next_u32() % 2 != 0;
|
||||
@@ -622,11 +622,11 @@ macro_rules! curve_impl {
|
||||
pub mod g1 {
|
||||
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
|
||||
use super::g2::G2Affine;
|
||||
use crate::{Engine, PairingCurveAffine};
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||
use rand_core::RngCore;
|
||||
use std::fmt;
|
||||
use {Engine, PairingCurveAffine};
|
||||
|
||||
curve_impl!(
|
||||
"G1",
|
||||
@@ -656,7 +656,7 @@ pub mod g1 {
|
||||
}
|
||||
|
||||
impl fmt::Debug for G1Uncompressed {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
self.0[..].fmt(formatter)
|
||||
}
|
||||
}
|
||||
@@ -766,7 +766,7 @@ pub mod g1 {
|
||||
}
|
||||
|
||||
impl fmt::Debug for G1Compressed {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
self.0[..].fmt(formatter)
|
||||
}
|
||||
}
|
||||
@@ -934,7 +934,7 @@ pub mod g1 {
|
||||
|
||||
#[test]
|
||||
fn g1_generator() {
|
||||
use crate::SqrtField;
|
||||
use SqrtField;
|
||||
|
||||
let mut x = Fq::zero();
|
||||
let mut i = 0;
|
||||
@@ -1291,11 +1291,11 @@ pub mod g1 {
|
||||
pub mod g2 {
|
||||
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
|
||||
use super::g1::G1Affine;
|
||||
use crate::{Engine, PairingCurveAffine};
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||
use rand_core::RngCore;
|
||||
use std::fmt;
|
||||
use {Engine, PairingCurveAffine};
|
||||
|
||||
curve_impl!(
|
||||
"G2",
|
||||
@@ -1325,7 +1325,7 @@ pub mod g2 {
|
||||
}
|
||||
|
||||
impl fmt::Debug for G2Uncompressed {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
self.0[..].fmt(formatter)
|
||||
}
|
||||
}
|
||||
@@ -1451,7 +1451,7 @@ pub mod g2 {
|
||||
}
|
||||
|
||||
impl fmt::Debug for G2Compressed {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
self.0[..].fmt(formatter)
|
||||
}
|
||||
}
|
||||
@@ -1640,7 +1640,7 @@ pub mod g2 {
|
||||
|
||||
#[test]
|
||||
fn g2_generator() {
|
||||
use crate::SqrtField;
|
||||
use SqrtField;
|
||||
|
||||
let mut x = Fq2::zero();
|
||||
let mut i = 0;
|
||||
|
||||
@@ -2225,10 +2225,10 @@ fn test_fq_root_of_unity() {
|
||||
|
||||
#[test]
|
||||
fn fq_field_tests() {
|
||||
crate::tests::field::random_field_tests::<Fq>();
|
||||
crate::tests::field::random_sqrt_tests::<Fq>();
|
||||
crate::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
|
||||
crate::tests::field::from_str_tests::<Fq>();
|
||||
::tests::field::random_field_tests::<Fq>();
|
||||
::tests::field::random_sqrt_tests::<Fq>();
|
||||
::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
|
||||
::tests::field::from_str_tests::<Fq>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2244,7 +2244,7 @@ fn test_fq_ordering() {
|
||||
|
||||
#[test]
|
||||
fn fq_repr_tests() {
|
||||
crate::tests::repr::random_repr_tests::<Fq>();
|
||||
::tests::repr::random_repr_tests::<Fq>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct Fq12 {
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq12 {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ impl Fq12 {
|
||||
}
|
||||
|
||||
impl Field for Fq12 {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
Fq12 {
|
||||
c0: Fq6::random(rng),
|
||||
c1: Fq6::random(rng),
|
||||
@@ -187,6 +187,6 @@ fn test_fq12_mul_by_014() {
|
||||
fn fq12_field_tests() {
|
||||
use ff::PrimeField;
|
||||
|
||||
crate::tests::field::random_field_tests::<Fq12>();
|
||||
crate::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
||||
::tests::field::random_field_tests::<Fq12>();
|
||||
::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct Fq2 {
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq2 {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ impl Fq2 {
|
||||
}
|
||||
|
||||
impl Field for Fq2 {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
Fq2 {
|
||||
c0: Fq::random(rng),
|
||||
c1: Fq::random(rng),
|
||||
@@ -958,7 +958,7 @@ fn test_fq2_mul_nonresidue() {
|
||||
fn fq2_field_tests() {
|
||||
use ff::PrimeField;
|
||||
|
||||
crate::tests::field::random_field_tests::<Fq2>();
|
||||
crate::tests::field::random_sqrt_tests::<Fq2>();
|
||||
crate::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
||||
::tests::field::random_field_tests::<Fq2>();
|
||||
::tests::field::random_sqrt_tests::<Fq2>();
|
||||
::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct Fq6 {
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fq6 {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ impl Fq6 {
|
||||
}
|
||||
|
||||
impl Field for Fq6 {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
fn random<R: RngCore>(rng: &mut R) -> Self {
|
||||
Fq6 {
|
||||
c0: Fq2::random(rng),
|
||||
c1: Fq2::random(rng),
|
||||
@@ -378,6 +378,6 @@ fn test_fq6_mul_by_01() {
|
||||
fn fq6_field_tests() {
|
||||
use ff::PrimeField;
|
||||
|
||||
crate::tests::field::random_field_tests::<Fq6>();
|
||||
crate::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
||||
::tests::field::random_field_tests::<Fq6>();
|
||||
::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
|
||||
}
|
||||
|
||||
@@ -1015,13 +1015,13 @@ fn test_fr_root_of_unity() {
|
||||
|
||||
#[test]
|
||||
fn fr_field_tests() {
|
||||
crate::tests::field::random_field_tests::<Fr>();
|
||||
crate::tests::field::random_sqrt_tests::<Fr>();
|
||||
crate::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
|
||||
crate::tests::field::from_str_tests::<Fr>();
|
||||
::tests::field::random_field_tests::<Fr>();
|
||||
::tests::field::random_sqrt_tests::<Fr>();
|
||||
::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
|
||||
::tests::field::from_str_tests::<Fr>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fr_repr_tests() {
|
||||
crate::tests::repr::random_repr_tests::<Fr>();
|
||||
::tests::repr::random_repr_tests::<Fr>();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
//! An implementation of the BLS12-381 pairing-friendly elliptic curve
|
||||
//! construction.
|
||||
|
||||
mod ec;
|
||||
mod fq;
|
||||
mod fq12;
|
||||
@@ -369,5 +366,5 @@ impl G2Prepared {
|
||||
|
||||
#[test]
|
||||
fn bls12_engine_tests() {
|
||||
crate::tests::engine::engine_tests::<Bls12>();
|
||||
::tests::engine::engine_tests::<Bls12>();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use ff::PrimeFieldRepr;
|
||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||
|
||||
use super::*;
|
||||
use crate::*;
|
||||
use *;
|
||||
|
||||
#[test]
|
||||
fn test_pairing_result_against_relic() {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! A library for working with pairing-friendly curves.
|
||||
|
||||
// `clippy` is a code linting tool for improving code quality by catching
|
||||
// common mistakes or strange code patterns. If the `cargo-clippy` feature
|
||||
// is provided, all compiler warnings are prohibited.
|
||||
@@ -10,11 +8,17 @@
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))]
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
// Force public structures to implement Debug
|
||||
#![deny(missing_debug_implementations)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate ff;
|
||||
extern crate group;
|
||||
extern crate rand_core;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand_xorshift;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
||||
@@ -83,7 +87,7 @@ pub trait Engine: ScalarEngine {
|
||||
>;
|
||||
|
||||
/// Perform final exponentiation of the result of a miller loop.
|
||||
fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk>;
|
||||
fn final_exponentiation(&Self::Fqk) -> Option<Self::Fqk>;
|
||||
|
||||
/// Performs a complete pairing operation `(p, q)`.
|
||||
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
|
||||
|
||||
@@ -2,7 +2,7 @@ use group::{CurveAffine, CurveProjective};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use crate::{Engine, Field, PairingCurveAffine, PrimeField};
|
||||
use {Engine, Field, PairingCurveAffine, PrimeField};
|
||||
|
||||
pub fn engine_tests<E: Engine>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
[package]
|
||||
name = "zcash_client_backend"
|
||||
description = "APIs for creating shielded Zcash light clients"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
authors = [
|
||||
"Jack Grigg <jack@z.cash>",
|
||||
]
|
||||
homepage = "https://github.com/zcash/librustzcash"
|
||||
repository = "https://github.com/zcash/librustzcash"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bech32 = "0.7"
|
||||
ff = { version = "0.5.0", path = "../ff" }
|
||||
bs58 = { version = "0.2", features = ["check"] }
|
||||
ff = { path = "../ff" }
|
||||
hex = "0.3"
|
||||
pairing = { version = "0.15.0", path = "../pairing" }
|
||||
pairing = { path = "../pairing" }
|
||||
protobuf = "2"
|
||||
subtle = "2"
|
||||
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||
zcash_primitives = { path = "../zcash_primitives" }
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen-pure = "2"
|
||||
|
||||
[dev-dependencies]
|
||||
rand_core = "0.5"
|
||||
rand_os = "0.2"
|
||||
rand_xorshift = "0.2"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# zcash_client_backend
|
||||
|
||||
This library contains Rust structs and traits for creating shielded Zcash light
|
||||
clients.
|
||||
This library contains Rust structs and traits for creating shielded Zcash light clients.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
//! Zcash global and per-network constants.
|
||||
|
||||
pub mod mainnet;
|
||||
pub mod regtest;
|
||||
pub mod testnet;
|
||||
pub mod regtest;
|
||||
|
||||
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,9 +1,7 @@
|
||||
//! Constants for the Zcash main network.
|
||||
|
||||
/// The mainnet coin type for ZEC, as defined by [SLIP 44].
|
||||
///
|
||||
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||
pub const COIN_TYPE: u32 = 133;
|
||||
pub const COIN_TYPE: u32 = 141;
|
||||
|
||||
/// The HRP for a Bech32-encoded mainnet [`ExtendedSpendingKey`].
|
||||
///
|
||||
@@ -25,6 +23,16 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews";
|
||||
///
|
||||
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
|
||||
///
|
||||
/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
|
||||
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress
|
||||
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
|
||||
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,45 +1,38 @@
|
||||
//! # Regtest constants
|
||||
//!
|
||||
//! `regtest` is a `zcashd`-specific environment used for local testing. They mostly reuse
|
||||
//! the testnet constants.
|
||||
//! These constants are defined in [the `zcashd` codebase].
|
||||
//! [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L482-L496
|
||||
|
||||
/// The regtest cointype reuses the testnet cointype
|
||||
/// The testnet coin type for ZEC, as defined by [SLIP 44].
|
||||
///
|
||||
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||
pub const COIN_TYPE: u32 = 1;
|
||||
|
||||
/// The HRP for a Bech32-encoded regtest [`ExtendedSpendingKey`].
|
||||
/// The HRP for a Bech32-encoded testnet [`ExtendedSpendingKey`].
|
||||
///
|
||||
/// It is defined in [the `zcashd` codebase].
|
||||
/// Defined in [ZIP 32].
|
||||
///
|
||||
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
|
||||
/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L496
|
||||
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
|
||||
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-regtest";
|
||||
|
||||
/// The HRP for a Bech32-encoded regtest [`ExtendedFullViewingKey`].
|
||||
/// The HRP for a Bech32-encoded testnet [`ExtendedFullViewingKey`].
|
||||
///
|
||||
/// It is defined in [the `zcashd` codebase].
|
||||
/// Defined in [ZIP 32].
|
||||
///
|
||||
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
|
||||
/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L494
|
||||
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
|
||||
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewregtestsapling";
|
||||
|
||||
/// The HRP for a Bech32-encoded regtest [`PaymentAddress`].
|
||||
/// The HRP for a Bech32-encoded testnet [`PaymentAddress`].
|
||||
///
|
||||
/// It is defined in [the `zcashd` codebase].
|
||||
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
|
||||
///
|
||||
/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
|
||||
/// [the `zcashd` codebase]: https://github.com/zcash/zcash/blob/128d863fb8be39ee294fda397c1ce3ba3b889cb2/src/chainparams.cpp#L493
|
||||
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress
|
||||
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
|
||||
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zregtestsapling";
|
||||
|
||||
/// The prefix for a Base58Check-encoded regtest [`TransparentAddress::PublicKey`].
|
||||
/// Same as the testnet prefix.
|
||||
/// 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 regtest [`TransparentAddress::Script`].
|
||||
/// Same as the testnet prefix.
|
||||
/// 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];
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! Constants for the Zcash test network.
|
||||
|
||||
/// The testnet coin type for ZEC, as defined by [SLIP 44].
|
||||
///
|
||||
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||
@@ -25,6 +23,16 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling";
|
||||
///
|
||||
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
|
||||
///
|
||||
/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
|
||||
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress
|
||||
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
|
||||
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];
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
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,14 +2,17 @@
|
||||
//!
|
||||
//! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`]
|
||||
//! module.
|
||||
//!
|
||||
//! [`constants`]: crate::constants
|
||||
|
||||
use bech32::{self, Error, FromBase32, ToBase32};
|
||||
use bs58::{self, decode::DecodeError};
|
||||
use pairing::bls12_381::Bls12;
|
||||
use std::io::{self, Write};
|
||||
use zcash_primitives::{
|
||||
primitives::PaymentAddress,
|
||||
jubjub::edwards,
|
||||
primitives::{Diversifier, PaymentAddress},
|
||||
};
|
||||
use zcash_primitives::{
|
||||
legacy::TransparentAddress,
|
||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||
JUBJUB,
|
||||
};
|
||||
@@ -112,11 +115,10 @@ pub fn decode_extended_full_viewing_key(
|
||||
/// 0xbc, 0xe5,
|
||||
/// ]);
|
||||
///
|
||||
/// let pa = PaymentAddress::from_parts(
|
||||
/// Diversifier([0u8; 11]),
|
||||
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
/// )
|
||||
/// .unwrap();
|
||||
/// let pa = PaymentAddress {
|
||||
/// diversifier: Diversifier([0u8; 11]),
|
||||
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
|
||||
@@ -124,7 +126,10 @@ pub fn decode_extended_full_viewing_key(
|
||||
/// );
|
||||
/// ```
|
||||
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
|
||||
bech32_encode(hrp, |w| w.write_all(&addr.to_bytes()))
|
||||
bech32_encode(hrp, |w| {
|
||||
w.write_all(&addr.diversifier.0)?;
|
||||
addr.pk_d.write(w)
|
||||
})
|
||||
}
|
||||
|
||||
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
|
||||
@@ -150,11 +155,10 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
|
||||
/// 0xbc, 0xe5,
|
||||
/// ]);
|
||||
///
|
||||
/// let pa = PaymentAddress::from_parts(
|
||||
/// Diversifier([0u8; 11]),
|
||||
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
/// )
|
||||
/// .unwrap();
|
||||
/// let pa = PaymentAddress {
|
||||
/// diversifier: Diversifier([0u8; 11]),
|
||||
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// decode_payment_address(
|
||||
@@ -166,16 +170,127 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
|
||||
/// ```
|
||||
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
|
||||
bech32_decode(hrp, s, |data| {
|
||||
if data.len() != 43 {
|
||||
let mut diversifier = Diversifier([0; 11]);
|
||||
diversifier.0.copy_from_slice(&data[0..11]);
|
||||
// Check that the diversifier is valid
|
||||
if diversifier.g_d::<Bls12>(&JUBJUB).is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut bytes = [0; 43];
|
||||
bytes.copy_from_slice(&data);
|
||||
PaymentAddress::<Bls12>::from_bytes(&bytes, &JUBJUB)
|
||||
edwards::Point::<Bls12, _>::read(&data[11..], &JUBJUB)
|
||||
.ok()?
|
||||
.as_prime_order(&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)]
|
||||
mod tests {
|
||||
use pairing::bls12_381::Bls12;
|
||||
@@ -185,95 +300,11 @@ mod tests {
|
||||
use zcash_primitives::{
|
||||
jubjub::edwards,
|
||||
primitives::{Diversifier, PaymentAddress},
|
||||
zip32::ExtendedSpendingKey,
|
||||
};
|
||||
|
||||
use super::{
|
||||
decode_extended_full_viewing_key, decode_extended_spending_key, decode_payment_address,
|
||||
encode_extended_full_viewing_key, encode_extended_spending_key, encode_payment_address,
|
||||
};
|
||||
use super::{decode_payment_address, encode_payment_address};
|
||||
use crate::constants;
|
||||
|
||||
#[test]
|
||||
fn extended_spending_key() {
|
||||
let extsk = ExtendedSpendingKey::master(&[0; 32][..]);
|
||||
|
||||
let encoded_main = "secret-extended-key-main1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs87qvlj";
|
||||
let encoded_test = "secret-extended-key-test1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsvzyw8j";
|
||||
|
||||
assert_eq!(
|
||||
encode_extended_spending_key(
|
||||
constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
&extsk
|
||||
),
|
||||
encoded_main
|
||||
);
|
||||
assert_eq!(
|
||||
decode_extended_spending_key(
|
||||
constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
encoded_main
|
||||
)
|
||||
.unwrap(),
|
||||
Some(extsk.clone())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
encode_extended_spending_key(
|
||||
constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
&extsk
|
||||
),
|
||||
encoded_test
|
||||
);
|
||||
assert_eq!(
|
||||
decode_extended_spending_key(
|
||||
constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
encoded_test
|
||||
)
|
||||
.unwrap(),
|
||||
Some(extsk)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extended_full_viewing_key() {
|
||||
let extfvk = (&ExtendedSpendingKey::master(&[0; 32][..])).into();
|
||||
|
||||
let encoded_main = "zxviews1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsxmansf";
|
||||
let encoded_test = "zxviewtestsapling1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs8evfkz";
|
||||
|
||||
assert_eq!(
|
||||
encode_extended_full_viewing_key(
|
||||
constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
|
||||
&extfvk
|
||||
),
|
||||
encoded_main
|
||||
);
|
||||
assert_eq!(
|
||||
decode_extended_full_viewing_key(
|
||||
constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
|
||||
encoded_main
|
||||
)
|
||||
.unwrap(),
|
||||
Some(extfvk.clone())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
encode_extended_full_viewing_key(
|
||||
constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
|
||||
&extfvk
|
||||
),
|
||||
encoded_test
|
||||
);
|
||||
assert_eq!(
|
||||
decode_extended_full_viewing_key(
|
||||
constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
|
||||
encoded_test
|
||||
)
|
||||
.unwrap(),
|
||||
Some(extfvk)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payment_address() {
|
||||
let rng = &mut XorShiftRng::from_seed([
|
||||
@@ -281,11 +312,10 @@ mod tests {
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let addr = PaymentAddress::from_parts(
|
||||
Diversifier([0u8; 11]),
|
||||
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
)
|
||||
.unwrap();
|
||||
let addr = PaymentAddress {
|
||||
diversifier: Diversifier([0u8; 11]),
|
||||
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
};
|
||||
|
||||
let encoded_main =
|
||||
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
|
||||
@@ -326,11 +356,10 @@ mod tests {
|
||||
0xbc, 0xe5,
|
||||
]);
|
||||
|
||||
let addr = PaymentAddress::from_parts(
|
||||
Diversifier([1u8; 11]),
|
||||
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
)
|
||||
.unwrap();
|
||||
let addr = PaymentAddress {
|
||||
diversifier: Diversifier([1u8; 11]),
|
||||
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||
};
|
||||
|
||||
let encoded_main =
|
||||
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr);
|
||||
|
||||
@@ -5,10 +5,6 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey};
|
||||
/// Derives the ZIP 32 [`ExtendedSpendingKey`] for a given coin type and account from the
|
||||
/// given seed.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `seed` is shorter than 32 bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -17,10 +13,6 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey};
|
||||
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
|
||||
/// ```
|
||||
pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendingKey {
|
||||
if seed.len() < 32 {
|
||||
panic!("ZIP 32 seeds MUST be at least 32 bytes");
|
||||
}
|
||||
|
||||
ExtendedSpendingKey::from_path(
|
||||
&ExtendedSpendingKey::master(&seed),
|
||||
&[
|
||||
@@ -30,14 +22,3 @@ 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,15 +3,9 @@
|
||||
//! `zcash_client_backend` contains Rust structs and traits for creating shielded Zcash
|
||||
//! light clients.
|
||||
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
|
||||
pub mod constants;
|
||||
mod decrypt;
|
||||
pub mod encoding;
|
||||
pub mod keys;
|
||||
pub mod proto;
|
||||
pub mod wallet;
|
||||
pub mod welding_rig;
|
||||
|
||||
pub use decrypt::{decrypt_transaction, DecryptedOutput};
|
||||
|
||||
@@ -134,11 +134,12 @@ pub fn scan_block(
|
||||
// mutable references to wtxs for too long.
|
||||
let mut block_witnesses: Vec<_> = wtxs
|
||||
.iter_mut()
|
||||
.flat_map(|tx| {
|
||||
.map(|tx| {
|
||||
tx.shielded_outputs
|
||||
.iter_mut()
|
||||
.map(|output| &mut output.witness)
|
||||
})
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
for to_scan in tx.outputs.into_iter().enumerate() {
|
||||
@@ -185,7 +186,8 @@ pub fn scan_block(
|
||||
mod tests {
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
||||
use pairing::bls12_381::{Bls12, Fr};
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use rand_core::RngCore;
|
||||
use rand_os::OsRng;
|
||||
use zcash_primitives::{
|
||||
jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform},
|
||||
merkle_tree::CommitmentTree,
|
||||
@@ -252,8 +254,8 @@ mod tests {
|
||||
// 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(),
|
||||
g_d: to.diversifier.g_d::<Bls12>(&JUBJUB).unwrap(),
|
||||
pk_d: to.pk_d.clone(),
|
||||
value: value.into(),
|
||||
r: Fs::random(&mut rng),
|
||||
};
|
||||
|
||||
27
zcash_client_sqlite/Cargo.toml
Normal file
27
zcash_client_sqlite/Cargo.toml
Normal file
@@ -0,0 +1,27 @@
|
||||
[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 = []
|
||||
60
zcash_client_sqlite/README.md
Normal file
60
zcash_client_sqlite/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# 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.
|
||||
|
||||
63
zcash_client_sqlite/src/address.rs
Normal file
63
zcash_client_sqlite/src/address.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
//! 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,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
469
zcash_client_sqlite/src/chain.rs
Normal file
469
zcash_client_sqlite/src/chain.rs
Normal file
@@ -0,0 +1,469 @@
|
||||
//! 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);
|
||||
}
|
||||
}
|
||||
132
zcash_client_sqlite/src/error.rs
Normal file
132
zcash_client_sqlite/src/error.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
303
zcash_client_sqlite/src/init.rs
Normal file
303
zcash_client_sqlite/src/init.rs
Normal file
@@ -0,0 +1,303 @@
|
||||
//! 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);
|
||||
}
|
||||
}
|
||||
262
zcash_client_sqlite/src/lib.rs
Normal file
262
zcash_client_sqlite/src/lib.rs
Normal file
@@ -0,0 +1,262 @@
|
||||
//! *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();
|
||||
}
|
||||
}
|
||||
201
zcash_client_sqlite/src/query.rs
Normal file
201
zcash_client_sqlite/src/query.rs
Normal file
@@ -0,0 +1,201 @@
|
||||
//! 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());
|
||||
}
|
||||
}
|
||||
500
zcash_client_sqlite/src/scan.rs
Normal file
500
zcash_client_sqlite/src/scan.rs
Normal file
@@ -0,0 +1,500 @@
|
||||
//! 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);
|
||||
}
|
||||
}
|
||||
665
zcash_client_sqlite/src/transact.rs
Normal file
665
zcash_client_sqlite/src/transact.rs
Normal file
@@ -0,0 +1,665 @@
|
||||
//! 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
4
zcash_history/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
.idea
|
||||
@@ -1,14 +0,0 @@
|
||||
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.
|
||||
@@ -1,17 +0,0 @@
|
||||
[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" }
|
||||
@@ -1,201 +0,0 @@
|
||||
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.
|
||||
@@ -1,25 +0,0 @@
|
||||
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.
|
||||
@@ -1,26 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,2 +0,0 @@
|
||||
// dummy example
|
||||
pub fn main() {}
|
||||
@@ -1,86 +0,0 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
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()
|
||||
);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
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(..)"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
//! 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user