297 Commits
sdl ... paper

Author SHA1 Message Date
Duke Leto
aabbc856c7 Merge pull request #3 from gilardh/master
paper librustzcash sync
2020-03-14 08:09:30 -07:00
ebfull
d9ac7bd19b Merge pull request #214 from ebfull/manifest-updates
Update manifest for zcash_history
2020-03-04 10:05:44 -07:00
Sean Bowe
0f0d84e9ca Update manifest for zcash_history. 2020-03-04 09:28:56 -07:00
ebfull
49459d030f Merge pull request #213 from str4d/zip-221-change
Update zcash_history with ZIP 221 change
2020-03-04 09:04:14 -07:00
Jack Grigg
cafbe61eba Commit to number of Sapling transactions instead of shielded transactions 2020-03-04 18:25:35 +13:00
str4d
fc247fc898 Merge pull request #212 from ebfull/depends-updates-2
Dependency updates
2020-03-04 15:11:42 +13:00
Sean Bowe
46e88a5e69 cargo fmt 2020-03-03 18:14:44 -07:00
Sean Bowe
5e1a2f9d3f Rename zcash_mmr to zcash_history.
This crate will contain all chain history logic.
2020-03-03 18:14:40 -07:00
Sean Bowe
9379eec1b8 Bring zcash_mmr into workspace. 2020-03-03 18:04:18 -07:00
Sean Bowe
4f0f10a53c Add 'zcash_history/' from commit 'e2c131fdc308265adcab774e54d4d5804c23b368'
git-subtree-dir: zcash_history
git-subtree-mainline: be0ee9eb82
git-subtree-split: e2c131fdc3
2020-03-03 17:51:19 -07:00
Sean Bowe
be0ee9eb82 Changes to Cargo.lock to reflect upstream crate changes. 2020-03-03 17:49:24 -07:00
Sean Bowe
b52e4aac3a Fix Field::random method declarations. 2020-03-03 17:47:29 -07:00
Sean Bowe
c49aaceb5d Merge commit 'f337eb1f5c67d9305b1fb4fb35507bb50817259b' into depends-updates-2 2020-03-03 17:46:59 -07:00
Sean Bowe
f337eb1f5c Squashed 'bellman/' changes from 4272cfa..2279da4
2279da4 Merge pull request #38 from debris/docs
2e57190 Remove documentation entry from Cargo.toml
346d540 bellman 0.2.0
8d79665 Merge pull request #93 from defuse/qed-it-lrz
f50079f Crate docs
701cb2b Update READMEs
ccf1ee9 CI: Check intra-doc links
ddd390a Add READMEs to Cargo.toml files
54d3122 Add missing cs.is_satisfied() to bellman test
52bf23c Fix build warnings
581ad35 boolean: adds tests for alloc_conditionally
0403396 blake2s: adds test vectors from go-jubjub
9f24e47 Fix blake2s test data length assertion.
42d5b3b Add blake2s test vectors for varying sizes from go-jubjub
b2597de pedersen_hash: removes debug prints
c903fad pedersen hashes: example of size limit bug
bc697c1 bellman: Fix compile errors without multicore feature
a4e5df9 Upgrade to hex-literal 0.2
c063509 Migrate bellman to crossbeam 0.7
1775843 Take self directly in into_* functions
614d784 Rename into_ -> to_ where &self is used.
08664b1 Address various clippy warnings/errors in bellman
bb11ef2 cargo fmt
cff2e2f cargo fix --edition-idioms for bellman
dc2a280 Add edition = 2018
1a2bc19 cargo fmt
ad37878 cargo fix --edition for bellman
e73d1a2 cargo fmt bellman
dfb86fc Move generic circuit gadgets into bellman
9b3d766 Migrate to rand 0.7
055280f Migrate ff, group, pairing, and bellman to rand 0.6
533d586 Migrate bellman to rand 0.5
bfa9aaf Merge pull request #61 from rex4539/fix-typos
3dd8490 Place bellman multicore operations behind a (default) feature flag
955e679 Merge pull request #46 from str4d/ff-traits
d4ddaa9 Fix typos
12f93f2 Add ff and group crates to Cargo workspace
2e35a32 Update sapling-crypto crate to use ff crate
2019e63 Update workspace after pulling in external crates

git-subtree-dir: bellman
git-subtree-split: 2279da422ca9d7b83e84cb85018c713976b873e5
2020-03-03 17:46:04 -07:00
Sean Bowe
805aa45f9e Merge commit '05a9f3360f7fbd298f3e6f8f7e55e9426d04f2f0' into depends-updates-2 2020-03-03 17:45:49 -07:00
Sean Bowe
05a9f3360f Squashed 'pairing/' changes from 3d41ee5..3870f11
3870f11 Bump version and ff dependency
e8657fa Merge pull request #108 from kigawas/fix-rngcore-sized
7fddfc7 Update Cargo.toml
df0217c bump group version
cbc1917 bump version
98133d9 fix RngCore Sized
583c744 pairing 0.15.0
0de7279 Fix pairing benchmarks
dd2fbb3 Crate docs
97c45f2 Update READMEs
9aef129 CI: Check intra-doc links
f552b49 Add READMEs to Cargo.toml files
8c59b4a cargo fmt
33993f4 cargo fix --edition-idioms for pairing
36788f3 Add edition = 2018
df88a3f cargo fmt
d9a1288 cargo fix --edition for pairing
013fd03 cargo fmt pairing
5635612 Fix clippy linter errors in pairing crate
ba40e2f Use modern clippy linter syntax
fa4eaeb Migrate to rand 0.7
1d00588 Migrate ff, group, pairing, and bellman to rand 0.6
95a749b Migrate pairing to rand 0.5
01e7212 Add ff and group crates to Cargo workspace

git-subtree-dir: pairing
git-subtree-split: 3870f1172f33aa85c49426fec6c6e7040b4889f9
2020-03-03 17:44:53 -07:00
Sean Bowe
31d887c943 Merge commit 'd7f78db121ee6f720e97401ded0c9c597d1a4857' into depends-updates-2 2020-03-03 17:44:28 -07:00
Sean Bowe
d7f78db121 Squashed 'ff/' changes from 661558e..ddff465
ddff465 Bump version and dependency on ff_derive
25d11d6 Bump version
95e2723 Bump version to 0.5.1
f76872a Add ?Sized to RngCore trait bounds (#14)
09a32b1 ff 0.5.0
32543ab Crate docs
22031dc Update READMEs
658fe6d CI: Check intra-doc links
35f5026 Add READMEs to Cargo.toml files
6804225 Migrate ff_derive to proc-macro2 1.0
b9a79ce cargo fmt
82574c2 cargo fix --edition-idioms for ff
3b0cf72 Add edition = 2018
8a2b51b Replace try! macro
40fc9ba cargo fix --edition for ff
22c67f3 cargo fmt
312141c Clarify masking of bits in Field::random impls
89a68e1 Migrate to rand 0.7
58415fb Migrate ff, group, pairing, and bellman to rand 0.6
8b6e6b1 Migrate ff to rand_core 0.3 (used by rand 0.5)

git-subtree-dir: ff
git-subtree-split: ddff4658ddd7496bb29cc636c391b7aaaca24673
2020-03-03 17:43:16 -07:00
str4d
d4bae99cb7 Merge pull request #204 from str4d/proving-api-ux-refactors
UX refactors for proving-related APIs
2020-02-16 18:56:46 +00:00
Jack Grigg
2d30c29d06 Pass &impl TxProver to Builder::build
This allows the caller to build multiple transactions with a single
proving backend.
2020-02-08 00:37:31 +00:00
Jack Grigg
76e0f658c1 Rename CommitmentTreeWitness -> MerklePath 2020-02-08 00:36:40 +00:00
Jack Grigg
3a3008caf9 Pass CommitmentTreeWitness directly into Builder::add_sapling_spend
This is more likely to be the data that the caller has available, and
is all we need now that a CommitmentTreeWitness can compute its root.
2020-02-07 17:31:38 +00:00
Jack Grigg
8a210ec271 CommitmentTreeWitness::root(leaf) 2020-02-07 17:21:59 +00:00
Jack Grigg
2064d1c801 Refactor zcash_primitives::merkle_tree::CommitmentTreeWitness
- The internal Option wrapper was an unnecessary leftover from when this
  code was directly inside the prover, where Some(x) represents an
  assigned variable.
- CommitmentTreeWitness::from_slice_with_depth is more idiomatic Rust.
2020-02-07 00:06:57 +00:00
str4d
ee32f7facb Merge pull request #198 from huitseeker/warts
Addressing some trivial Rust option/iterator lints
2020-02-04 18:18:09 +00:00
str4d
903c02b991 Merge pull request #202 from str4d/coverage-build-fix
CI: Use stable Rust release for code coverage
2020-02-03 19:05:34 +00:00
Jack Grigg
1f38523fff CI: Use stable Rust release for code coverage
Fixes an issue where cargo-tarpaulin failed to build the crate on
1.37.0, but the crate itself builds fine with that version.
2020-02-03 18:08:56 +00:00
François Garillot
865275e2a2 Correcting some trivial Rust option/iterator warts 2020-01-29 13:53:05 -08:00
str4d
18aceea225 Merge pull request #50 from zancas/mv_tests_extract_data
Mv tests extract data
2019-12-12 17:46:38 +00:00
zancas
0aa127d7b1 refactor to manage test code and data in separate modules 2019-12-11 23:58:13 +00:00
str4d
8626cc7f26 Merge pull request #186 from str4d/multicore-feature-flag
Pass bellman's multicore feature flag through to zcash_proofs
2019-12-11 20:04:45 +00:00
Jack Grigg
eed7e8199f Pass bellman's multicore feature flag through to zcash_proofs
This enables someone using zcash_proofs to disable multicore.
2019-12-11 18:46:47 +00:00
str4d
c3f9e2d73c Merge pull request #180 from str4d/mmr-cleanups
MMR API cleanups
2019-12-05 16:35:28 +00:00
Jack Grigg
cca1670248 Clarify length of return buffer for appended leaves 2019-12-05 14:33:03 +00:00
Jack Grigg
f1619f896c Clearer variable names in MMR tests 2019-12-05 14:29:08 +00:00
Jack Grigg
edcd884fe8 Simplify short array copies 2019-12-05 14:24:31 +00:00
Jack Grigg
583a04b4de Pass array references correctly in MMR tests 2019-12-05 14:23:59 +00:00
Jack Grigg
573510115d Clean up remainder of MMR code 2019-12-05 11:11:32 +00:00
Jack Grigg
8ad33e50a6 Use explicit sizes for pointers to arrays in FFI 2019-12-05 11:09:55 +00:00
Jack Grigg
fb8c73c950 Use iterators in construct_mmr_tree 2019-12-05 11:06:26 +00:00
Nikolay Volf
9ea0427678 MMR trees API (#118)
The main design goals of this mmr implementation were

1. Avoid database 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).

2. Reuse as much code/logic between rust and c++ clients.

3. Close to zero memory consumption.
2019-12-04 21:24:26 +00:00
ebfull
dd112813a1 Merge pull request #174 from str4d/152-nu-awareness
Network upgrade awareness
2019-12-04 10:42:16 -07:00
str4d
0907adf608 Merge pull request #176 from str4d/156-scan_transaction
zcash_client_backend::decrypt_transaction
2019-12-03 23:15:17 +00:00
Nikolay Volf
e2c131fdc3 Merge pull request #5 from NikVolf/review-fixes
Review and other fixes
2019-11-29 01:10:27 -08:00
NikVolf
9059f53873 fix review notes and other issues 2019-11-28 15:36:39 +03:00
NikVolf
26be46573e add hash of the node method 2019-11-28 10:31:16 +03:00
str4d
7820548ea3 Merge pull request #173 from gregdhill/bvk-to-cv_sum
rename bvk to cv_sum
2019-11-27 20:42:54 +00:00
Jack Grigg
d5ed684701 zcash_client_backend::decrypt_transaction 2019-11-27 15:45:13 +00:00
Jack Grigg
e6a8630b35 impl TryFrom<u32> for BranchId 2019-11-27 13:18:31 +00:00
Jack Grigg
cd326f2b6a Consensus parameters for network upgrades 2019-11-27 13:18:30 +00:00
Jack Grigg
c24024b8e1 Create a consensus::BranchId enum for type safety
Includes all currently-known consensus branch IDs for the Zcash network.
2019-11-25 21:04:20 +00:00
Gregory Hill
7eb4a6d1d9 rename bvk to cv_sum
Signed-off-by: Gregory Hill <gregorydhill@outlook.com>
2019-11-25 14:28:19 +00:00
str4d
141821d3c1 Merge pull request #157 from adityapk00/transparent_compile_fix
Compile Fix: Add missing sighash declaration
2019-11-14 19:03:59 +00:00
Aditya Kulkarni
219391ac92 Add missing sighash declaration 2019-11-14 09:54:07 -08:00
str4d
67d700feab Merge pull request #107 from str4d/transaction-builder-transparent-inputs
Add P2PKH transparent input support to transaction::Builder
2019-11-13 22:15:55 +00:00
str4d
ac983b7612 Merge pull request #149 from str4d/ffi-cleanup
FFI cleanup
2019-11-13 19:24:22 +00:00
Jack Grigg
8d6f882510 Renames after moves 2019-11-13 19:21:47 +00:00
Jack Grigg
8d967c8609 Move transparent input pushing onto TransparentInputs 2019-11-13 19:20:09 +00:00
Jack Grigg
d9d50b98ab Move transparent signing onto TransparentInputs 2019-11-13 19:12:55 +00:00
str4d
88e02329eb Merge pull request #129 from adityapk00/regtest_constants
Add regtest constants
2019-11-13 13:54:55 +00:00
Jack Grigg
60eac4e8b7 Escape non-link square brackets in comments 2019-11-11 10:29:36 +00:00
Jack Grigg
4ae238ea1f librustzcash crate doc 2019-11-11 10:21:38 +00:00
str4d
8d7f27ffb9 Merge pull request #154 from zcash/codecov-action
Move code coverage from Travis CI to Actions
2019-11-07 22:01:15 +00:00
str4d
2ecdbc2036 Merge pull request #153 from str4d/doc-link-fixes
Doc link fixes
2019-11-07 21:57:35 +00:00
Jack Grigg
bb39037798 Move code coverage from Travis CI to Actions 2019-11-07 18:16:27 +00:00
Jack Grigg
7be66ad8f8 Rename Actions workflow 2019-11-07 17:40:41 +00:00
Jack Grigg
7fda177da8 Doc link fixes 2019-11-07 17:33:22 +00:00
Jack Grigg
d1bc61800c Move Sprout proof logic into zcash_proofs 2019-11-06 08:58:42 +00:00
Jack Grigg
de5943aea4 Ignore clippy::not_unsafe_ptr_arg_deref lint 2019-11-06 07:51:46 +00:00
Jack Grigg
8651bb41ce Clean up librustzcash imports 2019-11-06 07:51:45 +00:00
Jack Grigg
9cb8c0b3c4 Pull librustzcash.h documentation into crate 2019-11-06 07:51:44 +00:00
Jack Grigg
7181d603be Explicitly pass [u8; 32] into read_fr and read_fs 2019-11-06 07:51:43 +00:00
Jack Grigg
5d03619466 Rename read_le to read_fr to match read_fs 2019-11-06 07:51:42 +00:00
Jack Grigg
7722b1a50b Inline write_le 2019-11-06 07:51:41 +00:00
Jack Grigg
0028048800 Use C calling convention for librustzcash
The Rust-to-C++ interface speaks the C ABI. The "system" ABI happens to
be equivalent to the C ABI on the platforms we currently target (in
particular, we don't target win32 with an x86 architecture, which would
use the stdcall ABI).
2019-11-06 07:51:40 +00:00
str4d
720ee6476f Merge pull request #144 from str4d/warning-fix
Fix rand_os warning in tests
2019-11-04 23:03:07 +00:00
str4d
5823a695e2 Merge pull request #139 from adityapk00/tx_compare
Implement sorting for TxIDs
2019-11-04 16:29:59 +00:00
Jack Grigg
bc35a62e10 Remove rand_os dependency 2019-10-31 18:57:52 +00:00
str4d
40d22ea2cc Merge pull request #135 from NikVolf/extern-purge
Remove unneeded "extern crate"
2019-10-31 17:12:26 +00:00
Jack Grigg
ac4acfa26e Fix rand_os warning in tests 2019-10-31 16:36:54 +00:00
Aditya Kulkarni
4d290e7a32 Add regtest constants 2019-10-14 22:20:58 -07:00
Aditya Kulkarni
b479981689 Implement clone for some structs 2019-10-15 17:47:53 +13:00
Aditya Kulkarni
885e09a82f Create OutPoint from hash and n 2019-10-15 17:47:23 +13:00
Aditya Kulkarni
fab9160b26 transparent inputs should use serialize_der() 2019-10-15 17:46:33 +13:00
Aditya Kulkarni
5a177eea27 Make mod serialize public 2019-10-15 17:42:49 +13:00
Jack Grigg
3425eabda2 transaction::Builder::add_transparent_input() 2019-10-15 17:42:46 +13:00
Jack Grigg
70857b0eed Pass Script to signature_hash by reference 2019-10-15 17:32:34 +13:00
Jack Grigg
42b7f328fb legacy::Script::address
This is the counterpart to legacy::TransparentAddress::script.
2019-10-15 17:32:34 +13:00
Aditya Kulkarni
cd1083e086 Implememt sorting for TxIDs 2019-10-14 13:40:08 -07:00
Nikolay Volf
71c6188bf8 Merge pull request #2 from NikVolf/review-fixes
Address offline review
2019-10-11 09:48:06 +03:00
NikVolf
acad37924b test updates 2019-10-11 09:14:19 +03:00
NikVolf
481e43689c use NodeData::combine 2019-10-11 09:04:04 +03:00
NikVolf
f24ec04340 add is_empty 2019-10-11 09:00:47 +03:00
NikVolf
b9bfc07146 style fixes 2019-10-11 08:58:59 +03:00
NikVolf
3082593fcc add intented use of the api 2019-10-11 08:50:44 +03:00
NikVolf
6b7a3dec9c store generated as vec 2019-10-11 08:44:20 +03:00
NikVolf
e701687b69 various small fixes 2019-10-11 08:28:50 +03:00
NikVolf
29b8d7a756 serialization roundtrip test 2019-10-11 08:18:36 +03:00
NikVolf
cb818ecbe3 serialization sizes notice 2019-10-11 08:04:42 +03:00
NikVolf
9d4412103b more clear leaf_count 2019-10-11 08:00:07 +03:00
NikVolf
84dc3bf73c license notice 2019-10-11 07:58:29 +03:00
str4d
2cd8a7f4d2 Merge pull request #114 from str4d/compact-blocks
CompactBlock parsing and scanning
2019-10-10 08:11:50 +13:00
Jack Grigg
274f860202 Actions: Build before formatting check
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.
2019-10-09 14:15:07 +13:00
Jack Grigg
e9f94119bc Store witness inside WalletShieldedOutput 2019-10-09 14:12:30 +13:00
Jack Grigg
b44653e686 Move cmu and epk parsing onto CompactOutput struct 2019-10-09 14:12:30 +13:00
Jack Grigg
7fa51e38c0 Travis CI: Build before formatting check
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.
2019-10-09 14:12:30 +13:00
Jack Grigg
1e2bc7f65c Test nullifiers in constant time
Checking for spent notes in a block is still not completely constant
time, due to filtering out negative results of the constant-time
comparison.

Part of #84.
2019-10-09 14:12:27 +13:00
Jack Grigg
2bbd25b36b Add prevHash field to CompactBlock
This enables basic verification of chain validity when CompactBlocks are
received without the full header.
2019-10-09 14:11:14 +13:00
Jack Grigg
a3b85b8fe6 Compute and store BlockHash inside BlockHeader 2019-10-09 14:11:14 +13:00
Jack Grigg
b66ac11775 Update new witnesses with subsequent transactions in the same block 2019-10-09 14:11:14 +13:00
Jack Grigg
e746f7b6f9 Add tx index within block to WalletTx struct 2019-10-09 14:11:14 +13:00
Jack Grigg
3b9dfc1e0b Detect change notes while scanning blocks 2019-10-09 14:11:14 +13:00
Jack Grigg
fb9e9bb12f Check for spent notes while scanning blocks 2019-10-09 14:11:14 +13:00
Jack Grigg
2b71121681 Return the entire note and recipient address when scanning an output 2019-10-09 14:11:14 +13:00
Jack Grigg
f899ecfce5 Increment the commitment tree and witnesses while scanning blocks 2019-10-09 14:11:14 +13:00
Jack Grigg
591b1fc28f Parse compact blocks to find wallet transactions 2019-10-09 14:11:12 +13:00
Jack Grigg
af7e263bcc Build protobufs for compact formats 2019-10-09 14:09:41 +13:00
str4d
a57dc7f478 Merge pull request #138 from str4d/release-0.1.0
Release 0.1.0
2019-10-09 11:11:58 +13:00
NikVolf
7844394364 Remove unneeded extern crate 2019-10-08 16:12:14 +03:00
Jack Grigg
98731c8374 librustzcash 0.2.0 2019-10-08 17:43:33 +13:00
Jack Grigg
b0ba7fe4d2 zcash_client_backend 0.1.0 2019-10-08 17:43:33 +13:00
Jack Grigg
d6bc2fe0af zcash_proofs 0.1.0 2019-10-08 17:43:33 +13:00
Jack Grigg
8b08528bb0 zcash_primitives 0.1.0 2019-10-08 17:43:33 +13:00
Jack Grigg
25558893ab bellman 0.2.0 2019-10-08 17:43:33 +13:00
Jack Grigg
68cada53cf pairing 0.15.0 2019-10-08 17:43:33 +13:00
Jack Grigg
aa37783c37 group 0.2.0 2019-10-08 17:43:33 +13:00
Jack Grigg
216f0f62a0 ff 0.5.0 2019-10-08 17:43:33 +13:00
Jack Grigg
80d339848f Actions: Various CI improvements
- Test against MSRV
- Parallel linting
- Compile benchmarks on nightly
2019-10-08 15:59:40 +13:00
Jack Grigg
b872e9fc49 Fix pairing benchmarks
They were broken by #91 but went unnoticed because CI does not compile
the benchmarks, which requires the nightly toolchain.
2019-10-08 15:25:41 +13:00
str4d
39971891d5 Merge pull request #131 from str4d/119-key-encoding-tests
Unit tests for key encodings
2019-10-08 13:48:05 +13:00
str4d
2399d9f3e3 Merge pull request #93 from defuse/qed-it-lrz
Bring in QED-it Tests
2019-10-08 13:47:36 +13:00
str4d
212b6e67d0 Merge pull request #133 from NikVolf/patch-1
Bump Rust to 1.37 on CI
2019-10-08 09:43:18 +13:00
str4d
6de1cb1397 Merge pull request #137 from jimpo/rand_core-version
Specify rand_core >= 0.5.1 for OsRng import.
2019-10-05 16:12:26 -06:00
Jim Posen
93563c3c65 Specify rand_core >= 0.5.1 for OsRng import. 2019-10-05 12:16:10 +02:00
str4d
524055bbb4 Merge pull request #134 from NikVolf/z-rng
Avoid using rand_os crate (fixes warnings)
2019-10-02 17:35:56 -06:00
NikVolf
730d2cbc7e fix outdated example 2019-09-28 12:50:28 +03:00
NikVolf
7476e4ea28 alphabetical order 2019-09-28 12:49:37 +03:00
str4d
c68e15e4f3 Merge pull request #130 from str4d/crate-docs
Crate documentation updates
2019-09-28 10:32:35 +01:00
NikVolf
b42477a0bf update from rand_os to fix warnings 2019-09-28 10:48:43 +03:00
Nikolay Volf
4c9ebcce9a Bump Rust to 1.37 on CI 2019-09-25 12:07:48 +02:00
NikVolf
da0d0a669a more idiomatic naming 2019-09-25 09:53:06 +02:00
NikVolf
443f45f430 dry example a bit and reduce api 2019-09-25 09:50:59 +02:00
Jack Grigg
e7b5329e95 Unit tests for key encodings
Closes #119.
2019-09-24 17:47:10 +01:00
NikVolf
43efdf992b prepare tree for deleting also 2019-09-24 15:28:34 +02:00
Jack Grigg
4ad3988e43 Crate docs 2019-09-24 14:23:59 +01:00
Jack Grigg
7f3036d2c8 Update READMEs 2019-09-24 14:23:55 +01:00
NikVolf
08806cc109 proper elapsed time calc 2019-09-24 13:47:32 +02:00
NikVolf
1eb4fb91f6 add invariant about number of returned links 2019-09-24 13:15:35 +02:00
NikVolf
7879b63321 add docs 2019-09-24 12:45:23 +02:00
NikVolf
ce2416623f get rid of derive_more 2019-09-24 12:19:25 +02:00
Jack Grigg
1fbf38280e Fix intra-doc links 2019-09-24 10:42:49 +01:00
Jack Grigg
d9a0b9c83f CI: Check intra-doc links
Credit: https://twitter.com/tomaka17/status/1176017851410526208
2019-09-24 10:35:12 +01:00
NikVolf
fb6fef4e9c update readme 2019-09-24 10:36:28 +02:00
NikVolf
38aa09b6a1 change package name 2019-09-24 10:35:44 +02:00
NikVolf
4cbc0451c1 switch to blake2_simd 2019-09-24 10:34:27 +02:00
Jack Grigg
1d02363752 Add READMEs to Cargo.toml files
This will cause crates.io to render each crate's README as its
information page.
2019-09-23 14:42:38 +01:00
str4d
30660e1d1c Merge pull request #128 from str4d/125-zip-32-seed-length
Panic if spending_key is given a seed shorter than 32 bytes
2019-09-19 06:52:10 -04:00
str4d
d2da9cfcb0 Merge pull request #127 from str4d/more-crate-updates
More crate updates
2019-09-18 11:44:13 -04:00
Jack Grigg
450087e280 Add test for spending_key panic on short seed 2019-09-17 23:58:14 +01:00
Jack Grigg
9377b78b35 Panic if spending_key is given a seed shorter than 32 bytes
This enforces the MUST requirement in ZIP 32. A panic is used instead of
an error because this should be considered an implementation error.
Ideally the type system would prevent this from occurring at all.

Closes #125.
2019-09-17 21:28:23 +01:00
Jack Grigg
39a73c8eda Add missing cs.is_satisfied() to bellman test 2019-09-17 00:16:33 +01:00
Taylor Hornby
3701c2b442 Increase the number of pedersen hash generators, exercise all test vectors. 2019-09-17 00:05:54 +01:00
Taylor Hornby
4dff8055bf Remove unfinished find_group_hash test. 2019-09-16 23:44:14 +01:00
Taylor Hornby
caab37c336 Fix build warnings 2019-09-16 23:06:48 +01:00
Taylor Hornby
e391594e9e Fix broken linear relation checking 2019-09-16 22:51:11 +01:00
Kobi Gurkan
15b4c37ab0 adds test for linear relation between pedersen hash generators 2019-09-16 22:23:28 +01:00
Aurélien Nicolas
3efb7f9146 Test PH circuit for the size used in the Merkle tree 2019-09-16 22:20:51 +01:00
Aurélien Nicolas
d56758d426 Calculate number of constraints and more comprehensive test 2019-09-16 22:14:14 +01:00
Kobi Gurkan
f3533e291f boolean: adds tests for alloc_conditionally 2019-09-16 22:08:30 +01:00
Kobi Gurkan
e0c5ef22bc ecc: makes assert_not_small_order tests deeper 2019-09-16 22:08:27 +01:00
Kobi Gurkan
ace929c5ba ecc: test_assert_not_small_order also tests for the generators 2019-09-16 22:03:22 +01:00
Kobi Gurkan
bb0a769162 ecc: tests for assert_not_small_order 2019-09-16 22:01:00 +01:00
Aurélien Nicolas
68ba93f5fd PH test vectors for edge-cases 2019-09-16 21:58:28 +01:00
Aurélien Nicolas
6240c02208 Long and random PH test vectors 2019-09-16 21:57:53 +01:00
Aurélien Nicolas
be18eb240c Move test vectors into own module 2019-09-16 21:57:42 +01:00
Aurélien Nicolas
9080b4ccf3 Many test vectors for pedersen hash 2019-09-16 20:52:11 +01:00
Aurélien Nicolas
f5dfe07355 A test vector for pedersen hash 2019-09-16 20:50:22 +01:00
Kobi Gurkan
2ee7b108af blake2s: adds test vectors from go-jubjub 2019-09-16 20:34:21 +01:00
Kobi Gurkan
2edcc12e8e group_hash: adds test vectors generated by go-jubjub 2019-09-16 20:34:14 +01:00
Kobi Gurkan
2b92493a45 input circuit: removes debug prints 2019-09-16 20:28:23 +01:00
Kobi Gurkan
abd03928b4 adds test vectors for NoteCommit 2019-09-16 20:28:10 +01:00
Taylor Hornby
cc2a41d86c Fix blake2s test data length assertion. 2019-09-16 20:24:31 +01:00
Kobi Gurkan
804f4cba67 Add blake2s test vectors for varying sizes from go-jubjub 2019-09-16 20:23:09 +01:00
Kobi Gurkan
7ee61c4f94 pedersen_hash: adds test vectors for the circuit implementation 2019-09-16 20:23:07 +01:00
Kobi Gurkan
43496857c9 pedersen_hash: removes debug prints 2019-09-16 20:20:03 +01:00
Kobi Gurkan
4835be05b0 pedersen_hash: show a tighter limit for hash sizes 2019-09-16 20:17:52 +01:00
Kobi Gurkan
15633ad434 pedersen hashes: example of size limit bug 2019-09-16 20:17:50 +01:00
Kobi Gurkan
414d651c9c pedersen_hash: adds tests for Daniel's vector 2019-09-16 20:12:38 +01:00
Kobi Gurkan
8541b2bde4 pedersen_hash: prints hashes, adds comments 2019-09-16 20:12:33 +01:00
Jack Grigg
28dcc1c346 bellman: Fix compile errors without multicore feature 2019-09-12 19:38:52 +01:00
Jack Grigg
b397a9c405 cargo update 2019-09-12 19:38:51 +01:00
Jack Grigg
0a3b0a9341 Upgrade to hex-literal 0.2 2019-09-12 19:38:51 +01:00
Jack Grigg
89dad572ef Migrate bellman to crossbeam 0.7 2019-09-12 19:38:50 +01:00
NikVolf
79cba2e500 run against long examples 2019-09-10 12:51:23 +03:00
NikVolf
4ec651d172 update naming 2019-09-09 18:28:23 +03:00
NikVolf
f5c1381c4c arrange data generator example properly 2019-09-09 18:23:18 +03:00
NikVolf
a0c33945ab write for entry 2019-09-09 14:33:49 +03:00
NikVolf
96b130e034 update readme 2019-09-09 14:06:05 +03:00
NikVolf
636f3e3751 update readme 2019-09-09 11:52:57 +03:00
NikVolf
b73f49c968 add optional file generation 2019-09-09 11:50:41 +03:00
NikVolf
a5c4d51652 improve on api 2019-09-08 09:32:47 +03:00
NikVolf
c87122561f add .travis.yml 2019-09-08 00:17:40 +03:00
NikVolf
9470610b75 license and readme 2019-09-07 19:23:13 +03:00
NikVolf
0afa122970 add .travis.yml 2019-09-07 19:18:49 +03:00
NikVolf
0bfd1d6b0d resolve_link is of course public 2019-09-07 18:30:57 +03:00
NikVolf
ad403f1cca add data reader 2019-09-07 18:22:37 +03:00
NikVolf
a9d2ce7150 from_bytes for NodeData 2019-09-07 18:15:18 +03:00
NikVolf
6d9deefb93 fix read and add from_bytes 2019-09-07 14:10:08 +03:00
NikVolf
49763d1c01 arrange constants 2019-09-07 14:04:51 +03:00
NikVolf
de053e1d8f reading for Entry 2019-09-07 14:00:34 +03:00
NikVolf
03524ba7d0 entry to the dedicated module 2019-09-07 13:52:19 +03:00
Nikolay Volf
265c6e5303 Merge pull request #1 from niklasad1/na-rust2018ify
fix: use rust2018 idioms
2019-09-07 11:35:06 +03:00
Niklas Adolfsson
c4f8f8ea04 fix: use rust2018 idioms 2019-09-07 10:31:01 +02:00
NikVolf
5d0aa521c2 missing deserialization bits 2019-09-07 11:19:06 +03:00
Jack Grigg
bef3e7e336 Migrate ff_derive to proc-macro2 1.0 2019-09-06 19:03:12 -04:00
str4d
2b6fbfd2d1 Merge pull request #109 from str4d/paymentaddress-encapsulation
PaymentAddress encapsulation
2019-09-06 15:43:48 -04:00
NikVolf
a46ace4c2d simplify tree preparation 2019-09-06 19:19:58 +03:00
NikVolf
942a976ef5 refactor numerous tests 2019-09-06 19:14:53 +03:00
NikVolf
5d2f84a154 carry root with tree 2019-09-06 18:52:27 +03:00
NikVolf
6b36cb5a51 example and neccessary fixes 2019-09-06 15:40:26 +03:00
Jack Grigg
d6f6b50ecd Check try_sapling_output_recovery fails with identity as pk_d 2019-09-04 19:48:39 -04:00
Jack Grigg
abbd43ff57 Make pk_d validity an invariant of PaymentAddress
Introduces a PaymentAddress::from_parts constructor, and getters for
the diversifier and pk_d fields (which are now private).
2019-09-04 19:48:35 -04:00
Jack Grigg
86142d044c PaymentAddress::to_bytes 2019-09-04 19:46:46 -04:00
Jack Grigg
73ee19239c Replace manual address decoding with PaymentAddress::from_bytes 2019-09-04 19:46:46 -04:00
str4d
b19b40ccf0 Merge pull request #110 from str4d/crate-cleanups
Crate cleanups
2019-09-04 19:44:47 -04:00
NikVolf
872ac5af7b avoid drain of vec 2019-09-03 18:31:51 +03:00
NikVolf
efac432128 remove todo and de-pub 2019-09-03 18:13:33 +03:00
NikVolf
b03b4cf958 hashing with blake2 2019-09-03 17:47:21 +03:00
NikVolf
d8c04e8143 node serialization 2019-09-03 17:22:21 +03:00
NikVolf
f432983f09 add notes and remove todos 2019-09-03 13:11:10 +03:00
NikVolf
bce88797f4 move node data to separate mod 2019-09-03 12:56:24 +03:00
NikVolf
771aa867c1 derive display for errors 2019-09-03 12:52:50 +03:00
NikVolf
615c4f662e refactor to rust-only structures 2019-09-02 19:51:00 +03:00
NikVolf
49f20e6735 extra long truncate 2019-09-02 18:26:51 +03:00
NikVolf
d58b33fb78 simplify and optimize get_peaks 2019-09-02 18:18:33 +03:00
NikVolf
4c49f09515 extra quick-checks and fixes for deleting from full root 2019-09-02 18:05:20 +03:00
NikVolf
68983dc0c1 extra tests and notes 2019-09-02 15:28:51 +03:00
NikVolf
fa04929891 refactored append and fixed tests 2019-09-02 15:11:23 +03:00
NikVolf
c05446d2ae add get_peaks method 2019-08-29 19:34:27 +03:00
str4d
0da4921055 Merge pull request #116 from str4d/code-coverage
Add code coverage with cargo-tarpaulin and Codecov
2019-08-29 09:58:19 +01:00
Jack Grigg
c00b65bb78 Extend Travis CI timeout 2019-08-29 01:06:41 +01:00
Jack Grigg
ee9c88ecb0 Exclude slow tests from code coverage 2019-08-28 20:46:23 +01:00
Jack Grigg
f9f17b291f Add code coverage with cargo-tarpaulin and Codecov 2019-08-28 12:26:54 +01:00
str4d
563961e606 Merge pull request #115 from str4d/windows-typo
librustzcash: Fix typo in Windows parameter init; Correctly map sprout_path. Includes a GitHub actions improvement
2019-08-26 13:09:57 +01:00
Jack Grigg
0c7eb84d36 impl FromStr for Memo
Memo::from_str was previously shadowing a built-in trait method.
2019-08-24 00:40:32 +01:00
Jack Grigg
7c1d4d9a5b Log distinct error cases in Equihash verification 2019-08-24 00:40:30 +01:00
Jack Grigg
9016548698 Take self directly in into_* functions 2019-08-24 00:39:55 +01:00
Jack Grigg
fe93f2ff6b Rename into_ -> to_ where &self is used. 2019-08-24 00:39:54 +01:00
Jack Grigg
91541675e2 Address various clippy warnings/errors in zcash_proofs 2019-08-24 00:39:53 +01:00
Jack Grigg
3a8efd9e67 Address various clippy warnings/errors in zcash_primitives 2019-08-24 00:39:49 +01:00
Jack Grigg
d65fe2cda9 Address various clippy warnings/errors in bellman 2019-08-24 00:38:32 +01:00
Jack Grigg
d78c94b2a2 Move Equihash validator into zcash_primitives 2019-08-24 00:37:00 +01:00
str4d
ad33798244 Merge pull request #113 from Eirik0/edition-2018-clean-up
Edition 2018 clean up
2019-08-24 00:34:57 +01:00
NikVolf
e7d67364cc add quickcheck(failing) also failing append 2019-08-23 16:25:34 +03:00
Jack Grigg
ba3705cb5e librustzcash: Correctly map sprout_path 2019-08-23 12:26:19 +01:00
Jack Grigg
5e706d5d84 Actions: Trigger testing workflow on pull_request 2019-08-23 12:13:23 +01:00
Jack Grigg
894421f3b5 librustzcash: Fix typo in Windows parameter init 2019-08-23 12:01:01 +01:00
str4d
4014f08105 Merge pull request #112 from zcash/github-workflow
Add a GitHub Actions workflow
2019-08-23 09:56:36 +01:00
NikVolf
5a479363ff new initializer 2019-08-22 20:35:56 +03:00
str4d
724e822d2d Merge pull request #98 from paracetamolo/optional-sprout-parameters
Make initialization of sprout validation key optional
2019-08-22 16:50:27 +01:00
NikVolf
27337f1bb0 use assert_matches! 2019-08-22 17:43:03 +03:00
NikVolf
bd8eea97b7 add docs/comments and fix bug 2019-08-22 17:36:05 +03:00
Jack Grigg
7ad0452c01 Add macOS support to GitHub Actions workflow 2019-08-22 15:10:28 +01:00
NikVolf
3a09eef6b3 some ascii fun 2019-08-22 15:59:18 +03:00
NikVolf
767d73f777 extra asserts 2019-08-22 15:49:43 +03:00
NikVolf
5c6d856714 extra test 2019-08-22 15:47:49 +03:00
NikVolf
f316c1b439 remove whitespaces 2019-08-22 14:37:26 +03:00
NikVolf
261ad90d33 truncate leaf and test 2019-08-22 14:36:57 +03:00
Eirik Ogilvie-Wigley
d63fa334ff Remove unnecessary raw marker
Co-Authored-By: str4d <thestr4d@gmail.com>
2019-08-21 16:14:28 -06:00
Eirik Ogilvie-Wigley
53182aa08e cargo fix --edition-idioms for zcash_proofs 2019-08-21 16:13:10 -06:00
Eirik Ogilvie-Wigley
fec961777c Add edition = 2018 to zcash_proofs 2019-08-21 16:11:29 -06:00
Marco Stronati
37531ed747 Fixes after feedback 2019-08-21 08:31:52 +02:00
Eirik Ogilvie-Wigley
76795a9014 cargo fmt 2019-08-20 22:22:03 -06:00
Eirik Ogilvie-Wigley
c28ae31c71 cargo fix --edition-idioms for zcash_primitives 2019-08-20 22:20:22 -06:00
Eirik Ogilvie-Wigley
573ffc4e06 cargo fix --edition-idioms for librustzcash 2019-08-20 22:17:29 -06:00
Eirik Ogilvie-Wigley
9807a5c1cc cargo fix --edition-idioms for pairing 2019-08-20 22:16:40 -06:00
Eirik Ogilvie-Wigley
07c690cf73 cargo fix --edition-idioms for group 2019-08-20 22:08:10 -06:00
Eirik Ogilvie-Wigley
a5f25c5058 cargo fix --edition-idioms for bellman 2019-08-20 22:07:24 -06:00
Eirik Ogilvie-Wigley
4b021fcf4d cargo fix --edition-idioms for ff 2019-08-20 22:01:46 -06:00
Eirik Ogilvie-Wigley
09882c6d08 Add edition = 2018 2019-08-20 18:31:20 -06:00
Eirik Ogilvie-Wigley
b35a819a09 Replace try! macro 2019-08-20 18:24:47 -06:00
Eirik Ogilvie-Wigley
7809711a81 cargo fix --edition for librustzcash 2019-08-20 18:19:11 -06:00
Eirik Ogilvie-Wigley
f523ac285d cargo fmt 2019-08-20 17:17:21 -06:00
Eirik Ogilvie-Wigley
cc0fc98c22 cargo fix --edition for pairing 2019-08-20 17:15:26 -06:00
Eirik Ogilvie-Wigley
b0d8747697 cargo fix --edition for zcash_primitives 2019-08-20 17:15:07 -06:00
Jack Grigg
f1b6e88f9f Add a GitHub Actions workflow for Ubuntu and Windows 2019-08-20 23:46:53 +01:00
Eirik Ogilvie-Wigley
e12d315ab9 Warning cleanup 2019-08-20 16:45:10 -06:00
Eirik Ogilvie-Wigley
fc3dd8198b cargo fix --edition for group 2019-08-20 16:45:10 -06:00
Eirik Ogilvie-Wigley
4991e53f48 cargo fix --edition for bellman 2019-08-20 16:45:10 -06:00
Eirik Ogilvie-Wigley
e59738b4ee cargo fix --edition for ff 2019-08-20 16:45:10 -06:00
NikVolf
66c31be6c5 commit from cbindgen 2019-08-20 20:10:29 +03:00
NikVolf
fec7e07764 refactor and fix warnings 2019-08-20 20:07:50 +03:00
NikVolf
845babc3c2 remove unuse dep 2019-08-20 18:42:44 +03:00
NikVolf
c794cdc680 fix signature and generate h 2019-08-20 18:09:09 +03:00
NikVolf
ae364f3ae7 initial commit 2019-08-20 18:05:14 +03:00
Nikolay Volf
081937162c Initial commit 2019-08-19 11:29:06 +03:00
Marco Stronati
3891fe57d4 Make initialization of sprout validation key optional
This makes the C interface behave like `zcash_proofs` and allows to
init the library without downloading the heavy sprout parameters.
In the special case where `librustzcash_init_zksnark_params` is called
with the sprout arguments path set to NULL and length set to 0, the
arguments are passed as None to `load_parameters`.
2019-08-07 18:01:12 +02:00
147 changed files with 13345 additions and 7005 deletions

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

@@ -0,0 +1,120 @@
name: CI checks
on: [push, pull_request]
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.37.0
override: true
# cargo fmt does not build the code, and running it in a fresh clone of
# the codebase will fail because the protobuf code has not been generated.
- name: cargo build
uses: actions-rs/cargo@v1
with:
command: build
args: --all
# Ensure all code has been formatted with rustfmt
- run: rustup component add rustfmt
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check --color always
test:
name: Test on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.37.0
override: true
- name: cargo fetch
uses: actions-rs/cargo@v1
with:
command: fetch
- name: Build tests
uses: actions-rs/cargo@v1
with:
command: build
args: --verbose --release --all --tests
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --release --all
- name: Run slow tests
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --release --all -- --ignored
codecov:
name: Code coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
# Use stable for this to ensure that cargo-tarpaulin can be built.
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Install cargo-tarpaulin
uses: actions-rs/cargo@v1
with:
command: install
args: cargo-tarpaulin
- name: Generate coverage report
uses: actions-rs/cargo@v1
with:
command: tarpaulin
args: --release --timeout 600 --out Xml --packages "librustzcash,zcash_client_backend,zcash_primitives,zcash_proofs"
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.0.3
with:
token: ${{secrets.CODECOV_TOKEN}}
doc-links:
name: Nightly lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: cargo fetch
uses: actions-rs/cargo@v1
with:
command: fetch
# Ensure intra-documentation links all resolve correctly
# Requires #![deny(intra_doc_link_resolution_failure)] in crates.
- name: Check intra-doc links
uses: actions-rs/cargo@v1
with:
command: doc
args: --all --document-private-items
# Build benchmarks to prevent bitrot
- name: Build benchmarks
uses: actions-rs/cargo@v1
with:
command: build
args: --verbose --all --benches

View File

@@ -1,6 +1,6 @@
language: rust language: rust
rust: rust:
- 1.36.0 - 1.37.0
cache: cargo cache: cargo
@@ -8,5 +8,12 @@ before_script:
- rustup component add rustfmt - rustup component add rustfmt
script: script:
- cargo build --verbose --release --all
- cargo fmt --all -- --check - cargo fmt --all -- --check
- cargo test --verbose --release --all - cargo test --verbose --release --all
- cargo test --verbose --release --all -- --ignored
before_cache:
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
- cargo install cargo-update || echo "cargo-update already installed"
- cargo install-update -a # update outdated cached binaries

663
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@ members = [
"librustzcash", "librustzcash",
"pairing", "pairing",
"zcash_client_backend", "zcash_client_backend",
"zcash_history",
"zcash_primitives", "zcash_primitives",
"zcash_proofs", "zcash_proofs",
] ]

View File

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

View File

@@ -1,12 +1,23 @@
# bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) # # bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) #
This is a research project being built for [Zcash](https://z.cash/). `bellman` is a crate for building zk-SNARK circuits. It provides circuit traits
and primitive structures, as well as basic gadget implementations such as
booleans and number abstractions.
## Roadmap
`bellman` is being refactored into a generic proving library. Currently it is
pairing-specific, and different types of proving systems need to be implemented
as sub-modules. After the refactor, `bellman` will be generic using the `ff` and
`group` crates, while specific proving systems will be separate crates that pull
in the dependencies they require.
## License ## License
Licensed under either of Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option. at your option.

View File

@@ -1,14 +1,15 @@
//! This module contains an `EvaluationDomain` abstraction for //! This module contains an [`EvaluationDomain`] abstraction for performing
//! performing various kinds of polynomial arithmetic on top of //! various kinds of polynomial arithmetic on top of the scalar field.
//! the scalar field.
//! //!
//! In pairing-based SNARKs like Groth16, we need to calculate //! In pairing-based SNARKs like [Groth16], we need to calculate a quotient
//! a quotient polynomial over a target polynomial with roots //! polynomial over a target polynomial with roots at distinct points associated
//! at distinct points associated with each constraint of the //! with each constraint of the constraint system. In order to be efficient, we
//! constraint system. In order to be efficient, we choose these //! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the
//! roots to be the powers of a 2^n root of unity in the field. //! field. This allows us to perform polynomial operations in O(n) by performing
//! This allows us to perform polynomial operations in O(n) //! an O(n log n) FFT over such a domain.
//! by performing an O(n log n) FFT over such a domain. //!
//! [`EvaluationDomain`]: crate::domain::EvaluationDomain
//! [Groth16]: https://eprint.iacr.org/2016/260
use ff::{Field, PrimeField, ScalarEngine}; use ff::{Field, PrimeField, ScalarEngine};
use group::CurveProjective; use group::CurveProjective;
@@ -26,15 +27,19 @@ pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
minv: E::Fr, minv: E::Fr,
} }
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> { impl<E: ScalarEngine, G: Group<E>> AsRef<[G]> for EvaluationDomain<E, G> {
pub fn as_ref(&self) -> &[G] { fn as_ref(&self) -> &[G] {
&self.coeffs &self.coeffs
} }
pub fn as_mut(&mut self) -> &mut [G] {
&mut self.coeffs
} }
impl<E: ScalarEngine, G: Group<E>> AsMut<[G]> for EvaluationDomain<E, G> {
fn as_mut(&mut self) -> &mut [G] {
&mut self.coeffs
}
}
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
pub fn into_coeffs(self) -> Vec<G> { pub fn into_coeffs(self) -> Vec<G> {
self.coeffs self.coeffs
} }
@@ -64,9 +69,9 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
coeffs.resize(m, G::group_zero()); coeffs.resize(m, G::group_zero());
Ok(EvaluationDomain { Ok(EvaluationDomain {
coeffs: coeffs, coeffs,
exp: exp, exp,
omega: omega, omega,
omegainv: omega.inverse().unwrap(), omegainv: omega.inverse().unwrap(),
geninv: E::Fr::multiplicative_generator().inverse().unwrap(), geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
minv: E::Fr::from_str(&format!("{}", m)) minv: E::Fr::from_str(&format!("{}", m))
@@ -87,7 +92,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
let minv = self.minv; let minv = self.minv;
for v in self.coeffs.chunks_mut(chunk) { for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || { scope.spawn(move |_scope| {
for v in v { for v in v {
v.group_mul_assign(&minv); v.group_mul_assign(&minv);
} }
@@ -99,7 +104,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) { pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) {
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
scope.spawn(move || { scope.spawn(move |_scope| {
let mut u = g.pow(&[(i * chunk) as u64]); let mut u = g.pow(&[(i * chunk) as u64]);
for v in v.iter_mut() { for v in v.iter_mut() {
v.group_mul_assign(&u); v.group_mul_assign(&u);
@@ -142,7 +147,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for v in self.coeffs.chunks_mut(chunk) { for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || { scope.spawn(move |_scope| {
for v in v { for v in v {
v.group_mul_assign(&i); v.group_mul_assign(&i);
} }
@@ -161,7 +166,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
.chunks_mut(chunk) .chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk)) .zip(other.coeffs.chunks(chunk))
{ {
scope.spawn(move || { scope.spawn(move |_scope| {
for (a, b) in a.iter_mut().zip(b.iter()) { for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_mul_assign(&b.0); a.group_mul_assign(&b.0);
} }
@@ -180,7 +185,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
.chunks_mut(chunk) .chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk)) .zip(other.coeffs.chunks(chunk))
{ {
scope.spawn(move || { scope.spawn(move |_scope| {
for (a, b) in a.iter_mut().zip(b.iter()) { for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_sub_assign(&b); a.group_sub_assign(&b);
} }
@@ -291,7 +296,7 @@ fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u
let mut m = 1; let mut m = 1;
for _ in 0..log_n { for _ in 0..log_n {
let w_m = omega.pow(&[(n / (2 * m)) as u64]); let w_m = omega.pow(&[u64::from(n / (2 * m))]);
let mut k = 0; let mut k = 0;
while k < n { while k < n {
@@ -331,18 +336,18 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
let a = &*a; let a = &*a;
for (j, tmp) in tmp.iter_mut().enumerate() { for (j, tmp) in tmp.iter_mut().enumerate() {
scope.spawn(move || { scope.spawn(move |_scope| {
// Shuffle into a sub-FFT // Shuffle into a sub-FFT
let omega_j = omega.pow(&[j as u64]); let omega_j = omega.pow(&[j as u64]);
let omega_step = omega.pow(&[(j as u64) << log_new_n]); let omega_step = omega.pow(&[(j as u64) << log_new_n]);
let mut elt = E::Fr::one(); let mut elt = E::Fr::one();
for i in 0..(1 << log_new_n) { for (i, tmp) in tmp.iter_mut().enumerate() {
for s in 0..num_cpus { for s in 0..num_cpus {
let idx = (i + (s << log_new_n)) % (1 << log_n); let idx = (i + (s << log_new_n)) % (1 << log_n);
let mut t = a[idx]; let mut t = a[idx];
t.group_mul_assign(&elt); t.group_mul_assign(&elt);
tmp[i].group_add_assign(&t); tmp.group_add_assign(&t);
elt.mul_assign(&omega_step); elt.mul_assign(&omega_step);
} }
elt.mul_assign(&omega_j); elt.mul_assign(&omega_j);
@@ -359,7 +364,7 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
let tmp = &tmp; let tmp = &tmp;
for (idx, a) in a.chunks_mut(chunk).enumerate() { for (idx, a) in a.chunks_mut(chunk).enumerate() {
scope.spawn(move || { scope.spawn(move |_scope| {
let mut idx = idx * chunk; let mut idx = idx * chunk;
let mask = (1 << log_cpus) - 1; let mask = (1 << log_cpus) - 1;
for a in a { for a in a {

View File

@@ -1,3 +1,5 @@
//! Self-contained sub-circuit implementations for various primitives.
pub mod test; pub mod test;
pub mod blake2s; pub mod blake2s;

View File

@@ -1,12 +1,10 @@
use pairing::Engine; //! The [BLAKE2s] hash function with personalization support.
//!
//! [BLAKE2s]: https://tools.ietf.org/html/rfc7693
use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32};
use crate::{ConstraintSystem, SynthesisError}; use crate::{ConstraintSystem, SynthesisError};
use ff::ScalarEngine;
use super::boolean::Boolean;
use super::uint32::UInt32;
use super::multieq::MultiEq;
/* /*
2.1. Parameters 2.1. Parameters
@@ -81,7 +79,7 @@ const SIGMA: [[usize; 16]; 10] = [
END FUNCTION. END FUNCTION.
*/ */
fn mixing_g<E: Engine, CS: ConstraintSystem<E>, M>( fn mixing_g<E: ScalarEngine, CS: ConstraintSystem<E>, M>(
mut cs: M, mut cs: M,
v: &mut [UInt32], v: &mut [UInt32],
a: usize, a: usize,
@@ -166,7 +164,7 @@ where
END FUNCTION. END FUNCTION.
*/ */
fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>( fn blake2s_compression<E: ScalarEngine, CS: ConstraintSystem<E>>(
mut cs: CS, mut cs: CS,
h: &mut [UInt32], h: &mut [UInt32],
m: &[UInt32], m: &[UInt32],
@@ -339,7 +337,7 @@ fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
END FUNCTION. END FUNCTION.
*/ */
pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>( pub fn blake2s<E: ScalarEngine, CS: ConstraintSystem<E>>(
mut cs: CS, mut cs: CS,
input: &[Boolean], input: &[Boolean],
personalization: &[u8], personalization: &[u8],
@@ -382,7 +380,7 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
blocks.push(this_block); blocks.push(this_block);
} }
if blocks.len() == 0 { if blocks.is_empty() {
blocks.push((0..16).map(|_| UInt32::constant(0)).collect()); blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
} }
@@ -404,12 +402,13 @@ pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
)?; )?;
} }
Ok(h.iter().flat_map(|b| b.into_bits()).collect()) Ok(h.into_iter().flat_map(|b| b.into_bits()).collect())
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use blake2s_simd::Params as Blake2sParams; use blake2s_simd::Params as Blake2sParams;
use hex_literal::hex;
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
@@ -433,7 +432,7 @@ mod test {
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8"); let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
let mut out = out.into_iter(); let mut out = out.into_iter();
for b in expected.into_iter() { for b in expected.iter() {
for i in 0..8 { for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap(); let c = out.next().unwrap().get_value().unwrap();
@@ -554,4 +553,145 @@ mod test {
} }
} }
} }
#[test]
fn test_blake2s_256_vars() {
let data: Vec<u8> = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec();
assert_eq!(data.len(), 256);
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let expected = hex!("0af5695115ced92c8a0341e43869209636e9aa6472e4576f0f2b996cf812b30e");
let mut out = r.into_iter();
for b in expected.iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_700_vars() {
let data: Vec<u8> = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec();
assert_eq!(data.len(), 700);
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let expected = hex!("2ab8f0683167ba220eef19dccf4f9b1a8193cc09b35e0235842323950530f18a");
let mut out = r.into_iter();
for b in expected.iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_test_vectors() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let expecteds = [
hex!("a1309e334376c8f36a736a4ab0e691ef931ee3ebdb9ea96187127136fea622a1"),
hex!("82fefff60f265cea255252f7c194a7f93965dffee0609ef74eb67f0d76cd41c6"),
];
for i in 0..2 {
let mut h = Blake2sParams::new()
.hash_length(32)
.personal(b"12345678")
.to_state();
let input_len = 1024;
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
h.update(&data);
let hash_result = h.finalize();
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let mut s = hash_result
.as_ref()
.iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r {
match b {
Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
}
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
}
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
}
}
}
assert_eq!(expecteds[i], hash_result.as_bytes());
}
}
} }

View File

@@ -1,5 +1,6 @@
use ff::{BitIterator, Field, PrimeField}; //! Gadgets for allocating bits in the circuit and performing boolean logic.
use pairing::Engine;
use ff::{BitIterator, Field, PrimeField, ScalarEngine};
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
@@ -31,7 +32,7 @@ impl AllocatedBit {
must_be_false: &AllocatedBit, must_be_false: &AllocatedBit,
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let var = cs.alloc( let var = cs.alloc(
@@ -60,7 +61,7 @@ impl AllocatedBit {
Ok(AllocatedBit { Ok(AllocatedBit {
variable: var, variable: var,
value: value, value,
}) })
} }
@@ -68,7 +69,7 @@ impl AllocatedBit {
/// boolean value. /// boolean value.
pub fn alloc<E, CS>(mut cs: CS, value: Option<bool>) -> Result<Self, SynthesisError> pub fn alloc<E, CS>(mut cs: CS, value: Option<bool>) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let var = cs.alloc( let var = cs.alloc(
@@ -93,7 +94,7 @@ impl AllocatedBit {
Ok(AllocatedBit { Ok(AllocatedBit {
variable: var, variable: var,
value: value, value,
}) })
} }
@@ -101,7 +102,7 @@ impl AllocatedBit {
/// an `AllocatedBit`. /// an `AllocatedBit`.
pub fn xor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError> pub fn xor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let mut result_value = None; let mut result_value = None;
@@ -153,7 +154,7 @@ impl AllocatedBit {
/// an `AllocatedBit`. /// an `AllocatedBit`.
pub fn and<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError> pub fn and<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let mut result_value = None; let mut result_value = None;
@@ -191,7 +192,7 @@ impl AllocatedBit {
/// Calculates `a AND (NOT b)`. /// Calculates `a AND (NOT b)`.
pub fn and_not<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError> pub fn and_not<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let mut result_value = None; let mut result_value = None;
@@ -229,7 +230,7 @@ impl AllocatedBit {
/// Calculates `(NOT a) AND (NOT b)`. /// Calculates `(NOT a) AND (NOT b)`.
pub fn nor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError> pub fn nor<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let mut result_value = None; let mut result_value = None;
@@ -265,7 +266,7 @@ impl AllocatedBit {
} }
} }
pub fn u64_into_boolean_vec_le<E: Engine, CS: ConstraintSystem<E>>( pub fn u64_into_boolean_vec_le<E: ScalarEngine, CS: ConstraintSystem<E>>(
mut cs: CS, mut cs: CS,
value: Option<u64>, value: Option<u64>,
) -> Result<Vec<Boolean>, SynthesisError> { ) -> Result<Vec<Boolean>, SynthesisError> {
@@ -296,16 +297,16 @@ pub fn u64_into_boolean_vec_le<E: Engine, CS: ConstraintSystem<E>>(
Ok(bits) Ok(bits)
} }
pub fn field_into_boolean_vec_le<E: Engine, CS: ConstraintSystem<E>, F: PrimeField>( pub fn field_into_boolean_vec_le<E: ScalarEngine, CS: ConstraintSystem<E>, F: PrimeField>(
cs: CS, cs: CS,
value: Option<F>, value: Option<F>,
) -> Result<Vec<Boolean>, SynthesisError> { ) -> Result<Vec<Boolean>, SynthesisError> {
let v = field_into_allocated_bits_le::<E, CS, F>(cs, value)?; let v = field_into_allocated_bits_le::<E, CS, F>(cs, value)?;
Ok(v.into_iter().map(|e| Boolean::from(e)).collect()) Ok(v.into_iter().map(Boolean::from).collect())
} }
pub fn field_into_allocated_bits_le<E: Engine, CS: ConstraintSystem<E>, F: PrimeField>( pub fn field_into_allocated_bits_le<E: ScalarEngine, CS: ConstraintSystem<E>, F: PrimeField>(
mut cs: CS, mut cs: CS,
value: Option<F>, value: Option<F>,
) -> Result<Vec<AllocatedBit>, SynthesisError> { ) -> Result<Vec<AllocatedBit>, SynthesisError> {
@@ -367,7 +368,7 @@ impl Boolean {
pub fn enforce_equal<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError> pub fn enforce_equal<E, CS>(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
match (a, b) { match (a, b) {
@@ -412,24 +413,24 @@ impl Boolean {
} }
pub fn get_value(&self) -> Option<bool> { pub fn get_value(&self) -> Option<bool> {
match self { match *self {
&Boolean::Constant(c) => Some(c), Boolean::Constant(c) => Some(c),
&Boolean::Is(ref v) => v.get_value(), Boolean::Is(ref v) => v.get_value(),
&Boolean::Not(ref v) => v.get_value().map(|b| !b), Boolean::Not(ref v) => v.get_value().map(|b| !b),
} }
} }
pub fn lc<E: Engine>(&self, one: Variable, coeff: E::Fr) -> LinearCombination<E> { pub fn lc<E: ScalarEngine>(&self, one: Variable, coeff: E::Fr) -> LinearCombination<E> {
match self { match *self {
&Boolean::Constant(c) => { Boolean::Constant(c) => {
if c { if c {
LinearCombination::<E>::zero() + (coeff, one) LinearCombination::<E>::zero() + (coeff, one)
} else { } else {
LinearCombination::<E>::zero() LinearCombination::<E>::zero()
} }
} }
&Boolean::Is(ref v) => LinearCombination::<E>::zero() + (coeff, v.get_variable()), Boolean::Is(ref v) => LinearCombination::<E>::zero() + (coeff, v.get_variable()),
&Boolean::Not(ref v) => { Boolean::Not(ref v) => {
LinearCombination::<E>::zero() + (coeff, one) - (coeff, v.get_variable()) LinearCombination::<E>::zero() + (coeff, one) - (coeff, v.get_variable())
} }
} }
@@ -442,17 +443,17 @@ impl Boolean {
/// Return a negated interpretation of this boolean. /// Return a negated interpretation of this boolean.
pub fn not(&self) -> Self { pub fn not(&self) -> Self {
match self { match *self {
&Boolean::Constant(c) => Boolean::Constant(!c), Boolean::Constant(c) => Boolean::Constant(!c),
&Boolean::Is(ref v) => Boolean::Not(v.clone()), Boolean::Is(ref v) => Boolean::Not(v.clone()),
&Boolean::Not(ref v) => Boolean::Is(v.clone()), Boolean::Not(ref v) => Boolean::Is(v.clone()),
} }
} }
/// Perform XOR over two boolean operands /// Perform XOR over two boolean operands
pub fn xor<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError> pub fn xor<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
match (a, b) { match (a, b) {
@@ -474,7 +475,7 @@ impl Boolean {
/// Perform AND over two boolean operands /// Perform AND over two boolean operands
pub fn and<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError> pub fn and<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
match (a, b) { match (a, b) {
@@ -508,7 +509,7 @@ impl Boolean {
c: &'a Self, c: &'a Self,
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let ch_value = match (a.get_value(), b.get_value(), c.get_value()) { let ch_value = match (a.get_value(), b.get_value(), c.get_value()) {
@@ -615,7 +616,7 @@ impl Boolean {
c: &'a Self, c: &'a Self,
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let maj_value = match (a.get_value(), b.get_value(), c.get_value()) { let maj_value = match (a.get_value(), b.get_value(), c.get_value()) {
@@ -1740,4 +1741,72 @@ mod test {
} }
} }
} }
#[test]
fn test_alloc_conditionally() {
{
let mut cs = TestConstraintSystem::<Bls12>::new();
let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap();
let value = None;
// if value is none, fail with SynthesisError
let is_err = AllocatedBit::alloc_conditionally(
cs.namespace(|| "alloc_conditionally"),
value,
&b,
)
.is_err();
assert!(is_err);
}
{
// since value is true, b must be false, so it should succeed
let mut cs = TestConstraintSystem::<Bls12>::new();
let value = Some(true);
let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap();
let allocated_value = AllocatedBit::alloc_conditionally(
cs.namespace(|| "alloc_conditionally"),
value,
&b,
)
.unwrap();
assert_eq!(allocated_value.get_value().unwrap(), true);
assert!(cs.is_satisfied());
}
{
// since value is true, b must be false, so it should fail
let mut cs = TestConstraintSystem::<Bls12>::new();
let value = Some(true);
let b = AllocatedBit::alloc(&mut cs, Some(true)).unwrap();
AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b)
.unwrap();
assert!(!cs.is_satisfied());
}
{
// since value is false, we don't care about the value of the bit
let value = Some(false);
//check with false bit
let mut cs = TestConstraintSystem::<Bls12>::new();
let b1 = AllocatedBit::alloc(&mut cs, Some(false)).unwrap();
AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b1)
.unwrap();
assert!(cs.is_satisfied());
//check with true bit
let mut cs = TestConstraintSystem::<Bls12>::new();
let b2 = AllocatedBit::alloc(&mut cs, Some(true)).unwrap();
AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b2)
.unwrap();
assert!(cs.is_satisfied());
}
}
} }

View File

@@ -1,5 +1,6 @@
use ff::Field; //! Window table lookup gadgets.
use pairing::Engine;
use ff::{Field, ScalarEngine};
use super::boolean::Boolean; use super::boolean::Boolean;
use super::num::{AllocatedNum, Num}; use super::num::{AllocatedNum, Num};
@@ -7,7 +8,7 @@ use super::*;
use crate::ConstraintSystem; use crate::ConstraintSystem;
// Synthesize the constants for each base pattern. // Synthesize the constants for each base pattern.
fn synth<'a, E: Engine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr]) fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
where where
I: IntoIterator<Item = &'a E::Fr>, I: IntoIterator<Item = &'a E::Fr>,
{ {
@@ -28,7 +29,7 @@ where
/// Performs a 3-bit window table lookup. `bits` is in /// Performs a 3-bit window table lookup. `bits` is in
/// little-endian order. /// little-endian order.
pub fn lookup3_xy<E: Engine, CS>( pub fn lookup3_xy<E: ScalarEngine, CS>(
mut cs: CS, mut cs: CS,
bits: &[Boolean], bits: &[Boolean],
coords: &[(E::Fr, E::Fr)], coords: &[(E::Fr, E::Fr)],
@@ -118,7 +119,7 @@ where
/// Performs a 3-bit window table lookup, where /// Performs a 3-bit window table lookup, where
/// one of the bits is a sign bit. /// one of the bits is a sign bit.
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>( pub fn lookup3_xy_with_conditional_negation<E: ScalarEngine, CS>(
mut cs: CS, mut cs: CS,
bits: &[Boolean], bits: &[Boolean],
coords: &[(E::Fr, E::Fr)], coords: &[(E::Fr, E::Fr)],

View File

@@ -1,9 +1,8 @@
use ff::{Field, PrimeField}; use ff::{Field, PrimeField, ScalarEngine};
use pairing::Engine;
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>> { pub struct MultiEq<E: ScalarEngine, CS: ConstraintSystem<E>> {
cs: CS, cs: CS,
ops: usize, ops: usize,
bits_used: usize, bits_used: usize,
@@ -11,10 +10,10 @@ pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>> {
rhs: LinearCombination<E>, rhs: LinearCombination<E>,
} }
impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> { impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
pub fn new(cs: CS) -> Self { pub fn new(cs: CS) -> Self {
MultiEq { MultiEq {
cs: cs, cs,
ops: 0, ops: 0,
bits_used: 0, bits_used: 0,
lhs: LinearCombination::zero(), lhs: LinearCombination::zero(),
@@ -58,7 +57,7 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
} }
} }
impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> { impl<E: ScalarEngine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
fn drop(&mut self) { fn drop(&mut self) {
if self.bits_used > 0 { if self.bits_used > 0 {
self.accumulate(); self.accumulate();
@@ -66,7 +65,7 @@ impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
} }
} }
impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> { impl<E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> {
type Root = Self; type Root = Self;
fn one() -> Variable { fn one() -> Variable {

View File

@@ -1,15 +1,16 @@
//! Helpers for packing vectors of bits into scalar field elements.
use super::boolean::Boolean; use super::boolean::Boolean;
use super::num::Num; use super::num::Num;
use super::Assignment; use super::Assignment;
use crate::{ConstraintSystem, SynthesisError}; use crate::{ConstraintSystem, SynthesisError};
use ff::{Field, PrimeField}; use ff::{Field, PrimeField, ScalarEngine};
use pairing::Engine;
/// Takes a sequence of booleans and exposes them as compact /// Takes a sequence of booleans and exposes them as compact
/// public inputs /// public inputs
pub fn pack_into_inputs<E, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError> pub fn pack_into_inputs<E, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() { for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() {
@@ -49,7 +50,7 @@ pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> {
.collect() .collect()
} }
pub fn compute_multipacking<E: Engine>(bits: &[bool]) -> Vec<E::Fr> { pub fn compute_multipacking<E: ScalarEngine>(bits: &[bool]) -> Vec<E::Fr> {
let mut result = vec![]; let mut result = vec![];
for bits in bits.chunks(E::Fr::CAPACITY as usize) { for bits in bits.chunks(E::Fr::CAPACITY as usize) {

View File

@@ -1,5 +1,6 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr}; //! Gadgets representing numbers in the scalar field of the underlying curve.
use pairing::Engine;
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
@@ -7,12 +8,12 @@ use super::Assignment;
use super::boolean::{self, AllocatedBit, Boolean}; use super::boolean::{self, AllocatedBit, Boolean};
pub struct AllocatedNum<E: Engine> { pub struct AllocatedNum<E: ScalarEngine> {
value: Option<E::Fr>, value: Option<E::Fr>,
variable: Variable, variable: Variable,
} }
impl<E: Engine> Clone for AllocatedNum<E> { impl<E: ScalarEngine> Clone for AllocatedNum<E> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
AllocatedNum { AllocatedNum {
value: self.value, value: self.value,
@@ -21,7 +22,7 @@ impl<E: Engine> Clone for AllocatedNum<E> {
} }
} }
impl<E: Engine> AllocatedNum<E> { impl<E: ScalarEngine> AllocatedNum<E> {
pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError> pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
where where
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
@@ -66,7 +67,7 @@ impl<E: Engine> AllocatedNum<E> {
/// order, requiring that the representation /// order, requiring that the representation
/// strictly exists "in the field" (i.e., a /// strictly exists "in the field" (i.e., a
/// congruency is not allowed.) /// congruency is not allowed.)
pub fn into_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
where where
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
@@ -75,10 +76,10 @@ impl<E: Engine> AllocatedNum<E> {
v: &[AllocatedBit], v: &[AllocatedBit],
) -> Result<AllocatedBit, SynthesisError> ) -> Result<AllocatedBit, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
assert!(v.len() > 0); assert!(!v.is_empty());
// Let's keep this simple for now and just AND them all // Let's keep this simple for now and just AND them all
// manually // manually
@@ -132,7 +133,7 @@ impl<E: Engine> AllocatedNum<E> {
current_run.push(a_bit.clone()); current_run.push(a_bit.clone());
result.push(a_bit); result.push(a_bit);
} else { } else {
if current_run.len() > 0 { if !current_run.is_empty() {
// This is the start of a run of zeros, but we need // This is the start of a run of zeros, but we need
// to k-ary AND against `last_run` first. // to k-ary AND against `last_run` first.
@@ -183,13 +184,13 @@ impl<E: Engine> AllocatedNum<E> {
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
// Convert into booleans, and reverse for little-endian bit order // Convert into booleans, and reverse for little-endian bit order
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect()) Ok(result.into_iter().map(Boolean::from).rev().collect())
} }
/// Convert the allocated number into its little-endian representation. /// Convert the allocated number into its little-endian representation.
/// Note that this does not strongly enforce that the commitment is /// Note that this does not strongly enforce that the commitment is
/// "in the field." /// "in the field."
pub fn into_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> pub fn to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
where where
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
@@ -208,7 +209,7 @@ impl<E: Engine> AllocatedNum<E> {
cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
Ok(bits.into_iter().map(|b| Boolean::from(b)).collect()) Ok(bits.into_iter().map(Boolean::from).collect())
} }
pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError> pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
@@ -238,7 +239,7 @@ impl<E: Engine> AllocatedNum<E> {
); );
Ok(AllocatedNum { Ok(AllocatedNum {
value: value, value,
variable: var, variable: var,
}) })
} }
@@ -270,7 +271,7 @@ impl<E: Engine> AllocatedNum<E> {
); );
Ok(AllocatedNum { Ok(AllocatedNum {
value: value, value,
variable: var, variable: var,
}) })
} }
@@ -359,12 +360,12 @@ impl<E: Engine> AllocatedNum<E> {
} }
} }
pub struct Num<E: Engine> { pub struct Num<E: ScalarEngine> {
value: Option<E::Fr>, value: Option<E::Fr>,
lc: LinearCombination<E>, lc: LinearCombination<E>,
} }
impl<E: Engine> From<AllocatedNum<E>> for Num<E> { impl<E: ScalarEngine> From<AllocatedNum<E>> for Num<E> {
fn from(num: AllocatedNum<E>) -> Num<E> { fn from(num: AllocatedNum<E>) -> Num<E> {
Num { Num {
value: num.value, value: num.value,
@@ -373,7 +374,7 @@ impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
} }
} }
impl<E: Engine> Num<E> { impl<E: ScalarEngine> Num<E> {
pub fn zero() -> Self { pub fn zero() -> Self {
Num { Num {
value: Some(E::Fr::zero()), value: Some(E::Fr::zero()),
@@ -522,7 +523,7 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap(); let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
n.into_bits_le_strict(&mut cs).unwrap(); n.to_bits_le_strict(&mut cs).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -550,9 +551,9 @@ mod test {
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
let bits = if i % 2 == 0 { let bits = if i % 2 == 0 {
n.into_bits_le(&mut cs).unwrap() n.to_bits_le(&mut cs).unwrap()
} else { } else {
n.into_bits_le_strict(&mut cs).unwrap() n.to_bits_le_strict(&mut cs).unwrap()
}; };
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());

View File

@@ -1,9 +1,15 @@
//! Circuits for the [SHA-256] hash function and its internal compression
//! function.
//!
//! [SHA-256]: https://tools.ietf.org/html/rfc6234
use super::boolean::Boolean; use super::boolean::Boolean;
use super::multieq::MultiEq; use super::multieq::MultiEq;
use super::uint32::UInt32; use super::uint32::UInt32;
use crate::{ConstraintSystem, SynthesisError}; use crate::{ConstraintSystem, SynthesisError};
use pairing::Engine; use ff::ScalarEngine;
#[allow(clippy::unreadable_literal)]
const ROUND_CONSTANTS: [u32; 64] = [ const ROUND_CONSTANTS: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
@@ -15,6 +21,7 @@ const ROUND_CONSTANTS: [u32; 64] = [
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
]; ];
#[allow(clippy::unreadable_literal)]
const IV: [u32; 8] = [ const IV: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
]; ];
@@ -24,7 +31,7 @@ pub fn sha256_block_no_padding<E, CS>(
input: &[Boolean], input: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
assert_eq!(input.len(), 512); assert_eq!(input.len(), 512);
@@ -39,7 +46,7 @@ where
pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError> pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
assert!(input.len() % 8 == 0); assert!(input.len() % 8 == 0);
@@ -76,7 +83,7 @@ fn sha256_compression_function<E, CS>(
current_hash_value: &[UInt32], current_hash_value: &[UInt32],
) -> Result<Vec<UInt32>, SynthesisError> ) -> Result<Vec<UInt32>, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
assert_eq!(input.len(), 512); assert_eq!(input.len(), 512);
@@ -123,14 +130,14 @@ where
impl Maybe { impl Maybe {
fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError> fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root = MultiEq<E, CS>>, M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
{ {
Ok(match self { Ok(match self {
Maybe::Concrete(ref v) => return Ok(v.clone()), Maybe::Concrete(ref v) => return Ok(v.clone()),
Maybe::Deferred(mut v) => { Maybe::Deferred(mut v) => {
v.extend(others.into_iter().cloned()); v.extend(others.iter().cloned());
UInt32::addmany(cs, &v)? UInt32::addmany(cs, &v)?
} }
}) })
@@ -266,6 +273,7 @@ mod test {
use super::*; use super::*;
use crate::gadgets::boolean::AllocatedBit; use crate::gadgets::boolean::AllocatedBit;
use crate::gadgets::test::TestConstraintSystem; use crate::gadgets::test::TestConstraintSystem;
use hex_literal::hex;
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
@@ -286,7 +294,7 @@ mod test {
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
let mut out = out_bits.into_iter(); let mut out = out_bits.into_iter();
for b in expected.into_iter() { for b in expected.iter() {
for i in (0..8).rev() { for i in (0..8).rev() {
let c = out.next().unwrap().get_value().unwrap(); let c = out.next().unwrap().get_value().unwrap();

View File

@@ -1,5 +1,6 @@
use ff::{Field, PrimeField, PrimeFieldRepr}; //! Helpers for testing circuit implementations.
use pairing::Engine;
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
@@ -20,7 +21,7 @@ enum NamedObject {
} }
/// Constraint system for testing purposes. /// Constraint system for testing purposes.
pub struct TestConstraintSystem<E: Engine> { pub struct TestConstraintSystem<E: ScalarEngine> {
named_objects: HashMap<String, NamedObject>, named_objects: HashMap<String, NamedObject>,
current_namespace: Vec<String>, current_namespace: Vec<String>,
constraints: Vec<( constraints: Vec<(
@@ -62,11 +63,11 @@ impl Ord for OrderedVariable {
} }
} }
fn proc_lc<E: Engine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> { fn proc_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
let mut map = BTreeMap::new(); let mut map = BTreeMap::new();
for &(var, coeff) in terms { for &(var, coeff) in terms {
map.entry(OrderedVariable(var)) map.entry(OrderedVariable(var))
.or_insert(E::Fr::zero()) .or_insert_with(E::Fr::zero)
.add_assign(&coeff); .add_assign(&coeff);
} }
@@ -85,7 +86,7 @@ fn proc_lc<E: Engine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable,
map map
} }
fn hash_lc<E: Engine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
let map = proc_lc::<E>(terms); let map = proc_lc::<E>(terms);
let mut buf = [0u8; 9 + 32]; let mut buf = [0u8; 9 + 32];
@@ -110,7 +111,7 @@ fn hash_lc<E: Engine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
} }
} }
fn eval_lc<E: Engine>( fn eval_lc<E: ScalarEngine>(
terms: &[(Variable, E::Fr)], terms: &[(Variable, E::Fr)],
inputs: &[(E::Fr, String)], inputs: &[(E::Fr, String)],
aux: &[(E::Fr, String)], aux: &[(E::Fr, String)],
@@ -130,7 +131,7 @@ fn eval_lc<E: Engine>(
acc acc
} }
impl<E: Engine> TestConstraintSystem<E> { impl<E: ScalarEngine> TestConstraintSystem<E> {
pub fn new() -> TestConstraintSystem<E> { pub fn new() -> TestConstraintSystem<E> {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert( map.insert(
@@ -157,7 +158,7 @@ impl<E: Engine> TestConstraintSystem<E> {
}; };
let powers_of_two = (0..E::Fr::NUM_BITS) let powers_of_two = (0..E::Fr::NUM_BITS)
.map(|i| E::Fr::from_str("2").unwrap().pow(&[i as u64])) .map(|i| E::Fr::from_str("2").unwrap().pow(&[u64::from(i)]))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let pp = |s: &mut String, lc: &LinearCombination<E>| { let pp = |s: &mut String, lc: &LinearCombination<E>| {
@@ -286,7 +287,7 @@ impl<E: Engine> TestConstraintSystem<E> {
} }
} }
return true; true
} }
pub fn num_inputs(&self) -> usize { pub fn num_inputs(&self) -> usize {
@@ -344,7 +345,7 @@ fn compute_path(ns: &[String], this: String) -> String {
name name
} }
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> { impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError> fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>

View File

@@ -1,5 +1,9 @@
use ff::{Field, PrimeField}; //! Circuit representation of a [`u32`], with helpers for the [`sha256`]
use pairing::Engine; //! gadgets.
//!
//! [`sha256`]: crate::gadgets::sha256
use ff::{Field, PrimeField, ScalarEngine};
use crate::{ConstraintSystem, LinearCombination, SynthesisError}; use crate::{ConstraintSystem, LinearCombination, SynthesisError};
@@ -33,7 +37,7 @@ impl UInt32 {
} }
UInt32 { UInt32 {
bits: bits, bits,
value: Some(value), value: Some(value),
} }
} }
@@ -41,7 +45,7 @@ impl UInt32 {
/// Allocate a `UInt32` in the constraint system /// Allocate a `UInt32` in the constraint system
pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError> pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let values = match value { let values = match value {
@@ -69,14 +73,13 @@ impl UInt32 {
}) })
.collect::<Result<Vec<_>, SynthesisError>>()?; .collect::<Result<Vec<_>, SynthesisError>>()?;
Ok(UInt32 { Ok(UInt32 { bits, value })
bits: bits,
value: value,
})
} }
pub fn into_bits_be(&self) -> Vec<Boolean> { pub fn into_bits_be(self) -> Vec<Boolean> {
self.bits.iter().rev().cloned().collect() let mut ret = self.bits;
ret.reverse();
ret
} }
pub fn from_bits_be(bits: &[Boolean]) -> Self { pub fn from_bits_be(bits: &[Boolean]) -> Self {
@@ -98,14 +101,14 @@ impl UInt32 {
} }
UInt32 { UInt32 {
value: value, value,
bits: bits.iter().rev().cloned().collect(), bits: bits.iter().rev().cloned().collect(),
} }
} }
/// Turns this `UInt32` into its little-endian byte order representation. /// Turns this `UInt32` into its little-endian byte order representation.
pub fn into_bits(&self) -> Vec<Boolean> { pub fn into_bits(self) -> Vec<Boolean> {
self.bits.clone() self.bits
} }
/// Converts a little-endian byte order representation of bits into a /// Converts a little-endian byte order representation of bits into a
@@ -119,20 +122,20 @@ impl UInt32 {
for b in new_bits.iter().rev() { for b in new_bits.iter().rev() {
value.as_mut().map(|v| *v <<= 1); value.as_mut().map(|v| *v <<= 1);
match b { match *b {
&Boolean::Constant(b) => { Boolean::Constant(b) => {
if b { if b {
value.as_mut().map(|v| *v |= 1); value.as_mut().map(|v| *v |= 1);
} }
} }
&Boolean::Is(ref b) => match b.get_value() { Boolean::Is(ref b) => match b.get_value() {
Some(true) => { Some(true) => {
value.as_mut().map(|v| *v |= 1); value.as_mut().map(|v| *v |= 1);
} }
Some(false) => {} Some(false) => {}
None => value = None, None => value = None,
}, },
&Boolean::Not(ref b) => match b.get_value() { Boolean::Not(ref b) => match b.get_value() {
Some(false) => { Some(false) => {
value.as_mut().map(|v| *v |= 1); value.as_mut().map(|v| *v |= 1);
} }
@@ -143,7 +146,7 @@ impl UInt32 {
} }
UInt32 { UInt32 {
value: value, value,
bits: new_bits, bits: new_bits,
} }
} }
@@ -195,7 +198,7 @@ impl UInt32 {
circuit_fn: U, circuit_fn: U,
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
F: Fn(u32, u32, u32) -> u32, F: Fn(u32, u32, u32) -> u32,
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>, U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
@@ -215,7 +218,7 @@ impl UInt32 {
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits,
value: new_value, value: new_value,
}) })
} }
@@ -224,7 +227,7 @@ impl UInt32 {
/// during SHA256. /// during SHA256.
pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError> pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
Self::triop( Self::triop(
@@ -241,7 +244,7 @@ impl UInt32 {
/// during SHA256. /// during SHA256.
pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError> pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
Self::triop( Self::triop(
@@ -257,7 +260,7 @@ impl UInt32 {
/// XOR this `UInt32` with another `UInt32` /// XOR this `UInt32` with another `UInt32`
pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError> pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
{ {
let new_value = match (self.value, other.value) { let new_value = match (self.value, other.value) {
@@ -274,7 +277,7 @@ impl UInt32 {
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits,
value: new_value, value: new_value,
}) })
} }
@@ -282,7 +285,7 @@ impl UInt32 {
/// Perform modular addition of several `UInt32` objects. /// Perform modular addition of several `UInt32` objects.
pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError> pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
where where
E: Engine, E: ScalarEngine,
CS: ConstraintSystem<E>, CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root = MultiEq<E, CS>>, M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
{ {
@@ -294,7 +297,7 @@ impl UInt32 {
// Compute the maximum value of the sum so we allocate enough bits for // Compute the maximum value of the sum so we allocate enough bits for
// the result // the result
let mut max_value = (operands.len() as u64) * (u32::max_value() as u64); let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value()));
// Keep track of the resulting value // Keep track of the resulting value
let mut result_value = Some(0u64); let mut result_value = Some(0u64);
@@ -310,7 +313,7 @@ impl UInt32 {
// Accumulate the value // Accumulate the value
match op.value { match op.value {
Some(val) => { Some(val) => {
result_value.as_mut().map(|v| *v += val as u64); result_value.as_mut().map(|v| *v += u64::from(val));
} }
None => { None => {
// If any of our operands have unknown value, we won't // If any of our operands have unknown value, we won't
@@ -401,15 +404,15 @@ mod test {
]); ]);
for _ in 0..1000 { for _ in 0..1000 {
let mut v = (0..32) let v = (0..32)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) .map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let b = UInt32::from_bits_be(&v); let b = UInt32::from_bits_be(&v);
for (i, bit) in b.bits.iter().enumerate() { for (i, bit) in b.bits.iter().enumerate() {
match bit { match *bit {
&Boolean::Constant(bit) => { Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
} }
_ => unreachable!(), _ => unreachable!(),
@@ -436,15 +439,15 @@ mod test {
]); ]);
for _ in 0..1000 { for _ in 0..1000 {
let mut v = (0..32) let v = (0..32)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) .map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let b = UInt32::from_bits(&v); let b = UInt32::from_bits(&v);
for (i, bit) in b.bits.iter().enumerate() { for (i, bit) in b.bits.iter().enumerate() {
match bit { match *bit {
&Boolean::Constant(bit) => { Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
} }
_ => unreachable!(), _ => unreachable!(),
@@ -491,14 +494,14 @@ mod test {
assert!(r.value == Some(expected)); assert!(r.value == Some(expected));
for b in r.bits.iter() { for b in r.bits.iter() {
match b { match *b {
&Boolean::Is(ref b) => { Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
} }
&Boolean::Not(ref b) => { Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
} }
&Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
} }
@@ -538,10 +541,10 @@ mod test {
assert!(r.value == Some(expected)); assert!(r.value == Some(expected));
for b in r.bits.iter() { for b in r.bits.iter() {
match b { match *b {
&Boolean::Is(_) => panic!(), Boolean::Is(_) => panic!(),
&Boolean::Not(_) => panic!(), Boolean::Not(_) => panic!(),
&Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
} }
@@ -576,8 +579,7 @@ mod test {
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
let r = { let r = {
let mut cs = MultiEq::new(&mut cs); let mut cs = MultiEq::new(&mut cs);
let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap(); UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap()
r
}; };
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -585,14 +587,14 @@ mod test {
assert!(r.value == Some(expected)); assert!(r.value == Some(expected));
for b in r.bits.iter() { for b in r.bits.iter() {
match b { match *b {
&Boolean::Is(ref b) => { Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
} }
&Boolean::Not(ref b) => { Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
} }
&Boolean::Constant(_) => unreachable!(), Boolean::Constant(_) => unreachable!(),
} }
expected >>= 1; expected >>= 1;
@@ -628,8 +630,8 @@ mod test {
let mut tmp = num; let mut tmp = num;
for b in &b.bits { for b in &b.bits {
match b { match *b {
&Boolean::Constant(b) => { Boolean::Constant(b) => {
assert_eq!(b, tmp & 1 == 1); assert_eq!(b, tmp & 1 == 1);
} }
_ => unreachable!(), _ => unreachable!(),

View File

@@ -8,11 +8,11 @@ use pairing::Engine;
use super::{Parameters, VerifyingKey}; use super::{Parameters, VerifyingKey};
use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
use domain::{EvaluationDomain, Scalar}; use crate::domain::{EvaluationDomain, Scalar};
use multicore::Worker; use crate::multicore::Worker;
/// Generates a random common reference string for /// Generates a random common reference string for
/// a circuit. /// a circuit.
@@ -227,7 +227,7 @@ where
let powers_of_tau = powers_of_tau.as_mut(); let powers_of_tau = powers_of_tau.as_mut();
worker.scope(powers_of_tau.len(), |scope, chunk| { worker.scope(powers_of_tau.len(), |scope, chunk| {
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() { for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
scope.spawn(move || { scope.spawn(move |_scope| {
let mut current_tau_power = tau.pow(&[(i * chunk) as u64]); let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
for p in powers_of_tau { for p in powers_of_tau {
@@ -251,7 +251,7 @@ where
{ {
let mut g1_wnaf = g1_wnaf.shared(); let mut g1_wnaf = g1_wnaf.shared();
scope.spawn(move || { scope.spawn(move |_scope| {
// Set values of the H query to g1^{(tau^i * t(tau)) / delta} // Set values of the H query to g1^{(tau^i * t(tau)) / delta}
for (h, p) in h.iter_mut().zip(p.iter()) { for (h, p) in h.iter_mut().zip(p.iter()) {
// Compute final exponent // Compute final exponent
@@ -330,7 +330,7 @@ where
let mut g1_wnaf = g1_wnaf.shared(); let mut g1_wnaf = g1_wnaf.shared();
let mut g2_wnaf = g2_wnaf.shared(); let mut g2_wnaf = g2_wnaf.shared();
scope.spawn(move || { scope.spawn(move |_scope| {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
.iter_mut() .iter_mut()
.zip(b_g1.iter_mut()) .zip(b_g1.iter_mut())
@@ -451,7 +451,7 @@ where
}; };
Ok(Parameters { Ok(Parameters {
vk: vk, vk,
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()), h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),

View File

@@ -1,10 +1,14 @@
//! The [Groth16] proving system.
//!
//! [Groth16]: https://eprint.iacr.org/2016/260
use group::{CurveAffine, EncodedPoint}; use group::{CurveAffine, EncodedPoint};
use pairing::{Engine, PairingCurveAffine}; use pairing::{Engine, PairingCurveAffine};
use SynthesisError; use crate::SynthesisError;
use crate::multiexp::SourceBuilder;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use multiexp::SourceBuilder;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::sync::Arc; use std::sync::Arc;
@@ -90,7 +94,7 @@ impl<E: Engine> Proof<E> {
} }
})?; })?;
Ok(Proof { a: a, b: b, c: c }) Ok(Proof { a, b, c })
} }
} }
@@ -208,13 +212,13 @@ impl<E: Engine> VerifyingKey<E> {
} }
Ok(VerifyingKey { Ok(VerifyingKey {
alpha_g1: alpha_g1, alpha_g1,
beta_g1: beta_g1, beta_g1,
beta_g2: beta_g2, beta_g2,
gamma_g2: gamma_g2, gamma_g2,
delta_g1: delta_g1, delta_g1,
delta_g2: delta_g2, delta_g2,
ic: ic, ic,
}) })
} }
} }
@@ -376,7 +380,7 @@ impl<E: Engine> Parameters<E> {
} }
Ok(Parameters { Ok(Parameters {
vk: vk, vk,
h: Arc::new(h), h: Arc::new(h),
l: Arc::new(l), l: Arc::new(l),
a: Arc::new(a), a: Arc::new(a),
@@ -465,7 +469,7 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
#[cfg(test)] #[cfg(test)]
mod test_with_bls12_381 { mod test_with_bls12_381 {
use super::*; use super::*;
use {Circuit, ConstraintSystem, SynthesisError}; use crate::{Circuit, ConstraintSystem, SynthesisError};
use ff::Field; use ff::Field;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};

View File

@@ -10,13 +10,13 @@ use pairing::Engine;
use super::{ParameterSource, Proof}; use super::{ParameterSource, Proof};
use {Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
use domain::{EvaluationDomain, Scalar}; use crate::domain::{EvaluationDomain, Scalar};
use multiexp::{multiexp, DensityTracker, FullDensity}; use crate::multiexp::{multiexp, DensityTracker, FullDensity};
use multicore::Worker; use crate::multicore::Worker;
fn eval<E: Engine>( fn eval<E: Engine>(
lc: &LinearCombination<E>, lc: &LinearCombination<E>,

View File

@@ -16,13 +16,13 @@ const MODULUS_R: Wrapping<u32> = Wrapping(64513);
pub struct Fr(Wrapping<u32>); pub struct Fr(Wrapping<u32>);
impl fmt::Display for Fr { impl fmt::Display for Fr {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", (self.0).0) write!(f, "{}", (self.0).0)
} }
} }
impl Field for Fr { impl Field for Fr {
fn random<R: RngCore>(rng: &mut R) -> Self { fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fr(Wrapping(rng.next_u32()) % MODULUS_R) Fr(Wrapping(rng.next_u32()) % MODULUS_R)
} }
@@ -149,7 +149,7 @@ impl PartialOrd for FrRepr {
} }
impl fmt::Display for FrRepr { impl fmt::Display for FrRepr {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", (self.0)[0]) write!(f, "{}", (self.0)[0])
} }
} }

View File

@@ -6,7 +6,7 @@ use self::dummy_engine::*;
use std::marker::PhantomData; use std::marker::PhantomData;
use {Circuit, ConstraintSystem, SynthesisError}; use crate::{Circuit, ConstraintSystem, SynthesisError};
use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof}; use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof};

View File

@@ -4,7 +4,7 @@ use pairing::{Engine, PairingCurveAffine};
use super::{PreparedVerifyingKey, Proof, VerifyingKey}; use super::{PreparedVerifyingKey, Proof, VerifyingKey};
use SynthesisError; use crate::SynthesisError;
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> { pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
let mut gamma = vk.gamma_g2; let mut gamma = vk.gamma_g2;
@@ -49,7 +49,7 @@ pub fn verify_proof<'a, E: Engine>(
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2), (&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
(&proof.c.prepare(), &pvk.neg_delta_g2), (&proof.c.prepare(), &pvk.neg_delta_g2),
] ]
.into_iter(), .iter(),
)) ))
.unwrap() .unwrap()
== pvk.alpha_g1_beta_g2) == pvk.alpha_g1_beta_g2)

View File

@@ -1,33 +1,139 @@
extern crate ff; //! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
extern crate group; //! traits and and primitive structures, as well as basic gadget implementations
#[cfg(feature = "pairing")] //! such as booleans and number abstractions.
extern crate pairing; //!
extern crate rand_core; //! # Example circuit
//!
//! Say we want to write a circuit that proves we know the preimage to some hash
//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a
//! fixed length known in advance (because the circuit parameters will depend on
//! it), but can otherwise have any value. We take the following strategy:
//!
//! - Witness each bit of the preimage.
//! - Compute `hash = SHA-256d(preimage)` inside the circuit.
//! - Expose `hash` as a public input using multiscalar packing.
//!
//! ```
//! use bellman::{
//! gadgets::{
//! boolean::{AllocatedBit, Boolean},
//! multipack,
//! sha256::sha256,
//! },
//! groth16, Circuit, ConstraintSystem, SynthesisError,
//! };
//! use pairing::{bls12_381::Bls12, Engine};
//! use rand::rngs::OsRng;
//! use sha2::{Digest, Sha256};
//!
//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order.
//! fn sha256d<E: Engine, CS: ConstraintSystem<E>>(
//! mut cs: CS,
//! data: &[Boolean],
//! ) -> Result<Vec<Boolean>, SynthesisError> {
//! // Flip endianness of each input byte
//! let input: Vec<_> = data
//! .chunks(8)
//! .map(|c| c.iter().rev())
//! .flatten()
//! .cloned()
//! .collect();
//!
//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;
//!
//! // Flip endianness of each output byte
//! Ok(res
//! .chunks(8)
//! .map(|c| c.iter().rev())
//! .flatten()
//! .cloned()
//! .collect())
//! }
//!
//! struct MyCircuit {
//! /// The input to SHA-256d we are proving that we know. Set to `None` when we
//! /// are verifying a proof (and do not have the witness data).
//! preimage: Option<[u8; 80]>,
//! }
//!
//! impl<E: Engine> Circuit<E> for MyCircuit {
//! fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
//! // Compute the values for the bits of the preimage. If we are verifying a proof,
//! // we still need to create the same constraints, so we return an equivalent-size
//! // Vec of None (indicating that the value of each bit is unknown).
//! let bit_values = if let Some(preimage) = self.preimage {
//! preimage
//! .into_iter()
//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
//! .flatten()
//! .map(|b| Some(b))
//! .collect()
//! } else {
//! vec![None; 80 * 8]
//! };
//! assert_eq!(bit_values.len(), 80 * 8);
//!
//! // Witness the bits of the preimage.
//! let preimage_bits = bit_values
//! .into_iter()
//! .enumerate()
//! // Allocate each bit.
//! .map(|(i, b)| {
//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b)
//! })
//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
//! .map(|b| b.map(Boolean::from))
//! .collect::<Result<Vec<_>, _>>()?;
//!
//! // Compute hash = SHA-256d(preimage).
//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;
//!
//! // Expose the vector of 32 boolean variables as compact public inputs.
//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
//! }
//! }
//!
//! // Create parameters for our circuit. In a production deployment these would
//! // be generated securely using a multiparty computation.
//! let params = {
//! let c = MyCircuit { preimage: None };
//! groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
//! };
//!
//! // Prepare the verification key (for proof verification).
//! let pvk = groth16::prepare_verifying_key(&params.vk);
//!
//! // Pick a preimage and compute its hash.
//! let preimage = [42; 80];
//! let hash = Sha256::digest(&Sha256::digest(&preimage));
//!
//! // Create an instance of our circuit (with the preimage as a witness).
//! let c = MyCircuit {
//! preimage: Some(preimage),
//! };
//!
//! // Create a Groth16 proof with our parameters.
//! let proof = groth16::create_random_proof(c, &params, &mut OsRng).unwrap();
//!
//! // Pack the hash as inputs for proof verification.
//! let hash_bits = multipack::bytes_to_bits_le(&hash);
//! let inputs = multipack::compute_multipacking::<Bls12>(&hash_bits);
//!
//! // Check the proof!
//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap());
//! ```
//!
//! # Roadmap
//!
//! `bellman` is being refactored into a generic proving library. Currently it
//! is pairing-specific, and different types of proving systems need to be
//! implemented as sub-modules. After the refactor, `bellman` will be generic
//! using the [`ff`] and [`group`] crates, while specific proving systems will
//! be separate crates that pull in the dependencies they require.
extern crate bit_vec; // Catch documentation errors caused by code changes.
extern crate blake2s_simd; #![deny(intra_doc_link_resolution_failure)]
extern crate byteorder;
extern crate futures;
#[cfg(feature = "multicore")]
extern crate crossbeam;
#[cfg(feature = "multicore")]
extern crate futures_cpupool;
#[cfg(feature = "multicore")]
extern crate num_cpus;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[cfg(test)]
extern crate rand;
#[cfg(test)]
extern crate rand_xorshift;
#[cfg(test)]
extern crate sha2;
pub mod domain; pub mod domain;
pub mod gadgets; pub mod gadgets;
@@ -109,6 +215,7 @@ impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> { impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> { fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
coeff.negate(); coeff.negate();
@@ -230,8 +337,8 @@ impl Error for SynthesisError {
} }
impl fmt::Display for SynthesisError { impl fmt::Display for SynthesisError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
if let &SynthesisError::IoError(ref e) = self { if let SynthesisError::IoError(ref e) = *self {
write!(f, "I/O error: ")?; write!(f, "I/O error: ")?;
e.fmt(f) e.fmt(f)
} else { } else {
@@ -296,7 +403,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
fn get_root(&mut self) -> &mut Self::Root; fn get_root(&mut self) -> &mut Self::Root;
/// Begin a namespace for this constraint system. /// Begin a namespace for this constraint system.
fn namespace<'a, NR, N>(&'a mut self, name_fn: N) -> Namespace<'a, E, Self::Root> fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root>
where where
NR: Into<String>, NR: Into<String>,
N: FnOnce() -> NR, N: FnOnce() -> NR,
@@ -309,7 +416,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// This is a "namespaced" constraint system which borrows a constraint system (pushing /// This is a "namespaced" constraint system which borrows a constraint system (pushing
/// a namespace context) and, when dropped, pops out of the namespace context. /// a namespace context) and, when dropped, pops out of the namespace context.
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>); pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E>>(&'a mut CS, PhantomData<E>);
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> { impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
type Root = CS::Root; type Root = CS::Root;

View File

@@ -1,12 +1,13 @@
//! This is an interface for dealing with the kinds of //! An interface for dealing with the kinds of parallel computations involved in
//! parallel computations involved in bellman. It's //! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and
//! currently just a thin wrapper around CpuPool and //! [`crossbeam`] but may be extended in the future to allow for various
//! crossbeam but may be extended in the future to //! parallelism strategies.
//! allow for various parallelism strategies. //!
//! [`CpuPool`]: futures_cpupool::CpuPool
#[cfg(feature = "multicore")] #[cfg(feature = "multicore")]
mod implementation { mod implementation {
use crossbeam::{self, Scope}; use crossbeam::{self, thread::Scope};
use futures::{Future, IntoFuture, Poll}; use futures::{Future, IntoFuture, Poll};
use futures_cpupool::{CpuFuture, CpuPool}; use futures_cpupool::{CpuFuture, CpuPool};
use num_cpus; use num_cpus;
@@ -23,7 +24,7 @@ mod implementation {
// CPUs configured. // CPUs configured.
pub(crate) fn new_with_cpus(cpus: usize) -> Worker { pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
Worker { Worker {
cpus: cpus, cpus,
pool: CpuPool::new(cpus), pool: CpuPool::new(cpus),
} }
} }
@@ -59,7 +60,9 @@ mod implementation {
elements / self.cpus elements / self.cpus
}; };
// TODO: Handle case where threads fail
crossbeam::scope(|scope| f(scope, chunk_size)) crossbeam::scope(|scope| f(scope, chunk_size))
.expect("Threads aren't allowed to fail yet")
} }
} }
@@ -152,8 +155,8 @@ mod implementation {
pub struct DummyScope; pub struct DummyScope;
impl DummyScope { impl DummyScope {
pub fn spawn<F: FnOnce()>(&self, f: F) { pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
f(); f(self);
} }
} }
} }

View File

@@ -153,7 +153,7 @@ fn multiexp_inner<Q, D, G, S>(
mut skip: u32, mut skip: u32,
c: u32, c: u32,
handle_trivial: bool, handle_trivial: bool,
) -> Box<Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>> ) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
where where
for<'a> &'a Q: QueryDensity, for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>, D: Send + Sync + 'static + Clone + AsRef<Q>,
@@ -256,7 +256,7 @@ pub fn multiexp<Q, D, G, S>(
bases: S, bases: S,
density_map: D, density_map: D,
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>, exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
) -> Box<Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>> ) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
where where
for<'a> &'a Q: QueryDensity, for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>, D: Send + Sync + 'static + Clone + AsRef<Q>,

View File

@@ -1,8 +1,3 @@
extern crate bellman;
extern crate ff;
extern crate pairing;
extern crate rand;
// For randomness (during paramgen and proof generation) // For randomness (during paramgen and proof generation)
use rand::thread_rng; use rand::thread_rng;
@@ -90,12 +85,12 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
let cs = &mut cs.namespace(|| format!("round {}", i)); let cs = &mut cs.namespace(|| format!("round {}", i));
// tmp = (xL + Ci)^2 // tmp = (xL + Ci)^2
let mut tmp_value = xl_value.map(|mut e| { let tmp_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]); e.add_assign(&self.constants[i]);
e.square(); e.square();
e e
}); });
let mut tmp = cs.alloc( let tmp = cs.alloc(
|| "tmp", || "tmp",
|| tmp_value.ok_or(SynthesisError::AssignmentMissing), || tmp_value.ok_or(SynthesisError::AssignmentMissing),
)?; )?;
@@ -110,14 +105,14 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
// new_xL = xR + (xL + Ci)^3 // new_xL = xR + (xL + Ci)^3
// new_xL = xR + tmp * (xL + Ci) // new_xL = xR + tmp * (xL + Ci)
// new_xL - xR = tmp * (xL + Ci) // new_xL - xR = tmp * (xL + Ci)
let mut new_xl_value = xl_value.map(|mut e| { let new_xl_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]); e.add_assign(&self.constants[i]);
e.mul_assign(&tmp_value.unwrap()); e.mul_assign(&tmp_value.unwrap());
e.add_assign(&xr_value.unwrap()); e.add_assign(&xr_value.unwrap());
e e
}); });
let mut new_xl = if i == (MIMC_ROUNDS - 1) { let new_xl = if i == (MIMC_ROUNDS - 1) {
// This is the last round, xL is our image and so // This is the last round, xL is our image and so
// we allocate a public input. // we allocate a public input.
cs.alloc_input( cs.alloc_input(

View File

@@ -1,18 +1,23 @@
[package] [package]
name = "ff" name = "ff"
version = "0.4.0" version = "0.5.2"
authors = ["Sean Bowe <ewillbefull@gmail.com>"] authors = ["Sean Bowe <ewillbefull@gmail.com>"]
description = "Library for building and interfacing with finite fields" description = "Library for building and interfacing with finite fields"
readme = "README.md"
documentation = "https://docs.rs/ff/" documentation = "https://docs.rs/ff/"
homepage = "https://github.com/ebfull/ff" homepage = "https://github.com/ebfull/ff"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
repository = "https://github.com/ebfull/ff" repository = "https://github.com/ebfull/ff"
edition = "2018"
[dependencies] [dependencies]
byteorder = "1" byteorder = "1"
ff_derive = { version = "0.3.0", path = "ff_derive", optional = true } ff_derive = { version = "^0.4.1", path = "ff_derive", optional = true }
rand_core = "0.5" rand_core = "0.5"
[features] [features]
default = [] default = []
derive = ["ff_derive"] derive = ["ff_derive"]
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -12,14 +12,18 @@ Add the `ff` crate to your `Cargo.toml`:
```toml ```toml
[dependencies] [dependencies]
ff = "0.4" ff = "0.5"
``` ```
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. See the **[documentation](https://docs.rs/ff/0.4.0/ff/)** for more. The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits.
See the **[documentation](https://docs.rs/ff/)** for more.
### #![derive(PrimeField)] ### #![derive(PrimeField)]
If you need an implementation of a prime field, this library also provides a procedural macro that will expand into an efficient implementation of a prime field when supplied with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is also quadratic nonresidue. If you need an implementation of a prime field, this library also provides a procedural
macro that will expand into an efficient implementation of a prime field when supplied
with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is
also quadratic nonresidue.
First, enable the `derive` crate feature: First, enable the `derive` crate feature:
@@ -41,13 +45,16 @@ extern crate ff;
struct Fp(FpRepr); struct Fp(FpRepr);
``` ```
And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement `SqrtField` if supported. The library implements `FpRepr` itself and derives `PrimeFieldRepr` for it. And that's it! `Fp` now implements `Field` and `PrimeField`. `Fp` will also implement
`SqrtField` if supported. The library implements `FpRepr` itself and derives
`PrimeFieldRepr` for it.
## License ## License
Licensed under either of Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option. at your option.

View File

@@ -1,12 +1,13 @@
[package] [package]
name = "ff_derive" name = "ff_derive"
version = "0.3.0" version = "0.4.1"
authors = ["Sean Bowe <ewillbefull@gmail.com>"] authors = ["Sean Bowe <ewillbefull@gmail.com>"]
description = "Procedural macro library used to build custom prime field implementations" description = "Procedural macro library used to build custom prime field implementations"
documentation = "https://docs.rs/ff/" documentation = "https://docs.rs/ff/"
homepage = "https://github.com/ebfull/ff" homepage = "https://github.com/ebfull/ff"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
repository = "https://github.com/ebfull/ff" repository = "https://github.com/ebfull/ff"
edition = "2018"
[lib] [lib]
proc-macro = true proc-macro = true
@@ -15,6 +16,9 @@ proc-macro = true
num-bigint = "0.2" num-bigint = "0.2"
num-traits = "0.2" num-traits = "0.2"
num-integer = "0.1" num-integer = "0.1"
proc-macro2 = "0.4" proc-macro2 = "1"
quote = "0.6" quote = "1"
syn = "0.14" syn = "1"
[badges]
maintenance = { status = "passively-maintained" }

View File

@@ -2,17 +2,11 @@
extern crate proc_macro; extern crate proc_macro;
extern crate proc_macro2; extern crate proc_macro2;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate num_bigint;
extern crate num_integer;
extern crate num_traits;
use num_bigint::BigUint; use num_bigint::BigUint;
use num_integer::Integer; use num_integer::Integer;
use num_traits::{One, ToPrimitive, Zero}; use num_traits::{One, ToPrimitive, Zero};
use quote::quote;
use quote::TokenStreamExt; use quote::TokenStreamExt;
use std::str::FromStr; use std::str::FromStr;
@@ -91,10 +85,10 @@ fn fetch_wrapped_ident(body: &syn::Data) -> Option<syn::Ident> {
/// Fetch an attribute string from the derived struct. /// Fetch an attribute string from the derived struct.
fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> { fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> {
for attr in attrs { for attr in attrs {
if let Some(meta) = attr.interpret_meta() { if let Ok(meta) = attr.parse_meta() {
match meta { match meta {
syn::Meta::NameValue(nv) => { syn::Meta::NameValue(nv) => {
if nv.ident.to_string() == name { if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) {
match nv.lit { match nv.lit {
syn::Lit::Str(ref s) => return Some(s.value()), syn::Lit::Str(ref s) => return Some(s.value()),
_ => { _ => {
@@ -122,9 +116,9 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
impl ::std::fmt::Debug for #repr impl ::std::fmt::Debug for #repr
{ {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
try!(write!(f, "0x")); write!(f, "0x")?;
for i in self.0.iter().rev() { for i in self.0.iter().rev() {
try!(write!(f, "{:016x}", *i)); write!(f, "{:016x}", *i)?;
} }
Ok(()) Ok(())
@@ -133,9 +127,9 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
impl ::std::fmt::Display for #repr { impl ::std::fmt::Display for #repr {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
try!(write!(f, "0x")); write!(f, "0x")?;
for i in self.0.iter().rev() { for i in self.0.iter().rev() {
try!(write!(f, "{:016x}", *i)); write!(f, "{:016x}", *i)?;
} }
Ok(()) Ok(())
@@ -883,7 +877,7 @@ fn prime_field_impl(
impl ::ff::Field for #name { impl ::ff::Field for #name {
/// Computes a uniformly random element using rejection sampling. /// Computes a uniformly random element using rejection sampling.
fn random<R: ::rand_core::RngCore>(rng: &mut R) -> Self { fn random<R: ::rand_core::RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
loop { loop {
let mut tmp = { let mut tmp = {
let mut repr = [0u64; #limbs]; let mut repr = [0u64; #limbs];

View File

@@ -1,12 +1,9 @@
//! This crate provides traits for working with finite fields.
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
#![allow(unused_imports)] #![allow(unused_imports)]
extern crate byteorder;
extern crate rand_core;
#[cfg(feature = "derive")]
#[macro_use]
extern crate ff_derive;
#[cfg(feature = "derive")] #[cfg(feature = "derive")]
pub use ff_derive::*; pub use ff_derive::*;
@@ -20,7 +17,7 @@ pub trait Field:
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
{ {
/// Returns an element chosen uniformly at random using a user-provided RNG. /// Returns an element chosen uniformly at random using a user-provided RNG.
fn random<R: RngCore>(rng: &mut R) -> Self; fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self;
/// Returns the zero element of the field, the additive identity. /// Returns the zero element of the field, the additive identity.
fn zero() -> Self; fn zero() -> Self;
@@ -210,7 +207,7 @@ impl Error for PrimeFieldDecodingError {
} }
impl fmt::Display for PrimeFieldDecodingError { impl fmt::Display for PrimeFieldDecodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self { match *self {
PrimeFieldDecodingError::NotInField(ref repr) => { PrimeFieldDecodingError::NotInField(ref repr) => {
write!(f, "{} is not an element of the field", repr) write!(f, "{} is not an element of the field", repr)
@@ -266,7 +263,7 @@ pub trait PrimeField: Field {
} }
/// Convert this prime field element into a biginteger representation. /// Convert this prime field element into a biginteger representation.
fn from_repr(Self::Repr) -> Result<Self, PrimeFieldDecodingError>; fn from_repr(_: Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
/// Convert a biginteger representation into a prime field element, if /// Convert a biginteger representation into a prime field element, if
/// the number is an element of the field. /// the number is an element of the field.

View File

@@ -1,18 +1,23 @@
[package] [package]
name = "group" name = "group"
version = "0.1.0" version = "0.2.0"
authors = [ authors = [
"Sean Bowe <ewillbefull@gmail.com>", "Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>", "Jack Grigg <jack@z.cash>",
] ]
readme = "README.md"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
description = "Elliptic curve group traits and utilities" description = "Elliptic curve group traits and utilities"
documentation = "https://docs.rs/group/" documentation = "https://docs.rs/group/"
homepage = "https://github.com/ebfull/group" homepage = "https://github.com/ebfull/group"
repository = "https://github.com/ebfull/group" repository = "https://github.com/ebfull/group"
edition = "2018"
[dependencies] [dependencies]
ff = { path = "../ff" } ff = { version = "0.5.0", path = "../ff" }
rand = "0.7" rand = "0.7"
rand_xorshift = "0.2" rand_xorshift = "0.2"
[badges]
maintenance = { status = "actively-developed" }

View File

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

View File

@@ -1,6 +1,5 @@
extern crate ff; // Catch documentation errors caused by code changes.
extern crate rand; #![deny(intra_doc_link_resolution_failure)]
extern crate rand_xorshift;
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField}; use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
use rand::RngCore; use rand::RngCore;
@@ -180,7 +179,7 @@ impl Error for GroupDecodingError {
} }
impl fmt::Display for GroupDecodingError { impl fmt::Display for GroupDecodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self { match *self {
GroupDecodingError::CoordinateDecodingError(description, ref err) => { GroupDecodingError::CoordinateDecodingError(description, ref err) => {
write!(f, "{} decoding error: {}", description, err) write!(f, "{} decoding error: {}", description, err)

View File

@@ -2,7 +2,7 @@ use ff::{Field, PrimeField};
use rand::SeedableRng; use rand::SeedableRng;
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use {CurveAffine, CurveProjective, EncodedPoint}; use crate::{CurveAffine, CurveProjective, EncodedPoint};
pub fn curve_tests<G: CurveProjective>() { pub fn curve_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@@ -71,7 +71,7 @@ pub fn curve_tests<G: CurveProjective>() {
} }
fn random_wnaf_tests<G: CurveProjective>() { fn random_wnaf_tests<G: CurveProjective>() {
use wnaf::*; use crate::wnaf::*;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,

View File

@@ -1,12 +1,18 @@
[package] [package]
name = "librustzcash" name = "librustzcash"
version = "0.1.0" description = "Rust FFI used by the zcashd binary. Not an official API."
version = "0.2.0"
authors = [ authors = [
"Sean Bowe <ewillbefull@gmail.com>", "Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>", "Jack Grigg <jack@z.cash>",
"Jay Graber <jay@z.cash>", "Jay Graber <jay@z.cash>",
"Simon Liu <simon@z.cash>" "Simon Liu <simon@z.cash>"
] ]
homepage = "https://github.com/zcash/librustzcash"
repository = "https://github.com/zcash/librustzcash"
readme = "README.md"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib] [lib]
name = "rustzcash" name = "rustzcash"
@@ -14,15 +20,17 @@ path = "src/rustzcash.rs"
crate-type = ["staticlib"] crate-type = ["staticlib"]
[dependencies] [dependencies]
bellman = { path = "../bellman" } bellman = { version = "0.2.0", path = "../bellman" }
blake2b_simd = "0.5" blake2b_simd = "0.5"
blake2s_simd = "0.5" blake2s_simd = "0.5"
ff = { path = "../ff" } ff = { version = "0.5.0", path = "../ff" }
libc = "0.2" libc = "0.2"
pairing = { path = "../pairing" } pairing = { version = "0.15.0", path = "../pairing" }
lazy_static = "1" lazy_static = "1"
byteorder = "1" rand_core = "0.5.1"
rand_core = "0.5" zcash_history = { version = "0.0.1", path = "../zcash_history" }
rand_os = "0.2" zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
zcash_primitives = { path = "../zcash_primitives" } zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
zcash_proofs = { path = "../zcash_proofs" }
[badges]
maintenance = { status = "deprecated" }

View File

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

View File

@@ -112,8 +112,7 @@ extern "C" {
bool librustzcash_sapling_output_proof( bool librustzcash_sapling_output_proof(
void *ctx, void *ctx,
const unsigned char *esk, const unsigned char *esk,
const unsigned char *diversifier, const unsigned char *payment_address,
const unsigned char *pk_d,
const unsigned char *rcm, const unsigned char *rcm,
const uint64_t value, const uint64_t value,
unsigned char *cv, unsigned char *cv,
@@ -308,6 +307,33 @@ extern "C" {
unsigned char *j_ret, unsigned char *j_ret,
unsigned char *addr_ret unsigned char *addr_ret
); );
uint32_t librustzcash_mmr_append(
uint32_t cbranch,
uint32_t t_len,
const uint32_t *ni_ptr,
const unsigned char *n_ptr,
size_t p_len,
const unsigned char *nn_ptr,
unsigned char *rt_ret,
unsigned char *buf_ret
);
uint32_t librustzcash_mmr_delete(
uint32_t cbranch,
uint32_t t_len,
const uint32_t *ni_ptr,
const unsigned char *n_ptr,
size_t p_len,
size_t e_len,
unsigned char *rt_ret
);
uint32_t librustzcash_mmr_hash_node(
uint32_t cbranch,
const unsigned char *n_ptr,
unsigned char *h_ret
);
} }
#endif // LIBRUSTZCASH_INCLUDE_H_ #endif // LIBRUSTZCASH_INCLUDE_H_

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,10 @@
use ff::{PrimeField, PrimeFieldRepr}; use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand_core::{OsRng, RngCore};
use rand_os::OsRng;
use zcash_primitives::jubjub::{edwards, JubjubBls12}; use zcash_primitives::jubjub::{edwards, JubjubBls12};
use zcash_primitives::primitives::{Diversifier, ViewingKey}; use zcash_primitives::primitives::{Diversifier, ViewingKey};
use { use crate::{
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree, librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
librustzcash_sapling_ka_derivepublic, librustzcash_sapling_ka_derivepublic,
}; };
@@ -25,7 +24,7 @@ fn test_key_agreement() {
let addr = loop { let addr = loop {
let mut d = [0; 11]; let mut d = [0; 11];
rng.fill_bytes(&mut d); rng.fill_bytes(&mut d);
match vk.into_payment_address(Diversifier(d), &params) { match vk.to_payment_address(Diversifier(d), &params) {
Some(a) => break a, Some(a) => break a,
None => {} None => {}
} }
@@ -47,7 +46,7 @@ fn test_key_agreement() {
// Serialize pk_d for the call to librustzcash_sapling_ka_agree // Serialize pk_d for the call to librustzcash_sapling_ka_agree
let mut addr_pk_d = [0u8; 32]; let mut addr_pk_d = [0u8; 32];
addr.pk_d.write(&mut addr_pk_d[..]).unwrap(); addr.pk_d().write(&mut addr_pk_d[..]).unwrap();
assert!(librustzcash_sapling_ka_agree( assert!(librustzcash_sapling_ka_agree(
&addr_pk_d, &addr_pk_d,
@@ -59,7 +58,7 @@ fn test_key_agreement() {
// using the diversifier and esk. // using the diversifier and esk.
let mut epk = [0u8; 32]; let mut epk = [0u8; 32];
assert!(librustzcash_sapling_ka_derivepublic( assert!(librustzcash_sapling_ka_derivepublic(
&addr.diversifier.0, &addr.diversifier().0,
&esk, &esk,
&mut epk &mut epk
)); ));

View File

@@ -7,7 +7,7 @@ use zcash_primitives::{
use super::JUBJUB; use super::JUBJUB;
use { use crate::{
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk, librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk, librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
}; };
@@ -678,7 +678,7 @@ fn key_components() {
} }
let pgk = ProofGenerationKey { ak, nsk }; let pgk = ProofGenerationKey { ak, nsk };
let fvk = pgk.into_viewing_key(&JUBJUB); let fvk = pgk.to_viewing_key(&JUBJUB);
{ {
let mut vec = Vec::new(); let mut vec = Vec::new();
fvk.nk.write(&mut vec).unwrap(); fvk.nk.write(&mut vec).unwrap();
@@ -704,10 +704,10 @@ fn key_components() {
let diversifier = Diversifier(tv.default_d); let diversifier = Diversifier(tv.default_d);
assert!(librustzcash_check_diversifier(&tv.default_d)); assert!(librustzcash_check_diversifier(&tv.default_d));
let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap(); let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
{ {
let mut vec = Vec::new(); let mut vec = Vec::new();
addr.pk_d.write(&mut vec).unwrap(); addr.pk_d().write(&mut vec).unwrap();
assert_eq!(&vec, &tv.default_pk_d); assert_eq!(&vec, &tv.default_pk_d);
} }
{ {

View File

@@ -0,0 +1,225 @@
use zcash_history::{Entry, EntryLink, NodeData};
use crate::{librustzcash_mmr_append, librustzcash_mmr_delete};
const NODE_DATA_16L: &[u8] = include_bytes!("./res/tree16.dat");
const NODE_DATA_1023L: &[u8] = include_bytes!("./res/tree1023.dat");
struct TreeView {
peaks: Vec<(u32, Entry)>,
extra: Vec<(u32, Entry)>,
}
fn draft(into: &mut Vec<(u32, Entry)>, nodes: &[NodeData], peak_pos: usize, h: u32) {
let node_data = nodes[peak_pos - 1].clone();
let peak: Entry = match h {
0 => node_data.into(),
_ => Entry::new(
node_data,
EntryLink::Stored((peak_pos - (1 << h) - 1) as u32),
EntryLink::Stored((peak_pos - 2) as u32),
),
};
into.push(((peak_pos - 1) as u32, peak));
}
fn prepare_tree(nodes: &[NodeData]) -> TreeView {
assert!(!nodes.is_empty());
// integer log2 of (nodes.len()+1), -1
let mut h = (32 - ((nodes.len() + 1) as u32).leading_zeros() - 1) - 1;
let mut peak_pos = (1 << (h + 1)) - 1;
let mut peaks = Vec::new();
// used later
let mut last_peak_pos = 0;
let mut last_peak_h = 0;
loop {
if peak_pos > nodes.len() {
// left child, -2^h
peak_pos -= 1 << h;
h -= 1;
}
if peak_pos <= nodes.len() {
draft(&mut peaks, nodes, peak_pos, h);
// save to be used in next loop
last_peak_pos = peak_pos;
last_peak_h = h;
// right sibling
peak_pos += (1 << (h + 1)) - 1;
}
if h == 0 {
break;
}
}
// for deletion, everything on the right slope of the last peak should be pre-loaded
let mut extra = Vec::new();
let mut h = last_peak_h;
let mut peak_pos = last_peak_pos;
while h > 0 {
let left_pos = peak_pos - (1 << h);
let right_pos = peak_pos - 1;
h -= 1;
// drafting left child
draft(&mut extra, nodes, left_pos, h);
// drafting right child
draft(&mut extra, nodes, right_pos, h);
// continuing on right slope
peak_pos = right_pos;
}
TreeView { peaks, extra }
}
fn preload_tree_append(nodes: &[NodeData]) -> (Vec<u32>, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>) {
assert!(!nodes.is_empty());
let tree_view = prepare_tree(nodes);
let mut indices = Vec::new();
let mut bytes = Vec::new();
for (idx, entry) in tree_view.peaks.into_iter() {
let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE];
entry
.write(&mut &mut buf[..])
.expect("Cannot fail if enough buffer length");
indices.push(idx);
bytes.push(buf);
}
(indices, bytes)
}
// also returns number of peaks
fn preload_tree_delete(
nodes: &[NodeData],
) -> (Vec<u32>, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>, usize) {
assert!(!nodes.is_empty());
let tree_view = prepare_tree(nodes);
let mut indices = Vec::new();
let mut bytes = Vec::new();
let peak_count = tree_view.peaks.len();
for (idx, entry) in tree_view
.peaks
.into_iter()
.chain(tree_view.extra.into_iter())
{
let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE];
entry
.write(&mut &mut buf[..])
.expect("Cannot fail if enough buffer length");
indices.push(idx);
bytes.push(buf);
}
(indices, bytes, peak_count)
}
fn load_nodes(bytes: &'static [u8]) -> Vec<NodeData> {
let mut res = Vec::new();
let mut cursor = std::io::Cursor::new(bytes);
while (cursor.position() as usize) < bytes.len() {
let node_data = zcash_history::NodeData::read(0, &mut cursor)
.expect("Statically checked to be correct");
res.push(node_data);
}
res
}
#[test]
fn append() {
let nodes = load_nodes(NODE_DATA_16L);
let (indices, peaks) = preload_tree_append(&nodes);
let mut rt_ret = [0u8; 32];
let mut buf_ret = Vec::<[u8; zcash_history::MAX_NODE_DATA_SIZE]>::with_capacity(32);
let mut new_node_data = [0u8; zcash_history::MAX_NODE_DATA_SIZE];
let new_node = NodeData {
consensus_branch_id: 0,
subtree_commitment: [0u8; 32],
start_time: 101,
end_time: 110,
start_target: 190,
end_target: 200,
start_sapling_root: [0u8; 32],
end_sapling_root: [0u8; 32],
subtree_total_work: Default::default(),
start_height: 10,
end_height: 10,
sapling_tx: 13,
};
new_node
.write(&mut &mut new_node_data[..])
.expect("Failed to write node data");
let result = librustzcash_mmr_append(
0,
nodes.len() as u32,
indices.as_ptr(),
peaks.as_ptr(),
peaks.len(),
&new_node_data,
&mut rt_ret,
buf_ret.as_mut_ptr(),
);
unsafe {
buf_ret.set_len(result as usize);
}
assert_eq!(result, 2);
let new_node_1 =
NodeData::from_bytes(0, &buf_ret[0][..]).expect("Failed to reconstruct return node #1");
let new_node_2 =
NodeData::from_bytes(0, &buf_ret[1][..]).expect("Failed to reconstruct return node #2");
assert_eq!(new_node_1.start_height, 10);
assert_eq!(new_node_1.end_height, 10);
// this is combined new node (which is `new_node_1`) + the one which was there before (for block #9)
assert_eq!(new_node_2.start_height, 9);
assert_eq!(new_node_2.end_height, 10);
assert_eq!(new_node_2.sapling_tx, 27);
}
#[test]
fn delete() {
let nodes = load_nodes(NODE_DATA_1023L);
let (indices, nodes, peak_count) = preload_tree_delete(&nodes);
let mut rt_ret = [0u8; 32];
let result = librustzcash_mmr_delete(
0,
nodes.len() as u32,
indices.as_ptr(),
nodes.as_ptr(),
peak_count,
indices.len() - peak_count,
&mut rt_ret,
);
// Deleting from full tree of 9 height would result in cascade deleting of 10 nodes
assert_eq!(result, 10);
}

View File

@@ -4,6 +4,7 @@ use super::JUBJUB;
mod key_agreement; mod key_agreement;
mod key_components; mod key_components;
mod mmr;
mod notes; mod notes;
mod signatures; mod signatures;

View File

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

Binary file not shown.

Binary file not shown.

View File

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

View File

@@ -1,6 +1,16 @@
# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) # # pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) #
This is a Rust crate for using pairing-friendly elliptic curves. Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) construction is implemented. `pairing` is a crate for using pairing-friendly elliptic curves.
Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html)
construction is implemented.
## Roadmap
`pairing` is being refactored into a generic library for working with
pairing-friendly curves. After the refactor, `pairing` will provide basic traits
for pairing-friendly elliptic curve constructions, while specific curves will be
in separate crates.
## [Documentation](https://docs.rs/pairing/) ## [Documentation](https://docs.rs/pairing/)
@@ -8,13 +18,15 @@ Bring the `pairing` crate into your project just as you normally would.
## Security Warnings ## Security Warnings
This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks. This library does not make any guarantees about constant-time operations, memory
access patterns, or resistance to side-channel attacks.
## License ## License
Licensed under either of Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option. at your option.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ macro_rules! curve_impl {
} }
impl ::std::fmt::Display for $affine { impl ::std::fmt::Display for $affine {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
if self.infinity { if self.infinity {
write!(f, "{}(Infinity)", $name) write!(f, "{}(Infinity)", $name)
} else { } else {
@@ -35,7 +35,7 @@ macro_rules! curve_impl {
} }
impl ::std::fmt::Display for $projective { impl ::std::fmt::Display for $projective {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{}", self.into_affine()) write!(f, "{}", self.into_affine())
} }
} }
@@ -200,7 +200,7 @@ macro_rules! curve_impl {
type Base = $basefield; type Base = $basefield;
type Affine = $affine; type Affine = $affine;
fn random<R: RngCore>(rng: &mut R) -> Self { fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
loop { loop {
let x = $basefield::random(rng); let x = $basefield::random(rng);
let greatest = rng.next_u32() % 2 != 0; let greatest = rng.next_u32() % 2 != 0;
@@ -622,11 +622,11 @@ macro_rules! curve_impl {
pub mod g1 { pub mod g1 {
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr}; use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
use super::g2::G2Affine; use super::g2::G2Affine;
use crate::{Engine, PairingCurveAffine};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand_core::RngCore; use rand_core::RngCore;
use std::fmt; use std::fmt;
use {Engine, PairingCurveAffine};
curve_impl!( curve_impl!(
"G1", "G1",
@@ -656,7 +656,7 @@ pub mod g1 {
} }
impl fmt::Debug for G1Uncompressed { impl fmt::Debug for G1Uncompressed {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -766,7 +766,7 @@ pub mod g1 {
} }
impl fmt::Debug for G1Compressed { impl fmt::Debug for G1Compressed {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -934,7 +934,7 @@ pub mod g1 {
#[test] #[test]
fn g1_generator() { fn g1_generator() {
use SqrtField; use crate::SqrtField;
let mut x = Fq::zero(); let mut x = Fq::zero();
let mut i = 0; let mut i = 0;
@@ -1291,11 +1291,11 @@ pub mod g1 {
pub mod g2 { pub mod g2 {
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr}; use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
use super::g1::G1Affine; use super::g1::G1Affine;
use crate::{Engine, PairingCurveAffine};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand_core::RngCore; use rand_core::RngCore;
use std::fmt; use std::fmt;
use {Engine, PairingCurveAffine};
curve_impl!( curve_impl!(
"G2", "G2",
@@ -1325,7 +1325,7 @@ pub mod g2 {
} }
impl fmt::Debug for G2Uncompressed { impl fmt::Debug for G2Uncompressed {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -1451,7 +1451,7 @@ pub mod g2 {
} }
impl fmt::Debug for G2Compressed { impl fmt::Debug for G2Compressed {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -1640,7 +1640,7 @@ pub mod g2 {
#[test] #[test]
fn g2_generator() { fn g2_generator() {
use SqrtField; use crate::SqrtField;
let mut x = Fq2::zero(); let mut x = Fq2::zero();
let mut i = 0; let mut i = 0;

View File

@@ -2225,10 +2225,10 @@ fn test_fq_root_of_unity() {
#[test] #[test]
fn fq_field_tests() { fn fq_field_tests() {
::tests::field::random_field_tests::<Fq>(); crate::tests::field::random_field_tests::<Fq>();
::tests::field::random_sqrt_tests::<Fq>(); crate::tests::field::random_sqrt_tests::<Fq>();
::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13); crate::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
::tests::field::from_str_tests::<Fq>(); crate::tests::field::from_str_tests::<Fq>();
} }
#[test] #[test]
@@ -2244,7 +2244,7 @@ fn test_fq_ordering() {
#[test] #[test]
fn fq_repr_tests() { fn fq_repr_tests() {
::tests::repr::random_repr_tests::<Fq>(); crate::tests::repr::random_repr_tests::<Fq>();
} }
#[test] #[test]

View File

@@ -12,7 +12,7 @@ pub struct Fq12 {
} }
impl ::std::fmt::Display for Fq12 { impl ::std::fmt::Display for Fq12 {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "Fq12({} + {} * w)", self.c0, self.c1) write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
} }
} }
@@ -40,7 +40,7 @@ impl Fq12 {
} }
impl Field for Fq12 { impl Field for Fq12 {
fn random<R: RngCore>(rng: &mut R) -> Self { fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fq12 { Fq12 {
c0: Fq6::random(rng), c0: Fq6::random(rng),
c1: Fq6::random(rng), c1: Fq6::random(rng),
@@ -187,6 +187,6 @@ fn test_fq12_mul_by_014() {
fn fq12_field_tests() { fn fq12_field_tests() {
use ff::PrimeField; use ff::PrimeField;
::tests::field::random_field_tests::<Fq12>(); crate::tests::field::random_field_tests::<Fq12>();
::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13); crate::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
} }

View File

@@ -12,7 +12,7 @@ pub struct Fq2 {
} }
impl ::std::fmt::Display for Fq2 { impl ::std::fmt::Display for Fq2 {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "Fq2({} + {} * u)", self.c0, self.c1) write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
} }
} }
@@ -57,7 +57,7 @@ impl Fq2 {
} }
impl Field for Fq2 { impl Field for Fq2 {
fn random<R: RngCore>(rng: &mut R) -> Self { fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fq2 { Fq2 {
c0: Fq::random(rng), c0: Fq::random(rng),
c1: Fq::random(rng), c1: Fq::random(rng),
@@ -958,7 +958,7 @@ fn test_fq2_mul_nonresidue() {
fn fq2_field_tests() { fn fq2_field_tests() {
use ff::PrimeField; use ff::PrimeField;
::tests::field::random_field_tests::<Fq2>(); crate::tests::field::random_field_tests::<Fq2>();
::tests::field::random_sqrt_tests::<Fq2>(); crate::tests::field::random_sqrt_tests::<Fq2>();
::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13); crate::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
} }

View File

@@ -12,7 +12,7 @@ pub struct Fq6 {
} }
impl ::std::fmt::Display for Fq6 { impl ::std::fmt::Display for Fq6 {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2) write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
} }
} }
@@ -100,7 +100,7 @@ impl Fq6 {
} }
impl Field for Fq6 { impl Field for Fq6 {
fn random<R: RngCore>(rng: &mut R) -> Self { fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fq6 { Fq6 {
c0: Fq2::random(rng), c0: Fq2::random(rng),
c1: Fq2::random(rng), c1: Fq2::random(rng),
@@ -378,6 +378,6 @@ fn test_fq6_mul_by_01() {
fn fq6_field_tests() { fn fq6_field_tests() {
use ff::PrimeField; use ff::PrimeField;
::tests::field::random_field_tests::<Fq6>(); crate::tests::field::random_field_tests::<Fq6>();
::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13); crate::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
} }

View File

@@ -1015,13 +1015,13 @@ fn test_fr_root_of_unity() {
#[test] #[test]
fn fr_field_tests() { fn fr_field_tests() {
::tests::field::random_field_tests::<Fr>(); crate::tests::field::random_field_tests::<Fr>();
::tests::field::random_sqrt_tests::<Fr>(); crate::tests::field::random_sqrt_tests::<Fr>();
::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13); crate::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
::tests::field::from_str_tests::<Fr>(); crate::tests::field::from_str_tests::<Fr>();
} }
#[test] #[test]
fn fr_repr_tests() { fn fr_repr_tests() {
::tests::repr::random_repr_tests::<Fr>(); crate::tests::repr::random_repr_tests::<Fr>();
} }

View File

@@ -1,3 +1,6 @@
//! An implementation of the BLS12-381 pairing-friendly elliptic curve
//! construction.
mod ec; mod ec;
mod fq; mod fq;
mod fq12; mod fq12;
@@ -366,5 +369,5 @@ impl G2Prepared {
#[test] #[test]
fn bls12_engine_tests() { fn bls12_engine_tests() {
::tests::engine::engine_tests::<Bls12>(); crate::tests::engine::engine_tests::<Bls12>();
} }

View File

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

View File

@@ -1,3 +1,5 @@
//! A library for working with pairing-friendly curves.
// `clippy` is a code linting tool for improving code quality by catching // `clippy` is a code linting tool for improving code quality by catching
// common mistakes or strange code patterns. If the `cargo-clippy` feature // common mistakes or strange code patterns. If the `cargo-clippy` feature
// is provided, all compiler warnings are prohibited. // is provided, all compiler warnings are prohibited.
@@ -8,17 +10,11 @@
#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))]
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
// Force public structures to implement Debug // Force public structures to implement Debug
#![deny(missing_debug_implementations)] #![deny(missing_debug_implementations)]
extern crate byteorder;
extern crate ff;
extern crate group;
extern crate rand_core;
#[cfg(test)]
extern crate rand_xorshift;
#[cfg(test)] #[cfg(test)]
pub mod tests; pub mod tests;
@@ -87,7 +83,7 @@ pub trait Engine: ScalarEngine {
>; >;
/// Perform final exponentiation of the result of a miller loop. /// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(&Self::Fqk) -> Option<Self::Fqk>; fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk>;
/// Performs a complete pairing operation `(p, q)`. /// Performs a complete pairing operation `(p, q)`.
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk

View File

@@ -2,7 +2,7 @@ use group::{CurveAffine, CurveProjective};
use rand_core::SeedableRng; use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use {Engine, Field, PairingCurveAffine, PrimeField}; use crate::{Engine, Field, PairingCurveAffine, PrimeField};
pub fn engine_tests<E: Engine>() { pub fn engine_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([

2
zcash_client_backend/.gitignore vendored Normal file
View File

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

View File

@@ -1,16 +1,31 @@
[package] [package]
name = "zcash_client_backend" name = "zcash_client_backend"
version = "0.0.0" description = "APIs for creating shielded Zcash light clients"
version = "0.1.0"
authors = [ authors = [
"Jack Grigg <jack@z.cash>", "Jack Grigg <jack@z.cash>",
] ]
homepage = "https://github.com/zcash/librustzcash"
repository = "https://github.com/zcash/librustzcash"
readme = "README.md"
license = "MIT OR Apache-2.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
bech32 = "0.7" bech32 = "0.7"
pairing = { path = "../pairing" } ff = { version = "0.5.0", path = "../ff" }
zcash_primitives = { path = "../zcash_primitives" } hex = "0.3"
pairing = { version = "0.15.0", path = "../pairing" }
protobuf = "2"
subtle = "2"
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
[build-dependencies]
protobuf-codegen-pure = "2"
[dev-dependencies] [dev-dependencies]
rand_core = "0.5" rand_core = "0.5"
rand_xorshift = "0.2" rand_xorshift = "0.2"
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -1,12 +1,14 @@
# zcash_client_backend # zcash_client_backend
This library contains Rust structs and traits for creating shielded Zcash light clients. This library contains Rust structs and traits for creating shielded Zcash light
clients.
## License ## License
Licensed under either of Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option. at your option.

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
//! Constants for the Zcash main network.
/// The mainnet coin type for ZEC, as defined by [SLIP 44]. /// The mainnet coin type for ZEC, as defined by [SLIP 44].
/// ///
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md /// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
@@ -23,6 +25,6 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews";
/// ///
/// Defined in section 5.6.4 of the [Zcash Protocol Specification]. /// Defined in section 5.6.4 of the [Zcash Protocol Specification].
/// ///
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress /// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs"; pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs";

View File

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

View File

@@ -1,3 +1,5 @@
//! Constants for the Zcash test network.
/// The testnet coin type for ZEC, as defined by [SLIP 44]. /// The testnet coin type for ZEC, as defined by [SLIP 44].
/// ///
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md /// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
@@ -23,6 +25,6 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling";
/// ///
/// Defined in section 5.6.4 of the [Zcash Protocol Specification]. /// Defined in section 5.6.4 of the [Zcash Protocol Specification].
/// ///
/// [`PaymentAddress`]: sapling_crypto::primitives::PaymentAddress /// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling"; pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling";

View File

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

View File

@@ -2,15 +2,14 @@
//! //!
//! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`] //! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`]
//! module. //! module.
//!
//! [`constants`]: crate::constants
use bech32::{self, Error, FromBase32, ToBase32}; use bech32::{self, Error, FromBase32, ToBase32};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use std::io::{self, Write}; use std::io::{self, Write};
use zcash_primitives::{ use zcash_primitives::{
jubjub::edwards, primitives::PaymentAddress,
primitives::{Diversifier, PaymentAddress},
};
use zcash_primitives::{
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB, JUBJUB,
}; };
@@ -113,10 +112,11 @@ pub fn decode_extended_full_viewing_key(
/// 0xbc, 0xe5, /// 0xbc, 0xe5,
/// ]); /// ]);
/// ///
/// let pa = PaymentAddress { /// let pa = PaymentAddress::from_parts(
/// diversifier: Diversifier([0u8; 11]), /// Diversifier([0u8; 11]),
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), /// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// }; /// )
/// .unwrap();
/// ///
/// assert_eq!( /// assert_eq!(
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa), /// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
@@ -124,10 +124,7 @@ pub fn decode_extended_full_viewing_key(
/// ); /// );
/// ``` /// ```
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String { pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
bech32_encode(hrp, |w| { bech32_encode(hrp, |w| w.write_all(&addr.to_bytes()))
w.write_all(&addr.diversifier.0)?;
addr.pk_d.write(w)
})
} }
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string. /// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
@@ -153,10 +150,11 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
/// 0xbc, 0xe5, /// 0xbc, 0xe5,
/// ]); /// ]);
/// ///
/// let pa = PaymentAddress { /// let pa = PaymentAddress::from_parts(
/// diversifier: Diversifier([0u8; 11]), /// Diversifier([0u8; 11]),
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), /// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// }; /// )
/// .unwrap();
/// ///
/// assert_eq!( /// assert_eq!(
/// decode_payment_address( /// decode_payment_address(
@@ -168,17 +166,13 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
/// ``` /// ```
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> { pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
bech32_decode(hrp, s, |data| { bech32_decode(hrp, s, |data| {
let mut diversifier = Diversifier([0; 11]); if data.len() != 43 {
diversifier.0.copy_from_slice(&data[0..11]);
// Check that the diversifier is valid
if diversifier.g_d::<Bls12>(&JUBJUB).is_none() {
return None; return None;
} }
edwards::Point::<Bls12, _>::read(&data[11..], &JUBJUB) let mut bytes = [0; 43];
.ok()? bytes.copy_from_slice(&data);
.as_prime_order(&JUBJUB) PaymentAddress::<Bls12>::from_bytes(&bytes, &JUBJUB)
.map(|pk_d| PaymentAddress { pk_d, diversifier })
}) })
} }
@@ -191,11 +185,95 @@ mod tests {
use zcash_primitives::{ use zcash_primitives::{
jubjub::edwards, jubjub::edwards,
primitives::{Diversifier, PaymentAddress}, primitives::{Diversifier, PaymentAddress},
zip32::ExtendedSpendingKey,
}; };
use super::{decode_payment_address, encode_payment_address}; use super::{
decode_extended_full_viewing_key, decode_extended_spending_key, decode_payment_address,
encode_extended_full_viewing_key, encode_extended_spending_key, encode_payment_address,
};
use crate::constants; use crate::constants;
#[test]
fn extended_spending_key() {
let extsk = ExtendedSpendingKey::master(&[0; 32][..]);
let encoded_main = "secret-extended-key-main1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs87qvlj";
let encoded_test = "secret-extended-key-test1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsvzyw8j";
assert_eq!(
encode_extended_spending_key(
constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
&extsk
),
encoded_main
);
assert_eq!(
decode_extended_spending_key(
constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
encoded_main
)
.unwrap(),
Some(extsk.clone())
);
assert_eq!(
encode_extended_spending_key(
constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
&extsk
),
encoded_test
);
assert_eq!(
decode_extended_spending_key(
constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
encoded_test
)
.unwrap(),
Some(extsk)
);
}
#[test]
fn extended_full_viewing_key() {
let extfvk = (&ExtendedSpendingKey::master(&[0; 32][..])).into();
let encoded_main = "zxviews1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsxmansf";
let encoded_test = "zxviewtestsapling1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs8evfkz";
assert_eq!(
encode_extended_full_viewing_key(
constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
&extfvk
),
encoded_main
);
assert_eq!(
decode_extended_full_viewing_key(
constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
encoded_main
)
.unwrap(),
Some(extfvk.clone())
);
assert_eq!(
encode_extended_full_viewing_key(
constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
&extfvk
),
encoded_test
);
assert_eq!(
decode_extended_full_viewing_key(
constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
encoded_test
)
.unwrap(),
Some(extfvk)
);
}
#[test] #[test]
fn payment_address() { fn payment_address() {
let rng = &mut XorShiftRng::from_seed([ let rng = &mut XorShiftRng::from_seed([
@@ -203,10 +281,11 @@ mod tests {
0xbc, 0xe5, 0xbc, 0xe5,
]); ]);
let addr = PaymentAddress { let addr = PaymentAddress::from_parts(
diversifier: Diversifier([0u8; 11]), Diversifier([0u8; 11]),
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
}; )
.unwrap();
let encoded_main = let encoded_main =
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd"; "zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
@@ -247,10 +326,11 @@ mod tests {
0xbc, 0xe5, 0xbc, 0xe5,
]); ]);
let addr = PaymentAddress { let addr = PaymentAddress::from_parts(
diversifier: Diversifier([1u8; 11]), Diversifier([1u8; 11]),
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
}; )
.unwrap();
let encoded_main = let encoded_main =
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr); encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr);

View File

@@ -5,6 +5,10 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey};
/// Derives the ZIP 32 [`ExtendedSpendingKey`] for a given coin type and account from the /// Derives the ZIP 32 [`ExtendedSpendingKey`] for a given coin type and account from the
/// given seed. /// given seed.
/// ///
/// # Panics
///
/// Panics if `seed` is shorter than 32 bytes.
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
@@ -13,6 +17,10 @@ use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey};
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0); /// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
/// ``` /// ```
pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendingKey { pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendingKey {
if seed.len() < 32 {
panic!("ZIP 32 seeds MUST be at least 32 bytes");
}
ExtendedSpendingKey::from_path( ExtendedSpendingKey::from_path(
&ExtendedSpendingKey::master(&seed), &ExtendedSpendingKey::master(&seed),
&[ &[
@@ -22,3 +30,14 @@ pub fn spending_key(seed: &[u8], coin_type: u32, account: u32) -> ExtendedSpendi
], ],
) )
} }
#[cfg(test)]
mod tests {
use super::spending_key;
#[test]
#[should_panic]
fn spending_key_panics_on_short_seed() {
let _ = spending_key(&[0; 31][..], 0, 0);
}
}

View File

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

View File

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

View File

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

View File

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

4
zcash_history/.gitignore vendored Normal file
View File

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

14
zcash_history/COPYRIGHT Normal file
View File

@@ -0,0 +1,14 @@
Copyrights in the "zcash_history" library are retained by their contributors. No
copyright assignment is required to contribute to the "zcash_history" library.
The "zcash_history" library is licensed under either of
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

17
zcash_history/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "zcash_history"
version = "0.0.1"
authors = ["NikVolf <nikvolf@gmail.com>"]
edition = "2018"
license = "MIT/Apache-2.0"
documentation = "https://docs.rs/zcash_history/"
description = "Library for Zcash blockchain history tools"
[dev-dependencies]
assert_matches = "1.3.0"
quickcheck = "0.8"
[dependencies]
bigint = "4"
byteorder = "1"
blake2 = { package = "blake2b_simd", version = "0.5" }

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

25
zcash_history/LICENSE-MIT Normal file
View File

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

26
zcash_history/README.md Normal file
View File

@@ -0,0 +1,26 @@
# zcash_history
Special implementation of Merkle mountain ranges (MMR) for Zcash!
[![Build Status](https://travis-ci.org/NikVolf/zcash-mmr.svg?branch=master)](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.

View File

@@ -0,0 +1,2 @@
// dummy example
pub fn main() {}

View File

@@ -0,0 +1,86 @@
use zcash_history::{Entry, EntryLink, NodeData, Tree};
pub struct NodeDataIterator {
return_stack: Vec<NodeData>,
tree: Tree,
cursor: usize,
leaf_cursor: usize,
}
impl Iterator for NodeDataIterator {
type Item = NodeData;
fn next(&mut self) -> Option<NodeData> {
let result = if self.cursor == 1 {
self.leaf_cursor = 2;
Some(leaf(1))
} else if self.cursor == 2 {
self.leaf_cursor = 3;
Some(leaf(2))
} else if self.cursor == 3 {
Some(self.tree.root_node().expect("always exists").data().clone())
} else if self.return_stack.len() > 0 {
self.return_stack.pop()
} else {
for n_append in self
.tree
.append_leaf(leaf(self.leaf_cursor as u32))
.expect("full tree cannot fail")
.into_iter()
.rev()
{
self.return_stack.push(
self.tree
.resolve_link(n_append)
.expect("just pushed")
.data()
.clone(),
)
}
self.leaf_cursor += 1;
self.return_stack.pop()
};
self.cursor += 1;
result
}
}
impl NodeDataIterator {
pub fn new() -> Self {
let root = Entry::new(
NodeData::combine(&leaf(1), &leaf(2)),
EntryLink::Stored(0),
EntryLink::Stored(1),
);
let tree = Tree::new(
3,
vec![(2, root)],
vec![(0, leaf(1).into()), (1, leaf(2).into())],
);
NodeDataIterator {
return_stack: Vec::new(),
tree,
cursor: 1,
leaf_cursor: 1,
}
}
}
fn leaf(height: u32) -> NodeData {
NodeData {
consensus_branch_id: 0,
subtree_commitment: [0u8; 32],
start_time: height * 10 + 1,
end_time: (height + 1) * 10,
start_target: 100 + height * 10,
end_target: 100 + (height + 1) * 10,
start_sapling_root: [0u8; 32],
end_sapling_root: [0u8; 32],
subtree_total_work: 0.into(),
start_height: height as u64,
end_height: height as u64,
sapling_tx: 5 + height as u64,
}
}

View File

@@ -0,0 +1,113 @@
use zcash_history::{Entry, EntryLink, NodeData, Tree};
#[path = "lib/shared.rs"]
mod share;
fn draft(into: &mut Vec<(u32, Entry)>, vec: &Vec<NodeData>, peak_pos: usize, h: u32) {
let node_data = vec[peak_pos - 1].clone();
let peak: Entry = match h {
0 => node_data.into(),
_ => Entry::new(
node_data,
EntryLink::Stored((peak_pos - (1 << h) - 1) as u32),
EntryLink::Stored((peak_pos - 2) as u32),
),
};
println!("Entry #{}: {}", into.len(), peak);
into.push(((peak_pos - 1) as u32, peak));
}
fn prepare_tree(vec: &Vec<NodeData>) -> Tree {
assert!(vec.len() > 0);
// integer log2 of (vec.len()+1), -1
let mut h = (32 - ((vec.len() + 1) as u32).leading_zeros() - 1) - 1;
let mut peak_pos = (1 << (h + 1)) - 1;
let mut nodes = Vec::new();
// used later
let mut last_peak_pos = 0;
let mut last_peak_h = 0;
loop {
if peak_pos > vec.len() {
// left child, -2^h
peak_pos = peak_pos - (1 << h);
h = h - 1;
}
if peak_pos <= vec.len() {
draft(&mut nodes, vec, peak_pos, h);
// save to be used in next loop
last_peak_pos = peak_pos;
last_peak_h = h;
// right sibling
peak_pos = peak_pos + (1 << (h + 1)) - 1;
}
if h == 0 {
break;
}
}
// for deletion, everything on the right slope of the last peak should be pre-loaded
let mut extra = Vec::new();
let mut h = last_peak_h;
let mut peak_pos = last_peak_pos;
while h > 0 {
let left_pos = peak_pos - (1 << h);
let right_pos = peak_pos - 1;
h = h - 1;
// drafting left child
draft(&mut extra, vec, left_pos, h);
// drafting right child
draft(&mut extra, vec, right_pos, h);
// continuing on right slope
peak_pos = right_pos;
}
println!("Total extra of {} required for deletion!", extra.len());
Tree::new(vec.len() as u32, nodes, extra)
}
fn main() {
let number = match std::env::args().skip(1).next() {
None => {
eprintln!("writer <number of nodes> [<out_file>]");
std::process::exit(1);
}
Some(number) => number.parse::<usize>().expect("invalid number"),
};
let long_vec = share::NodeDataIterator::new()
.take(number)
.collect::<Vec<NodeData>>();
let now = std::time::Instant::now();
let tree = prepare_tree(&long_vec);
let elapsed = now.elapsed();
println!(
"Tree final root: {}-{}",
tree.root_node().expect("root").data().start_height,
tree.root_node().expect("root").data().end_height,
);
println!(
"Prepare tree of {} length: {} ns / {} mcs / {} ms",
number,
elapsed.as_nanos(),
elapsed.as_micros(),
elapsed.as_millis()
);
}

View File

@@ -0,0 +1,44 @@
#[path = "lib/shared.rs"]
mod share;
// Test data generator
// $ cargo run --example writer -- 16 nodes.dat
// or
// $ cargo run --example writer -- 16
// to preview
fn main() {
let mut args = std::env::args().skip(1);
let (number, out_file) = match args.next() {
None => {
eprintln!("writer <number of nodes> [<out_file>]");
std::process::exit(1);
}
Some(number) => (
number.parse::<usize>().expect("invalid number"),
args.next(),
),
};
let iterator = share::NodeDataIterator::new().take(number);
if let Some(out_file_path) = out_file {
use std::io::Write;
let mut buf = Vec::new();
for node in iterator {
node.write(&mut buf).expect("Failed to write data");
}
let mut file = std::fs::File::create(&out_file_path).expect("Failed to create output file");
file.write_all(&buf[..])
.expect("Failed to write data to file");
} else {
for n in iterator {
println!("{:?}", n);
}
}
}

123
zcash_history/src/entry.rs Normal file
View File

@@ -0,0 +1,123 @@
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::{EntryKind, EntryLink, Error, NodeData, MAX_NODE_DATA_SIZE};
/// Max serialized length of entry data.
pub const MAX_ENTRY_SIZE: usize = MAX_NODE_DATA_SIZE + 9;
/// MMR Entry.
#[derive(Debug)]
pub struct Entry {
pub(crate) kind: EntryKind,
pub(crate) data: NodeData,
}
impl Entry {
/// New entry of type node.
pub fn new(data: NodeData, left: EntryLink, right: EntryLink) -> Self {
Entry {
kind: EntryKind::Node(left, right),
data,
}
}
/// Returns if is this node complete (has total of 2^N leaves)
pub fn complete(&self) -> bool {
let leaves = self.leaf_count();
leaves & (leaves - 1) == 0
}
/// Number of leaves under this node.
pub fn leaf_count(&self) -> u64 {
self.data.end_height - (self.data.start_height - 1)
}
/// Is this node a leaf.
pub fn leaf(&self) -> bool {
if let EntryKind::Leaf = self.kind {
true
} else {
false
}
}
/// Left child
pub fn left(&self) -> Result<EntryLink, Error> {
match self.kind {
EntryKind::Leaf => Err(Error::node_expected()),
EntryKind::Node(left, _) => Ok(left),
}
}
/// Right child.
pub fn right(&self) -> Result<EntryLink, Error> {
match self.kind {
EntryKind::Leaf => Err(Error::node_expected()),
EntryKind::Node(_, right) => Ok(right),
}
}
/// Read from byte representation.
pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> {
let kind = {
match r.read_u8()? {
0 => {
let left = r.read_u32::<LittleEndian>()?;
let right = r.read_u32::<LittleEndian>()?;
EntryKind::Node(EntryLink::Stored(left), EntryLink::Stored(right))
}
1 => EntryKind::Leaf,
_ => return Err(std::io::Error::from(std::io::ErrorKind::InvalidData)),
}
};
let data = NodeData::read(consensus_branch_id, r)?;
Ok(Entry { kind, data })
}
/// Write to byte representation.
pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
match self.kind {
EntryKind::Node(EntryLink::Stored(left), EntryLink::Stored(right)) => {
w.write_u8(0)?;
w.write_u32::<LittleEndian>(left)?;
w.write_u32::<LittleEndian>(right)?;
}
EntryKind::Leaf => {
w.write_u8(1)?;
}
_ => {
return Err(std::io::Error::from(std::io::ErrorKind::InvalidData));
}
}
self.data.write(w)?;
Ok(())
}
/// Convert from byte representation.
pub fn from_bytes<T: AsRef<[u8]>>(consensus_branch_id: u32, buf: T) -> std::io::Result<Self> {
let mut cursor = std::io::Cursor::new(buf);
Self::read(consensus_branch_id, &mut cursor)
}
}
impl From<NodeData> for Entry {
fn from(s: NodeData) -> Self {
Entry {
kind: EntryKind::Leaf,
data: s,
}
}
}
impl std::fmt::Display for Entry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.kind {
EntryKind::Node(l, r) => write!(f, "node({}, {}, ..)", l, r),
EntryKind::Leaf => write!(f, "leaf(..)"),
}
}
}

79
zcash_history/src/lib.rs Normal file
View File

@@ -0,0 +1,79 @@
//! Chain history library for Zcash
//!
//! To be used in zebra and via FFI bindings in zcashd
#![warn(missing_docs)]
mod entry;
mod node_data;
mod tree;
pub use entry::{Entry, MAX_ENTRY_SIZE};
pub use node_data::{NodeData, MAX_NODE_DATA_SIZE};
pub use tree::Tree;
/// Crate-level error type
#[derive(Debug)]
pub enum Error {
/// Entry expected to be presented in the tree view while it was not.
ExpectedInMemory(EntryLink),
/// Entry expected to be a node (specifying for which link this is not true).
ExpectedNode(Option<EntryLink>),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::ExpectedInMemory(l) => write!(f, "Node/leaf expected to be in memory: {}", l),
Self::ExpectedNode(None) => write!(f, "Node expected"),
Self::ExpectedNode(Some(l)) => write!(f, "Node expected, not leaf: {}", l),
}
}
}
/// Reference to to the tree node.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum EntryLink {
/// Reference to the stored (in the array representation) leaf/node.
Stored(u32),
/// Reference to the generated leaf/node.
Generated(u32),
}
impl std::fmt::Display for EntryLink {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::Stored(v) => write!(f, "stored({})", v),
Self::Generated(v) => write!(f, "generated({})", v),
}
}
}
/// MMR Node. It is leaf when `left`, `right` are `None` and node when they are not.
#[repr(C)]
#[derive(Debug)]
pub enum EntryKind {
/// Leaf entry.
Leaf,
/// Node entry with children links.
Node(EntryLink, EntryLink),
}
impl Error {
/// Entry expected to be a node (specifying for which link this is not true).
pub fn link_node_expected(link: EntryLink) -> Self {
Self::ExpectedNode(Some(link))
}
/// Some entry is expected to be node
pub fn node_expected() -> Self {
Self::ExpectedNode(None)
}
pub(crate) fn augment(self, link: EntryLink) -> Self {
match self {
Error::ExpectedNode(_) => Error::ExpectedNode(Some(link)),
val => val,
}
}
}

View File

@@ -0,0 +1,238 @@
use bigint::U256;
use blake2::Params as Blake2Params;
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
/// Maximum serialized size of the node metadata.
pub const MAX_NODE_DATA_SIZE: usize = 32 + // subtree commitment
4 + // start time
4 + // end time
4 + // start target
4 + // end target
32 + // start sapling tree root
32 + // end sapling tree root
32 + // subtree total work
9 + // start height (compact uint)
9 + // end height (compact uint)
9; // Sapling tx count (compact uint)
// = total of 171
/// Node metadata.
#[repr(C)]
#[derive(Debug, Clone, Default)]
#[cfg_attr(test, derive(PartialEq))]
pub struct NodeData {
/// Consensus branch id, should be provided by deserializing node.
pub consensus_branch_id: u32,
/// Subtree commitment - either block hash for leaves or hashsum of children for nodes.
pub subtree_commitment: [u8; 32],
/// Start time.
pub start_time: u32,
/// End time.
pub end_time: u32,
/// Start target.
pub start_target: u32,
/// End target.
pub end_target: u32,
/// Start sapling tree root.
pub start_sapling_root: [u8; 32],
/// End sapling tree root.
pub end_sapling_root: [u8; 32],
/// Part of tree total work.
pub subtree_total_work: U256,
/// Start height.
pub start_height: u64,
/// End height
pub end_height: u64,
/// Number of Sapling transactions.
pub sapling_tx: u64,
}
fn blake2b_personal(personalization: &[u8], input: &[u8]) -> [u8; 32] {
let hash_result = Blake2Params::new()
.hash_length(32)
.personal(personalization)
.to_state()
.update(input)
.finalize();
let mut result = [0u8; 32];
result.copy_from_slice(hash_result.as_bytes());
result
}
fn personalization(branch_id: u32) -> [u8; 16] {
let mut result = [0u8; 16];
result[..12].copy_from_slice(b"ZcashHistory");
LittleEndian::write_u32(&mut result[12..], branch_id);
result
}
impl NodeData {
/// Combine two nodes metadata.
pub fn combine(left: &NodeData, right: &NodeData) -> NodeData {
assert_eq!(left.consensus_branch_id, right.consensus_branch_id);
let mut hash_buf = [0u8; MAX_NODE_DATA_SIZE * 2];
let size = {
let mut cursor = ::std::io::Cursor::new(&mut hash_buf[..]);
left.write(&mut cursor)
.expect("Writing to memory buf with enough length cannot fail; qed");
right
.write(&mut cursor)
.expect("Writing to memory buf with enough length cannot fail; qed");
cursor.position() as usize
};
let hash = blake2b_personal(
&personalization(left.consensus_branch_id),
&hash_buf[..size],
);
NodeData {
consensus_branch_id: left.consensus_branch_id,
subtree_commitment: hash,
start_time: left.start_time,
end_time: right.end_time,
start_target: left.start_target,
end_target: right.end_target,
start_sapling_root: left.start_sapling_root,
end_sapling_root: right.end_sapling_root,
subtree_total_work: left.subtree_total_work + right.subtree_total_work,
start_height: left.start_height,
end_height: right.end_height,
sapling_tx: left.sapling_tx + right.sapling_tx,
}
}
fn write_compact<W: std::io::Write>(w: &mut W, compact: u64) -> std::io::Result<()> {
match compact {
0..=0xfc => w.write_all(&[compact as u8])?,
0xfd..=0xffff => {
w.write_all(&[0xfd])?;
w.write_u16::<LittleEndian>(compact as u16)?;
}
0x10000..=0xffff_ffff => {
w.write_all(&[0xfe])?;
w.write_u32::<LittleEndian>(compact as u32)?;
}
_ => {
w.write_all(&[0xff])?;
w.write_u64::<LittleEndian>(compact)?;
}
}
Ok(())
}
fn read_compact<R: std::io::Read>(reader: &mut R) -> std::io::Result<u64> {
let result = match reader.read_u8()? {
i @ 0..=0xfc => i.into(),
0xfd => reader.read_u16::<LittleEndian>()?.into(),
0xfe => reader.read_u32::<LittleEndian>()?.into(),
_ => reader.read_u64::<LittleEndian>()?,
};
Ok(result)
}
/// Write to the byte representation.
pub fn write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
w.write_all(&self.subtree_commitment)?;
w.write_u32::<LittleEndian>(self.start_time)?;
w.write_u32::<LittleEndian>(self.end_time)?;
w.write_u32::<LittleEndian>(self.start_target)?;
w.write_u32::<LittleEndian>(self.end_target)?;
w.write_all(&self.start_sapling_root)?;
w.write_all(&self.end_sapling_root)?;
let mut work_buf = [0u8; 32];
self.subtree_total_work.to_little_endian(&mut work_buf[..]);
w.write_all(&work_buf)?;
Self::write_compact(w, self.start_height)?;
Self::write_compact(w, self.end_height)?;
Self::write_compact(w, self.sapling_tx)?;
Ok(())
}
/// Read from the byte representation.
pub fn read<R: std::io::Read>(consensus_branch_id: u32, r: &mut R) -> std::io::Result<Self> {
let mut data = Self::default();
data.consensus_branch_id = consensus_branch_id;
r.read_exact(&mut data.subtree_commitment)?;
data.start_time = r.read_u32::<LittleEndian>()?;
data.end_time = r.read_u32::<LittleEndian>()?;
data.start_target = r.read_u32::<LittleEndian>()?;
data.end_target = r.read_u32::<LittleEndian>()?;
r.read_exact(&mut data.start_sapling_root)?;
r.read_exact(&mut data.end_sapling_root)?;
let mut work_buf = [0u8; 32];
r.read_exact(&mut work_buf)?;
data.subtree_total_work = U256::from_little_endian(&work_buf);
data.start_height = Self::read_compact(r)?;
data.end_height = Self::read_compact(r)?;
data.sapling_tx = Self::read_compact(r)?;
Ok(data)
}
/// Convert to byte representation.
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = [0u8; MAX_NODE_DATA_SIZE];
let pos = {
let mut cursor = std::io::Cursor::new(&mut buf[..]);
self.write(&mut cursor).expect("Cursor cannot fail");
cursor.position() as usize
};
buf[0..pos].to_vec()
}
/// Convert from byte representation.
pub fn from_bytes<T: AsRef<[u8]>>(consensus_branch_id: u32, buf: T) -> std::io::Result<Self> {
let mut cursor = std::io::Cursor::new(buf);
Self::read(consensus_branch_id, &mut cursor)
}
/// Hash node metadata
pub fn hash(&self) -> [u8; 32] {
let bytes = self.to_bytes();
blake2b_personal(&personalization(self.consensus_branch_id), &bytes)
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for NodeData {
fn arbitrary<G: quickcheck::Gen>(gen: &mut G) -> Self {
let mut node_data = NodeData::default();
node_data.consensus_branch_id = 0;
gen.fill_bytes(&mut node_data.subtree_commitment[..]);
node_data.start_time = gen.next_u32();
node_data.end_time = gen.next_u32();
node_data.start_target = gen.next_u32();
node_data.end_target = gen.next_u32();
gen.fill_bytes(&mut node_data.start_sapling_root[..]);
gen.fill_bytes(&mut node_data.end_sapling_root[..]);
let mut number = [0u8; 32];
gen.fill_bytes(&mut number[..]);
node_data.subtree_total_work = U256::from_little_endian(&number[..]);
node_data.start_height = gen.next_u64();
node_data.end_height = gen.next_u64();
node_data.sapling_tx = gen.next_u64();
node_data
}
}
#[cfg(test)]
mod tests {
use super::NodeData;
use quickcheck::{quickcheck, TestResult};
quickcheck! {
fn serialization_round_trip(node_data: NodeData) -> TestResult {
TestResult::from_bool(NodeData::from_bytes(0, &node_data.to_bytes()).unwrap() == node_data)
}
}
}

741
zcash_history/src/tree.rs Normal file
View File

@@ -0,0 +1,741 @@
use std::collections::HashMap;
use crate::{Entry, EntryKind, EntryLink, Error, NodeData};
/// Represents partially loaded tree.
///
/// Some kind of "view" into the array representation of the MMR tree.
/// With only some of the leaves/nodes pre-loaded / pre-generated.
/// Exact amount of the loaded data can be calculated by the constructing party,
/// depending on the length of the tree and maximum amount of operations that are going
/// to happen after construction. `Tree` should not be used as self-contained data structure,
/// since it's internal state can grow indefinitely after serial operations.
/// Intended use of this `Tree` is to instantiate it based on partially loaded data (see example
/// how to pick right nodes from the array representation of MMR Tree), perform several operations
/// (append-s/delete-s) and then drop it.
pub struct Tree {
stored: HashMap<u32, Entry>,
// This can grow indefinitely if `Tree` is misused as a self-contained data structure
generated: Vec<Entry>,
// number of persistent(!) tree entries
stored_count: u32,
root: EntryLink,
}
impl Tree {
/// Resolve link originated from this tree
pub fn resolve_link(&self, link: EntryLink) -> Result<IndexedNode, Error> {
match link {
EntryLink::Generated(index) => self.generated.get(index as usize),
EntryLink::Stored(index) => self.stored.get(&index),
}
.map(|node| IndexedNode { node, link })
.ok_or(Error::ExpectedInMemory(link))
}
fn push(&mut self, data: Entry) -> EntryLink {
let idx = self.stored_count;
self.stored_count += 1;
self.stored.insert(idx, data);
EntryLink::Stored(idx)
}
fn push_generated(&mut self, data: Entry) -> EntryLink {
self.generated.push(data);
EntryLink::Generated(self.generated.len() as u32 - 1)
}
/// Populate tree with plain list of the leaves/nodes. For now, only for tests,
/// since this `Tree` structure is for partially loaded tree (but it might change)
#[cfg(test)]
pub fn populate(loaded: Vec<Entry>, root: EntryLink) -> Self {
let mut result = Tree::invalid();
result.stored_count = loaded.len() as u32;
for (idx, item) in loaded.into_iter().enumerate() {
result.stored.insert(idx as u32, item);
}
result.root = root;
result
}
// Empty tree with invalid root
fn invalid() -> Self {
Tree {
root: EntryLink::Generated(0),
generated: Default::default(),
stored: Default::default(),
stored_count: 0,
}
}
/// New view into the the tree array representation
///
/// `length` is total length of the array representation (is generally not a sum of
/// peaks.len + extra.len)
/// `peaks` is peaks of the mmr tree
/// `extra` is some extra nodes that calculated to be required during next one or more
/// operations on the tree.
///
/// # Panics
///
/// Will panic if `peaks` is empty.
pub fn new(length: u32, peaks: Vec<(u32, Entry)>, extra: Vec<(u32, Entry)>) -> Self {
assert!(peaks.len() > 0);
let mut result = Tree::invalid();
result.stored_count = length;
let mut gen = 0;
let mut root = EntryLink::Stored(peaks[0].0);
for (idx, node) in peaks.into_iter() {
result.stored.insert(idx, node);
if gen != 0 {
let next_generated = combine_nodes(
result
.resolve_link(root)
.expect("Inserted before, cannot fail; qed"),
result
.resolve_link(EntryLink::Stored(idx))
.expect("Inserted before, cannot fail; qed"),
);
root = result.push_generated(next_generated);
}
gen += 1;
}
for (idx, node) in extra {
result.stored.insert(idx, node);
}
result.root = root;
result
}
fn get_peaks(&self, root: EntryLink, target: &mut Vec<EntryLink>) -> Result<(), Error> {
let (left_child_link, right_child_link) = {
let root = self.resolve_link(root)?;
if root.node.complete() {
target.push(root.link);
return Ok(());
}
(root.left()?, root.right()?)
};
self.get_peaks(left_child_link, target)?;
self.get_peaks(right_child_link, target)?;
Ok(())
}
/// Append one leaf to the tree.
///
/// Returns links to actual nodes that has to be persisted as the result of the append.
/// If completed without error, at least one link to the appended
/// node (with metadata provided in `new_leaf`) will be returned.
pub fn append_leaf(&mut self, new_leaf: NodeData) -> Result<Vec<EntryLink>, Error> {
let root = self.root;
let new_leaf_link = self.push(new_leaf.into());
let mut appended = Vec::new();
appended.push(new_leaf_link);
let mut peaks = Vec::new();
self.get_peaks(root, &mut peaks)?;
let mut merge_stack = Vec::new();
merge_stack.push(new_leaf_link);
// Scan the peaks right-to-left, merging together equal-sized adjacent
// complete subtrees. After this, merge_stack only contains peaks of
// unequal-sized subtrees.
while let Some(next_peak) = peaks.pop() {
let next_merge = merge_stack
.pop()
.expect("there should be at least one, initial or re-pushed");
if let Some(stored) = {
let peak = self.resolve_link(next_peak)?;
let m = self.resolve_link(next_merge)?;
if peak.node.leaf_count() == m.node.leaf_count() {
Some(combine_nodes(peak, m))
} else {
None
}
} {
let link = self.push(stored);
merge_stack.push(link);
appended.push(link);
continue;
} else {
merge_stack.push(next_merge);
merge_stack.push(next_peak);
}
}
let mut new_root = merge_stack
.pop()
.expect("Loop above cannot reduce the merge_stack");
// Scan the peaks left-to-right, producing new generated nodes that
// connect the subtrees
while let Some(next_child) = merge_stack.pop() {
new_root = self.push_generated(combine_nodes(
self.resolve_link(new_root)?,
self.resolve_link(next_child)?,
))
}
self.root = new_root;
Ok(appended)
}
#[cfg(test)]
fn for_children<F: Fn(EntryLink, EntryLink)>(&self, node: EntryLink, f: F) {
let (left, right) = {
let link = self
.resolve_link(node)
.expect("Failed to resolve link in test");
(
link.left().expect("Failed to find node in test"),
link.right().expect("Failed to find node in test"),
)
};
f(left, right);
}
fn pop(&mut self) {
self.stored.remove(&(self.stored_count - 1));
self.stored_count = self.stored_count - 1;
}
/// Truncate one leaf from the end of the tree.
///
/// Returns actual number of nodes that should be removed by the caller
/// from the end of the array representation.
pub fn truncate_leaf(&mut self) -> Result<u32, Error> {
let root = {
let (leaves, root_left_child) = {
let n = self.resolve_link(self.root)?;
(n.node.leaf_count(), n.node.left()?)
};
if leaves & 1 != 0 {
self.pop();
self.root = root_left_child;
return Ok(1);
} else {
self.resolve_link(self.root)?
}
};
let mut peaks = vec![root.left()?];
let mut subtree_root_link = root.right()?;
let mut truncated = 1;
loop {
let left_link = self.resolve_link(subtree_root_link)?.node;
if let EntryKind::Node(left, right) = left_link.kind {
peaks.push(left);
subtree_root_link = right;
truncated += 1;
} else {
if root.node.complete() {
truncated += 1;
}
break;
}
}
let mut new_root = *peaks.get(0).expect("At lest 1 elements in peaks");
for next_peak in peaks.into_iter().skip(1) {
new_root = self.push_generated(combine_nodes(
self.resolve_link(new_root)?,
self.resolve_link(next_peak)?,
));
}
for _ in 0..truncated {
self.pop();
}
self.root = new_root;
Ok(truncated)
}
/// Length of array representation of the tree.
pub fn len(&self) -> u32 {
self.stored_count
}
/// Link to the root node
pub fn root(&self) -> EntryLink {
self.root
}
/// Reference to the root node.
pub fn root_node(&self) -> Result<IndexedNode, Error> {
self.resolve_link(self.root)
}
/// If this tree is empty.
pub fn is_empty(&self) -> bool {
self.stored_count == 0
}
}
/// Reference to the node with link attached.
#[derive(Debug)]
pub struct IndexedNode<'a> {
node: &'a Entry,
link: EntryLink,
}
impl<'a> IndexedNode<'a> {
fn left(&self) -> Result<EntryLink, Error> {
self.node.left().map_err(|e| e.augment(self.link))
}
fn right(&self) -> Result<EntryLink, Error> {
self.node.right().map_err(|e| e.augment(self.link))
}
/// Reference to the entry struct.
pub fn node(&self) -> &Entry {
self.node
}
/// Reference to the entry metadata.
pub fn data(&self) -> &NodeData {
&self.node.data
}
/// Actual link by what this node was resolved.
pub fn link(&self) -> EntryLink {
self.link
}
}
fn combine_nodes<'a>(left: IndexedNode<'a>, right: IndexedNode<'a>) -> Entry {
Entry {
kind: EntryKind::Node(left.link, right.link),
data: NodeData::combine(&left.node.data, &right.node.data),
}
}
#[cfg(test)]
mod tests {
use super::{Entry, EntryKind, EntryLink, NodeData, Tree};
use assert_matches::assert_matches;
use quickcheck::{quickcheck, TestResult};
fn leaf(height: u32) -> NodeData {
NodeData {
consensus_branch_id: 1,
subtree_commitment: [0u8; 32],
start_time: 0,
end_time: 0,
start_target: 0,
end_target: 0,
start_sapling_root: [0u8; 32],
end_sapling_root: [0u8; 32],
subtree_total_work: 0.into(),
start_height: height as u64,
end_height: height as u64,
sapling_tx: 7,
}
}
fn initial() -> Tree {
let node1: Entry = leaf(1).into();
let node2: Entry = leaf(2).into();
let node3 = Entry {
data: NodeData::combine(&node1.data, &node2.data),
kind: EntryKind::Leaf,
};
Tree::populate(vec![node1, node2, node3], EntryLink::Stored(2))
}
// returns tree with specified number of leafs and it's root
fn generated(length: u32) -> Tree {
assert!(length >= 3);
let mut tree = initial();
for i in 2..length {
tree.append_leaf(leaf(i + 1).into())
.expect("Failed to append");
}
tree
}
#[test]
fn discrete_append() {
let mut tree = initial();
// ** APPEND 3 **
let appended = tree.append_leaf(leaf(3)).expect("Failed to append");
let new_root = tree.root_node().expect("Failed to resolve root").node;
// initial tree: (2)
// / \
// (0) (1)
//
// new tree:
// (4g)
// / \
// (2) \
// / \ \
// (0) (1) (3)
//
// so only (3) is added as real leaf
// while new root, (4g) is generated one
assert_eq!(new_root.data.end_height, 3);
assert_eq!(appended.len(), 1);
// ** APPEND 4 **
let appended = tree.append_leaf(leaf(4)).expect("Failed to append");
let new_root = tree.root_node().expect("Failed to resolve root").node;
// intermediate tree:
// (4g)
// / \
// (2) \
// / \ \
// (0) (1) (3)
//
// new tree:
// ( 6 )
// / \
// (2) (5)
// / \ / \
// (0) (1) (3) (4)
//
// so (4), (5), (6) are added as real leaves
// and new root, (6) is stored one
assert_eq!(new_root.data.end_height, 4);
assert_eq!(appended.len(), 3);
assert_matches!(tree.root(), EntryLink::Stored(6));
// ** APPEND 5 **
let appended = tree.append_leaf(leaf(5)).expect("Failed to append");
let new_root = tree.root_node().expect("Failed to resolve root").node;
// intermediate tree:
// ( 6 )
// / \
// (2) (5)
// / \ / \
// (0) (1) (3) (4)
//
// new tree:
// ( 8g )
// / \
// ( 6 ) \
// / \ \
// (2) (5) \
// / \ / \ \
// (0) (1) (3) (4) (7)
//
// so (7) is added as real leaf
// and new root, (8g) is generated one
assert_eq!(new_root.data.end_height, 5);
assert_eq!(appended.len(), 1);
assert_matches!(tree.root(), EntryLink::Generated(_));
tree.for_children(tree.root(), |l, r| {
assert_matches!(l, EntryLink::Stored(6));
assert_matches!(r, EntryLink::Stored(7));
});
// *** APPEND #6 ***
let appended = tree.append_leaf(leaf(6)).expect("Failed to append");
let new_root = tree.root_node().expect("Failed to resolve root").node;
// intermediate tree:
// ( 8g )
// / \
// ( 6 ) \
// / \ \
// (2) (5) \
// / \ / \ \
// (0) (1) (3) (4) (7)
//
// new tree:
// (---10g--)
// / \
// ( 6 ) \
// / \ \
// (2) (5) (9)
// / \ / \ / \
// (0) (1) (3) (4) (7) (8)
//
// so (7) is added as real leaf
// and new root, (10g) is generated one
assert_eq!(new_root.data.end_height, 6);
assert_eq!(appended.len(), 2);
assert_matches!(tree.root(), EntryLink::Generated(_));
tree.for_children(tree.root(), |l, r| {
assert_matches!(l, EntryLink::Stored(6));
assert_matches!(r, EntryLink::Stored(9));
});
// *** APPEND #7 ***
let appended = tree.append_leaf(leaf(7)).expect("Failed to append");
let new_root = tree.root_node().expect("Failed to resolve root").node;
// intermediate tree:
// (---8g---)
// / \
// ( 6 ) \
// / \ \
// (2) (5) (9)
// / \ / \ / \
// (0) (1) (3) (4) (7) (8)
//
// new tree:
// (---12g--)
// / \
// (---11g---) \
// / \ \
// ( 6 ) \ \
// / \ \ \
// (2) (5) (9) \
// / \ / \ / \ \
// (0) (1) (3) (4) (7) (8) (10)
//
// so (10) is added as real leaf
// and new root, (12g) is generated one
assert_eq!(new_root.data.end_height, 7);
assert_eq!(appended.len(), 1);
assert_matches!(tree.root(), EntryLink::Generated(_));
tree.for_children(tree.root(), |l, r| {
assert_matches!(l, EntryLink::Generated(_));
tree.for_children(l, |l, r| {
assert_matches!((l, r), (EntryLink::Stored(6), EntryLink::Stored(9)))
});
assert_matches!(r, EntryLink::Stored(10));
});
}
#[test]
fn truncate_simple() {
let mut tree = generated(9);
let total_truncated = tree.truncate_leaf().expect("Failed to truncate");
// initial tree:
//
// (-------16g------)
// / \
// (--------14-------) \
// / \ \
// ( 6 ) ( 13 ) \
// / \ / \ \
// (2) (5) (9) (12) \
// / \ / \ / \ / \ \
// (0) (1) (3) (4) (7) (8) (10) (11) (15)
//
// new tree:
// (--------14-------)
// / \
// ( 6 ) ( 13 )
// / \ / \
// (2) (5) (9) (12)
// / \ / \ / \ / \
// (0) (1) (3) (4) (7) (8) (10) (11)
//
// so (15) is truncated
// and new root, (14) is a stored one now
assert_matches!(tree.root(), EntryLink::Stored(14));
assert_eq!(total_truncated, 1);
assert_eq!(tree.len(), 15);
}
#[test]
fn truncate_generated() {
let mut tree = generated(10);
let deleted = tree.truncate_leaf().expect("Failed to truncate");
// initial tree:
//
// (--------18g--------)
// / \
// (--------14-------) \
// / \ \
// ( 6 ) ( 13 ) \
// / \ / \ \
// (2) (5) (9) (12) (17)
// / \ / \ / \ / \ / \
// (0) (1) (3) (4) (7) (8) (10) (11) (15) (16)
//
// new tree:
// (-------16g------)
// / \
// (--------14-------) \
// / \ \
// ( 6 ) ( 13 ) \
// / \ / \ \
// (2) (5) (9) (12) \
// / \ / \ / \ / \ \
// (0) (1) (3) (4) (7) (8) (10) (11) (15)
// new root is generated
assert_matches!(tree.root(), EntryLink::Generated(_));
tree.for_children(tree.root(), |left, right| {
assert_matches!(
(left, right),
(EntryLink::Stored(14), EntryLink::Stored(15))
)
});
// two stored nodes should leave us (leaf 16 and no longer needed node 17)
assert_eq!(deleted, 2);
assert_eq!(tree.len(), 16);
}
#[test]
fn tree_len() {
let mut tree = initial();
assert_eq!(tree.len(), 3);
for i in 0..2 {
tree.append_leaf(leaf(i + 3)).expect("Failed to append");
}
assert_eq!(tree.len(), 7);
tree.truncate_leaf().expect("Failed to truncate");
assert_eq!(tree.len(), 4);
}
#[test]
fn tree_len_long() {
let mut tree = initial();
assert_eq!(tree.len(), 3);
for i in 0..4094 {
tree.append_leaf(leaf(i + 3)).expect("Failed to append");
}
assert_eq!(tree.len(), 8191); // 4096*2-1 (full tree)
for _ in 0..2049 {
tree.truncate_leaf().expect("Failed to truncate");
}
assert_eq!(tree.len(), 4083); // 4095 - log2(4096)
}
quickcheck! {
fn there_and_back(number: u32) -> TestResult {
if number > 1024*1024 {
TestResult::discard()
} else {
let mut tree = initial();
for i in 0..number {
tree.append_leaf(leaf(i+3)).expect("Failed to append");
}
for _ in 0..number {
tree.truncate_leaf().expect("Failed to truncate");
}
TestResult::from_bool(if let EntryLink::Stored(2) = tree.root() { true } else { false })
}
}
fn leaf_count(number: u32) -> TestResult {
if number > 1024 * 1024 || number < 3 {
TestResult::discard()
} else {
let mut tree = initial();
for i in 1..(number-1) {
tree.append_leaf(leaf(i+2)).expect("Failed to append");
}
TestResult::from_bool(
tree.root_node().expect("no root").node.leaf_count() == number as u64
)
}
}
fn parity(number: u32) -> TestResult {
if number > 2048 * 2048 || number < 3 {
TestResult::discard()
} else {
let mut tree = initial();
for i in 1..(number-1) {
tree.append_leaf(leaf(i+2)).expect("Failed to append");
}
TestResult::from_bool(
if number & (number - 1) == 0 {
if let EntryLink::Stored(_) = tree.root() { true }
else { false }
} else {
if let EntryLink::Generated(_) = tree.root() { true }
else { false }
}
)
}
}
fn parity_with_truncate(add: u32, delete: u32) -> TestResult {
// First we add `add` number of leaves, then delete `delete` number of leaves
// What is left should be consistent with generated-stored structure
if add > 2048 * 2048 || add < delete {
TestResult::discard()
} else {
let mut tree = initial();
for i in 0..add {
tree.append_leaf(leaf(i+3)).expect("Failed to append");
}
for _ in 0..delete {
tree.truncate_leaf().expect("Failed to truncate");
}
let total = add - delete + 2;
TestResult::from_bool(
if total & total - 1 == 0 {
if let EntryLink::Stored(_) = tree.root() { true }
else { false }
} else {
if let EntryLink::Generated(_) = tree.root() { true }
else { false }
}
)
}
}
// Length of tree is always less than number of leaves squared
fn stored_length(add: u32, delete: u32) -> TestResult {
if add > 2048 * 2048 || add < delete {
TestResult::discard()
} else {
let mut tree = initial();
for i in 0..add {
tree.append_leaf(leaf(i+3)).expect("Failed to append");
}
for _ in 0..delete {
tree.truncate_leaf().expect("Failed to truncate");
}
let total = add - delete + 2;
TestResult::from_bool(total * total > tree.len())
}
}
}
}

View File

@@ -1,9 +1,15 @@
[package] [package]
name = "zcash_primitives" name = "zcash_primitives"
version = "0.0.0" description = "Rust implementations of the Zcash primitives"
version = "0.1.0"
authors = [ authors = [
"Jack Grigg <jack@z.cash>", "Jack Grigg <jack@z.cash>",
] ]
homepage = "https://github.com/zcash/librustzcash"
repository = "https://github.com/zcash/librustzcash"
readme = "README.md"
license = "MIT OR Apache-2.0"
edition = "2018"
[dependencies] [dependencies]
aes = "0.3" aes = "0.3"
@@ -11,16 +17,24 @@ blake2b_simd = "0.5"
blake2s_simd = "0.5" blake2s_simd = "0.5"
byteorder = "1" byteorder = "1"
crypto_api_chachapoly = "0.2.1" crypto_api_chachapoly = "0.2.1"
ff = { path = "../ff" } ff = { version = "0.5.0", path = "../ff" }
fpe = "0.2" fpe = "0.2"
hex = "0.3" hex = "0.3"
lazy_static = "1" lazy_static = "1"
pairing = { path = "../pairing" } log = "0.4"
pairing = { version = "0.15.0", path = "../pairing" }
rand = "0.7" rand = "0.7"
rand_core = "0.5" rand_core = "0.5.1"
rand_os = "0.2" ripemd160 = { version = "0.8", optional = true }
secp256k1 = { version = "=0.15.0", optional = true }
sha2 = "0.8" sha2 = "0.8"
[dev-dependencies] [dev-dependencies]
hex-literal = "0.1" hex-literal = "0.2"
rand_xorshift = "0.2" rand_xorshift = "0.2"
[features]
transparent-inputs = ["ripemd160", "secp256k1"]
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -6,7 +6,8 @@ This library contains Rust implementations of the Zcash primitives.
Licensed under either of Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option. at your option.

View File

@@ -2,13 +2,11 @@
extern crate pairing; extern crate pairing;
extern crate rand_core; extern crate rand_core;
extern crate rand_os;
extern crate test; extern crate test;
extern crate zcash_primitives; extern crate zcash_primitives;
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand_core::RngCore; use rand_core::{OsRng, RngCore};
use rand_os::OsRng;
use zcash_primitives::jubjub::JubjubBls12; use zcash_primitives::jubjub::JubjubBls12;
use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization}; use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization};

View File

@@ -1,30 +1,52 @@
//! Structs and methods for handling Zcash block headers.
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use hex; use hex;
use sha2::{Digest, Sha256};
use std::fmt; use std::fmt;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::ops::Deref; use std::ops::Deref;
use serialize::Vector; use crate::serialize::Vector;
pub mod equihash;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BlockHash(pub [u8; 32]); pub struct BlockHash(pub [u8; 32]);
impl fmt::Display for BlockHash { impl fmt::Display for BlockHash {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut data = self.0.clone(); let mut data = self.0;
data.reverse(); data.reverse();
formatter.write_str(&hex::encode(data)) formatter.write_str(&hex::encode(data))
} }
} }
impl BlockHash {
/// Constructs a [`BlockHash`] from the given slice.
///
/// # Panics
///
/// This function will panic if the slice is not exactly 32 bytes.
pub fn from_slice(bytes: &[u8]) -> Self {
assert_eq!(bytes.len(), 32);
let mut hash = [0; 32];
hash.copy_from_slice(&bytes);
BlockHash(hash)
}
}
/// A Zcash block header. /// A Zcash block header.
pub struct BlockHeader(BlockHeaderData); pub struct BlockHeader {
hash: BlockHash,
data: BlockHeaderData,
}
impl Deref for BlockHeader { impl Deref for BlockHeader {
type Target = BlockHeaderData; type Target = BlockHeaderData;
fn deref(&self) -> &BlockHeaderData { fn deref(&self) -> &BlockHeaderData {
&self.0 &self.data
} }
} }
@@ -40,12 +62,31 @@ pub struct BlockHeaderData {
} }
impl BlockHeaderData { impl BlockHeaderData {
pub fn freeze(self) -> BlockHeader { pub fn freeze(self) -> io::Result<BlockHeader> {
BlockHeader(self) BlockHeader::from_data(self)
} }
} }
impl BlockHeader { impl BlockHeader {
fn from_data(data: BlockHeaderData) -> io::Result<Self> {
let mut header = BlockHeader {
hash: BlockHash([0; 32]),
data,
};
let mut raw = vec![];
header.write(&mut raw)?;
header
.hash
.0
.copy_from_slice(&Sha256::digest(&Sha256::digest(&raw)));
Ok(header)
}
/// Returns the hash of this header.
pub fn hash(&self) -> BlockHash {
self.hash
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> { pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let version = reader.read_i32::<LittleEndian>()?; let version = reader.read_i32::<LittleEndian>()?;
@@ -66,7 +107,7 @@ impl BlockHeader {
let solution = Vector::read(&mut reader, |r| r.read_u8())?; let solution = Vector::read(&mut reader, |r| r.read_u8())?;
Ok(BlockHeader(BlockHeaderData { BlockHeader::from_data(BlockHeaderData {
version, version,
prev_block, prev_block,
merkle_root, merkle_root,
@@ -75,7 +116,7 @@ impl BlockHeader {
bits, bits,
nonce, nonce,
solution, solution,
})) })
} }
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> { pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
@@ -202,6 +243,10 @@ mod tests {
#[test] #[test]
fn header_read_write() { fn header_read_write() {
let header = BlockHeader::read(&HEADER_MAINNET_415000[..]).unwrap(); let header = BlockHeader::read(&HEADER_MAINNET_415000[..]).unwrap();
assert_eq!(
format!("{}", header.hash()),
"0000000001ab37793ce771262b2ffa082519aa3fe891250a1adb43baaf856168"
);
let mut encoded = Vec::with_capacity(HEADER_MAINNET_415000.len()); let mut encoded = Vec::with_capacity(HEADER_MAINNET_415000.len());
header.write(&mut encoded).unwrap(); header.write(&mut encoded).unwrap();
assert_eq!(&HEADER_MAINNET_415000[..], &encoded[..]); assert_eq!(&HEADER_MAINNET_415000[..], &encoded[..]);

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