388 Commits

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
str4d
7b11d64cf9 Merge pull request #111 from jimpo/master
Fix off-by-one so pedersen_hash doesn't consume too many generators.
2019-08-20 21:14:48 +01: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
Jim Posen
d4b6c0e1a2 Use expect to remove unreachable break. 2019-08-20 17:18:26 +02: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
Jim Posen
40f768ed60 Fix off-by-one so pedersen_hash doesn't consume too many generators. 2019-08-17 12:01:11 +02:00
ebfull
6f0080ba72 Merge pull request #105 from Eirik0/cargo-clean-up
cargo fmt
2019-08-16 20:30:00 -06:00
Eirik Ogilvie-Wigley
7461f8936d Update travis to require formatting 2019-08-15 10:45:24 -06:00
Eirik Ogilvie-Wigley
a7c5993597 cargo fmt 2019-08-15 10:41:48 -06:00
Eirik Ogilvie-Wigley
272be62212 cargo fmt zcash_proofs 2019-08-15 10:40:07 -06:00
Eirik Ogilvie-Wigley
81c58172c3 cargo fmt zcash_primitives 2019-08-15 10:39:55 -06:00
Eirik Ogilvie-Wigley
9a4f6812f1 cargo fmt bellman 2019-08-15 10:38:41 -06:00
Eirik Ogilvie-Wigley
bc7ea564d3 cargo fmt pairing 2019-08-15 10:38:40 -06:00
str4d
3584485516 Merge pull request #104 from str4d/remove-sapling-crypto
Refactor to remove sapling-crypto
2019-08-15 17:20:07 +01:00
Jack Grigg
1b865ecfdf Remove sapling-crypto 2019-08-14 10:48:06 +01:00
Jack Grigg
5fb9b86ba0 Move Jubjub, Pedersen hash and primitives into zcash_primitives 2019-08-14 10:47:22 +01:00
Jack Grigg
b8af749b40 Move generic circuit gadgets into bellman 2019-08-14 10:45:58 +01:00
Jack Grigg
61c633db1e Move Jubjub and Pedersen hash gadgets into zcash_proofs
These are currently too Zcash-specific to be generalized, and need some
targeted refactoring.
2019-08-14 10:45:57 +01:00
Jack Grigg
2ae5804a67 Move Sprout and Sapling circuits into zcash_proofs 2019-08-14 10:45:55 +01:00
Jack Grigg
7ea6d10480 Move redjubjub into zcash_primitives 2019-08-14 10:43:25 +01:00
str4d
f931562431 Merge pull request #94 from str4d/upgrade-deps
Upgrade dependencies
2019-08-14 10:39:42 +01:00
Jack Grigg
81786c24c0 Fix clippy linter errors in pairing crate 2019-08-14 01:14:05 +01:00
Jack Grigg
13933d8c51 Use modern clippy linter syntax 2019-08-14 01:14:04 +01:00
Jack Grigg
3e35dd215c cargo update 2019-08-14 01:14:02 +01:00
Jack Grigg
3211536324 Upgrade to crypto_api_chachapoly >= 0.2.1 2019-08-14 01:12:26 +01:00
Jack Grigg
ec321382e1 Upgrade to bech32 0.7 2019-08-14 01:12:25 +01:00
Jack Grigg
a12b8053b0 Upgrade to digest 0.8 2019-08-14 01:12:24 +01:00
Jack Grigg
2f1cae62b1 Upgrade to fpe 0.2 2019-08-14 01:12:23 +01:00
str4d
52ea437e11 Merge pull request #92 from str4d/transaction-builder
Transaction builder
2019-08-14 01:11:12 +01:00
str4d
4a6c9ec425 Comment that we support a minimal set of script opcodes
Co-Authored-By: Daira Hopwood <daira@jacaranda.org>
2019-08-14 00:30:18 +01:00
Jack Grigg
a28d94ff2e Panic if Amount addition or subtraction overflows 2019-08-14 00:16:09 +01:00
Jack Grigg
1760b275a7 Simplify transaction builder tests
Requires impl PartialEq for Transaction, which is implemented as a TxId
comparison (relying on the invariant that Transaction is immutable).
2019-08-13 15:24:08 +01:00
Jack Grigg
3a73f946c5 Simplify structure of transaction builder errors 2019-08-13 15:10:57 +01:00
Jack Grigg
e6663212ff Improve documentation for Amount 2019-08-08 09:21:09 +01:00
Jack Grigg
7c07914bfd Separate Amount::{from_i64, from_nonnegative_i64} APIs
This is more intuitive than a boolean flag for handling non-negative
Amounts stored in i64 values.
2019-08-08 00:55:23 +01: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
str4d
4255b44b21 Merge pull request #13 from str4d/nullifier-test-vectors
Test note nullifier in key component test vectors
2019-08-02 10:03:07 +01:00
Jack Grigg
59ed258c7f Make Amount opaque, and use it more
This helps to ensure type-safety of values that are required to satisfy
zatoshi range bounds.
2019-07-27 00:35:29 +01:00
Jack Grigg
ab60b8804a impl operators for Amount 2019-07-27 00:35:28 +01:00
Jack Grigg
0ea4408d46 Amount::{zero, is_positive, is_negative} 2019-07-27 00:35:27 +01:00
Jack Grigg
fa50d551c8 Move Amount impl into a submodule 2019-07-27 00:35:26 +01:00
Jack Grigg
17f6bbcc67 Pass tx builder RNG to spend_sig() 2019-07-27 00:35:04 +01:00
Jack Grigg
532299d46e Allow transaction::Builder RNG to be configured 2019-07-27 00:32:56 +01:00
Jack Grigg
4b61120cd0 Pass tx builder RNG to SaplingNoteEncryption 2019-07-27 00:31:16 +01:00
Jack Grigg
54ef63bace Place zcash_proofs::prover::LocalTxProver behind a feature flag
This enables zcash_proofs to be compiled to WASM, which the directories
crate doesn't support.
2019-07-27 00:28:03 +01:00
Jack Grigg
a1cd9dfbac transaction::Builder::add_transparent_output() 2019-07-27 00:28:02 +01:00
Jack Grigg
c26188a0bb TransparentAddress struct for P2PKH and P2SH 2019-07-27 00:28:01 +01:00
Jack Grigg
dab3c002b7 Script opcode and data support
Overrides the shift-left operator for pushing opcodes onto the Script,
matching the notation used in zcashd.
2019-07-27 00:28:00 +01:00
Jack Grigg
1862354ea6 Sapling transaction builder 2019-07-27 00:27:50 +01:00
Jack Grigg
01618038bf TxProver trait to abstract over the circuit parameters
An implementation using local parameters is provided in the zcash_proofs
crate.
2019-07-26 23:56:00 +01:00
str4d
05f098e893 Merge pull request #91 from str4d/upgrade-rand
Upgrade rand crate dependency to 0.7
2019-07-26 23:31:42 +01:00
str4d
0255dca16e Clarify masking of bits in Field::random impls
Co-Authored-By: Daira Hopwood <daira@jacaranda.org>
2019-07-26 19:43:42 +01:00
str4d
5a48d179b8 Merge pull request #90 from adityapk00/paperwallet
Make some methods pub for use in External Wallet Generators

The exposed APIs may change in future crate versions.
2019-07-22 13:25:25 +01:00
Aditya Kulkarni
7f60f0f881 Make some methods in DiversifierKey, DiversiferIndex pub 2019-07-18 16:19:45 -07:00
Jack Grigg
c4e14ad0b1 Address libc deprecations 2019-07-19 00:47:42 +02:00
Jack Grigg
6f9083b5ab Migrate to rand 0.7 2019-07-19 00:47:40 +02:00
Jack Grigg
b0913afdd7 Migrate remaining crates to rand_core 0.4 2019-07-19 00:46:37 +02:00
Jack Grigg
8f7adec0d9 Migrate zcash_primitives to rand_core 0.4 2019-07-19 00:46:37 +02:00
Jack Grigg
60d344a0a7 Migrate sapling-crypto to rand_core 0.4 2019-07-19 00:46:34 +02:00
Jack Grigg
83e1af104e Migrate ff, group, pairing, and bellman to rand 0.6 2019-07-19 00:42:39 +02:00
Jack Grigg
5728bda2c1 Replace rust-crypto with sha2 in sapling-crypto dev-dependencies
This removes rand < 0.5 from our Cargo.lock.
2019-07-19 00:35:06 +02:00
Jack Grigg
ccf75c39c1 Migrate remaining crates to rand 0.5 2019-07-19 00:35:06 +02:00
Jack Grigg
6149166ccb Migrate zcash_primitives to rand 0.5 2019-07-19 00:35:06 +02:00
Jack Grigg
adfc88926b Migrate sapling-crypto to rand 0.5 2019-07-19 00:35:06 +02:00
Jack Grigg
4606a0cefb Migrate bellman to rand 0.5 2019-07-19 00:35:06 +02:00
Jack Grigg
a7e22b3550 Migrate pairing to rand 0.5 2019-07-19 00:35:06 +02:00
Jack Grigg
ce6e2a5825 Migrate group to rand 0.5 2019-07-19 00:35:06 +02:00
Jack Grigg
7a6642b221 Migrate ff to rand_core 0.3 (used by rand 0.5) 2019-07-19 00:35:06 +02:00
str4d
9e758dc7d9 Merge pull request #83 from str4d/blake2_simd
Migrate to blake2b_simd and blake2s_simd crates
2019-07-18 17:41:59 +01:00
str4d
8361674efc Merge branch 'master' into blake2_simd 2019-07-18 18:22:20 +02:00
str4d
5e3409ea85 Merge pull request #68 from str4d/sapling-commitment-tree
Sapling commitment tree
2019-07-15 16:52:46 +02:00
Jack Grigg
504c3eaeae Address Daira's review comments 2019-07-15 16:33:43 +02:00
Jack Grigg
07dbfbef59 Address Eirik's review comments 2019-07-10 13:53:22 -04:00
Jack Grigg
79006ecbdf Unify Sapling tree depth constants
When sapling-crypto is refactored, the zcash_primitives::sapling
constant would become the canonical one.
2019-07-10 13:53:22 -04:00
Jack Grigg
e67560b154 Document merkle_tree module 2019-07-10 13:53:22 -04:00
Jack Grigg
b9cea33804 Move merkle_tree::Node into sapling module
This makes the merkle_tree module properly generic over the tree hash.
It still hard-codes a depth 32 tree, because Rust doesn't yet support
generic sizes, and we are unlikely to need to alter the tree depth in
future circuit changes.
2019-07-10 13:53:22 -04:00
Jack Grigg
263bbe1207 Use Iterator::all() in place of Iterator::fold() 2019-07-10 13:53:22 -04:00
Jack Grigg
98d7621135 Don't assert when parsing a CommitmentTreeWitness 2019-07-10 13:53:22 -04:00
Jack Grigg
9b4186705a Add serialization APIs to CommitmentTree and IncrementalWitness 2019-07-10 13:53:22 -04:00
Jack Grigg
70a7069058 Helper for serializing Option<T> 2019-07-10 13:53:22 -04:00
Jack Grigg
667d6101c9 Sapling incremental witnesses 2019-07-10 13:53:20 -04:00
Jack Grigg
bf74915053 Move CommitmentTreeWitness into zcash_primitives 2019-07-10 13:52:47 -04:00
Jack Grigg
f4059a5faa Sapling commitment tree 2019-07-10 13:44:21 -04:00
Jack Grigg
8f3f95ee08 Sapling commitment tree empty roots 2019-07-10 13:44:21 -04:00
Jack Grigg
785f22ca5a Move Sapling commitment tree hash into zcash_primitives 2019-07-10 13:44:19 -04:00
Jack Grigg
d1ce6749fe Bump minimum Rust version to 1.36 2019-07-10 13:12:26 -04:00
Jack Grigg
999dcbfcab Migrate to blake2b_simd and blake2s_simd crates
The primary reason for migrating is that these crates provide APIs for
setting the personalisation string. This enables us to depend solely on
published crates, and thus publish our own crates.

The SIMD implementations are ported from libsodium.

Closes #67.
2019-07-10 13:12:14 -04:00
str4d
91c6b0b3f0 Merge pull request #88 from str4d/address-encodings
Sapling address encodings
2019-07-10 07:04:37 -04:00
Jack Grigg
a3a9ee2682 Validate PaymentAddress diversifier when decoding 2019-07-02 00:07:48 +01:00
Jack Grigg
dd9c9ffa3f Add encodings for ExtendedSpendingKey and ExtendedFullViewingKey 2019-06-27 16:21:32 +01:00
Jack Grigg
17f60a0354 Implement PaymentAddress encoding and decoding 2019-06-27 16:21:00 +01:00
Jack Grigg
81b2b1b554 Wallet spending key derivation path 2019-06-27 16:19:33 +01:00
Jack Grigg
f25a8a557e Convert zip32 crate into a module of zcash_primitives 2019-06-27 16:14:24 +01:00
Jack Grigg
fae919ec1c Rename zcash_wallet to zcash_client_backend, set to 2018 edition 2019-06-27 16:02:12 +01:00
str4d
d5b6b9aa1e Test note nullifier in key component test vectors 2019-01-03 22:01:02 +00:00
178 changed files with 22159 additions and 13164 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,18 +1,19 @@
language: rust language: rust
rust: rust:
- 1.32.0 - 1.37.0
env:
global:
# See https://stackoverflow.com/a/43339593
- RUST_BACKTRACE=1
before_cache:
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
cache: cargo cache: cargo
before_script:
- rustup component add rustfmt
script: script:
- cargo build --verbose --release --all - cargo build --verbose --release --all
- cargo fmt --all -- --check
- cargo test --verbose --release --all - cargo test --verbose --release --all
- cargo test --verbose --release --all -- --ignored
before_cache:
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
- cargo install cargo-update || echo "cargo-update already installed"
- cargo install-update -a # update outdated cached binaries

View File

@@ -1,3 +0,0 @@
The Hush Developers
Duke Leto, https://git.hush.is/duke

982
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,11 +5,10 @@ members = [
"group", "group",
"librustzcash", "librustzcash",
"pairing", "pairing",
"sapling-crypto", "zcash_client_backend",
"zcash_history",
"zcash_primitives", "zcash_primitives",
"zcash_proofs", "zcash_proofs",
"zcash_wallet",
"zip32",
] ]
[profile.release] [profile.release]

619
LICENSE
View File

@@ -1,619 +0,0 @@
GENERAL GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GENERAL General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GENERAL General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GENERAL General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GENERAL GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GENERAL General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GENERAL Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GENERAL Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GENERAL Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GENERAL General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GENERAL General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GENERAL General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GENERAL General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

View File

@@ -1,7 +1,7 @@
# Hush Rust crates # Zcash Rust crates
This repository contains a set of Rust crates for This repository contains a (work-in-progress) set of Rust crates for
working with low-level Hush stuff. working with Zcash.
## Security Warnings ## Security Warnings
@@ -9,5 +9,16 @@ These libraries are currently under development and have not been fully-reviewed
## License ## License
GNU Public License 3 All code in this workspace is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

View File

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

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,21 +1,20 @@
//! This module contains an `EvaluationDomain` abstraction for //! This module contains an [`EvaluationDomain`] abstraction for performing
//! performing various kinds of polynomial arithmetic on top of //! various kinds of polynomial arithmetic on top of the scalar field.
//! the scalar field.
//! //!
//! In pairing-based SNARKs like Groth16, we need to calculate //! In pairing-based SNARKs like [Groth16], we need to calculate a quotient
//! a quotient polynomial over a target polynomial with roots //! polynomial over a target polynomial with roots at distinct points associated
//! at distinct points associated with each constraint of the //! with each constraint of the constraint system. In order to be efficient, we
//! constraint system. In order to be efficient, we choose these //! choose these roots to be the powers of a 2<sup>n</sup> root of unity in the
//! roots to be the powers of a 2^n root of unity in the field. //! field. This allows us to perform polynomial operations in O(n) by performing
//! This allows us to perform polynomial operations in O(n) //! an O(n log n) FFT over such a domain.
//! by performing an O(n log n) FFT over such a domain. //!
//! [`EvaluationDomain`]: crate::domain::EvaluationDomain
//! [Groth16]: https://eprint.iacr.org/2016/260
use ff::{Field, PrimeField, ScalarEngine}; use ff::{Field, PrimeField, ScalarEngine};
use group::CurveProjective; use group::CurveProjective;
use super::{ use super::SynthesisError;
SynthesisError
};
use super::multicore::Worker; use super::multicore::Worker;
@@ -25,24 +24,27 @@ pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
omega: E::Fr, omega: E::Fr,
omegainv: E::Fr, omegainv: E::Fr,
geninv: E::Fr, geninv: E::Fr,
minv: E::Fr minv: E::Fr,
}
impl<E: ScalarEngine, G: Group<E>> AsRef<[G]> for EvaluationDomain<E, G> {
fn as_ref(&self) -> &[G] {
&self.coeffs
}
}
impl<E: ScalarEngine, G: Group<E>> AsMut<[G]> for EvaluationDomain<E, G> {
fn as_mut(&mut self) -> &mut [G] {
&mut self.coeffs
}
} }
impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> { impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
pub fn as_ref(&self) -> &[G] {
&self.coeffs
}
pub fn as_mut(&mut self) -> &mut [G] {
&mut self.coeffs
}
pub fn into_coeffs(self) -> Vec<G> { pub fn into_coeffs(self) -> Vec<G> {
self.coeffs self.coeffs
} }
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError> pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError> {
{
// Compute the size of our evaluation domain // Compute the size of our evaluation domain
let mut m = 1; let mut m = 1;
let mut exp = 0; let mut exp = 0;
@@ -53,7 +55,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
// The pairing-friendly curve may not be able to support // The pairing-friendly curve may not be able to support
// large enough (radix2) evaluation domains. // large enough (radix2) evaluation domains.
if exp >= E::Fr::S { if exp >= E::Fr::S {
return Err(SynthesisError::PolynomialDegreeTooLarge) return Err(SynthesisError::PolynomialDegreeTooLarge);
} }
} }
@@ -67,29 +69,30 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
coeffs.resize(m, G::group_zero()); coeffs.resize(m, G::group_zero());
Ok(EvaluationDomain { Ok(EvaluationDomain {
coeffs: coeffs, coeffs,
exp: exp, exp,
omega: omega, omega,
omegainv: omega.inverse().unwrap(), omegainv: omega.inverse().unwrap(),
geninv: E::Fr::multiplicative_generator().inverse().unwrap(), geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap() minv: E::Fr::from_str(&format!("{}", m))
.unwrap()
.inverse()
.unwrap(),
}) })
} }
pub fn fft(&mut self, worker: &Worker) pub fn fft(&mut self, worker: &Worker) {
{
best_fft(&mut self.coeffs, worker, &self.omega, self.exp); best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
} }
pub fn ifft(&mut self, worker: &Worker) pub fn ifft(&mut self, worker: &Worker) {
{
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp); best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
let minv = self.minv; let minv = self.minv;
for v in self.coeffs.chunks_mut(chunk) { for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || { scope.spawn(move |_scope| {
for v in v { for v in v {
v.group_mul_assign(&minv); v.group_mul_assign(&minv);
} }
@@ -98,11 +101,10 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
}); });
} }
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) {
{
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
scope.spawn(move || { scope.spawn(move |_scope| {
let mut u = g.pow(&[(i * chunk) as u64]); let mut u = g.pow(&[(i * chunk) as u64]);
for v in v.iter_mut() { for v in v.iter_mut() {
v.group_mul_assign(&u); v.group_mul_assign(&u);
@@ -113,14 +115,12 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
}); });
} }
pub fn coset_fft(&mut self, worker: &Worker) pub fn coset_fft(&mut self, worker: &Worker) {
{
self.distribute_powers(worker, E::Fr::multiplicative_generator()); self.distribute_powers(worker, E::Fr::multiplicative_generator());
self.fft(worker); self.fft(worker);
} }
pub fn icoset_fft(&mut self, worker: &Worker) pub fn icoset_fft(&mut self, worker: &Worker) {
{
let geninv = self.geninv; let geninv = self.geninv;
self.ifft(worker); self.ifft(worker);
@@ -139,13 +139,15 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
/// The target polynomial is the zero polynomial in our /// The target polynomial is the zero polynomial in our
/// evaluation domain, so we must perform division over /// evaluation domain, so we must perform division over
/// a coset. /// a coset.
pub fn divide_by_z_on_coset(&mut self, worker: &Worker) pub fn divide_by_z_on_coset(&mut self, worker: &Worker) {
{ let i = self
let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap(); .z(&E::Fr::multiplicative_generator())
.inverse()
.unwrap();
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for v in self.coeffs.chunks_mut(chunk) { for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || { scope.spawn(move |_scope| {
for v in v { for v in v {
v.group_mul_assign(&i); v.group_mul_assign(&i);
} }
@@ -159,8 +161,12 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
assert_eq!(self.coeffs.len(), other.coeffs.len()); assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { for (a, b) in self
scope.spawn(move || { .coeffs
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move |_scope| {
for (a, b) in a.iter_mut().zip(b.iter()) { for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_mul_assign(&b.0); a.group_mul_assign(&b.0);
} }
@@ -174,8 +180,12 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
assert_eq!(self.coeffs.len(), other.coeffs.len()); assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { for (a, b) in self
scope.spawn(move || { .coeffs
.chunks_mut(chunk)
.zip(other.coeffs.chunks(chunk))
{
scope.spawn(move |_scope| {
for (a, b) in a.iter_mut().zip(b.iter()) { for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_sub_assign(&b); a.group_sub_assign(&b);
} }
@@ -200,7 +210,7 @@ impl<G: CurveProjective> PartialEq for Point<G> {
} }
} }
impl<G: CurveProjective> Copy for Point<G> { } impl<G: CurveProjective> Copy for Point<G> {}
impl<G: CurveProjective> Clone for Point<G> { impl<G: CurveProjective> Clone for Point<G> {
fn clone(&self) -> Point<G> { fn clone(&self) -> Point<G> {
@@ -231,7 +241,7 @@ impl<E: ScalarEngine> PartialEq for Scalar<E> {
} }
} }
impl<E: ScalarEngine> Copy for Scalar<E> { } impl<E: ScalarEngine> Copy for Scalar<E> {}
impl<E: ScalarEngine> Clone for Scalar<E> { impl<E: ScalarEngine> Clone for Scalar<E> {
fn clone(&self) -> Scalar<E> { fn clone(&self) -> Scalar<E> {
@@ -254,8 +264,7 @@ impl<E: ScalarEngine> Group<E> for Scalar<E> {
} }
} }
fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) {
{
let log_cpus = worker.log_num_cpus(); let log_cpus = worker.log_num_cpus();
if log_n <= log_cpus { if log_n <= log_cpus {
@@ -265,8 +274,7 @@ fn best_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], worker: &Worker, omega: &
} }
} }
fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32) fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u32) {
{
fn bitreverse(mut n: u32, l: u32) -> u32 { fn bitreverse(mut n: u32, l: u32) -> u32 {
let mut r = 0; let mut r = 0;
for _ in 0..l { for _ in 0..l {
@@ -288,22 +296,22 @@ fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u
let mut m = 1; let mut m = 1;
for _ in 0..log_n { for _ in 0..log_n {
let w_m = omega.pow(&[(n / (2*m)) as u64]); let w_m = omega.pow(&[u64::from(n / (2 * m))]);
let mut k = 0; let mut k = 0;
while k < n { while k < n {
let mut w = E::Fr::one(); let mut w = E::Fr::one();
for j in 0..m { for j in 0..m {
let mut t = a[(k+j+m) as usize]; let mut t = a[(k + j + m) as usize];
t.group_mul_assign(&w); t.group_mul_assign(&w);
let mut tmp = a[(k+j) as usize]; let mut tmp = a[(k + j) as usize];
tmp.group_sub_assign(&t); tmp.group_sub_assign(&t);
a[(k+j+m) as usize] = tmp; a[(k + j + m) as usize] = tmp;
a[(k+j) as usize].group_add_assign(&t); a[(k + j) as usize].group_add_assign(&t);
w.mul_assign(&w_m); w.mul_assign(&w_m);
} }
k += 2*m; k += 2 * m;
} }
m *= 2; m *= 2;
@@ -315,9 +323,8 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
worker: &Worker, worker: &Worker,
omega: &E::Fr, omega: &E::Fr,
log_n: u32, log_n: u32,
log_cpus: u32 log_cpus: u32,
) ) {
{
assert!(log_n >= log_cpus); assert!(log_n >= log_cpus);
let num_cpus = 1 << log_cpus; let num_cpus = 1 << log_cpus;
@@ -329,18 +336,18 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
let a = &*a; let a = &*a;
for (j, tmp) in tmp.iter_mut().enumerate() { for (j, tmp) in tmp.iter_mut().enumerate() {
scope.spawn(move || { scope.spawn(move |_scope| {
// Shuffle into a sub-FFT // Shuffle into a sub-FFT
let omega_j = omega.pow(&[j as u64]); let omega_j = omega.pow(&[j as u64]);
let omega_step = omega.pow(&[(j as u64) << log_new_n]); let omega_step = omega.pow(&[(j as u64) << log_new_n]);
let mut elt = E::Fr::one(); let mut elt = E::Fr::one();
for i in 0..(1 << log_new_n) { for (i, tmp) in tmp.iter_mut().enumerate() {
for s in 0..num_cpus { for s in 0..num_cpus {
let idx = (i + (s << log_new_n)) % (1 << log_n); let idx = (i + (s << log_new_n)) % (1 << log_n);
let mut t = a[idx]; let mut t = a[idx];
t.group_mul_assign(&elt); t.group_mul_assign(&elt);
tmp[i].group_add_assign(&t); tmp.group_add_assign(&t);
elt.mul_assign(&omega_step); elt.mul_assign(&omega_step);
} }
elt.mul_assign(&omega_j); elt.mul_assign(&omega_j);
@@ -357,7 +364,7 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
let tmp = &tmp; let tmp = &tmp;
for (idx, a) in a.chunks_mut(chunk).enumerate() { for (idx, a) in a.chunks_mut(chunk).enumerate() {
scope.spawn(move || { scope.spawn(move |_scope| {
let mut idx = idx * chunk; let mut idx = idx * chunk;
let mask = (1 << log_cpus) - 1; let mask = (1 << log_cpus) - 1;
for a in a { for a in a {
@@ -375,16 +382,19 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
#[test] #[test]
fn polynomial_arith() { fn polynomial_arith() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand::{self, Rand}; use rand_core::RngCore;
fn test_mul<E: ScalarEngine, R: rand::Rng>(rng: &mut R) fn test_mul<E: ScalarEngine, R: RngCore>(rng: &mut R) {
{
let worker = Worker::new(); let worker = Worker::new();
for coeffs_a in 0..70 { for coeffs_a in 0..70 {
for coeffs_b in 0..70 { for coeffs_b in 0..70 {
let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect(); let mut a: Vec<_> = (0..coeffs_a)
let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect(); .map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect();
let mut b: Vec<_> = (0..coeffs_b)
.map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect();
// naive evaluation // naive evaluation
let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b]; let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b];
@@ -423,10 +433,9 @@ fn polynomial_arith() {
#[test] #[test]
fn fft_composition() { fn fft_composition() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand; use rand_core::RngCore;
fn test_comp<E: ScalarEngine, R: rand::Rng>(rng: &mut R) fn test_comp<E: ScalarEngine, R: RngCore>(rng: &mut R) {
{
let worker = Worker::new(); let worker = Worker::new();
for coeffs in 0..10 { for coeffs in 0..10 {
@@ -434,7 +443,7 @@ fn fft_composition() {
let mut v = vec![]; let mut v = vec![];
for _ in 0..coeffs { for _ in 0..coeffs {
v.push(Scalar::<E>(rng.gen())); v.push(Scalar::<E>(E::Fr::random(rng)));
} }
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap(); let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
@@ -462,22 +471,23 @@ fn fft_composition() {
#[test] #[test]
fn parallel_fft_consistency() { fn parallel_fft_consistency() {
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand::{self, Rand}; use rand_core::RngCore;
use std::cmp::min; use std::cmp::min;
fn test_consistency<E: ScalarEngine, R: rand::Rng>(rng: &mut R) fn test_consistency<E: ScalarEngine, R: RngCore>(rng: &mut R) {
{
let worker = Worker::new(); let worker = Worker::new();
for _ in 0..5 { for _ in 0..5 {
for log_d in 0..10 { for log_d in 0..10 {
let d = 1 << log_d; let d = 1 << log_d;
let v1 = (0..d).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect::<Vec<_>>(); let v1 = (0..d)
.map(|_| Scalar::<E>(E::Fr::random(rng)))
.collect::<Vec<_>>();
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap(); let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap(); let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
for log_cpus in log_d..min(log_d+1, 3) { for log_cpus in log_d..min(log_d + 1, 3) {
parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus); parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus);
serial_fft(&mut v2.coeffs, &v2.omega, log_d); serial_fft(&mut v2.coeffs, &v2.omega, log_d);

View File

@@ -1,23 +1,17 @@
#[cfg(test)] //! Self-contained sub-circuit implementations for various primitives.
pub mod test; pub mod test;
pub mod boolean;
pub mod multieq;
pub mod uint32;
pub mod blake2s; pub mod blake2s;
pub mod num; pub mod boolean;
pub mod lookup; pub mod lookup;
pub mod ecc; pub mod multieq;
pub mod pedersen_hash;
pub mod multipack; pub mod multipack;
pub mod num;
pub mod sha256; pub mod sha256;
pub mod uint32;
pub mod sapling; use crate::SynthesisError;
pub mod sprout;
use bellman::{
SynthesisError
};
// TODO: This should probably be removed and we // TODO: This should probably be removed and we
// should use existing helper methods on `Option` // should use existing helper methods on `Option`
@@ -25,7 +19,7 @@ use bellman::{
/// This basically is just an extension to `Option` /// This basically is just an extension to `Option`
/// which allows for a convenient mapping to an /// which allows for a convenient mapping to an
/// error on `None`. /// error on `None`.
trait Assignment<T> { pub trait Assignment<T> {
fn get(&self) -> Result<&T, SynthesisError>; fn get(&self) -> Result<&T, SynthesisError>;
} }
@@ -33,7 +27,7 @@ impl<T> Assignment<T> for Option<T> {
fn get(&self) -> Result<&T, SynthesisError> { fn get(&self) -> Result<&T, SynthesisError> {
match *self { match *self {
Some(ref v) => Ok(v), Some(ref v) => Ok(v),
None => Err(SynthesisError::AssignmentMissing) None => Err(SynthesisError::AssignmentMissing),
} }
} }
} }

View File

@@ -0,0 +1,697 @@
//! The [BLAKE2s] hash function with personalization support.
//!
//! [BLAKE2s]: https://tools.ietf.org/html/rfc7693
use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32};
use crate::{ConstraintSystem, SynthesisError};
use ff::ScalarEngine;
/*
2.1. Parameters
The following table summarizes various parameters and their ranges:
| BLAKE2b | BLAKE2s |
--------------+------------------+------------------+
Bits in word | w = 64 | w = 32 |
Rounds in F | r = 12 | r = 10 |
Block bytes | bb = 128 | bb = 64 |
Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 |
Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 |
Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 |
--------------+------------------+------------------+
G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
constants = | (32, 24, 16, 63) | (16, 12, 8, 7) |
--------------+------------------+------------------+
*/
const R1: usize = 16;
const R2: usize = 12;
const R3: usize = 8;
const R4: usize = 7;
/*
Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
----------+-------------------------------------------------+
SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 |
SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 |
SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 |
SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 |
SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 |
SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 |
SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 |
SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 |
SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 |
----------+-------------------------------------------------+
*/
const SIGMA: [[usize; 16]; 10] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
];
/*
3.1. Mixing Function G
The G primitive function mixes two input words, "x" and "y", into
four words indexed by "a", "b", "c", and "d" in the working vector
v[0..15]. The full modified vector is returned. The rotation
constants (R1, R2, R3, R4) are given in Section 2.1.
FUNCTION G( v[0..15], a, b, c, d, x, y )
|
| v[a] := (v[a] + v[b] + x) mod 2**w
| v[d] := (v[d] ^ v[a]) >>> R1
| v[c] := (v[c] + v[d]) mod 2**w
| v[b] := (v[b] ^ v[c]) >>> R2
| v[a] := (v[a] + v[b] + y) mod 2**w
| v[d] := (v[d] ^ v[a]) >>> R3
| v[c] := (v[c] + v[d]) mod 2**w
| v[b] := (v[b] ^ v[c]) >>> R4
|
| RETURN v[0..15]
|
END FUNCTION.
*/
fn mixing_g<E: ScalarEngine, CS: ConstraintSystem<E>, M>(
mut cs: M,
v: &mut [UInt32],
a: usize,
b: usize,
c: usize,
d: usize,
x: &UInt32,
y: &UInt32,
) -> Result<(), SynthesisError>
where
M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
{
v[a] = UInt32::addmany(
cs.namespace(|| "mixing step 1"),
&[v[a].clone(), v[b].clone(), x.clone()],
)?;
v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1);
v[c] = UInt32::addmany(
cs.namespace(|| "mixing step 3"),
&[v[c].clone(), v[d].clone()],
)?;
v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2);
v[a] = UInt32::addmany(
cs.namespace(|| "mixing step 5"),
&[v[a].clone(), v[b].clone(), y.clone()],
)?;
v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3);
v[c] = UInt32::addmany(
cs.namespace(|| "mixing step 7"),
&[v[c].clone(), v[d].clone()],
)?;
v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4);
Ok(())
}
/*
3.2. Compression Function F
Compression function F takes as an argument the state vector "h",
message block vector "m" (last block is padded with zeros to full
block size, if required), 2w-bit offset counter "t", and final block
indicator flag "f". Local vector v[0..15] is used in processing. F
returns a new state vector. The number of rounds, "r", is 12 for
BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
FUNCTION F( h[0..7], m[0..15], t, f )
|
| // Initialize local work vector v[0..15]
| v[0..7] := h[0..7] // First half from state.
| v[8..15] := IV[0..7] // Second half from IV.
|
| v[12] := v[12] ^ (t mod 2**w) // Low word of the offset.
| v[13] := v[13] ^ (t >> w) // High word.
|
| IF f = TRUE THEN // last block flag?
| | v[14] := v[14] ^ 0xFF..FF // Invert all bits.
| END IF.
|
| // Cryptographic mixing
| FOR i = 0 TO r - 1 DO // Ten or twelve rounds.
| |
| | // Message word selection permutation for this round.
| | s[0..15] := SIGMA[i mod 10][0..15]
| |
| | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] )
| | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] )
| | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
| | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
| |
| | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
| | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
| | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] )
| | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] )
| |
| END FOR
|
| FOR i = 0 TO 7 DO // XOR the two halves.
| | h[i] := h[i] ^ v[i] ^ v[i + 8]
| END FOR.
|
| RETURN h[0..7] // New state.
|
END FUNCTION.
*/
fn blake2s_compression<E: ScalarEngine, CS: ConstraintSystem<E>>(
mut cs: CS,
h: &mut [UInt32],
m: &[UInt32],
t: u64,
f: bool,
) -> Result<(), SynthesisError> {
assert_eq!(h.len(), 8);
assert_eq!(m.len(), 16);
/*
static const uint32_t blake2s_iv[8] =
{
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
*/
let mut v = Vec::with_capacity(16);
v.extend_from_slice(h);
v.push(UInt32::constant(0x6A09E667));
v.push(UInt32::constant(0xBB67AE85));
v.push(UInt32::constant(0x3C6EF372));
v.push(UInt32::constant(0xA54FF53A));
v.push(UInt32::constant(0x510E527F));
v.push(UInt32::constant(0x9B05688C));
v.push(UInt32::constant(0x1F83D9AB));
v.push(UInt32::constant(0x5BE0CD19));
assert_eq!(v.len(), 16);
v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?;
v[13] = v[13].xor(
cs.namespace(|| "second xor"),
&UInt32::constant((t >> 32) as u32),
)?;
if f {
v[14] = v[14].xor(
cs.namespace(|| "third xor"),
&UInt32::constant(u32::max_value()),
)?;
}
{
let mut cs = MultiEq::new(&mut cs);
for i in 0..10 {
let mut cs = cs.namespace(|| format!("round {}", i));
let s = SIGMA[i % 10];
mixing_g(
cs.namespace(|| "mixing invocation 1"),
&mut v,
0,
4,
8,
12,
&m[s[0]],
&m[s[1]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 2"),
&mut v,
1,
5,
9,
13,
&m[s[2]],
&m[s[3]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 3"),
&mut v,
2,
6,
10,
14,
&m[s[4]],
&m[s[5]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 4"),
&mut v,
3,
7,
11,
15,
&m[s[6]],
&m[s[7]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 5"),
&mut v,
0,
5,
10,
15,
&m[s[8]],
&m[s[9]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 6"),
&mut v,
1,
6,
11,
12,
&m[s[10]],
&m[s[11]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 7"),
&mut v,
2,
7,
8,
13,
&m[s[12]],
&m[s[13]],
)?;
mixing_g(
cs.namespace(|| "mixing invocation 8"),
&mut v,
3,
4,
9,
14,
&m[s[14]],
&m[s[15]],
)?;
}
}
for i in 0..8 {
let mut cs = cs.namespace(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i = i));
h[i] = h[i].xor(cs.namespace(|| "first xor"), &v[i])?;
h[i] = h[i].xor(cs.namespace(|| "second xor"), &v[i + 8])?;
}
Ok(())
}
/*
FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
|
| h[0..7] := IV[0..7] // Initialization Vector.
|
| // Parameter block p[0]
| h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
|
| // Process padded key and data blocks
| IF dd > 1 THEN
| | FOR i = 0 TO dd - 2 DO
| | | h := F( h, d[i], (i + 1) * bb, FALSE )
| | END FOR.
| END IF.
|
| // Final block.
| IF kk = 0 THEN
| | h := F( h, d[dd - 1], ll, TRUE )
| ELSE
| | h := F( h, d[dd - 1], ll + bb, TRUE )
| END IF.
|
| RETURN first "nn" bytes from little-endian word array h[].
|
END FUNCTION.
*/
pub fn blake2s<E: ScalarEngine, CS: ConstraintSystem<E>>(
mut cs: CS,
input: &[Boolean],
personalization: &[u8],
) -> Result<Vec<Boolean>, SynthesisError> {
use byteorder::{ByteOrder, LittleEndian};
assert_eq!(personalization.len(), 8);
assert!(input.len() % 8 == 0);
let mut h = Vec::with_capacity(8);
h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32));
h.push(UInt32::constant(0xBB67AE85));
h.push(UInt32::constant(0x3C6EF372));
h.push(UInt32::constant(0xA54FF53A));
h.push(UInt32::constant(0x510E527F));
h.push(UInt32::constant(0x9B05688C));
// Personalization is stored here
h.push(UInt32::constant(
0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4]),
));
h.push(UInt32::constant(
0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8]),
));
let mut blocks: Vec<Vec<UInt32>> = vec![];
for block in input.chunks(512) {
let mut this_block = Vec::with_capacity(16);
for word in block.chunks(32) {
let mut tmp = word.to_vec();
while tmp.len() < 32 {
tmp.push(Boolean::constant(false));
}
this_block.push(UInt32::from_bits(&tmp));
}
while this_block.len() < 16 {
this_block.push(UInt32::constant(0));
}
blocks.push(this_block);
}
if blocks.is_empty() {
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
}
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
let cs = cs.namespace(|| format!("block {}", i));
blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
}
{
let cs = cs.namespace(|| "final block");
blake2s_compression(
cs,
&mut h,
&blocks[blocks.len() - 1],
(input.len() / 8) as u64,
true,
)?;
}
Ok(h.into_iter().flat_map(|b| b.into_bits()).collect())
}
#[cfg(test)]
mod test {
use blake2s_simd::Params as Blake2sParams;
use hex_literal::hex;
use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use super::blake2s;
use crate::gadgets::boolean::{AllocatedBit, Boolean};
use crate::gadgets::test::TestConstraintSystem;
use crate::ConstraintSystem;
#[test]
fn test_blank_hash() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits = vec![];
let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 0);
// >>> import blake2s from hashlib
// >>> h = blake2s(digest_size=32, person=b'12345678')
// >>> h.hexdigest()
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
let mut out = out.into_iter();
for b in expected.iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits: Vec<_> = (0..512)
.map(|i| {
AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true))
.unwrap()
.into()
})
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518);
}
#[test]
fn test_blake2s_precomp_constraints() {
// Test that 512 fixed leading bits (constants)
// doesn't result in more constraints.
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.chain((0..512).map(|i| {
AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true))
.unwrap()
.into()
}))
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518);
}
#[test]
fn test_blake2s_constant_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert_eq!(cs.num_constraints(), 0);
}
#[test]
fn test_blake2s() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
let mut h = Blake2sParams::new()
.hash_length(32)
.personal(b"12345678")
.to_state();
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
h.update(&data);
let hash_result = h.finalize();
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let mut s = hash_result
.as_ref()
.iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r {
match b {
Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
}
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
}
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
}
}
}
}
}
#[test]
fn test_blake2s_256_vars() {
let data: Vec<u8> = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec();
assert_eq!(data.len(), 256);
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let expected = hex!("0af5695115ced92c8a0341e43869209636e9aa6472e4576f0f2b996cf812b30e");
let mut out = r.into_iter();
for b in expected.iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_700_vars() {
let data: Vec<u8> = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec();
assert_eq!(data.len(), 700);
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let expected = hex!("2ab8f0683167ba220eef19dccf4f9b1a8193cc09b35e0235842323950530f18a");
let mut out = r.into_iter();
for b in expected.iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_test_vectors() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let expecteds = [
hex!("a1309e334376c8f36a736a4ab0e691ef931ee3ebdb9ea96187127136fea622a1"),
hex!("82fefff60f265cea255252f7c194a7f93965dffee0609ef74eb67f0d76cd41c6"),
];
for i in 0..2 {
let mut h = Blake2sParams::new()
.hash_length(32)
.personal(b"12345678")
.to_state();
let input_len = 1024;
let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
h.update(&data);
let hash_result = h.finalize();
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let mut s = hash_result
.as_ref()
.iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r {
match b {
Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
}
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
}
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
}
}
}
assert_eq!(expecteds[i], hash_result.as_bytes());
}
}
}

View File

@@ -1,23 +1,16 @@
use ff::Field; //! Window table lookup gadgets.
use pairing::Engine;
use ff::{Field, ScalarEngine};
use super::*;
use super::num::{
AllocatedNum,
Num
};
use super::boolean::Boolean; use super::boolean::Boolean;
use bellman::{ use super::num::{AllocatedNum, Num};
ConstraintSystem use super::*;
}; use crate::ConstraintSystem;
// Synthesize the constants for each base pattern. // Synthesize the constants for each base pattern.
fn synth<'a, E: Engine, I>( fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr])
window_size: usize, where
constants: I, I: IntoIterator<Item = &'a E::Fr>,
assignment: &mut [E::Fr]
)
where I: IntoIterator<Item=&'a E::Fr>
{ {
assert_eq!(assignment.len(), 1 << window_size); assert_eq!(assignment.len(), 1 << window_size);
@@ -36,19 +29,23 @@ fn synth<'a, E: Engine, I>(
/// Performs a 3-bit window table lookup. `bits` is in /// Performs a 3-bit window table lookup. `bits` is in
/// little-endian order. /// little-endian order.
pub fn lookup3_xy<E: Engine, CS>( pub fn lookup3_xy<E: ScalarEngine, CS>(
mut cs: CS, mut cs: CS,
bits: &[Boolean], bits: &[Boolean],
coords: &[(E::Fr, E::Fr)] coords: &[(E::Fr, E::Fr)],
) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError> ) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError>
where CS: ConstraintSystem<E> where
CS: ConstraintSystem<E>,
{ {
assert_eq!(bits.len(), 3); assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 8); assert_eq!(coords.len(), 8);
// Calculate the index into `coords` // Calculate the index into `coords`
let i = let i = match (
match (bits[0].get_value(), bits[1].get_value(), bits[2].get_value()) { bits[0].get_value(),
bits[1].get_value(),
bits[2].get_value(),
) {
(Some(a_value), Some(b_value), Some(c_value)) => { (Some(a_value), Some(b_value), Some(c_value)) => {
let mut tmp = 0; let mut tmp = 0;
if a_value { if a_value {
@@ -61,25 +58,15 @@ pub fn lookup3_xy<E: Engine, CS>(
tmp += 4; tmp += 4;
} }
Some(tmp) Some(tmp)
}, }
_ => None _ => None,
}; };
// Allocate the x-coordinate resulting from the lookup // Allocate the x-coordinate resulting from the lookup
let res_x = AllocatedNum::alloc( let res_x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(coords[*i.get()?].0))?;
cs.namespace(|| "x"),
|| {
Ok(coords[*i.get()?].0)
}
)?;
// Allocate the y-coordinate resulting from the lookup // Allocate the y-coordinate resulting from the lookup
let res_y = AllocatedNum::alloc( let res_y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords[*i.get()?].1))?;
cs.namespace(|| "y"),
|| {
Ok(coords[*i.get()?].1)
}
)?;
// Compute the coefficients for the lookup constraints // Compute the coefficients for the lookup constraints
let mut x_coeffs = [E::Fr::zero(); 8]; let mut x_coeffs = [E::Fr::zero(); 8];
@@ -93,30 +80,38 @@ pub fn lookup3_xy<E: Engine, CS>(
cs.enforce( cs.enforce(
|| "x-coordinate lookup", || "x-coordinate lookup",
|lc| lc + (x_coeffs[0b001], one) |lc| {
lc + (x_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, x_coeffs[0b011]) + &bits[1].lc::<E>(one, x_coeffs[0b011])
+ &bits[2].lc::<E>(one, x_coeffs[0b101]) + &bits[2].lc::<E>(one, x_coeffs[0b101])
+ &precomp.lc::<E>(one, x_coeffs[0b111]), + &precomp.lc::<E>(one, x_coeffs[0b111])
},
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()), |lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|lc| lc + res_x.get_variable() |lc| {
lc + res_x.get_variable()
- (x_coeffs[0b000], one) - (x_coeffs[0b000], one)
- &bits[1].lc::<E>(one, x_coeffs[0b010]) - &bits[1].lc::<E>(one, x_coeffs[0b010])
- &bits[2].lc::<E>(one, x_coeffs[0b100]) - &bits[2].lc::<E>(one, x_coeffs[0b100])
- &precomp.lc::<E>(one, x_coeffs[0b110]), - &precomp.lc::<E>(one, x_coeffs[0b110])
},
); );
cs.enforce( cs.enforce(
|| "y-coordinate lookup", || "y-coordinate lookup",
|lc| lc + (y_coeffs[0b001], one) |lc| {
lc + (y_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, y_coeffs[0b011]) + &bits[1].lc::<E>(one, y_coeffs[0b011])
+ &bits[2].lc::<E>(one, y_coeffs[0b101]) + &bits[2].lc::<E>(one, y_coeffs[0b101])
+ &precomp.lc::<E>(one, y_coeffs[0b111]), + &precomp.lc::<E>(one, y_coeffs[0b111])
},
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()), |lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|lc| lc + res_y.get_variable() |lc| {
lc + res_y.get_variable()
- (y_coeffs[0b000], one) - (y_coeffs[0b000], one)
- &bits[1].lc::<E>(one, y_coeffs[0b010]) - &bits[1].lc::<E>(one, y_coeffs[0b010])
- &bits[2].lc::<E>(one, y_coeffs[0b100]) - &bits[2].lc::<E>(one, y_coeffs[0b100])
- &precomp.lc::<E>(one, y_coeffs[0b110]), - &precomp.lc::<E>(one, y_coeffs[0b110])
},
); );
Ok((res_x, res_y)) Ok((res_x, res_y))
@@ -124,19 +119,19 @@ pub fn lookup3_xy<E: Engine, CS>(
/// Performs a 3-bit window table lookup, where /// Performs a 3-bit window table lookup, where
/// one of the bits is a sign bit. /// one of the bits is a sign bit.
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>( pub fn lookup3_xy_with_conditional_negation<E: ScalarEngine, CS>(
mut cs: CS, mut cs: CS,
bits: &[Boolean], bits: &[Boolean],
coords: &[(E::Fr, E::Fr)] coords: &[(E::Fr, E::Fr)],
) -> Result<(Num<E>, Num<E>), SynthesisError> ) -> Result<(Num<E>, Num<E>), SynthesisError>
where CS: ConstraintSystem<E> where
CS: ConstraintSystem<E>,
{ {
assert_eq!(bits.len(), 3); assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 4); assert_eq!(coords.len(), 4);
// Calculate the index into `coords` // Calculate the index into `coords`
let i = let i = match (bits[0].get_value(), bits[1].get_value()) {
match (bits[0].get_value(), bits[1].get_value()) {
(Some(a_value), Some(b_value)) => { (Some(a_value), Some(b_value)) => {
let mut tmp = 0; let mut tmp = 0;
if a_value { if a_value {
@@ -146,22 +141,19 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
tmp += 2; tmp += 2;
} }
Some(tmp) Some(tmp)
}, }
_ => None _ => None,
}; };
// Allocate the y-coordinate resulting from the lookup // Allocate the y-coordinate resulting from the lookup
// and conditional negation // and conditional negation
let y = AllocatedNum::alloc( let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
cs.namespace(|| "y"), let mut tmp = coords[*i.get()?].1;
|| { if *bits[2].get_value().get()? {
let mut tmp = coords[*i.get()?].1; tmp.negate();
if *bits[2].get_value().get()? {
tmp.negate();
}
Ok(tmp)
} }
)?; Ok(tmp)
})?;
let one = CS::one(); let one = CS::one();
@@ -174,21 +166,21 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?; let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?;
let x = Num::zero() let x = Num::zero()
.add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00]) .add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00])
.add_bool_with_coeff(one, &bits[0], x_coeffs[0b01]) .add_bool_with_coeff(one, &bits[0], x_coeffs[0b01])
.add_bool_with_coeff(one, &bits[1], x_coeffs[0b10]) .add_bool_with_coeff(one, &bits[1], x_coeffs[0b10])
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]); .add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
let y_lc = precomp.lc::<E>(one, y_coeffs[0b11]) + let y_lc = precomp.lc::<E>(one, y_coeffs[0b11])
&bits[1].lc::<E>(one, y_coeffs[0b10]) + + &bits[1].lc::<E>(one, y_coeffs[0b10])
&bits[0].lc::<E>(one, y_coeffs[0b01]) + + &bits[0].lc::<E>(one, y_coeffs[0b01])
(y_coeffs[0b00], one); + (y_coeffs[0b00], one);
cs.enforce( cs.enforce(
|| "y-coordinate lookup", || "y-coordinate lookup",
|lc| lc + &y_lc + &y_lc, |lc| lc + &y_lc + &y_lc,
|lc| lc + &bits[2].lc::<E>(one, E::Fr::one()), |lc| lc + &bits[2].lc::<E>(one, E::Fr::one()),
|lc| lc + &y_lc - y.get_variable() |lc| lc + &y_lc - y.get_variable(),
); );
Ok((x, y.into())) Ok((x, y.into()))
@@ -196,46 +188,52 @@ pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use rand::{SeedableRng, Rand, Rng, XorShiftRng};
use super::*; use super::*;
use ::circuit::test::*; use crate::gadgets::boolean::{AllocatedBit, Boolean};
use ::circuit::boolean::{Boolean, AllocatedBit}; use crate::gadgets::test::*;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_lookup3_xy() { fn test_lookup3_xy() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0656]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.gen(); let a_val = rng.next_u32() % 2 != 0;
let a = Boolean::from( let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.gen(); let b_val = rng.next_u32() % 2 != 0;
let b = Boolean::from( let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.gen(); let c_val = rng.next_u32() % 2 != 0;
let c = Boolean::from( let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let bits = vec![a, b, c]; let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..8).map(|_| (rng.gen(), rng.gen())).collect(); let points: Vec<(Fr, Fr)> = (0..8)
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
.collect();
let res = lookup3_xy(&mut cs, &bits, &points).unwrap(); let res = lookup3_xy(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut index = 0; let mut index = 0;
if a_val { index += 1 } if a_val {
if b_val { index += 2 } index += 1
if c_val { index += 4 } }
if b_val {
index += 2
}
if c_val {
index += 4
}
assert_eq!(res.0.get_value().unwrap(), points[index].0); assert_eq!(res.0.get_value().unwrap(), points[index].0);
assert_eq!(res.1.get_value().unwrap(), points[index].1); assert_eq!(res.1.get_value().unwrap(), points[index].1);
@@ -244,53 +242,63 @@ mod test {
#[test] #[test]
fn test_lookup3_xy_with_conditional_negation() { fn test_lookup3_xy_with_conditional_negation() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.gen(); let a_val = rng.next_u32() % 2 != 0;
let a = Boolean::from( let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.gen(); let b_val = rng.next_u32() % 2 != 0;
let b = Boolean::from( let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.gen(); let c_val = rng.next_u32() % 2 != 0;
let c = Boolean::from( let c = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap());
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let bits = vec![a, b, c]; let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..4).map(|_| (rng.gen(), rng.gen())).collect(); let points: Vec<(Fr, Fr)> = (0..4)
.map(|_| (Fr::random(&mut rng), Fr::random(&mut rng)))
.collect();
let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap(); let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut index = 0; let mut index = 0;
if a_val { index += 1 } if a_val {
if b_val { index += 2 } index += 1
}
if b_val {
index += 2
}
assert_eq!(res.0.get_value().unwrap(), points[index].0); assert_eq!(res.0.get_value().unwrap(), points[index].0);
let mut tmp = points[index].1; let mut tmp = points[index].1;
if c_val { tmp.negate() } if c_val {
tmp.negate()
}
assert_eq!(res.1.get_value().unwrap(), tmp); assert_eq!(res.1.get_value().unwrap(), tmp);
} }
} }
#[test] #[test]
fn test_synth() { fn test_synth() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let window_size = 4; let window_size = 4;
let mut assignment = vec![Fr::zero(); 1 << window_size]; let mut assignment = vec![Fr::zero(); 1 << window_size];
let constants: Vec<_> = (0..(1 << window_size)).map(|_| Fr::rand(&mut rng)).collect(); let constants: Vec<_> = (0..(1 << window_size))
.map(|_| Fr::random(&mut rng))
.collect();
synth::<Bls12, _>(window_size, &constants, &mut assignment); synth::<Bls12, _>(window_size, &constants, &mut assignment);

View File

@@ -1,14 +1,8 @@
use ff::{Field, PrimeField}; use ff::{Field, PrimeField, ScalarEngine};
use pairing::Engine;
use bellman::{ use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>>{ pub struct MultiEq<E: ScalarEngine, CS: ConstraintSystem<E>> {
cs: CS, cs: CS,
ops: usize, ops: usize,
bits_used: usize, bits_used: usize,
@@ -16,19 +10,18 @@ pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>>{
rhs: LinearCombination<E>, rhs: LinearCombination<E>,
} }
impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> { impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
pub fn new(cs: CS) -> Self { pub fn new(cs: CS) -> Self {
MultiEq { MultiEq {
cs: cs, cs,
ops: 0, ops: 0,
bits_used: 0, bits_used: 0,
lhs: LinearCombination::zero(), lhs: LinearCombination::zero(),
rhs: LinearCombination::zero() rhs: LinearCombination::zero(),
} }
} }
fn accumulate(&mut self) fn accumulate(&mut self) {
{
let ops = self.ops; let ops = self.ops;
let lhs = self.lhs.clone(); let lhs = self.lhs.clone();
let rhs = self.rhs.clone(); let rhs = self.rhs.clone();
@@ -36,7 +29,7 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|| format!("multieq {}", ops), || format!("multieq {}", ops),
|_| lhs, |_| lhs,
|lc| lc + CS::one(), |lc| lc + CS::one(),
|_| rhs |_| rhs,
); );
self.lhs = LinearCombination::zero(); self.lhs = LinearCombination::zero();
self.rhs = LinearCombination::zero(); self.rhs = LinearCombination::zero();
@@ -48,9 +41,8 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
&mut self, &mut self,
num_bits: usize, num_bits: usize,
lhs: &LinearCombination<E>, lhs: &LinearCombination<E>,
rhs: &LinearCombination<E> rhs: &LinearCombination<E>,
) ) {
{
// Check if we will exceed the capacity // Check if we will exceed the capacity
if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) { if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) {
self.accumulate(); self.accumulate();
@@ -65,70 +57,63 @@ impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
} }
} }
impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> { impl<E: ScalarEngine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
fn drop(&mut self) { fn drop(&mut self) {
if self.bits_used > 0 { if self.bits_used > 0 {
self.accumulate(); self.accumulate();
} }
} }
} }
impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> impl<E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS> {
{
type Root = Self; type Root = Self;
fn one() -> Variable { fn one() -> Variable {
CS::one() CS::one()
} }
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.cs.alloc(annotation, f) self.cs.alloc(annotation, f)
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.cs.alloc_input(annotation, f) self.cs.alloc_input(annotation, f)
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
self.cs.enforce(annotation, a, b, c) self.cs.enforce(annotation, a, b, c)
} }
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
self.cs.get_root().push_namespace(name_fn) self.cs.get_root().push_namespace(name_fn)
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
self.cs.get_root().pop_namespace() self.cs.get_root().pop_namespace()
} }
fn get_root(&mut self) -> &mut Self::Root fn get_root(&mut self) -> &mut Self::Root {
{
self self
} }
} }

View File

@@ -0,0 +1,111 @@
//! Helpers for packing vectors of bits into scalar field elements.
use super::boolean::Boolean;
use super::num::Num;
use super::Assignment;
use crate::{ConstraintSystem, SynthesisError};
use ff::{Field, PrimeField, ScalarEngine};
/// Takes a sequence of booleans and exposes them as compact
/// public inputs
pub fn pack_into_inputs<E, CS>(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError>
where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() {
let mut num = Num::<E>::zero();
let mut coeff = E::Fr::one();
for bit in bits {
num = num.add_bool_with_coeff(CS::one(), bit, coeff);
coeff.double();
}
let input = cs.alloc_input(|| format!("input {}", i), || Ok(*num.get_value().get()?))?;
// num * 1 = input
cs.enforce(
|| format!("packing constraint {}", i),
|_| num.lc(E::Fr::one()),
|lc| lc + CS::one(),
|lc| lc + input,
);
}
Ok(())
}
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
bytes
.iter()
.flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool> {
bytes
.iter()
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn compute_multipacking<E: ScalarEngine>(bits: &[bool]) -> Vec<E::Fr> {
let mut result = vec![];
for bits in bits.chunks(E::Fr::CAPACITY as usize) {
let mut cur = E::Fr::zero();
let mut coeff = E::Fr::one();
for bit in bits {
if *bit {
cur.add_assign(&coeff);
}
coeff.double();
}
result.push(cur);
}
result
}
#[test]
fn test_multipacking() {
use crate::ConstraintSystem;
use pairing::bls12_381::Bls12;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use super::boolean::{AllocatedBit, Boolean};
use crate::gadgets::test::*;
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for num_bits in 0..1500 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let bits: Vec<bool> = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect();
let circuit_bits = bits
.iter()
.enumerate()
.map(|(i, &b)| {
Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(b)).unwrap(),
)
})
.collect::<Vec<_>>();
let expected_inputs = compute_multipacking::<Bls12>(&bits);
pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap();
assert!(cs.is_satisfied());
assert!(cs.verify(&expected_inputs));
}
}

View File

@@ -1,78 +1,62 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr}; //! Gadgets representing numbers in the scalar field of the underlying curve.
use pairing::Engine;
use bellman::{ use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine};
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
use super::{ use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
Assignment
};
use super::boolean::{ use super::Assignment;
self,
Boolean,
AllocatedBit
};
pub struct AllocatedNum<E: Engine> { use super::boolean::{self, AllocatedBit, Boolean};
pub struct AllocatedNum<E: ScalarEngine> {
value: Option<E::Fr>, value: Option<E::Fr>,
variable: Variable variable: Variable,
} }
impl<E: Engine> Clone for AllocatedNum<E> { impl<E: ScalarEngine> Clone for AllocatedNum<E> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
AllocatedNum { AllocatedNum {
value: self.value, value: self.value,
variable: self.variable variable: self.variable,
} }
} }
} }
impl<E: Engine> AllocatedNum<E> { impl<E: ScalarEngine> AllocatedNum<E> {
pub fn alloc<CS, F>( pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
mut cs: CS, where
value: F, CS: ConstraintSystem<E>,
) -> Result<Self, SynthesisError> F: FnOnce() -> Result<E::Fr, SynthesisError>,
where CS: ConstraintSystem<E>,
F: FnOnce() -> Result<E::Fr, SynthesisError>
{ {
let mut new_value = None; let mut new_value = None;
let var = cs.alloc(|| "num", || { let var = cs.alloc(
let tmp = value()?; || "num",
|| {
let tmp = value()?;
new_value = Some(tmp); new_value = Some(tmp);
Ok(tmp) Ok(tmp)
})?; },
)?;
Ok(AllocatedNum { Ok(AllocatedNum {
value: new_value, value: new_value,
variable: var variable: var,
}) })
} }
pub fn inputize<CS>( pub fn inputize<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{ {
let input = cs.alloc_input( let input = cs.alloc_input(|| "input variable", || Ok(*self.value.get()?))?;
|| "input variable",
|| {
Ok(*self.value.get()?)
}
)?;
cs.enforce( cs.enforce(
|| "enforce input is correct", || "enforce input is correct",
|lc| lc + input, |lc| lc + input,
|lc| lc + CS::one(), |lc| lc + CS::one(),
|lc| lc + self.variable |lc| lc + self.variable,
); );
Ok(()) Ok(())
@@ -83,20 +67,19 @@ impl<E: Engine> AllocatedNum<E> {
/// order, requiring that the representation /// order, requiring that the representation
/// strictly exists "in the field" (i.e., a /// strictly exists "in the field" (i.e., a
/// congruency is not allowed.) /// congruency is not allowed.)
pub fn into_bits_le_strict<CS>( pub fn to_bits_le_strict<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
{ {
pub fn kary_and<E, CS>( pub fn kary_and<E, CS>(
mut cs: CS, mut cs: CS,
v: &[AllocatedBit] v: &[AllocatedBit],
) -> Result<AllocatedBit, SynthesisError> ) -> Result<AllocatedBit, SynthesisError>
where E: Engine, where
CS: ConstraintSystem<E> E: ScalarEngine,
CS: ConstraintSystem<E>,
{ {
assert!(v.len() > 0); assert!(!v.is_empty());
// Let's keep this simple for now and just AND them all // Let's keep this simple for now and just AND them all
// manually // manually
@@ -109,7 +92,7 @@ impl<E: Engine> AllocatedNum<E> {
cur = Some(AllocatedBit::and( cur = Some(AllocatedBit::and(
cs.namespace(|| format!("and {}", i)), cs.namespace(|| format!("and {}", i)),
cur.as_ref().unwrap(), cur.as_ref().unwrap(),
v v,
)?); )?);
} }
} }
@@ -145,15 +128,12 @@ impl<E: Engine> AllocatedNum<E> {
if b { if b {
// This is part of a run of ones. Let's just // This is part of a run of ones. Let's just
// allocate the boolean with the expected value. // allocate the boolean with the expected value.
let a_bit = AllocatedBit::alloc( let a_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), a_bit)?;
cs.namespace(|| format!("bit {}", i)),
a_bit
)?;
// ... and add it to the current run of ones. // ... and add it to the current run of ones.
current_run.push(a_bit.clone()); current_run.push(a_bit.clone());
result.push(a_bit); result.push(a_bit);
} else { } else {
if current_run.len() > 0 { if !current_run.is_empty() {
// This is the start of a run of zeros, but we need // This is the start of a run of zeros, but we need
// to k-ary AND against `last_run` first. // to k-ary AND against `last_run` first.
@@ -162,7 +142,7 @@ impl<E: Engine> AllocatedNum<E> {
} }
last_run = Some(kary_and( last_run = Some(kary_and(
cs.namespace(|| format!("run ending at {}", i)), cs.namespace(|| format!("run ending at {}", i)),
&current_run &current_run,
)?); )?);
current_run.truncate(0); current_run.truncate(0);
} }
@@ -175,7 +155,7 @@ impl<E: Engine> AllocatedNum<E> {
let a_bit = AllocatedBit::alloc_conditionally( let a_bit = AllocatedBit::alloc_conditionally(
cs.namespace(|| format!("bit {}", i)), cs.namespace(|| format!("bit {}", i)),
a_bit, a_bit,
&last_run.as_ref().expect("char always starts with a one") &last_run.as_ref().expect("char always starts with a one"),
)?; )?;
result.push(a_bit); result.push(a_bit);
} }
@@ -201,30 +181,20 @@ impl<E: Engine> AllocatedNum<E> {
lc = lc - self.variable; lc = lc - self.variable;
cs.enforce( cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|| "unpacking constraint",
|lc| lc,
|lc| lc,
|_| lc
);
// Convert into booleans, and reverse for little-endian bit order // Convert into booleans, and reverse for little-endian bit order
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect()) Ok(result.into_iter().map(Boolean::from).rev().collect())
} }
/// Convert the allocated number into its little-endian representation. /// Convert the allocated number into its little-endian representation.
/// Note that this does not strongly enforce that the commitment is /// Note that this does not strongly enforce that the commitment is
/// "in the field." /// "in the field."
pub fn into_bits_le<CS>( pub fn to_bits_le<CS>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let bits = boolean::field_into_allocated_bits_le( let bits = boolean::field_into_allocated_bits_le(&mut cs, self.value)?;
&mut cs,
self.value
)?;
let mut lc = LinearCombination::zero(); let mut lc = LinearCombination::zero();
let mut coeff = E::Fr::one(); let mut coeff = E::Fr::one();
@@ -237,94 +207,91 @@ impl<E: Engine> AllocatedNum<E> {
lc = lc - self.variable; lc = lc - self.variable;
cs.enforce( cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc);
|| "unpacking constraint",
|lc| lc,
|lc| lc,
|_| lc
);
Ok(bits.into_iter().map(|b| Boolean::from(b)).collect()) Ok(bits.into_iter().map(Boolean::from).collect())
} }
pub fn mul<CS>( pub fn mul<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
&self, where
mut cs: CS, CS: ConstraintSystem<E>,
other: &Self
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let mut value = None; let mut value = None;
let var = cs.alloc(|| "product num", || { let var = cs.alloc(
let mut tmp = *self.value.get()?; || "product num",
tmp.mul_assign(other.value.get()?); || {
let mut tmp = *self.value.get()?;
tmp.mul_assign(other.value.get()?);
value = Some(tmp); value = Some(tmp);
Ok(tmp) Ok(tmp)
})?; },
)?;
// Constrain: a * b = ab // Constrain: a * b = ab
cs.enforce( cs.enforce(
|| "multiplication constraint", || "multiplication constraint",
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + other.variable, |lc| lc + other.variable,
|lc| lc + var |lc| lc + var,
); );
Ok(AllocatedNum { Ok(AllocatedNum {
value: value, value,
variable: var variable: var,
}) })
} }
pub fn square<CS>( pub fn square<CS>(&self, mut cs: CS) -> Result<Self, SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{ {
let mut value = None; let mut value = None;
let var = cs.alloc(|| "squared num", || { let var = cs.alloc(
let mut tmp = *self.value.get()?; || "squared num",
tmp.square(); || {
let mut tmp = *self.value.get()?;
tmp.square();
value = Some(tmp); value = Some(tmp);
Ok(tmp) Ok(tmp)
})?; },
)?;
// Constrain: a * a = aa // Constrain: a * a = aa
cs.enforce( cs.enforce(
|| "squaring constraint", || "squaring constraint",
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + var |lc| lc + var,
); );
Ok(AllocatedNum { Ok(AllocatedNum {
value: value, value,
variable: var variable: var,
}) })
} }
pub fn assert_nonzero<CS>( pub fn assert_nonzero<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
&self, where
mut cs: CS CS: ConstraintSystem<E>,
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{ {
let inv = cs.alloc(|| "ephemeral inverse", || { let inv = cs.alloc(
let tmp = *self.value.get()?; || "ephemeral inverse",
|| {
if tmp.is_zero() { let tmp = *self.value.get()?;
Err(SynthesisError::DivisionByZero)
} else { if tmp.is_zero() {
Ok(tmp.inverse().unwrap()) Err(SynthesisError::DivisionByZero)
} } else {
})?; Ok(tmp.inverse().unwrap())
}
},
)?;
// Constrain a * inv = 1, which is only valid // Constrain a * inv = 1, which is only valid
// iff a has a multiplicative inverse, untrue // iff a has a multiplicative inverse, untrue
@@ -333,7 +300,7 @@ impl<E: Engine> AllocatedNum<E> {
|| "nonzero assertion constraint", || "nonzero assertion constraint",
|lc| lc + self.variable, |lc| lc + self.variable,
|lc| lc + inv, |lc| lc + inv,
|lc| lc + CS::one() |lc| lc + CS::one(),
); );
Ok(()) Ok(())
@@ -346,44 +313,39 @@ impl<E: Engine> AllocatedNum<E> {
mut cs: CS, mut cs: CS,
a: &Self, a: &Self,
b: &Self, b: &Self,
condition: &Boolean condition: &Boolean,
) -> Result<(Self, Self), SynthesisError> ) -> Result<(Self, Self), SynthesisError>
where CS: ConstraintSystem<E> where
CS: ConstraintSystem<E>,
{ {
let c = Self::alloc( let c = Self::alloc(cs.namespace(|| "conditional reversal result 1"), || {
cs.namespace(|| "conditional reversal result 1"), if *condition.get_value().get()? {
|| { Ok(*b.value.get()?)
if *condition.get_value().get()? { } else {
Ok(*b.value.get()?) Ok(*a.value.get()?)
} else {
Ok(*a.value.get()?)
}
} }
)?; })?;
cs.enforce( cs.enforce(
|| "first conditional reversal", || "first conditional reversal",
|lc| lc + a.variable - b.variable, |lc| lc + a.variable - b.variable,
|_| condition.lc(CS::one(), E::Fr::one()), |_| condition.lc(CS::one(), E::Fr::one()),
|lc| lc + a.variable - c.variable |lc| lc + a.variable - c.variable,
); );
let d = Self::alloc( let d = Self::alloc(cs.namespace(|| "conditional reversal result 2"), || {
cs.namespace(|| "conditional reversal result 2"), if *condition.get_value().get()? {
|| { Ok(*a.value.get()?)
if *condition.get_value().get()? { } else {
Ok(*a.value.get()?) Ok(*b.value.get()?)
} else {
Ok(*b.value.get()?)
}
} }
)?; })?;
cs.enforce( cs.enforce(
|| "second conditional reversal", || "second conditional reversal",
|lc| lc + b.variable - a.variable, |lc| lc + b.variable - a.variable,
|_| condition.lc(CS::one(), E::Fr::one()), |_| condition.lc(CS::one(), E::Fr::one()),
|lc| lc + b.variable - d.variable |lc| lc + b.variable - d.variable,
); );
Ok((c, d)) Ok((c, d))
@@ -398,25 +360,25 @@ impl<E: Engine> AllocatedNum<E> {
} }
} }
pub struct Num<E: Engine> { pub struct Num<E: ScalarEngine> {
value: Option<E::Fr>, value: Option<E::Fr>,
lc: LinearCombination<E> lc: LinearCombination<E>,
} }
impl<E: Engine> From<AllocatedNum<E>> for Num<E> { impl<E: ScalarEngine> From<AllocatedNum<E>> for Num<E> {
fn from(num: AllocatedNum<E>) -> Num<E> { fn from(num: AllocatedNum<E>) -> Num<E> {
Num { Num {
value: num.value, value: num.value,
lc: LinearCombination::<E>::zero() + num.variable lc: LinearCombination::<E>::zero() + num.variable,
} }
} }
} }
impl<E: Engine> Num<E> { impl<E: ScalarEngine> Num<E> {
pub fn zero() -> Self { pub fn zero() -> Self {
Num { Num {
value: Some(E::Fr::zero()), value: Some(E::Fr::zero()),
lc: LinearCombination::zero() lc: LinearCombination::zero(),
} }
} }
@@ -428,13 +390,7 @@ impl<E: Engine> Num<E> {
LinearCombination::zero() + (coeff, &self.lc) LinearCombination::zero() + (coeff, &self.lc)
} }
pub fn add_bool_with_coeff( pub fn add_bool_with_coeff(self, one: Variable, bit: &Boolean, coeff: E::Fr) -> Self {
self,
one: Variable,
bit: &Boolean,
coeff: E::Fr
) -> Self
{
let newval = match (self.value, bit.get_value()) { let newval = match (self.value, bit.get_value()) {
(Some(mut curval), Some(bval)) => { (Some(mut curval), Some(bval)) => {
if bval { if bval {
@@ -442,25 +398,27 @@ impl<E: Engine> Num<E> {
} }
Some(curval) Some(curval)
}, }
_ => None _ => None,
}; };
Num { Num {
value: newval, value: newval,
lc: self.lc + &bit.lc(one, coeff) lc: self.lc + &bit.lc(one, coeff),
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use rand::{SeedableRng, Rand, Rng, XorShiftRng}; use crate::ConstraintSystem;
use bellman::{ConstraintSystem};
use ff::{BitIterator, Field, PrimeField}; use ff::{BitIterator, Field, PrimeField};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use ::circuit::test::*; use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use super::{AllocatedNum, Boolean}; use super::{AllocatedNum, Boolean};
use crate::gadgets::test::*;
#[test] #[test]
fn test_allocated_num() { fn test_allocated_num() {
@@ -489,8 +447,10 @@ mod test {
fn test_num_multiplication() { fn test_num_multiplication() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap(); let n =
let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap(); AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap();
let n2 =
AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap();
let n3 = n.mul(&mut cs, &n2).unwrap(); let n3 = n.mul(&mut cs, &n2).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -502,12 +462,15 @@ mod test {
#[test] #[test]
fn test_num_conditional_reversal() { fn test_num_conditional_reversal() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
{ {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap(); let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap(); let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
let condition = Boolean::constant(false); let condition = Boolean::constant(false);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
@@ -520,8 +483,8 @@ mod test {
{ {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap(); let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap(); let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).unwrap();
let condition = Boolean::constant(true); let condition = Boolean::constant(true);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
@@ -560,7 +523,7 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap(); let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
n.into_bits_le_strict(&mut cs).unwrap(); n.to_bits_le_strict(&mut cs).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -568,28 +531,37 @@ mod test {
cs.set("bit 254/boolean", Fr::one()); cs.set("bit 254/boolean", Fr::one());
// this makes the conditional boolean constraint fail // this makes the conditional boolean constraint fail
assert_eq!(cs.which_is_unsatisfied().unwrap(), "bit 254/boolean constraint"); assert_eq!(
cs.which_is_unsatisfied().unwrap(),
"bit 254/boolean constraint"
);
} }
#[test] #[test]
fn test_into_bits() { fn test_into_bits() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for i in 0..200 { for i in 0..200 {
let r = Fr::rand(&mut rng); let r = Fr::random(&mut rng);
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
let bits = if i % 2 == 0 { let bits = if i % 2 == 0 {
n.into_bits_le(&mut cs).unwrap() n.to_bits_le(&mut cs).unwrap()
} else { } else {
n.into_bits_le_strict(&mut cs).unwrap() n.to_bits_le_strict(&mut cs).unwrap()
}; };
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
for (b, a) in BitIterator::new(r.into_repr()).skip(1).zip(bits.iter().rev()) { for (b, a) in BitIterator::new(r.into_repr())
.skip(1)
.zip(bits.iter().rev())
{
if let &Boolean::Is(ref a) = a { if let &Boolean::Is(ref a) = a {
assert_eq!(b, a.get_value().unwrap()); assert_eq!(b, a.get_value().unwrap());
} else { } else {
@@ -597,7 +569,7 @@ mod test {
} }
} }
cs.set("num", Fr::rand(&mut rng)); cs.set("num", Fr::random(&mut rng));
assert!(!cs.is_satisfied()); assert!(!cs.is_satisfied());
cs.set("num", r); cs.set("num", r);
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());

View File

@@ -1,9 +1,15 @@
use super::uint32::UInt32; //! Circuits for the [SHA-256] hash function and its internal compression
use super::multieq::MultiEq; //! function.
use super::boolean::Boolean; //!
use bellman::{ConstraintSystem, SynthesisError}; //! [SHA-256]: https://tools.ietf.org/html/rfc6234
use pairing::Engine;
use super::boolean::Boolean;
use super::multieq::MultiEq;
use super::uint32::UInt32;
use crate::{ConstraintSystem, SynthesisError};
use ff::ScalarEngine;
#[allow(clippy::unreadable_literal)]
const ROUND_CONSTANTS: [u32; 64] = [ const ROUND_CONSTANTS: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
@@ -12,37 +18,36 @@ const ROUND_CONSTANTS: [u32; 64] = [
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
]; ];
#[allow(clippy::unreadable_literal)]
const IV: [u32; 8] = [ const IV: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]; ];
pub fn sha256_block_no_padding<E, CS>( pub fn sha256_block_no_padding<E, CS>(
mut cs: CS, mut cs: CS,
input: &[Boolean] input: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(input.len(), 512); assert_eq!(input.len(), 512);
Ok(sha256_compression_function( Ok(
&mut cs, sha256_compression_function(&mut cs, &input, &get_sha256_iv())?
&input, .into_iter()
&get_sha256_iv() .flat_map(|e| e.into_bits_be())
)? .collect(),
.into_iter() )
.flat_map(|e| e.into_bits_be())
.collect())
} }
pub fn sha256<E, CS>( pub fn sha256<E, CS>(mut cs: CS, input: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
mut cs: CS, where
input: &[Boolean] E: ScalarEngine,
) -> Result<Vec<Boolean>, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>
{ {
assert!(input.len() % 8 == 0); assert!(input.len() % 8 == 0);
@@ -62,16 +67,10 @@ pub fn sha256<E, CS>(
let mut cur = get_sha256_iv(); let mut cur = get_sha256_iv();
for (i, block) in padded.chunks(512).enumerate() { for (i, block) in padded.chunks(512).enumerate() {
cur = sha256_compression_function( cur = sha256_compression_function(cs.namespace(|| format!("block {}", i)), block, &cur)?;
cs.namespace(|| format!("block {}", i)),
block,
&cur
)?;
} }
Ok(cur.into_iter() Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect())
.flat_map(|e| e.into_bits_be())
.collect())
} }
fn get_sha256_iv() -> Vec<UInt32> { fn get_sha256_iv() -> Vec<UInt32> {
@@ -81,16 +80,19 @@ fn get_sha256_iv() -> Vec<UInt32> {
fn sha256_compression_function<E, CS>( fn sha256_compression_function<E, CS>(
cs: CS, cs: CS,
input: &[Boolean], input: &[Boolean],
current_hash_value: &[UInt32] current_hash_value: &[UInt32],
) -> Result<Vec<UInt32>, SynthesisError> ) -> Result<Vec<UInt32>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: ScalarEngine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(input.len(), 512); assert_eq!(input.len(), 512);
assert_eq!(current_hash_value.len(), 8); assert_eq!(current_hash_value.len(), 8);
let mut w = input.chunks(32) let mut w = input
.map(|e| UInt32::from_bits_be(e)) .chunks(32)
.collect::<Vec<_>>(); .map(|e| UInt32::from_bits_be(e))
.collect::<Vec<_>>();
// We can save some constraints by combining some of // We can save some constraints by combining some of
// the constraints in different u32 additions // the constraints in different u32 additions
@@ -100,30 +102,18 @@ fn sha256_compression_function<E, CS>(
let cs = &mut cs.namespace(|| format!("w extension {}", i)); let cs = &mut cs.namespace(|| format!("w extension {}", i));
// s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3) // s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
let mut s0 = w[i-15].rotr(7); let mut s0 = w[i - 15].rotr(7);
s0 = s0.xor( s0 = s0.xor(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?;
cs.namespace(|| "first xor for s0"), s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?;
&w[i-15].rotr(18)
)?;
s0 = s0.xor(
cs.namespace(|| "second xor for s0"),
&w[i-15].shr(3)
)?;
// s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10) // s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
let mut s1 = w[i-2].rotr(17); let mut s1 = w[i - 2].rotr(17);
s1 = s1.xor( s1 = s1.xor(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?;
cs.namespace(|| "first xor for s1"), s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?;
&w[i-2].rotr(19)
)?;
s1 = s1.xor(
cs.namespace(|| "second xor for s1"),
&w[i-2].shr(10)
)?;
let tmp = UInt32::addmany( let tmp = UInt32::addmany(
cs.namespace(|| "computation of w[i]"), cs.namespace(|| "computation of w[i]"),
&[w[i-16].clone(), s0, w[i-7].clone(), s1] &[w[i - 16].clone(), s0, w[i - 7].clone(), s1],
)?; )?;
// w[i] := w[i-16] + s0 + w[i-7] + s1 // w[i] := w[i-16] + s0 + w[i-7] + s1
@@ -134,29 +124,21 @@ fn sha256_compression_function<E, CS>(
enum Maybe { enum Maybe {
Deferred(Vec<UInt32>), Deferred(Vec<UInt32>),
Concrete(UInt32) Concrete(UInt32),
} }
impl Maybe { impl Maybe {
fn compute<E, CS, M>( fn compute<E, CS, M>(self, cs: M, others: &[UInt32]) -> Result<UInt32, SynthesisError>
self, where
cs: M, E: ScalarEngine,
others: &[UInt32] CS: ConstraintSystem<E>,
) -> Result<UInt32, SynthesisError> M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
where E: Engine,
CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root=MultiEq<E, CS>>
{ {
Ok(match self { Ok(match self {
Maybe::Concrete(ref v) => { Maybe::Concrete(ref v) => return Ok(v.clone()),
return Ok(v.clone())
},
Maybe::Deferred(mut v) => { Maybe::Deferred(mut v) => {
v.extend(others.into_iter().cloned()); v.extend(others.iter().cloned());
UInt32::addmany( UInt32::addmany(cs, &v)?
cs,
&v
)?
} }
}) })
} }
@@ -177,22 +159,11 @@ fn sha256_compression_function<E, CS>(
// S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25) // S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?; let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?;
let mut s1 = new_e.rotr(6); let mut s1 = new_e.rotr(6);
s1 = s1.xor( s1 = s1.xor(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?;
cs.namespace(|| "first xor for s1"), s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?;
&new_e.rotr(11)
)?;
s1 = s1.xor(
cs.namespace(|| "second xor for s1"),
&new_e.rotr(25)
)?;
// ch := (e and f) xor ((not e) and g) // ch := (e and f) xor ((not e) and g)
let ch = UInt32::sha256_ch( let ch = UInt32::sha256_ch(cs.namespace(|| "ch"), &new_e, &f, &g)?;
cs.namespace(|| "ch"),
&new_e,
&f,
&g
)?;
// temp1 := h + S1 + ch + k[i] + w[i] // temp1 := h + S1 + ch + k[i] + w[i]
let temp1 = vec![ let temp1 = vec![
@@ -200,28 +171,17 @@ fn sha256_compression_function<E, CS>(
s1, s1,
ch, ch,
UInt32::constant(ROUND_CONSTANTS[i]), UInt32::constant(ROUND_CONSTANTS[i]),
w[i].clone() w[i].clone(),
]; ];
// S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22) // S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?; let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?;
let mut s0 = new_a.rotr(2); let mut s0 = new_a.rotr(2);
s0 = s0.xor( s0 = s0.xor(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?;
cs.namespace(|| "first xor for s0"), s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?;
&new_a.rotr(13)
)?;
s0 = s0.xor(
cs.namespace(|| "second xor for s0"),
&new_a.rotr(22)
)?;
// maj := (a and b) xor (a and c) xor (b and c) // maj := (a and b) xor (a and c) xor (b and c)
let maj = UInt32::sha256_maj( let maj = UInt32::sha256_maj(cs.namespace(|| "maj"), &new_a, &b, &c)?;
cs.namespace(|| "maj"),
&new_a,
&b,
&c
)?;
// temp2 := S0 + maj // temp2 := S0 + maj
let temp2 = vec![s0, maj]; let temp2 = vec![s0, maj];
@@ -244,7 +204,13 @@ fn sha256_compression_function<E, CS>(
d = c; d = c;
c = b; c = b;
b = new_a; b = new_a;
a = Maybe::Deferred(temp1.iter().cloned().chain(temp2.iter().cloned()).collect::<Vec<_>>()); a = Maybe::Deferred(
temp1
.iter()
.cloned()
.chain(temp2.iter().cloned())
.collect::<Vec<_>>(),
);
} }
/* /*
@@ -261,42 +227,42 @@ fn sha256_compression_function<E, CS>(
let h0 = a.compute( let h0 = a.compute(
cs.namespace(|| "deferred h0 computation"), cs.namespace(|| "deferred h0 computation"),
&[current_hash_value[0].clone()] &[current_hash_value[0].clone()],
)?; )?;
let h1 = UInt32::addmany( let h1 = UInt32::addmany(
cs.namespace(|| "new h1"), cs.namespace(|| "new h1"),
&[current_hash_value[1].clone(), b] &[current_hash_value[1].clone(), b],
)?; )?;
let h2 = UInt32::addmany( let h2 = UInt32::addmany(
cs.namespace(|| "new h2"), cs.namespace(|| "new h2"),
&[current_hash_value[2].clone(), c] &[current_hash_value[2].clone(), c],
)?; )?;
let h3 = UInt32::addmany( let h3 = UInt32::addmany(
cs.namespace(|| "new h3"), cs.namespace(|| "new h3"),
&[current_hash_value[3].clone(), d] &[current_hash_value[3].clone(), d],
)?; )?;
let h4 = e.compute( let h4 = e.compute(
cs.namespace(|| "deferred h4 computation"), cs.namespace(|| "deferred h4 computation"),
&[current_hash_value[4].clone()] &[current_hash_value[4].clone()],
)?; )?;
let h5 = UInt32::addmany( let h5 = UInt32::addmany(
cs.namespace(|| "new h5"), cs.namespace(|| "new h5"),
&[current_hash_value[5].clone(), f] &[current_hash_value[5].clone(), f],
)?; )?;
let h6 = UInt32::addmany( let h6 = UInt32::addmany(
cs.namespace(|| "new h6"), cs.namespace(|| "new h6"),
&[current_hash_value[6].clone(), g] &[current_hash_value[6].clone(), g],
)?; )?;
let h7 = UInt32::addmany( let h7 = UInt32::addmany(
cs.namespace(|| "new h7"), cs.namespace(|| "new h7"),
&[current_hash_value[7].clone(), h] &[current_hash_value[7].clone(), h],
)?; )?;
Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7]) Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7])
@@ -305,10 +271,12 @@ fn sha256_compression_function<E, CS>(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use circuit::boolean::AllocatedBit; use crate::gadgets::boolean::AllocatedBit;
use crate::gadgets::test::TestConstraintSystem;
use hex_literal::hex;
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use circuit::test::TestConstraintSystem; use rand_core::{RngCore, SeedableRng};
use rand::{XorShiftRng, SeedableRng, Rng}; use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_blank_hash() { fn test_blank_hash() {
@@ -317,11 +285,7 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect();
input_bits[0] = Boolean::Constant(true); input_bits[0] = Boolean::Constant(true);
let out = sha256_compression_function( let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap();
&mut cs,
&input_bits,
&iv
).unwrap();
let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect(); let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -330,7 +294,7 @@ mod test {
let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
let mut out = out_bits.into_iter(); let mut out = out_bits.into_iter();
for b in expected.into_iter() { for b in expected.iter() {
for i in (0..8).rev() { for i in (0..8).rev() {
let c = out.next().unwrap().get_value().unwrap(); let c = out.next().unwrap().get_value().unwrap();
@@ -341,25 +305,27 @@ mod test {
#[test] #[test]
fn test_full_block() { fn test_full_block() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let iv = get_sha256_iv(); let iv = get_sha256_iv();
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits: Vec<_> = (0..512).map(|i| { let input_bits: Vec<_> = (0..512)
Boolean::from( .map(|i| {
AllocatedBit::alloc( Boolean::from(
cs.namespace(|| format!("input bit {}", i)), AllocatedBit::alloc(
Some(rng.gen()) cs.namespace(|| format!("input bit {}", i)),
).unwrap() Some(rng.next_u32() % 2 != 0),
) )
}).collect(); .unwrap(),
)
})
.collect();
sha256_compression_function( sha256_compression_function(cs.namespace(|| "sha256"), &input_bits, &iv).unwrap();
cs.namespace(|| "sha256"),
&input_bits,
&iv
).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints() - 512, 25840); assert_eq!(cs.num_constraints() - 512, 25840);
@@ -367,18 +333,18 @@ mod test {
#[test] #[test]
fn test_against_vectors() { fn test_against_vectors() {
use crypto::sha2::Sha256; use sha2::{Digest, Sha256};
use crypto::digest::Digest;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
{
let mut h = Sha256::new(); let mut h = Sha256::new();
let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect(); let data: Vec<u8> = (0..input_len).map(|_| rng.next_u32() as u8).collect();
h.input(&data); h.input(&data);
let mut hash_result = [0u8; 32]; let hash_result = h.result();
h.result(&mut hash_result[..]);
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![]; let mut input_bits = vec![];
@@ -387,7 +353,11 @@ mod test {
for bit_i in (0..8).rev() { for bit_i in (0..8).rev() {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into()); input_bits.push(
AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8))
.unwrap()
.into(),
);
} }
} }
@@ -395,17 +365,19 @@ mod test {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let mut s = hash_result.as_ref().iter() let mut s = hash_result
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8)); .as_ref()
.iter()
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r { for b in r {
match b { match b {
Boolean::Is(b) => { Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap()); assert!(s.next().unwrap() == b.get_value().unwrap());
}, }
Boolean::Not(b) => { Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap()); assert!(s.next().unwrap() != b.get_value().unwrap());
}, }
Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(input_len == 0); assert!(input_len == 0);
assert!(s.next().unwrap() == b); assert!(s.next().unwrap() == b);

View File

@@ -1,13 +1,8 @@
use ff::{Field, PrimeField, PrimeFieldRepr}; //! Helpers for testing circuit implementations.
use pairing::Engine;
use bellman::{ use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
LinearCombination,
SynthesisError, use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
ConstraintSystem,
Variable,
Index
};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
@@ -16,27 +11,27 @@ use byteorder::{BigEndian, ByteOrder};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use blake2_rfc::blake2s::Blake2s; use blake2s_simd::{Params as Blake2sParams, State as Blake2sState};
#[derive(Debug)] #[derive(Debug)]
enum NamedObject { enum NamedObject {
Constraint(usize), Constraint(usize),
Var(Variable), Var(Variable),
Namespace Namespace,
} }
/// Constraint system for testing purposes. /// Constraint system for testing purposes.
pub struct TestConstraintSystem<E: Engine> { pub struct TestConstraintSystem<E: ScalarEngine> {
named_objects: HashMap<String, NamedObject>, named_objects: HashMap<String, NamedObject>,
current_namespace: Vec<String>, current_namespace: Vec<String>,
constraints: Vec<( constraints: Vec<(
LinearCombination<E>, LinearCombination<E>,
LinearCombination<E>, LinearCombination<E>,
LinearCombination<E>, LinearCombination<E>,
String String,
)>, )>,
inputs: Vec<(E::Fr, String)>, inputs: Vec<(E::Fr, String)>,
aux: Vec<(E::Fr, String)> aux: Vec<(E::Fr, String)>,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -48,7 +43,7 @@ impl PartialEq for OrderedVariable {
match (self.0.get_unchecked(), other.0.get_unchecked()) { match (self.0.get_unchecked(), other.0.get_unchecked()) {
(Index::Input(ref a), Index::Input(ref b)) => a == b, (Index::Input(ref a), Index::Input(ref b)) => a == b,
(Index::Aux(ref a), Index::Aux(ref b)) => a == b, (Index::Aux(ref a), Index::Aux(ref b)) => a == b,
_ => false _ => false,
} }
} }
} }
@@ -63,20 +58,17 @@ impl Ord for OrderedVariable {
(Index::Input(ref a), Index::Input(ref b)) => a.cmp(b), (Index::Input(ref a), Index::Input(ref b)) => a.cmp(b),
(Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b), (Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b),
(Index::Input(_), Index::Aux(_)) => Ordering::Less, (Index::Input(_), Index::Aux(_)) => Ordering::Less,
(Index::Aux(_), Index::Input(_)) => Ordering::Greater (Index::Aux(_), Index::Input(_)) => Ordering::Greater,
} }
} }
} }
fn proc_lc<E: Engine>( fn proc_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)]) -> BTreeMap<OrderedVariable, E::Fr> {
terms: &[(Variable, E::Fr)],
) -> BTreeMap<OrderedVariable, E::Fr>
{
let mut map = BTreeMap::new(); let mut map = BTreeMap::new();
for &(var, coeff) in terms { for &(var, coeff) in terms {
map.entry(OrderedVariable(var)) map.entry(OrderedVariable(var))
.or_insert(E::Fr::zero()) .or_insert_with(E::Fr::zero)
.add_assign(&coeff); .add_assign(&coeff);
} }
// Remove terms that have a zero coefficient to normalize // Remove terms that have a zero coefficient to normalize
@@ -94,11 +86,7 @@ fn proc_lc<E: Engine>(
map map
} }
fn hash_lc<E: Engine>( fn hash_lc<E: ScalarEngine>(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) {
terms: &[(Variable, E::Fr)],
h: &mut Blake2s
)
{
let map = proc_lc::<E>(terms); let map = proc_lc::<E>(terms);
let mut buf = [0u8; 9 + 32]; let mut buf = [0u8; 9 + 32];
@@ -110,31 +98,30 @@ fn hash_lc<E: Engine>(
Index::Input(i) => { Index::Input(i) => {
buf[0] = b'I'; buf[0] = b'I';
BigEndian::write_u64(&mut buf[1..9], i as u64); BigEndian::write_u64(&mut buf[1..9], i as u64);
}, }
Index::Aux(i) => { Index::Aux(i) => {
buf[0] = b'A'; buf[0] = b'A';
BigEndian::write_u64(&mut buf[1..9], i as u64); BigEndian::write_u64(&mut buf[1..9], i as u64);
} }
} }
coeff.into_repr().write_be(&mut buf[9..]).unwrap(); coeff.into_repr().write_be(&mut buf[9..]).unwrap();
h.update(&buf); h.update(&buf);
} }
} }
fn eval_lc<E: Engine>( fn eval_lc<E: ScalarEngine>(
terms: &[(Variable, E::Fr)], terms: &[(Variable, E::Fr)],
inputs: &[(E::Fr, String)], inputs: &[(E::Fr, String)],
aux: &[(E::Fr, String)] aux: &[(E::Fr, String)],
) -> E::Fr ) -> E::Fr {
{
let mut acc = E::Fr::zero(); let mut acc = E::Fr::zero();
for &(var, ref coeff) in terms { for &(var, ref coeff) in terms {
let mut tmp = match var.get_unchecked() { let mut tmp = match var.get_unchecked() {
Index::Input(index) => inputs[index].0, Index::Input(index) => inputs[index].0,
Index::Aux(index) => aux[index].0 Index::Aux(index) => aux[index].0,
}; };
tmp.mul_assign(&coeff); tmp.mul_assign(&coeff);
@@ -144,17 +131,20 @@ fn eval_lc<E: Engine>(
acc acc
} }
impl<E: Engine> TestConstraintSystem<E> { impl<E: ScalarEngine> TestConstraintSystem<E> {
pub fn new() -> TestConstraintSystem<E> { pub fn new() -> TestConstraintSystem<E> {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert("ONE".into(), NamedObject::Var(TestConstraintSystem::<E>::one())); map.insert(
"ONE".into(),
NamedObject::Var(TestConstraintSystem::<E>::one()),
);
TestConstraintSystem { TestConstraintSystem {
named_objects: map, named_objects: map,
current_namespace: vec![], current_namespace: vec![],
constraints: vec![], constraints: vec![],
inputs: vec![(E::Fr::one(), "ONE".into())], inputs: vec![(E::Fr::one(), "ONE".into())],
aux: vec![] aux: vec![],
} }
} }
@@ -167,9 +157,9 @@ impl<E: Engine> TestConstraintSystem<E> {
tmp tmp
}; };
let powers_of_two = (0..E::Fr::NUM_BITS).map(|i| { let powers_of_two = (0..E::Fr::NUM_BITS)
E::Fr::from_str("2").unwrap().pow(&[i as u64]) .map(|i| E::Fr::from_str("2").unwrap().pow(&[u64::from(i)]))
}).collect::<Vec<_>>(); .collect::<Vec<_>>();
let pp = |s: &mut String, lc: &LinearCombination<E>| { let pp = |s: &mut String, lc: &LinearCombination<E>| {
write!(s, "(").unwrap(); write!(s, "(").unwrap();
@@ -196,7 +186,7 @@ impl<E: Engine> TestConstraintSystem<E> {
match var.0.get_unchecked() { match var.0.get_unchecked() {
Index::Input(i) => { Index::Input(i) => {
write!(s, "`{}`", &self.inputs[i].1).unwrap(); write!(s, "`{}`", &self.inputs[i].1).unwrap();
}, }
Index::Aux(i) => { Index::Aux(i) => {
write!(s, "`{}`", &self.aux[i].1).unwrap(); write!(s, "`{}`", &self.aux[i].1).unwrap();
} }
@@ -226,7 +216,7 @@ impl<E: Engine> TestConstraintSystem<E> {
} }
pub fn hash(&self) -> String { pub fn hash(&self) -> String {
let mut h = Blake2s::new(32); let mut h = Blake2sParams::new().hash_length(32).to_state();
{ {
let mut buf = [0u8; 24]; let mut buf = [0u8; 24];
@@ -259,57 +249,52 @@ impl<E: Engine> TestConstraintSystem<E> {
a.mul_assign(&b); a.mul_assign(&b);
if a != c { if a != c {
return Some(&*path) return Some(&*path);
} }
} }
None None
} }
pub fn is_satisfied(&self) -> bool pub fn is_satisfied(&self) -> bool {
{
self.which_is_unsatisfied().is_none() self.which_is_unsatisfied().is_none()
} }
pub fn num_constraints(&self) -> usize pub fn num_constraints(&self) -> usize {
{
self.constraints.len() self.constraints.len()
} }
pub fn set(&mut self, path: &str, to: E::Fr) pub fn set(&mut self, path: &str, to: E::Fr) {
{
match self.named_objects.get(path) { match self.named_objects.get(path) {
Some(&NamedObject::Var(ref v)) => { Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
match v.get_unchecked() { Index::Input(index) => self.inputs[index].0 = to,
Index::Input(index) => self.inputs[index].0 = to, Index::Aux(index) => self.aux[index].0 = to,
Index::Aux(index) => self.aux[index].0 = to },
} Some(e) => panic!(
} "tried to set path `{}` to value, but `{:?}` already exists there.",
Some(e) => panic!("tried to set path `{}` to value, but `{:?}` already exists there.", path, e), path, e
_ => panic!("no variable exists at path: {}", path) ),
_ => panic!("no variable exists at path: {}", path),
} }
} }
pub fn verify(&self, expected: &[E::Fr]) -> bool pub fn verify(&self, expected: &[E::Fr]) -> bool {
{
assert_eq!(expected.len() + 1, self.inputs.len()); assert_eq!(expected.len() + 1, self.inputs.len());
for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) {
{
if &a.0 != b { if &a.0 != b {
return false return false;
} }
} }
return true; true
} }
pub fn num_inputs(&self) -> usize { pub fn num_inputs(&self) -> usize {
self.inputs.len() self.inputs.len()
} }
pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr {
{
let (assignment, name) = self.inputs[index].clone(); let (assignment, name) = self.inputs[index].clone();
assert_eq!(path, name); assert_eq!(path, name);
@@ -317,17 +302,17 @@ impl<E: Engine> TestConstraintSystem<E> {
assignment assignment
} }
pub fn get(&mut self, path: &str) -> E::Fr pub fn get(&mut self, path: &str) -> E::Fr {
{
match self.named_objects.get(path) { match self.named_objects.get(path) {
Some(&NamedObject::Var(ref v)) => { Some(&NamedObject::Var(ref v)) => match v.get_unchecked() {
match v.get_unchecked() { Index::Input(index) => self.inputs[index].0,
Index::Input(index) => self.inputs[index].0, Index::Aux(index) => self.aux[index].0,
Index::Aux(index) => self.aux[index].0 },
} Some(e) => panic!(
} "tried to get value of path `{}`, but `{:?}` exists there (not a variable)",
Some(e) => panic!("tried to get value of path `{}`, but `{:?}` exists there (not a variable)", path, e), path, e
_ => panic!("no variable exists at path: {}", path) ),
_ => panic!("no variable exists at path: {}", path),
} }
} }
@@ -348,8 +333,7 @@ fn compute_path(ns: &[String], this: String) -> String {
let mut name = String::new(); let mut name = String::new();
let mut needs_separation = false; let mut needs_separation = false;
for ns in ns.iter().chain(Some(&this).into_iter()) for ns in ns.iter().chain(Some(&this).into_iter()) {
{
if needs_separation { if needs_separation {
name += "/"; name += "/";
} }
@@ -361,15 +345,14 @@ fn compute_path(ns: &[String], this: String) -> String {
name name
} }
impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> { impl<E: ScalarEngine> ConstraintSystem<E> for TestConstraintSystem<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
let index = self.aux.len(); let index = self.aux.len();
let path = compute_path(&self.current_namespace, annotation().into()); let path = compute_path(&self.current_namespace, annotation().into());
@@ -380,12 +363,11 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var) Ok(var)
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
let index = self.inputs.len(); let index = self.inputs.len();
let path = compute_path(&self.current_namespace, annotation().into()); let path = compute_path(&self.current_namespace, annotation().into());
@@ -396,17 +378,13 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
Ok(var) Ok(var)
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
let path = compute_path(&self.current_namespace, annotation().into()); let path = compute_path(&self.current_namespace, annotation().into());
let index = self.constraints.len(); let index = self.constraints.len();
@@ -420,7 +398,9 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
} }
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
let name = name_fn().into(); let name = name_fn().into();
let path = compute_path(&self.current_namespace, name.clone()); let path = compute_path(&self.current_namespace, name.clone());
@@ -428,13 +408,11 @@ impl<E: Engine> ConstraintSystem<E> for TestConstraintSystem<E> {
self.current_namespace.push(name); self.current_namespace.push(name);
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
assert!(self.current_namespace.pop().is_some()); assert!(self.current_namespace.pop().is_some());
} }
fn get_root(&mut self) -> &mut Self::Root fn get_root(&mut self) -> &mut Self::Root {
{
self self
} }
} }
@@ -447,28 +425,26 @@ fn test_cs() {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 0); assert_eq!(cs.num_constraints(), 0);
let a = cs.namespace(|| "a").alloc(|| "var", || Ok(Fr::from_str("10").unwrap())).unwrap(); let a = cs
let b = cs.namespace(|| "b").alloc(|| "var", || Ok(Fr::from_str("4").unwrap())).unwrap(); .namespace(|| "a")
let c = cs.alloc(|| "product", || Ok(Fr::from_str("40").unwrap())).unwrap(); .alloc(|| "var", || Ok(Fr::from_str("10").unwrap()))
.unwrap();
let b = cs
.namespace(|| "b")
.alloc(|| "var", || Ok(Fr::from_str("4").unwrap()))
.unwrap();
let c = cs
.alloc(|| "product", || Ok(Fr::from_str("40").unwrap()))
.unwrap();
cs.enforce( cs.enforce(|| "mult", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|| "mult",
|lc| lc + a,
|lc| lc + b,
|lc| lc + c
);
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1); assert_eq!(cs.num_constraints(), 1);
cs.set("a/var", Fr::from_str("4").unwrap()); cs.set("a/var", Fr::from_str("4").unwrap());
let one = TestConstraintSystem::<Bls12>::one(); let one = TestConstraintSystem::<Bls12>::one();
cs.enforce( cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b);
|| "eq",
|lc| lc + a,
|lc| lc + one,
|lc| lc + b
);
assert!(!cs.is_satisfied()); assert!(!cs.is_satisfied());
assert!(cs.which_is_unsatisfied() == Some("mult")); assert!(cs.which_is_unsatisfied() == Some("mult"));

View File

@@ -1,16 +1,13 @@
use ff::{Field, PrimeField}; //! Circuit representation of a [`u32`], with helpers for the [`sha256`]
use pairing::Engine; //! gadgets.
//!
//! [`sha256`]: crate::gadgets::sha256
use bellman::{ use ff::{Field, PrimeField, ScalarEngine};
SynthesisError,
ConstraintSystem,
LinearCombination
};
use super::boolean::{ use crate::{ConstraintSystem, LinearCombination, SynthesisError};
Boolean,
AllocatedBit use super::boolean::{AllocatedBit, Boolean};
};
use super::multieq::MultiEq; use super::multieq::MultiEq;
@@ -20,13 +17,12 @@ use super::multieq::MultiEq;
pub struct UInt32 { pub struct UInt32 {
// Least significant bit first // Least significant bit first
bits: Vec<Boolean>, bits: Vec<Boolean>,
value: Option<u32> value: Option<u32>,
} }
impl UInt32 { impl UInt32 {
/// Construct a constant `UInt32` from a `u32` /// Construct a constant `UInt32` from a `u32`
pub fn constant(value: u32) -> Self pub fn constant(value: u32) -> Self {
{
let mut bits = Vec::with_capacity(32); let mut bits = Vec::with_capacity(32);
let mut tmp = value; let mut tmp = value;
@@ -41,18 +37,16 @@ impl UInt32 {
} }
UInt32 { UInt32 {
bits: bits, bits,
value: Some(value) value: Some(value),
} }
} }
/// Allocate a `UInt32` in the constraint system /// Allocate a `UInt32` in the constraint system
pub fn alloc<E, CS>( pub fn alloc<E, CS>(mut cs: CS, value: Option<u32>) -> Result<Self, SynthesisError>
mut cs: CS, where
value: Option<u32> E: ScalarEngine,
) -> Result<Self, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine,
CS: ConstraintSystem<E>
{ {
let values = match value { let values = match value {
Some(mut val) => { Some(mut val) => {
@@ -64,28 +58,28 @@ impl UInt32 {
} }
v v
}, }
None => vec![None; 32] None => vec![None; 32],
}; };
let bits = values.into_iter() let bits = values
.enumerate() .into_iter()
.map(|(i, v)| { .enumerate()
Ok(Boolean::from(AllocatedBit::alloc( .map(|(i, v)| {
cs.namespace(|| format!("allocated bit {}", i)), Ok(Boolean::from(AllocatedBit::alloc(
v cs.namespace(|| format!("allocated bit {}", i)),
)?)) v,
}) )?))
.collect::<Result<Vec<_>, SynthesisError>>()?; })
.collect::<Result<Vec<_>, SynthesisError>>()?;
Ok(UInt32 { Ok(UInt32 { bits, value })
bits: bits,
value: value
})
} }
pub fn into_bits_be(&self) -> Vec<Boolean> { pub fn into_bits_be(self) -> Vec<Boolean> {
self.bits.iter().rev().cloned().collect() let mut ret = self.bits;
ret.reverse();
ret
} }
pub fn from_bits_be(bits: &[Boolean]) -> Self { pub fn from_bits_be(bits: &[Boolean]) -> Self {
@@ -96,28 +90,30 @@ impl UInt32 {
value.as_mut().map(|v| *v <<= 1); value.as_mut().map(|v| *v <<= 1);
match b.get_value() { match b.get_value() {
Some(true) => { value.as_mut().map(|v| *v |= 1); }, Some(true) => {
Some(false) => {}, value.as_mut().map(|v| *v |= 1);
None => { value = None; } }
Some(false) => {}
None => {
value = None;
}
} }
} }
UInt32 { UInt32 {
value: value, value,
bits: bits.iter().rev().cloned().collect() bits: bits.iter().rev().cloned().collect(),
} }
} }
/// Turns this `UInt32` into its little-endian byte order representation. /// Turns this `UInt32` into its little-endian byte order representation.
pub fn into_bits(&self) -> Vec<Boolean> { pub fn into_bits(self) -> Vec<Boolean> {
self.bits.clone() self.bits
} }
/// Converts a little-endian byte order representation of bits into a /// Converts a little-endian byte order representation of bits into a
/// `UInt32`. /// `UInt32`.
pub fn from_bits(bits: &[Boolean]) -> Self pub fn from_bits(bits: &[Boolean]) -> Self {
{
assert_eq!(bits.len(), 32); assert_eq!(bits.len(), 32);
let new_bits = bits.to_vec(); let new_bits = bits.to_vec();
@@ -126,48 +122,50 @@ impl UInt32 {
for b in new_bits.iter().rev() { for b in new_bits.iter().rev() {
value.as_mut().map(|v| *v <<= 1); value.as_mut().map(|v| *v <<= 1);
match b { match *b {
&Boolean::Constant(b) => { Boolean::Constant(b) => {
if b { if b {
value.as_mut().map(|v| *v |= 1); value.as_mut().map(|v| *v |= 1);
} }
},
&Boolean::Is(ref b) => {
match b.get_value() {
Some(true) => { value.as_mut().map(|v| *v |= 1); },
Some(false) => {},
None => { value = None }
}
},
&Boolean::Not(ref b) => {
match b.get_value() {
Some(false) => { value.as_mut().map(|v| *v |= 1); },
Some(true) => {},
None => { value = None }
}
} }
Boolean::Is(ref b) => match b.get_value() {
Some(true) => {
value.as_mut().map(|v| *v |= 1);
}
Some(false) => {}
None => value = None,
},
Boolean::Not(ref b) => match b.get_value() {
Some(false) => {
value.as_mut().map(|v| *v |= 1);
}
Some(true) => {}
None => value = None,
},
} }
} }
UInt32 { UInt32 {
value: value, value,
bits: new_bits bits: new_bits,
} }
} }
pub fn rotr(&self, by: usize) -> Self { pub fn rotr(&self, by: usize) -> Self {
let by = by % 32; let by = by % 32;
let new_bits = self.bits.iter() let new_bits = self
.skip(by) .bits
.chain(self.bits.iter()) .iter()
.take(32) .skip(by)
.cloned() .chain(self.bits.iter())
.collect(); .take(32)
.cloned()
.collect();
UInt32 { UInt32 {
bits: new_bits, bits: new_bits,
value: self.value.map(|v| v.rotate_right(by as u32)) value: self.value.map(|v| v.rotate_right(by as u32)),
} }
} }
@@ -176,17 +174,18 @@ impl UInt32 {
let fill = Boolean::constant(false); let fill = Boolean::constant(false);
let new_bits = self.bits let new_bits = self
.iter() // The bits are least significant first .bits
.skip(by) // Skip the bits that will be lost during the shift .iter() // The bits are least significant first
.chain(Some(&fill).into_iter().cycle()) // Rest will be zeros .skip(by) // Skip the bits that will be lost during the shift
.take(32) // Only 32 bits needed! .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros
.cloned() .take(32) // Only 32 bits needed!
.collect(); .cloned()
.collect();
UInt32 { UInt32 {
bits: new_bits, bits: new_bits,
value: self.value.map(|v| v >> by as u32) value: self.value.map(|v| v >> by as u32),
} }
} }
@@ -196,121 +195,99 @@ impl UInt32 {
b: &Self, b: &Self,
c: &Self, c: &Self,
tri_fn: F, tri_fn: F,
circuit_fn: U circuit_fn: U,
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where E: Engine, where
CS: ConstraintSystem<E>, E: ScalarEngine,
F: Fn(u32, u32, u32) -> u32, CS: ConstraintSystem<E>,
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError> F: Fn(u32, u32, u32) -> u32,
U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result<Boolean, SynthesisError>,
{ {
let new_value = match (a.value, b.value, c.value) { let new_value = match (a.value, b.value, c.value) {
(Some(a), Some(b), Some(c)) => { (Some(a), Some(b), Some(c)) => Some(tri_fn(a, b, c)),
Some(tri_fn(a, b, c)) _ => None,
},
_ => None
}; };
let bits = a.bits.iter() let bits = a
.zip(b.bits.iter()) .bits
.zip(c.bits.iter()) .iter()
.enumerate() .zip(b.bits.iter())
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c)) .zip(c.bits.iter())
.collect::<Result<_, _>>()?; .enumerate()
.map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c))
.collect::<Result<_, _>>()?;
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits,
value: new_value value: new_value,
}) })
} }
/// Compute the `maj` value (a and b) xor (a and c) xor (b and c) /// Compute the `maj` value (a and b) xor (a and c) xor (b and c)
/// during SHA256. /// during SHA256.
pub fn sha256_maj<E, CS>( pub fn sha256_maj<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
cs: CS, where
a: &Self, E: ScalarEngine,
b: &Self, CS: ConstraintSystem<E>,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ (a & c) ^ (b & c), Self::triop(
|cs, i, a, b, c| { cs,
Boolean::sha256_maj( a,
cs.namespace(|| format!("maj {}", i)), b,
a, c,
b, |a, b, c| (a & b) ^ (a & c) ^ (b & c),
c |cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c),
)
}
) )
} }
/// Compute the `ch` value `(a and b) xor ((not a) and c)` /// Compute the `ch` value `(a and b) xor ((not a) and c)`
/// during SHA256. /// during SHA256.
pub fn sha256_ch<E, CS>( pub fn sha256_ch<E, CS>(cs: CS, a: &Self, b: &Self, c: &Self) -> Result<Self, SynthesisError>
cs: CS, where
a: &Self, E: ScalarEngine,
b: &Self, CS: ConstraintSystem<E>,
c: &Self
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ ((!a) & c), Self::triop(
|cs, i, a, b, c| { cs,
Boolean::sha256_ch( a,
cs.namespace(|| format!("ch {}", i)), b,
a, c,
b, |a, b, c| (a & b) ^ ((!a) & c),
c |cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c),
)
}
) )
} }
/// XOR this `UInt32` with another `UInt32` /// XOR this `UInt32` with another `UInt32`
pub fn xor<E, CS>( pub fn xor<E, CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
&self, where
mut cs: CS, E: ScalarEngine,
other: &Self CS: ConstraintSystem<E>,
) -> Result<Self, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{ {
let new_value = match (self.value, other.value) { let new_value = match (self.value, other.value) {
(Some(a), Some(b)) => { (Some(a), Some(b)) => Some(a ^ b),
Some(a ^ b) _ => None,
},
_ => None
}; };
let bits = self.bits.iter() let bits = self
.zip(other.bits.iter()) .bits
.enumerate() .iter()
.map(|(i, (a, b))| { .zip(other.bits.iter())
Boolean::xor( .enumerate()
cs.namespace(|| format!("xor of bit {}", i)), .map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b))
a, .collect::<Result<_, _>>()?;
b
)
})
.collect::<Result<_, _>>()?;
Ok(UInt32 { Ok(UInt32 {
bits: bits, bits,
value: new_value value: new_value,
}) })
} }
/// Perform modular addition of several `UInt32` objects. /// Perform modular addition of several `UInt32` objects.
pub fn addmany<E, CS, M>( pub fn addmany<E, CS, M>(mut cs: M, operands: &[Self]) -> Result<Self, SynthesisError>
mut cs: M, where
operands: &[Self] E: ScalarEngine,
) -> Result<Self, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, M: ConstraintSystem<E, Root = MultiEq<E, CS>>,
CS: ConstraintSystem<E>,
M: ConstraintSystem<E, Root=MultiEq<E, CS>>
{ {
// Make some arbitrary bounds for ourselves to avoid overflows // Make some arbitrary bounds for ourselves to avoid overflows
// in the scalar field // in the scalar field
@@ -320,7 +297,7 @@ impl UInt32 {
// Compute the maximum value of the sum so we allocate enough bits for // Compute the maximum value of the sum so we allocate enough bits for
// the result // the result
let mut max_value = (operands.len() as u64) * (u32::max_value() as u64); let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value()));
// Keep track of the resulting value // Keep track of the resulting value
let mut result_value = Some(0u64); let mut result_value = Some(0u64);
@@ -336,8 +313,8 @@ impl UInt32 {
// Accumulate the value // Accumulate the value
match op.value { match op.value {
Some(val) => { Some(val) => {
result_value.as_mut().map(|v| *v += val as u64); result_value.as_mut().map(|v| *v += u64::from(val));
}, }
None => { None => {
// If any of our operands have unknown value, we won't // If any of our operands have unknown value, we won't
// know the value of the result // know the value of the result
@@ -381,7 +358,7 @@ impl UInt32 {
// Allocate the bit // Allocate the bit
let b = AllocatedBit::alloc( let b = AllocatedBit::alloc(
cs.namespace(|| format!("result bit {}", i)), cs.namespace(|| format!("result bit {}", i)),
result_value.map(|v| (v >> i) & 1 == 1) result_value.map(|v| (v >> i) & 1 == 1),
)?; )?;
// Add this bit to the result combination // Add this bit to the result combination
@@ -402,48 +379,53 @@ impl UInt32 {
Ok(UInt32 { Ok(UInt32 {
bits: result_bits, bits: result_bits,
value: modular_value value: modular_value,
}) })
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use rand::{XorShiftRng, SeedableRng, Rng}; use super::UInt32;
use ::circuit::boolean::{Boolean}; use crate::gadgets::boolean::Boolean;
use super::{UInt32}; use crate::gadgets::multieq::MultiEq;
use crate::gadgets::test::*;
use crate::ConstraintSystem;
use ff::Field; use ff::Field;
use pairing::bls12_381::{Bls12}; use pairing::bls12_381::Bls12;
use ::circuit::test::*; use rand_core::{RngCore, SeedableRng};
use bellman::{ConstraintSystem}; use rand_xorshift::XorShiftRng;
use circuit::multieq::MultiEq;
#[test] #[test]
fn test_uint32_from_bits_be() { fn test_uint32_from_bits_be() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::<Vec<_>>(); let v = (0..32)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits_be(&v); let b = UInt32::from_bits_be(&v);
for (i, bit) in b.bits.iter().enumerate() { for (i, bit) in b.bits.iter().enumerate() {
match bit { match *bit {
&Boolean::Constant(bit) => { Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
}, }
_ => unreachable!() _ => unreachable!(),
} }
} }
let expected_to_be_same = b.into_bits_be(); let expected_to_be_same = b.into_bits_be();
for x in v.iter().zip(expected_to_be_same.iter()) for x in v.iter().zip(expected_to_be_same.iter()) {
{
match x { match x {
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}, (&Boolean::Constant(true), &Boolean::Constant(true)) => {}
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}, (&Boolean::Constant(false), &Boolean::Constant(false)) => {}
_ => unreachable!() _ => unreachable!(),
} }
} }
} }
@@ -451,30 +433,34 @@ mod test {
#[test] #[test]
fn test_uint32_from_bits() { fn test_uint32_from_bits() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::<Vec<_>>(); let v = (0..32)
.map(|_| Boolean::constant(rng.next_u32() % 2 != 0))
.collect::<Vec<_>>();
let b = UInt32::from_bits(&v); let b = UInt32::from_bits(&v);
for (i, bit) in b.bits.iter().enumerate() { for (i, bit) in b.bits.iter().enumerate() {
match bit { match *bit {
&Boolean::Constant(bit) => { Boolean::Constant(bit) => {
assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); assert!(bit == ((b.value.unwrap() >> i) & 1 == 1));
}, }
_ => unreachable!() _ => unreachable!(),
} }
} }
let expected_to_be_same = b.into_bits(); let expected_to_be_same = b.into_bits();
for x in v.iter().zip(expected_to_be_same.iter()) for x in v.iter().zip(expected_to_be_same.iter()) {
{
match x { match x {
(&Boolean::Constant(true), &Boolean::Constant(true)) => {}, (&Boolean::Constant(true), &Boolean::Constant(true)) => {}
(&Boolean::Constant(false), &Boolean::Constant(false)) => {}, (&Boolean::Constant(false), &Boolean::Constant(false)) => {}
_ => unreachable!() _ => unreachable!(),
} }
} }
} }
@@ -482,14 +468,17 @@ mod test {
#[test] #[test]
fn test_uint32_xor() { fn test_uint32_xor() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen(); let a = rng.next_u32();
let b: u32 = rng.gen(); let b = rng.next_u32();
let c: u32 = rng.gen(); let c = rng.next_u32();
let mut expected = a ^ b ^ c; let mut expected = a ^ b ^ c;
@@ -505,14 +494,14 @@ mod test {
assert!(r.value == Some(expected)); assert!(r.value == Some(expected));
for b in r.bits.iter() { for b in r.bits.iter() {
match b { match *b {
&Boolean::Is(ref b) => { Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Not(ref b) => { Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
} }
@@ -524,14 +513,17 @@ mod test {
#[test] #[test]
fn test_uint32_addmany_constants() { fn test_uint32_addmany_constants() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen(); let a = rng.next_u32();
let b: u32 = rng.gen(); let b = rng.next_u32();
let c: u32 = rng.gen(); let c = rng.next_u32();
let a_bit = UInt32::constant(a); let a_bit = UInt32::constant(a);
let b_bit = UInt32::constant(b); let b_bit = UInt32::constant(b);
@@ -541,17 +533,18 @@ mod test {
let r = { let r = {
let mut cs = MultiEq::new(&mut cs); let mut cs = MultiEq::new(&mut cs);
let r = UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); let r =
UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap();
r r
}; };
assert!(r.value == Some(expected)); assert!(r.value == Some(expected));
for b in r.bits.iter() { for b in r.bits.iter() {
match b { match *b {
&Boolean::Is(_) => panic!(), Boolean::Is(_) => panic!(),
&Boolean::Not(_) => panic!(), Boolean::Not(_) => panic!(),
&Boolean::Constant(b) => { Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
} }
@@ -563,15 +556,18 @@ mod test {
#[test] #[test]
fn test_uint32_addmany() { fn test_uint32_addmany() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen(); let a = rng.next_u32();
let b: u32 = rng.gen(); let b = rng.next_u32();
let c: u32 = rng.gen(); let c = rng.next_u32();
let d: u32 = rng.gen(); let d = rng.next_u32();
let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d);
@@ -583,8 +579,7 @@ mod test {
let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap();
let r = { let r = {
let mut cs = MultiEq::new(&mut cs); let mut cs = MultiEq::new(&mut cs);
let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap(); UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap()
r
}; };
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
@@ -592,16 +587,14 @@ mod test {
assert!(r.value == Some(expected)); assert!(r.value == Some(expected));
for b in r.bits.iter() { for b in r.bits.iter() {
match b { match *b {
&Boolean::Is(ref b) => { Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
},
&Boolean::Constant(_) => {
unreachable!()
} }
Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}
Boolean::Constant(_) => unreachable!(),
} }
expected >>= 1; expected >>= 1;
@@ -620,9 +613,12 @@ mod test {
#[test] #[test]
fn test_uint32_rotr() { fn test_uint32_rotr() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let mut num = rng.gen(); let mut num = rng.next_u32();
let a = UInt32::constant(num); let a = UInt32::constant(num);
@@ -634,11 +630,11 @@ mod test {
let mut tmp = num; let mut tmp = num;
for b in &b.bits { for b in &b.bits {
match b { match *b {
&Boolean::Constant(b) => { Boolean::Constant(b) => {
assert_eq!(b, tmp & 1 == 1); assert_eq!(b, tmp & 1 == 1);
}, }
_ => unreachable!() _ => unreachable!(),
} }
tmp >>= 1; tmp >>= 1;
@@ -650,11 +646,14 @@ mod test {
#[test] #[test]
fn test_uint32_shr() { fn test_uint32_shr() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..50 { for _ in 0..50 {
for i in 0..60 { for i in 0..60 {
let num = rng.gen(); let num = rng.next_u32();
let a = UInt32::constant(num).shr(i); let a = UInt32::constant(num).shr(i);
let b = UInt32::constant(num.wrapping_shr(i as u32)); let b = UInt32::constant(num.wrapping_shr(i as u32));
@@ -670,14 +669,17 @@ mod test {
#[test] #[test]
fn test_uint32_sha256_maj() { fn test_uint32_sha256_maj() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen(); let a = rng.next_u32();
let b: u32 = rng.gen(); let b = rng.next_u32();
let c: u32 = rng.gen(); let c = rng.next_u32();
let mut expected = (a & b) ^ (a & c) ^ (b & c); let mut expected = (a & b) ^ (a & c) ^ (b & c);
@@ -695,10 +697,10 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }
@@ -711,14 +713,17 @@ mod test {
#[test] #[test]
fn test_uint32_sha256_ch() { fn test_uint32_sha256_ch() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let a: u32 = rng.gen(); let a = rng.next_u32();
let b: u32 = rng.gen(); let b = rng.next_u32();
let c: u32 = rng.gen(); let c = rng.next_u32();
let mut expected = (a & b) ^ ((!a) & c); let mut expected = (a & b) ^ ((!a) & c);
@@ -736,10 +741,10 @@ mod test {
match b { match b {
&Boolean::Is(ref b) => { &Boolean::Is(ref b) => {
assert!(b.get_value().unwrap() == (expected & 1 == 1)); assert!(b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Not(ref b) => { &Boolean::Not(ref b) => {
assert!(!b.get_value().unwrap() == (expected & 1 == 1)); assert!(!b.get_value().unwrap() == (expected & 1 == 1));
}, }
&Boolean::Constant(b) => { &Boolean::Constant(b) => {
assert!(b == (expected & 1 == 1)); assert!(b == (expected & 1 == 1));
} }

View File

@@ -1,4 +1,4 @@
use rand::Rng; use rand_core::RngCore;
use std::sync::Arc; use std::sync::Arc;
@@ -6,55 +6,34 @@ use ff::{Field, PrimeField};
use group::{CurveAffine, CurveProjective, Wnaf}; use group::{CurveAffine, CurveProjective, Wnaf};
use pairing::Engine; use pairing::Engine;
use super::{ use super::{Parameters, VerifyingKey};
Parameters,
VerifyingKey
};
use ::{ use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use ::domain::{ use crate::domain::{EvaluationDomain, Scalar};
EvaluationDomain,
Scalar
};
use ::multicore::{ use crate::multicore::Worker;
Worker
};
/// Generates a random common reference string for /// Generates a random common reference string for
/// a circuit. /// a circuit.
pub fn generate_random_parameters<E, C, R>( pub fn generate_random_parameters<E, C, R>(
circuit: C, circuit: C,
rng: &mut R rng: &mut R,
) -> Result<Parameters<E>, SynthesisError> ) -> Result<Parameters<E>, SynthesisError>
where E: Engine, C: Circuit<E>, R: Rng where
E: Engine,
C: Circuit<E>,
R: RngCore,
{ {
let g1 = rng.gen(); let g1 = E::G1::random(rng);
let g2 = rng.gen(); let g2 = E::G2::random(rng);
let alpha = rng.gen(); let alpha = E::Fr::random(rng);
let beta = rng.gen(); let beta = E::Fr::random(rng);
let gamma = rng.gen(); let gamma = E::Fr::random(rng);
let delta = rng.gen(); let delta = E::Fr::random(rng);
let tau = rng.gen(); let tau = E::Fr::random(rng);
generate_parameters::<E, C>( generate_parameters::<E, C>(circuit, g1, g2, alpha, beta, gamma, delta, tau)
circuit,
g1,
g2,
alpha,
beta,
gamma,
delta,
tau
)
} }
/// This is our assembly structure that we'll use to synthesize the /// This is our assembly structure that we'll use to synthesize the
@@ -68,18 +47,17 @@ struct KeypairAssembly<E: Engine> {
ct_inputs: Vec<Vec<(E::Fr, usize)>>, ct_inputs: Vec<Vec<(E::Fr, usize)>>,
at_aux: Vec<Vec<(E::Fr, usize)>>, at_aux: Vec<Vec<(E::Fr, usize)>>,
bt_aux: Vec<Vec<(E::Fr, usize)>>, bt_aux: Vec<Vec<(E::Fr, usize)>>,
ct_aux: Vec<Vec<(E::Fr, usize)>> ct_aux: Vec<Vec<(E::Fr, usize)>>,
} }
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> { impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
&mut self, where
_: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
_: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
// There is no assignment, so we don't even invoke the // There is no assignment, so we don't even invoke the
// function for obtaining one. // function for obtaining one.
@@ -94,12 +72,11 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
Ok(Variable(Index::Aux(index))) Ok(Variable(Index::Aux(index)))
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
&mut self, where
_: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
_: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
// There is no assignment, so we don't even invoke the // There is no assignment, so we don't even invoke the
// function for obtaining one. // function for obtaining one.
@@ -114,48 +91,59 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
Ok(Variable(Index::Input(index))) Ok(Variable(Index::Input(index)))
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
&mut self, where
_: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
fn eval<E: Engine>( fn eval<E: Engine>(
l: LinearCombination<E>, l: LinearCombination<E>,
inputs: &mut [Vec<(E::Fr, usize)>], inputs: &mut [Vec<(E::Fr, usize)>],
aux: &mut [Vec<(E::Fr, usize)>], aux: &mut [Vec<(E::Fr, usize)>],
this_constraint: usize this_constraint: usize,
) ) {
{
for (index, coeff) in l.0 { for (index, coeff) in l.0 {
match index { match index {
Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)), Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)),
Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)) Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)),
} }
} }
} }
eval(a(LinearCombination::zero()), &mut self.at_inputs, &mut self.at_aux, self.num_constraints); eval(
eval(b(LinearCombination::zero()), &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints); a(LinearCombination::zero()),
eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints); &mut self.at_inputs,
&mut self.at_aux,
self.num_constraints,
);
eval(
b(LinearCombination::zero()),
&mut self.bt_inputs,
&mut self.bt_aux,
self.num_constraints,
);
eval(
c(LinearCombination::zero()),
&mut self.ct_inputs,
&mut self.ct_aux,
self.num_constraints,
);
self.num_constraints += 1; self.num_constraints += 1;
} }
fn push_namespace<NR, N>(&mut self, _: N) fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
@@ -173,9 +161,11 @@ pub fn generate_parameters<E, C>(
beta: E::Fr, beta: E::Fr,
gamma: E::Fr, gamma: E::Fr,
delta: E::Fr, delta: E::Fr,
tau: E::Fr tau: E::Fr,
) -> Result<Parameters<E>, SynthesisError> ) -> Result<Parameters<E>, SynthesisError>
where E: Engine, C: Circuit<E> where
E: Engine,
C: Circuit<E>,
{ {
let mut assembly = KeypairAssembly { let mut assembly = KeypairAssembly {
num_inputs: 0, num_inputs: 0,
@@ -186,7 +176,7 @@ pub fn generate_parameters<E, C>(
ct_inputs: vec![], ct_inputs: vec![],
at_aux: vec![], at_aux: vec![],
bt_aux: vec![], bt_aux: vec![],
ct_aux: vec![] ct_aux: vec![],
}; };
// Allocate the "one" input variable // Allocate the "one" input variable
@@ -198,11 +188,7 @@ pub fn generate_parameters<E, C>(
// Input constraints to ensure full density of IC query // Input constraints to ensure full density of IC query
// x * 0 = 0 // x * 0 = 0
for i in 0..assembly.num_inputs { for i in 0..assembly.num_inputs {
assembly.enforce(|| "", assembly.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
} }
// Create bases for blind evaluation of polynomials at tau // Create bases for blind evaluation of polynomials at tau
@@ -240,10 +226,9 @@ pub fn generate_parameters<E, C>(
{ {
let powers_of_tau = powers_of_tau.as_mut(); let powers_of_tau = powers_of_tau.as_mut();
worker.scope(powers_of_tau.len(), |scope, chunk| { worker.scope(powers_of_tau.len(), |scope, chunk| {
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
{ scope.spawn(move |_scope| {
scope.spawn(move || { let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
let mut current_tau_power = tau.pow(&[(i*chunk) as u64]);
for p in powers_of_tau { for p in powers_of_tau {
p.0 = current_tau_power; p.0 = current_tau_power;
@@ -260,14 +245,15 @@ pub fn generate_parameters<E, C>(
// Compute the H query with multiple threads // Compute the H query with multiple threads
worker.scope(h.len(), |scope, chunk| { worker.scope(h.len(), |scope, chunk| {
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk)) for (h, p) in h
.chunks_mut(chunk)
.zip(powers_of_tau.as_ref().chunks(chunk))
{ {
let mut g1_wnaf = g1_wnaf.shared(); let mut g1_wnaf = g1_wnaf.shared();
scope.spawn(move || { scope.spawn(move |_scope| {
// Set values of the H query to g1^{(tau^i * t(tau)) / delta} // Set values of the H query to g1^{(tau^i * t(tau)) / delta}
for (h, p) in h.iter_mut().zip(p.iter()) for (h, p) in h.iter_mut().zip(p.iter()) {
{
// Compute final exponent // Compute final exponent
let mut exp = p.0; let mut exp = p.0;
exp.mul_assign(&coeff); exp.mul_assign(&coeff);
@@ -320,9 +306,8 @@ pub fn generate_parameters<E, C>(
beta: &E::Fr, beta: &E::Fr,
// Worker // Worker
worker: &Worker worker: &Worker,
) ) {
{
// Sanity check // Sanity check
assert_eq!(a.len(), at.len()); assert_eq!(a.len(), at.len());
assert_eq!(a.len(), bt.len()); assert_eq!(a.len(), bt.len());
@@ -333,31 +318,32 @@ pub fn generate_parameters<E, C>(
// Evaluate polynomials in multiple threads // Evaluate polynomials in multiple threads
worker.scope(a.len(), |scope, chunk| { worker.scope(a.len(), |scope, chunk| {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk) for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
.zip(b_g1.chunks_mut(chunk)) .chunks_mut(chunk)
.zip(b_g2.chunks_mut(chunk)) .zip(b_g1.chunks_mut(chunk))
.zip(ext.chunks_mut(chunk)) .zip(b_g2.chunks_mut(chunk))
.zip(at.chunks(chunk)) .zip(ext.chunks_mut(chunk))
.zip(bt.chunks(chunk)) .zip(at.chunks(chunk))
.zip(ct.chunks(chunk)) .zip(bt.chunks(chunk))
.zip(ct.chunks(chunk))
{ {
let mut g1_wnaf = g1_wnaf.shared(); let mut g1_wnaf = g1_wnaf.shared();
let mut g2_wnaf = g2_wnaf.shared(); let mut g2_wnaf = g2_wnaf.shared();
scope.spawn(move || { scope.spawn(move |_scope| {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut() for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a
.zip(b_g1.iter_mut()) .iter_mut()
.zip(b_g2.iter_mut()) .zip(b_g1.iter_mut())
.zip(ext.iter_mut()) .zip(b_g2.iter_mut())
.zip(at.iter()) .zip(ext.iter_mut())
.zip(bt.iter()) .zip(at.iter())
.zip(ct.iter()) .zip(bt.iter())
.zip(ct.iter())
{ {
fn eval_at_tau<E: Engine>( fn eval_at_tau<E: Engine>(
powers_of_tau: &[Scalar<E>], powers_of_tau: &[Scalar<E>],
p: &[(E::Fr, usize)] p: &[(E::Fr, usize)],
) -> E::Fr ) -> E::Fr {
{
let mut acc = E::Fr::zero(); let mut acc = E::Fr::zero();
for &(ref coeff, index) in p { for &(ref coeff, index) in p {
@@ -422,7 +408,7 @@ pub fn generate_parameters<E, C>(
&gamma_inverse, &gamma_inverse,
&alpha, &alpha,
&beta, &beta,
&worker &worker,
); );
// Evaluate for auxiliary variables. // Evaluate for auxiliary variables.
@@ -440,7 +426,7 @@ pub fn generate_parameters<E, C>(
&delta_inverse, &delta_inverse,
&alpha, &alpha,
&beta, &beta,
&worker &worker,
); );
// Don't allow any elements be unconstrained, so that // Don't allow any elements be unconstrained, so that
@@ -461,17 +447,32 @@ pub fn generate_parameters<E, C>(
gamma_g2: g2.mul(gamma).into_affine(), gamma_g2: g2.mul(gamma).into_affine(),
delta_g1: g1.mul(delta).into_affine(), delta_g1: g1.mul(delta).into_affine(),
delta_g2: g2.mul(delta).into_affine(), delta_g2: g2.mul(delta).into_affine(),
ic: ic.into_iter().map(|e| e.into_affine()).collect() ic: ic.into_iter().map(|e| e.into_affine()).collect(),
}; };
Ok(Parameters { Ok(Parameters {
vk: vk, vk,
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()), h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
// Filter points at infinity away from A/B queries // Filter points at infinity away from A/B queries
a: Arc::new(a.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), a: Arc::new(
b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), a.into_iter()
b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()) .filter(|e| !e.is_zero())
.map(|e| e.into_affine())
.collect(),
),
b_g1: Arc::new(
b_g1.into_iter()
.filter(|e| !e.is_zero())
.map(|e| e.into_affine())
.collect(),
),
b_g2: Arc::new(
b_g2.into_iter()
.filter(|e| !e.is_zero())
.map(|e| e.into_affine())
.collect(),
),
}) })
} }

View File

@@ -1,17 +1,16 @@
//! The [Groth16] proving system.
//!
//! [Groth16]: https://eprint.iacr.org/2016/260
use group::{CurveAffine, EncodedPoint}; use group::{CurveAffine, EncodedPoint};
use pairing::{ use pairing::{Engine, PairingCurveAffine};
Engine,
PairingCurveAffine,
};
use ::{ use crate::SynthesisError;
SynthesisError
};
use multiexp::SourceBuilder; use crate::multiexp::SourceBuilder;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::sync::Arc; use std::sync::Arc;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@@ -28,23 +27,17 @@ pub use self::verifier::*;
pub struct Proof<E: Engine> { pub struct Proof<E: Engine> {
pub a: E::G1Affine, pub a: E::G1Affine,
pub b: E::G2Affine, pub b: E::G2Affine,
pub c: E::G1Affine pub c: E::G1Affine,
} }
impl<E: Engine> PartialEq for Proof<E> { impl<E: Engine> PartialEq for Proof<E> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.a == other.a && self.a == other.a && self.b == other.b && self.c == other.c
self.b == other.b &&
self.c == other.c
} }
} }
impl<E: Engine> Proof<E> { impl<E: Engine> Proof<E> {
pub fn write<W: Write>( pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
&self,
mut writer: W
) -> io::Result<()>
{
writer.write_all(self.a.into_compressed().as_ref())?; writer.write_all(self.a.into_compressed().as_ref())?;
writer.write_all(self.b.into_compressed().as_ref())?; writer.write_all(self.b.into_compressed().as_ref())?;
writer.write_all(self.c.into_compressed().as_ref())?; writer.write_all(self.c.into_compressed().as_ref())?;
@@ -52,48 +45,56 @@ impl<E: Engine> Proof<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>( pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty(); let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty(); let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty();
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let a = g1_repr let a = g1_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
})?; }
})?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let b = g2_repr let b = g2_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
})?; }
})?;
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let c = g1_repr let c = g1_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"point at infinity",
))
} else { } else {
Ok(e) Ok(e)
})?; }
})?;
Ok(Proof { Ok(Proof { a, b, c })
a: a,
b: b,
c: c
})
} }
} }
@@ -122,27 +123,23 @@ pub struct VerifyingKey<E: Engine> {
// for all public inputs. Because all public inputs have a dummy constraint, // for all public inputs. Because all public inputs have a dummy constraint,
// this is the same size as the number of inputs, and never contains points // this is the same size as the number of inputs, and never contains points
// at infinity. // at infinity.
pub ic: Vec<E::G1Affine> pub ic: Vec<E::G1Affine>,
} }
impl<E: Engine> PartialEq for VerifyingKey<E> { impl<E: Engine> PartialEq for VerifyingKey<E> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.alpha_g1 == other.alpha_g1 && self.alpha_g1 == other.alpha_g1
self.beta_g1 == other.beta_g1 && && self.beta_g1 == other.beta_g1
self.beta_g2 == other.beta_g2 && && self.beta_g2 == other.beta_g2
self.gamma_g2 == other.gamma_g2 && && self.gamma_g2 == other.gamma_g2
self.delta_g1 == other.delta_g1 && && self.delta_g1 == other.delta_g1
self.delta_g2 == other.delta_g2 && && self.delta_g2 == other.delta_g2
self.ic == other.ic && self.ic == other.ic
} }
} }
impl<E: Engine> VerifyingKey<E> { impl<E: Engine> VerifyingKey<E> {
pub fn write<W: Write>( pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
&self,
mut writer: W
) -> io::Result<()>
{
writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?; writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?;
writer.write_all(self.beta_g1.into_uncompressed().as_ref())?; writer.write_all(self.beta_g1.into_uncompressed().as_ref())?;
writer.write_all(self.beta_g2.into_uncompressed().as_ref())?; writer.write_all(self.beta_g2.into_uncompressed().as_ref())?;
@@ -157,30 +154,39 @@ impl<E: Engine> VerifyingKey<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>( pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty(); let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty(); let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let alpha_g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let beta_g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let beta_g2 = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let gamma_g2 = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let delta_g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?; reader.read_exact(g2_repr.as_mut())?;
let delta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; let delta_g2 = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let ic_len = reader.read_u32::<BigEndian>()? as usize; let ic_len = reader.read_u32::<BigEndian>()? as usize;
@@ -189,25 +195,30 @@ impl<E: Engine> VerifyingKey<E> {
for _ in 0..ic_len { for _ in 0..ic_len {
reader.read_exact(g1_repr.as_mut())?; reader.read_exact(g1_repr.as_mut())?;
let g1 = g1_repr let g1 = g1_repr
.into_affine() .into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
} else { Err(io::Error::new(
Ok(e) io::ErrorKind::InvalidData,
})?; "point at infinity",
))
} else {
Ok(e)
}
})?;
ic.push(g1); ic.push(g1);
} }
Ok(VerifyingKey { Ok(VerifyingKey {
alpha_g1: alpha_g1, alpha_g1,
beta_g1: beta_g1, beta_g1,
beta_g2: beta_g2, beta_g2,
gamma_g2: gamma_g2, gamma_g2,
delta_g1: delta_g1, delta_g1,
delta_g2: delta_g2, delta_g2,
ic: ic ic,
}) })
} }
} }
@@ -216,7 +227,7 @@ impl<E: Engine> VerifyingKey<E> {
pub struct Parameters<E: Engine> { pub struct Parameters<E: Engine> {
pub vk: VerifyingKey<E>, pub vk: VerifyingKey<E>,
// Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and // Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and
// m-2 inclusive. Never contains points at infinity. // m-2 inclusive. Never contains points at infinity.
pub h: Arc<Vec<E::G1Affine>>, pub h: Arc<Vec<E::G1Affine>>,
@@ -234,26 +245,22 @@ pub struct Parameters<E: Engine> {
// G1 and G2 for C/B queries, respectively. Never contains points at // G1 and G2 for C/B queries, respectively. Never contains points at
// infinity for the same reason as the "A" polynomials. // infinity for the same reason as the "A" polynomials.
pub b_g1: Arc<Vec<E::G1Affine>>, pub b_g1: Arc<Vec<E::G1Affine>>,
pub b_g2: Arc<Vec<E::G2Affine>> pub b_g2: Arc<Vec<E::G2Affine>>,
} }
impl<E: Engine> PartialEq for Parameters<E> { impl<E: Engine> PartialEq for Parameters<E> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.vk == other.vk && self.vk == other.vk
self.h == other.h && && self.h == other.h
self.l == other.l && && self.l == other.l
self.a == other.a && && self.a == other.a
self.b_g1 == other.b_g1 && && self.b_g1 == other.b_g1
self.b_g2 == other.b_g2 && self.b_g2 == other.b_g2
} }
} }
impl<E: Engine> Parameters<E> { impl<E: Engine> Parameters<E> {
pub fn write<W: Write>( pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
&self,
mut writer: W
) -> io::Result<()>
{
self.vk.write(&mut writer)?; self.vk.write(&mut writer)?;
writer.write_u32::<BigEndian>(self.h.len() as u32)?; writer.write_u32::<BigEndian>(self.h.len() as u32)?;
@@ -284,27 +291,26 @@ impl<E: Engine> Parameters<E> {
Ok(()) Ok(())
} }
pub fn read<R: Read>( pub fn read<R: Read>(mut reader: R, checked: bool) -> io::Result<Self> {
mut reader: R,
checked: bool
) -> io::Result<Self>
{
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> { let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty(); let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(repr.as_mut())?; reader.read_exact(repr.as_mut())?;
if checked { if checked {
repr repr.into_affine()
.into_affine()
} else { } else {
repr repr.into_affine_unchecked()
.into_affine_unchecked()
} }
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
} else { Err(io::Error::new(
Ok(e) io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
}
}) })
}; };
@@ -313,17 +319,20 @@ impl<E: Engine> Parameters<E> {
reader.read_exact(repr.as_mut())?; reader.read_exact(repr.as_mut())?;
if checked { if checked {
repr repr.into_affine()
.into_affine()
} else { } else {
repr repr.into_affine_unchecked()
.into_affine_unchecked()
} }
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() { .and_then(|e| {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) if e.is_zero() {
} else { Err(io::Error::new(
Ok(e) io::ErrorKind::InvalidData,
"point at infinity",
))
} else {
Ok(e)
}
}) })
}; };
@@ -371,12 +380,12 @@ impl<E: Engine> Parameters<E> {
} }
Ok(Parameters { Ok(Parameters {
vk: vk, vk,
h: Arc::new(h), h: Arc::new(h),
l: Arc::new(l), l: Arc::new(l),
a: Arc::new(a), a: Arc::new(a),
b_g1: Arc::new(b_g1), b_g1: Arc::new(b_g1),
b_g2: Arc::new(b_g2) b_g2: Arc::new(b_g2),
}) })
} }
} }
@@ -389,39 +398,30 @@ pub struct PreparedVerifyingKey<E: Engine> {
/// -delta in G2 /// -delta in G2
neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared, neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
/// Copy of IC from `VerifiyingKey`. /// Copy of IC from `VerifiyingKey`.
ic: Vec<E::G1Affine> ic: Vec<E::G1Affine>,
} }
pub trait ParameterSource<E: Engine> { pub trait ParameterSource<E: Engine> {
type G1Builder: SourceBuilder<E::G1Affine>; type G1Builder: SourceBuilder<E::G1Affine>;
type G2Builder: SourceBuilder<E::G2Affine>; type G2Builder: SourceBuilder<E::G2Affine>;
fn get_vk( fn get_vk(&mut self, num_ic: usize) -> Result<VerifyingKey<E>, SynthesisError>;
&mut self, fn get_h(&mut self, num_h: usize) -> Result<Self::G1Builder, SynthesisError>;
num_ic: usize fn get_l(&mut self, num_l: usize) -> Result<Self::G1Builder, SynthesisError>;
) -> Result<VerifyingKey<E>, SynthesisError>;
fn get_h(
&mut self,
num_h: usize
) -> Result<Self::G1Builder, SynthesisError>;
fn get_l(
&mut self,
num_l: usize
) -> Result<Self::G1Builder, SynthesisError>;
fn get_a( fn get_a(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
num_aux: usize num_aux: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g1( fn get_b_g1(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
num_aux: usize num_aux: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g2( fn get_b_g2(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
num_aux: usize num_aux: usize,
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>; ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
} }
@@ -429,54 +429,39 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
type G1Builder = (Arc<Vec<E::G1Affine>>, usize); type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
type G2Builder = (Arc<Vec<E::G2Affine>>, usize); type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
fn get_vk( fn get_vk(&mut self, _: usize) -> Result<VerifyingKey<E>, SynthesisError> {
&mut self,
_: usize
) -> Result<VerifyingKey<E>, SynthesisError>
{
Ok(self.vk.clone()) Ok(self.vk.clone())
} }
fn get_h( fn get_h(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
&mut self,
_: usize
) -> Result<Self::G1Builder, SynthesisError>
{
Ok((self.h.clone(), 0)) Ok((self.h.clone(), 0))
} }
fn get_l( fn get_l(&mut self, _: usize) -> Result<Self::G1Builder, SynthesisError> {
&mut self,
_: usize
) -> Result<Self::G1Builder, SynthesisError>
{
Ok((self.l.clone(), 0)) Ok((self.l.clone(), 0))
} }
fn get_a( fn get_a(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
_: usize _: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
{
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs))) Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
} }
fn get_b_g1( fn get_b_g1(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
_: usize _: usize,
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> {
{
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs))) Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
} }
fn get_b_g2( fn get_b_g2(
&mut self, &mut self,
num_inputs: usize, num_inputs: usize,
_: usize _: usize,
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> {
{
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs))) Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
} }
} }
@@ -484,41 +469,38 @@ impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
#[cfg(test)] #[cfg(test)]
mod test_with_bls12_381 { mod test_with_bls12_381 {
use super::*; use super::*;
use {Circuit, SynthesisError, ConstraintSystem}; use crate::{Circuit, ConstraintSystem, SynthesisError};
use ff::Field; use ff::Field;
use rand::{Rand, thread_rng};
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand::thread_rng;
#[test] #[test]
fn serialization() { fn serialization() {
struct MySillyCircuit<E: Engine> { struct MySillyCircuit<E: Engine> {
a: Option<E::Fr>, a: Option<E::Fr>,
b: Option<E::Fr> b: Option<E::Fr>,
} }
impl<E: Engine> Circuit<E> for MySillyCircuit<E> { impl<E: Engine> Circuit<E> for MySillyCircuit<E> {
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(
self, self,
cs: &mut CS cs: &mut CS,
) -> Result<(), SynthesisError> ) -> Result<(), SynthesisError> {
{
let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
let c = cs.alloc_input(|| "c", || { let c = cs.alloc_input(
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; || "c",
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; || {
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
a.mul_assign(&b); a.mul_assign(&b);
Ok(a) Ok(a)
})?; },
)?;
cs.enforce( cs.enforce(|| "a*b=c", |lc| lc + a, |lc| lc + b, |lc| lc + c);
|| "a*b=c",
|lc| lc + a,
|lc| lc + b,
|lc| lc + c
);
Ok(()) Ok(())
} }
@@ -526,10 +508,9 @@ mod test_with_bls12_381 {
let rng = &mut thread_rng(); let rng = &mut thread_rng();
let params = generate_random_parameters::<Bls12, _, _>( let params =
MySillyCircuit { a: None, b: None }, generate_random_parameters::<Bls12, _, _>(MySillyCircuit { a: None, b: None }, rng)
rng .unwrap();
).unwrap();
{ {
let mut v = vec![]; let mut v = vec![];
@@ -547,19 +528,20 @@ mod test_with_bls12_381 {
let pvk = prepare_verifying_key::<Bls12>(&params.vk); let pvk = prepare_verifying_key::<Bls12>(&params.vk);
for _ in 0..100 { for _ in 0..100 {
let a = Fr::rand(rng); let a = Fr::random(rng);
let b = Fr::rand(rng); let b = Fr::random(rng);
let mut c = a; let mut c = a;
c.mul_assign(&b); c.mul_assign(&b);
let proof = create_random_proof( let proof = create_random_proof(
MySillyCircuit { MySillyCircuit {
a: Some(a), a: Some(a),
b: Some(b) b: Some(b),
}, },
&params, &params,
rng rng,
).unwrap(); )
.unwrap();
let mut v = vec![]; let mut v = vec![];
proof.write(&mut v).unwrap(); proof.write(&mut v).unwrap();

View File

@@ -1,4 +1,4 @@
use rand::Rng; use rand_core::RngCore;
use std::sync::Arc; use std::sync::Arc;
@@ -8,43 +8,23 @@ use ff::{Field, PrimeField};
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use pairing::Engine; use pairing::Engine;
use super::{ use super::{ParameterSource, Proof};
ParameterSource,
Proof
};
use ::{ use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable};
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use ::domain::{ use crate::domain::{EvaluationDomain, Scalar};
EvaluationDomain,
Scalar
};
use ::multiexp::{ use crate::multiexp::{multiexp, DensityTracker, FullDensity};
DensityTracker,
FullDensity,
multiexp
};
use ::multicore::{ use crate::multicore::Worker;
Worker
};
fn eval<E: Engine>( fn eval<E: Engine>(
lc: &LinearCombination<E>, lc: &LinearCombination<E>,
mut input_density: Option<&mut DensityTracker>, mut input_density: Option<&mut DensityTracker>,
mut aux_density: Option<&mut DensityTracker>, mut aux_density: Option<&mut DensityTracker>,
input_assignment: &[E::Fr], input_assignment: &[E::Fr],
aux_assignment: &[E::Fr] aux_assignment: &[E::Fr],
) -> E::Fr ) -> E::Fr {
{
let mut acc = E::Fr::zero(); let mut acc = E::Fr::zero();
for &(index, coeff) in lc.0.iter() { for &(index, coeff) in lc.0.iter() {
@@ -56,7 +36,7 @@ fn eval<E: Engine>(
if let Some(ref mut v) = input_density { if let Some(ref mut v) = input_density {
v.inc(i); v.inc(i);
} }
}, }
Variable(Index::Aux(i)) => { Variable(Index::Aux(i)) => {
tmp = aux_assignment[i]; tmp = aux_assignment[i];
if let Some(ref mut v) = aux_density { if let Some(ref mut v) = aux_density {
@@ -66,10 +46,10 @@ fn eval<E: Engine>(
} }
if coeff == E::Fr::one() { if coeff == E::Fr::one() {
acc.add_assign(&tmp); acc.add_assign(&tmp);
} else { } else {
tmp.mul_assign(&coeff); tmp.mul_assign(&coeff);
acc.add_assign(&tmp); acc.add_assign(&tmp);
} }
} }
@@ -89,18 +69,17 @@ struct ProvingAssignment<E: Engine> {
// Assignments of variables // Assignments of variables
input_assignment: Vec<E::Fr>, input_assignment: Vec<E::Fr>,
aux_assignment: Vec<E::Fr> aux_assignment: Vec<E::Fr>,
} }
impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> { impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
type Root = Self; type Root = Self;
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
_: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.aux_assignment.push(f()?); self.aux_assignment.push(f()?);
self.a_aux_density.add_element(); self.a_aux_density.add_element();
@@ -109,12 +88,11 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1))) Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
_: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.input_assignment.push(f()?); self.input_assignment.push(f()?);
self.b_input_density.add_element(); self.b_input_density.add_element();
@@ -122,17 +100,13 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
Ok(Variable(Index::Input(self.input_assignment.len() - 1))) Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
&mut self, where
_: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
let a = a(LinearCombination::zero()); let a = a(LinearCombination::zero());
let b = b(LinearCombination::zero()); let b = b(LinearCombination::zero());
@@ -146,14 +120,14 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
None, None,
Some(&mut self.a_aux_density), Some(&mut self.a_aux_density),
&self.input_assignment, &self.input_assignment,
&self.aux_assignment &self.aux_assignment,
))); )));
self.b.push(Scalar(eval( self.b.push(Scalar(eval(
&b, &b,
Some(&mut self.b_input_density), Some(&mut self.b_input_density),
Some(&mut self.b_aux_density), Some(&mut self.b_aux_density),
&self.input_assignment, &self.input_assignment,
&self.aux_assignment &self.aux_assignment,
))); )));
self.c.push(Scalar(eval( self.c.push(Scalar(eval(
&c, &c,
@@ -164,18 +138,19 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
None, None,
None, None,
&self.input_assignment, &self.input_assignment,
&self.aux_assignment &self.aux_assignment,
))); )));
} }
fn push_namespace<NR, N>(&mut self, _: N) fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
// Do nothing; we don't care about namespaces in this context. // Do nothing; we don't care about namespaces in this context.
} }
@@ -187,12 +162,15 @@ impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>( pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
circuit: C, circuit: C,
params: P, params: P,
rng: &mut R rng: &mut R,
) -> Result<Proof<E>, SynthesisError> ) -> Result<Proof<E>, SynthesisError>
where E: Engine, C: Circuit<E>, R: Rng where
E: Engine,
C: Circuit<E>,
R: RngCore,
{ {
let r = rng.gen(); let r = E::Fr::random(rng);
let s = rng.gen(); let s = E::Fr::random(rng);
create_proof::<E, C, P>(circuit, params, r, s) create_proof::<E, C, P>(circuit, params, r, s)
} }
@@ -201,9 +179,11 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
circuit: C, circuit: C,
mut params: P, mut params: P,
r: E::Fr, r: E::Fr,
s: E::Fr s: E::Fr,
) -> Result<Proof<E>, SynthesisError> ) -> Result<Proof<E>, SynthesisError>
where E: Engine, C: Circuit<E> where
E: Engine,
C: Circuit<E>,
{ {
let mut prover = ProvingAssignment { let mut prover = ProvingAssignment {
a_aux_density: DensityTracker::new(), a_aux_density: DensityTracker::new(),
@@ -213,7 +193,7 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
b: vec![], b: vec![],
c: vec![], c: vec![],
input_assignment: vec![], input_assignment: vec![],
aux_assignment: vec![] aux_assignment: vec![],
}; };
prover.alloc_input(|| "", || Ok(E::Fr::one()))?; prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
@@ -221,11 +201,7 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
circuit.synthesize(&mut prover)?; circuit.synthesize(&mut prover)?;
for i in 0..prover.input_assignment.len() { for i in 0..prover.input_assignment.len() {
prover.enforce(|| "", prover.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc);
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
} }
let worker = Worker::new(); let worker = Worker::new();
@@ -259,31 +235,76 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
}; };
// TODO: parallelize if it's even helpful // TODO: parallelize if it's even helpful
let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>()); let input_assignment = Arc::new(
let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>()); prover
.input_assignment
.into_iter()
.map(|s| s.into_repr())
.collect::<Vec<_>>(),
);
let aux_assignment = Arc::new(
prover
.aux_assignment
.into_iter()
.map(|s| s.into_repr())
.collect::<Vec<_>>(),
);
let l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone()); let l = multiexp(
&worker,
params.get_l(aux_assignment.len())?,
FullDensity,
aux_assignment.clone(),
);
let a_aux_density_total = prover.a_aux_density.get_total_density(); let a_aux_density_total = prover.a_aux_density.get_total_density();
let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?; let (a_inputs_source, a_aux_source) =
params.get_a(input_assignment.len(), a_aux_density_total)?;
let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone()); let a_inputs = multiexp(
let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone()); &worker,
a_inputs_source,
FullDensity,
input_assignment.clone(),
);
let a_aux = multiexp(
&worker,
a_aux_source,
Arc::new(prover.a_aux_density),
aux_assignment.clone(),
);
let b_input_density = Arc::new(prover.b_input_density); let b_input_density = Arc::new(prover.b_input_density);
let b_input_density_total = b_input_density.get_total_density(); let b_input_density_total = b_input_density.get_total_density();
let b_aux_density = Arc::new(prover.b_aux_density); let b_aux_density = Arc::new(prover.b_aux_density);
let b_aux_density_total = b_aux_density.get_total_density(); let b_aux_density_total = b_aux_density.get_total_density();
let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?; let (b_g1_inputs_source, b_g1_aux_source) =
params.get_b_g1(b_input_density_total, b_aux_density_total)?;
let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone()); let b_g1_inputs = multiexp(
let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone()); &worker,
b_g1_inputs_source,
b_input_density.clone(),
input_assignment.clone(),
);
let b_g1_aux = multiexp(
&worker,
b_g1_aux_source,
b_aux_density.clone(),
aux_assignment.clone(),
);
let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?; let (b_g2_inputs_source, b_g2_aux_source) =
params.get_b_g2(b_input_density_total, b_aux_density_total)?;
let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment);
let b_g2_inputs = multiexp(
&worker,
b_g2_inputs_source,
b_input_density,
input_assignment,
);
let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment);
if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() { if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() {
@@ -325,6 +346,6 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
Ok(Proof { Ok(Proof {
a: g_a.into_affine(), a: g_a.into_affine(),
b: g_b.into_affine(), b: g_b.into_affine(),
c: g_c.into_affine() c: g_c.into_affine(),
}) })
} }

View File

@@ -1,12 +1,13 @@
use ff::{ use ff::{
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine,
PrimeFieldRepr, ScalarEngine, SqrtField}; SqrtField,
};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use pairing::{Engine, PairingCurveAffine}; use pairing::{Engine, PairingCurveAffine};
use rand_core::RngCore;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
use rand::{Rand, Rng};
use std::num::Wrapping; use std::num::Wrapping;
const MODULUS_R: Wrapping<u32> = Wrapping(64513); const MODULUS_R: Wrapping<u32> = Wrapping(64513);
@@ -15,18 +16,16 @@ const MODULUS_R: Wrapping<u32> = Wrapping(64513);
pub struct Fr(Wrapping<u32>); pub struct Fr(Wrapping<u32>);
impl fmt::Display for Fr { impl fmt::Display for Fr {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", (self.0).0) write!(f, "{}", (self.0).0)
} }
} }
impl Rand for Fr {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fr(Wrapping(rng.gen()) % MODULUS_R)
}
}
impl Field for Fr { impl Field for Fr {
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fr(Wrapping(rng.next_u32()) % MODULUS_R)
}
fn zero() -> Self { fn zero() -> Self {
Fr(Wrapping(0)) Fr(Wrapping(0))
} }
@@ -82,9 +81,13 @@ impl SqrtField for Fr {
fn legendre(&self) -> LegendreSymbol { fn legendre(&self) -> LegendreSymbol {
// s = self^((r - 1) // 2) // s = self^((r - 1) // 2)
let s = self.pow([32256]); let s = self.pow([32256]);
if s == <Fr as Field>::zero() { LegendreSymbol::Zero } if s == <Fr as Field>::zero() {
else if s == <Fr as Field>::one() { LegendreSymbol::QuadraticResidue } LegendreSymbol::Zero
else { LegendreSymbol::QuadraticNonResidue } } else if s == <Fr as Field>::one() {
LegendreSymbol::QuadraticResidue
} else {
LegendreSymbol::QuadraticNonResidue
}
} }
fn sqrt(&self) -> Option<Self> { fn sqrt(&self) -> Option<Self> {
@@ -102,7 +105,7 @@ impl SqrtField for Fr {
let mut m = Fr::S; let mut m = Fr::S;
while t != <Fr as Field>::one() { while t != <Fr as Field>::one() {
let mut i = 1; let mut i = 1;
{ {
let mut t2i = t; let mut t2i = t;
t2i.square(); t2i.square();
@@ -145,14 +148,8 @@ impl PartialOrd for FrRepr {
} }
} }
impl Rand for FrRepr {
fn rand<R: Rng>(rng: &mut R) -> Self {
FrRepr([rng.gen()])
}
}
impl fmt::Display for FrRepr { impl fmt::Display for FrRepr {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", (self.0)[0]) write!(f, "{}", (self.0)[0])
} }
} }
@@ -266,15 +263,18 @@ impl Engine for DummyEngine {
type G2Affine = Fr; type G2Affine = Fr;
type Fq = Fr; type Fq = Fr;
type Fqe = Fr; type Fqe = Fr;
// TODO: This should be F_645131 or something. Doesn't matter for now. // TODO: This should be F_645131 or something. Doesn't matter for now.
type Fqk = Fr; type Fqk = Fr;
fn miller_loop<'a, I>(i: I) -> Self::Fqk fn miller_loop<'a, I>(i: I) -> Self::Fqk
where I: IntoIterator<Item=&'a ( where
&'a <Self::G1Affine as PairingCurveAffine>::Prepared, I: IntoIterator<
&'a <Self::G2Affine as PairingCurveAffine>::Prepared Item = &'a (
)> &'a <Self::G1Affine as PairingCurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared,
),
>,
{ {
let mut acc = <Fr as Field>::zero(); let mut acc = <Fr as Field>::zero();
@@ -288,8 +288,7 @@ impl Engine for DummyEngine {
} }
/// Perform final exponentiation of the result of a miller loop. /// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk> fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk> {
{
Some(*this) Some(*this)
} }
} }
@@ -300,6 +299,10 @@ impl CurveProjective for Fr {
type Scalar = Fr; type Scalar = Fr;
type Engine = DummyEngine; type Engine = DummyEngine;
fn random<R: RngCore>(rng: &mut R) -> Self {
<Fr as Field>::random(rng)
}
fn zero() -> Self { fn zero() -> Self {
<Fr as Field>::zero() <Fr as Field>::zero()
} }
@@ -312,9 +315,7 @@ impl CurveProjective for Fr {
<Fr as Field>::is_zero(self) <Fr as Field>::is_zero(self)
} }
fn batch_normalization(_: &mut [Self]) { fn batch_normalization(_: &mut [Self]) {}
}
fn is_normalized(&self) -> bool { fn is_normalized(&self) -> bool {
true true
@@ -336,8 +337,7 @@ impl CurveProjective for Fr {
<Fr as Field>::negate(self); <Fr as Field>::negate(self);
} }
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) {
{
let tmp = Fr::from_repr(other.into()).unwrap(); let tmp = Fr::from_repr(other.into()).unwrap();
<Fr as Field>::mul_assign(self, &tmp); <Fr as Field>::mul_assign(self, &tmp);
@@ -419,8 +419,7 @@ impl CurveAffine for Fr {
<Fr as Field>::negate(self); <Fr as Field>::negate(self);
} }
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective {
{
let mut res = *self; let mut res = *self;
let tmp = Fr::from_repr(other.into()).unwrap(); let tmp = Fr::from_repr(other.into()).unwrap();

View File

@@ -6,86 +6,82 @@ use self::dummy_engine::*;
use std::marker::PhantomData; use std::marker::PhantomData;
use ::{ use crate::{Circuit, ConstraintSystem, SynthesisError};
Circuit,
ConstraintSystem,
SynthesisError
};
use super::{ use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof};
generate_parameters,
prepare_verifying_key,
create_proof,
verify_proof
};
struct XORDemo<E: Engine> { struct XORDemo<E: Engine> {
a: Option<bool>, a: Option<bool>,
b: Option<bool>, b: Option<bool>,
_marker: PhantomData<E> _marker: PhantomData<E>,
} }
impl<E: Engine> Circuit<E> for XORDemo<E> { impl<E: Engine> Circuit<E> for XORDemo<E> {
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
self, let a_var = cs.alloc(
cs: &mut CS || "a",
) -> Result<(), SynthesisError> || {
{ if self.a.is_some() {
let a_var = cs.alloc(|| "a", || { if self.a.unwrap() {
if self.a.is_some() { Ok(E::Fr::one())
if self.a.unwrap() { } else {
Ok(E::Fr::one()) Ok(E::Fr::zero())
}
} else { } else {
Ok(E::Fr::zero()) Err(SynthesisError::AssignmentMissing)
} }
} else { },
Err(SynthesisError::AssignmentMissing) )?;
}
})?;
cs.enforce( cs.enforce(
|| "a_boolean_constraint", || "a_boolean_constraint",
|lc| lc + CS::one() - a_var, |lc| lc + CS::one() - a_var,
|lc| lc + a_var, |lc| lc + a_var,
|lc| lc |lc| lc,
); );
let b_var = cs.alloc(|| "b", || { let b_var = cs.alloc(
if self.b.is_some() { || "b",
if self.b.unwrap() { || {
Ok(E::Fr::one()) if self.b.is_some() {
if self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else { } else {
Ok(E::Fr::zero()) Err(SynthesisError::AssignmentMissing)
} }
} else { },
Err(SynthesisError::AssignmentMissing) )?;
}
})?;
cs.enforce( cs.enforce(
|| "b_boolean_constraint", || "b_boolean_constraint",
|lc| lc + CS::one() - b_var, |lc| lc + CS::one() - b_var,
|lc| lc + b_var, |lc| lc + b_var,
|lc| lc |lc| lc,
); );
let c_var = cs.alloc_input(|| "c", || { let c_var = cs.alloc_input(
if self.a.is_some() && self.b.is_some() { || "c",
if self.a.unwrap() ^ self.b.unwrap() { || {
Ok(E::Fr::one()) if self.a.is_some() && self.b.is_some() {
if self.a.unwrap() ^ self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else { } else {
Ok(E::Fr::zero()) Err(SynthesisError::AssignmentMissing)
} }
} else { },
Err(SynthesisError::AssignmentMissing) )?;
}
})?;
cs.enforce( cs.enforce(
|| "c_xor_constraint", || "c_xor_constraint",
|lc| lc + a_var + a_var, |lc| lc + a_var + a_var,
|lc| lc + b_var, |lc| lc + b_var,
|lc| lc + a_var + b_var - c_var |lc| lc + a_var + b_var - c_var,
); );
Ok(()) Ok(())
@@ -106,19 +102,10 @@ fn test_xordemo() {
let c = XORDemo::<DummyEngine> { let c = XORDemo::<DummyEngine> {
a: None, a: None,
b: None, b: None,
_marker: PhantomData _marker: PhantomData,
}; };
generate_parameters( generate_parameters(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap()
c,
g1,
g2,
alpha,
beta,
gamma,
delta,
tau
).unwrap()
}; };
// This will synthesize the constraint system: // This will synthesize the constraint system:
@@ -226,32 +213,35 @@ fn test_xordemo() {
59158 59158
*/ */
let u_i = [59158, 48317, 21767, 10402].iter().map(|e| { let u_i = [59158, 48317, 21767, 10402]
Fr::from_str(&format!("{}", e)).unwrap() .iter()
}).collect::<Vec<Fr>>(); .map(|e| Fr::from_str(&format!("{}", e)).unwrap())
let v_i = [0, 0, 60619, 30791].iter().map(|e| { .collect::<Vec<Fr>>();
Fr::from_str(&format!("{}", e)).unwrap() let v_i = [0, 0, 60619, 30791]
}).collect::<Vec<Fr>>(); .iter()
let w_i = [0, 23320, 41193, 41193].iter().map(|e| { .map(|e| Fr::from_str(&format!("{}", e)).unwrap())
Fr::from_str(&format!("{}", e)).unwrap() .collect::<Vec<Fr>>();
}).collect::<Vec<Fr>>(); let w_i = [0, 23320, 41193, 41193]
.iter()
.map(|e| Fr::from_str(&format!("{}", e)).unwrap())
.collect::<Vec<Fr>>();
for (u, a) in u_i.iter() for (u, a) in u_i.iter().zip(&params.a[..]) {
.zip(&params.a[..])
{
assert_eq!(u, a); assert_eq!(u, a);
} }
for (v, b) in v_i.iter() for (v, b) in v_i
.filter(|&&e| e != Fr::zero()) .iter()
.zip(&params.b_g1[..]) .filter(|&&e| e != Fr::zero())
.zip(&params.b_g1[..])
{ {
assert_eq!(v, b); assert_eq!(v, b);
} }
for (v, b) in v_i.iter() for (v, b) in v_i
.filter(|&&e| e != Fr::zero()) .iter()
.zip(&params.b_g2[..]) .filter(|&&e| e != Fr::zero())
.zip(&params.b_g2[..])
{ {
assert_eq!(v, b); assert_eq!(v, b);
} }
@@ -296,15 +286,10 @@ fn test_xordemo() {
let c = XORDemo { let c = XORDemo {
a: Some(true), a: Some(true),
b: Some(false), b: Some(false),
_marker: PhantomData _marker: PhantomData,
}; };
create_proof( create_proof(c, &params, r, s).unwrap()
c,
&params,
r,
s
).unwrap()
}; };
// A(x) = // A(x) =
@@ -320,7 +305,7 @@ fn test_xordemo() {
expected_a.add_assign(&u_i[0]); // a_0 = 1 expected_a.add_assign(&u_i[0]); // a_0 = 1
expected_a.add_assign(&u_i[1]); // a_1 = 1 expected_a.add_assign(&u_i[1]); // a_1 = 1
expected_a.add_assign(&u_i[2]); // a_2 = 1 expected_a.add_assign(&u_i[2]); // a_2 = 1
// a_3 = 0 // a_3 = 0
assert_eq!(proof.a, expected_a); assert_eq!(proof.a, expected_a);
} }
@@ -337,7 +322,7 @@ fn test_xordemo() {
expected_b.add_assign(&v_i[0]); // a_0 = 1 expected_b.add_assign(&v_i[0]); // a_0 = 1
expected_b.add_assign(&v_i[1]); // a_1 = 1 expected_b.add_assign(&v_i[1]); // a_1 = 1
expected_b.add_assign(&v_i[2]); // a_2 = 1 expected_b.add_assign(&v_i[2]); // a_2 = 1
// a_3 = 0 // a_3 = 0
assert_eq!(proof.b, expected_b); assert_eq!(proof.b, expected_b);
} }
@@ -378,7 +363,10 @@ fn test_xordemo() {
expected_c.add_assign(&params.l[0]); expected_c.add_assign(&params.l[0]);
// H query answer // H query answer
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() { for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739]
.iter()
.enumerate()
{
let coeff = Fr::from_str(&format!("{}", coeff)).unwrap(); let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
let mut tmp = params.h[i]; let mut tmp = params.h[i];
@@ -389,9 +377,5 @@ fn test_xordemo() {
assert_eq!(expected_c, proof.c); assert_eq!(expected_c, proof.c);
} }
assert!(verify_proof( assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap());
&pvk,
&proof,
&[Fr::one()]
).unwrap());
} }

View File

@@ -2,20 +2,11 @@ use ff::PrimeField;
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use pairing::{Engine, PairingCurveAffine}; use pairing::{Engine, PairingCurveAffine};
use super::{ use super::{PreparedVerifyingKey, Proof, VerifyingKey};
Proof,
VerifyingKey,
PreparedVerifyingKey
};
use ::{ use crate::SynthesisError;
SynthesisError
};
pub fn prepare_verifying_key<E: Engine>( pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
vk: &VerifyingKey<E>
) -> PreparedVerifyingKey<E>
{
let mut gamma = vk.gamma_g2; let mut gamma = vk.gamma_g2;
gamma.negate(); gamma.negate();
let mut delta = vk.delta_g2; let mut delta = vk.delta_g2;
@@ -25,16 +16,15 @@ pub fn prepare_verifying_key<E: Engine>(
alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2), alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2),
neg_gamma_g2: gamma.prepare(), neg_gamma_g2: gamma.prepare(),
neg_delta_g2: delta.prepare(), neg_delta_g2: delta.prepare(),
ic: vk.ic.clone() ic: vk.ic.clone(),
} }
} }
pub fn verify_proof<'a, E: Engine>( pub fn verify_proof<'a, E: Engine>(
pvk: &'a PreparedVerifyingKey<E>, pvk: &'a PreparedVerifyingKey<E>,
proof: &Proof<E>, proof: &Proof<E>,
public_inputs: &[E::Fr] public_inputs: &[E::Fr],
) -> Result<bool, SynthesisError> ) -> Result<bool, SynthesisError> {
{
if (public_inputs.len() + 1) != pvk.ic.len() { if (public_inputs.len() + 1) != pvk.ic.len() {
return Err(SynthesisError::MalformedVerifyingKey); return Err(SynthesisError::MalformedVerifyingKey);
} }
@@ -53,11 +43,14 @@ pub fn verify_proof<'a, E: Engine>(
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
// which allows us to do a single final exponentiation. // which allows us to do a single final exponentiation.
Ok(E::final_exponentiation( Ok(E::final_exponentiation(&E::miller_loop(
&E::miller_loop([ [
(&proof.a.prepare(), &proof.b.prepare()), (&proof.a.prepare(), &proof.b.prepare()),
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2), (&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
(&proof.c.prepare(), &pvk.neg_delta_g2) (&proof.c.prepare(), &pvk.neg_delta_g2),
].into_iter()) ]
).unwrap() == pvk.alpha_g1_beta_g2) .iter(),
))
.unwrap()
== pvk.alpha_g1_beta_g2)
} }

View File

@@ -1,33 +1,154 @@
extern crate ff; //! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
extern crate group; //! traits and and primitive structures, as well as basic gadget implementations
#[cfg(feature = "pairing")] //! such as booleans and number abstractions.
extern crate pairing; //!
extern crate rand; //! # Example circuit
//!
//! Say we want to write a circuit that proves we know the preimage to some hash
//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a
//! fixed length known in advance (because the circuit parameters will depend on
//! it), but can otherwise have any value. We take the following strategy:
//!
//! - Witness each bit of the preimage.
//! - Compute `hash = SHA-256d(preimage)` inside the circuit.
//! - Expose `hash` as a public input using multiscalar packing.
//!
//! ```
//! use bellman::{
//! gadgets::{
//! boolean::{AllocatedBit, Boolean},
//! multipack,
//! sha256::sha256,
//! },
//! groth16, Circuit, ConstraintSystem, SynthesisError,
//! };
//! use pairing::{bls12_381::Bls12, Engine};
//! use rand::rngs::OsRng;
//! use sha2::{Digest, Sha256};
//!
//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order.
//! fn sha256d<E: Engine, CS: ConstraintSystem<E>>(
//! mut cs: CS,
//! data: &[Boolean],
//! ) -> Result<Vec<Boolean>, SynthesisError> {
//! // Flip endianness of each input byte
//! let input: Vec<_> = data
//! .chunks(8)
//! .map(|c| c.iter().rev())
//! .flatten()
//! .cloned()
//! .collect();
//!
//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;
//!
//! // Flip endianness of each output byte
//! Ok(res
//! .chunks(8)
//! .map(|c| c.iter().rev())
//! .flatten()
//! .cloned()
//! .collect())
//! }
//!
//! struct MyCircuit {
//! /// The input to SHA-256d we are proving that we know. Set to `None` when we
//! /// are verifying a proof (and do not have the witness data).
//! preimage: Option<[u8; 80]>,
//! }
//!
//! impl<E: Engine> Circuit<E> for MyCircuit {
//! fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
//! // Compute the values for the bits of the preimage. If we are verifying a proof,
//! // we still need to create the same constraints, so we return an equivalent-size
//! // Vec of None (indicating that the value of each bit is unknown).
//! let bit_values = if let Some(preimage) = self.preimage {
//! preimage
//! .into_iter()
//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
//! .flatten()
//! .map(|b| Some(b))
//! .collect()
//! } else {
//! vec![None; 80 * 8]
//! };
//! assert_eq!(bit_values.len(), 80 * 8);
//!
//! // Witness the bits of the preimage.
//! let preimage_bits = bit_values
//! .into_iter()
//! .enumerate()
//! // Allocate each bit.
//! .map(|(i, b)| {
//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b)
//! })
//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
//! .map(|b| b.map(Boolean::from))
//! .collect::<Result<Vec<_>, _>>()?;
//!
//! // Compute hash = SHA-256d(preimage).
//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;
//!
//! // Expose the vector of 32 boolean variables as compact public inputs.
//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
//! }
//! }
//!
//! // Create parameters for our circuit. In a production deployment these would
//! // be generated securely using a multiparty computation.
//! let params = {
//! let c = MyCircuit { preimage: None };
//! groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
//! };
//!
//! // Prepare the verification key (for proof verification).
//! let pvk = groth16::prepare_verifying_key(&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 futures; // Catch documentation errors caused by code changes.
extern crate bit_vec; #![deny(intra_doc_link_resolution_failure)]
extern crate byteorder;
#[cfg(feature = "multicore")]
extern crate crossbeam;
#[cfg(feature = "multicore")]
extern crate futures_cpupool;
#[cfg(feature = "multicore")]
extern crate num_cpus;
pub mod multicore;
mod multiexp;
pub mod domain; pub mod domain;
pub mod gadgets;
#[cfg(feature = "groth16")] #[cfg(feature = "groth16")]
pub mod groth16; pub mod groth16;
pub mod multicore;
mod multiexp;
use ff::{Field, ScalarEngine}; use ff::{Field, ScalarEngine};
use std::ops::{Add, Sub};
use std::fmt;
use std::error::Error; use std::error::Error;
use std::fmt;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::{Add, Sub};
/// Computations are expressed in terms of arithmetic circuits, in particular /// Computations are expressed in terms of arithmetic circuits, in particular
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a /// rank-1 quadratic constraint systems. The `Circuit` trait represents a
@@ -35,10 +156,7 @@ use std::marker::PhantomData;
/// CRS generation and during proving. /// CRS generation and during proving.
pub trait Circuit<E: ScalarEngine> { pub trait Circuit<E: ScalarEngine> {
/// Synthesize the circuit into a rank-1 quadratic constraint system /// Synthesize the circuit into a rank-1 quadratic constraint system
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError>;
self,
cs: &mut CS
) -> Result<(), SynthesisError>;
} }
/// Represents a variable in our constraint system. /// Represents a variable in our constraint system.
@@ -64,7 +182,7 @@ impl Variable {
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Copy, Clone, PartialEq, Debug)]
pub enum Index { pub enum Index {
Input(usize), Input(usize),
Aux(usize) Aux(usize),
} }
/// This represents a linear combination of some variables, with coefficients /// This represents a linear combination of some variables, with coefficients
@@ -97,6 +215,7 @@ impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> { impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>; type Output = LinearCombination<E>;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> { fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
coeff.negate(); coeff.negate();
@@ -191,7 +310,7 @@ pub enum SynthesisError {
/// During verification, our verifying key was malformed. /// During verification, our verifying key was malformed.
MalformedVerifyingKey, MalformedVerifyingKey,
/// During CRS generation, we observed an unconstrained auxiliary variable /// During CRS generation, we observed an unconstrained auxiliary variable
UnconstrainedVariable UnconstrainedVariable,
} }
impl From<io::Error> for SynthesisError { impl From<io::Error> for SynthesisError {
@@ -203,21 +322,23 @@ impl From<io::Error> for SynthesisError {
impl Error for SynthesisError { impl Error for SynthesisError {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed", SynthesisError::AssignmentMissing => {
"an assignment for a variable could not be computed"
}
SynthesisError::DivisionByZero => "division by zero", SynthesisError::DivisionByZero => "division by zero",
SynthesisError::Unsatisfiable => "unsatisfiable constraint system", SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large", SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS", SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
SynthesisError::IoError(_) => "encountered an I/O error", SynthesisError::IoError(_) => "encountered an I/O error",
SynthesisError::MalformedVerifyingKey => "malformed verifying key", SynthesisError::MalformedVerifyingKey => "malformed verifying key",
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained" SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained",
} }
} }
} }
impl fmt::Display for SynthesisError { impl fmt::Display for SynthesisError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
if let &SynthesisError::IoError(ref e) = self { if let SynthesisError::IoError(ref e) = *self {
write!(f, "I/O error: ")?; write!(f, "I/O error: ")?;
e.fmt(f) e.fmt(f)
} else { } else {
@@ -242,40 +363,36 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// determine the assignment of the variable. The given `annotation` function is invoked /// determine the assignment of the variable. The given `annotation` function is invoked
/// in testing contexts in order to derive a unique name for this variable in the current /// in testing contexts in order to derive a unique name for this variable in the current
/// namespace. /// namespace.
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>;
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
/// Allocate a public variable in the constraint system. The provided function is used to /// Allocate a public variable in the constraint system. The provided function is used to
/// determine the assignment of the variable. /// determine the assignment of the variable.
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>;
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>;
/// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts /// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
/// in order to derive a unique name for the constraint in the current namespace. /// in order to derive a unique name for the constraint in the current namespace.
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
/// Create a new (sub)namespace and enter into it. Not intended /// Create a new (sub)namespace and enter into it. Not intended
/// for downstream use; use `namespace` instead. /// for downstream use; use `namespace` instead.
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR; where
NR: Into<String>,
N: FnOnce() -> NR;
/// Exit out of the existing namespace. Not intended for /// Exit out of the existing namespace. Not intended for
/// downstream use; use `namespace` instead. /// downstream use; use `namespace` instead.
@@ -286,11 +403,10 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
fn get_root(&mut self) -> &mut Self::Root; fn get_root(&mut self) -> &mut Self::Root;
/// Begin a namespace for this constraint system. /// Begin a namespace for this constraint system.
fn namespace<'a, NR, N>( fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root>
&'a mut self, where
name_fn: N NR: Into<String>,
) -> Namespace<'a, E, Self::Root> N: FnOnce() -> NR,
where NR: Into<String>, N: FnOnce() -> NR
{ {
self.get_root().push_namespace(name_fn); self.get_root().push_namespace(name_fn);
@@ -300,7 +416,7 @@ pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// This is a "namespaced" constraint system which borrows a constraint system (pushing /// This is a "namespaced" constraint system which borrows a constraint system (pushing
/// a namespace context) and, when dropped, pops out of the namespace context. /// a namespace context) and, when dropped, pops out of the namespace context.
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>); pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E>>(&'a mut CS, PhantomData<E>);
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> { impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
type Root = CS::Root; type Root = CS::Root;
@@ -309,37 +425,31 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
CS::one() CS::one()
} }
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.0.alloc(annotation, f) self.0.alloc(annotation, f)
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
self.0.alloc_input(annotation, f) self.0.alloc_input(annotation, f)
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
self.0.enforce(annotation, a, b, c) self.0.enforce(annotation, a, b, c)
} }
@@ -349,18 +459,18 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Name
// never a root constraint system. // never a root constraint system.
fn push_namespace<NR, N>(&mut self, _: N) fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
panic!("only the root's push_namespace should be called"); panic!("only the root's push_namespace should be called");
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
panic!("only the root's pop_namespace should be called"); panic!("only the root's pop_namespace should be called");
} }
fn get_root(&mut self) -> &mut Self::Root fn get_root(&mut self) -> &mut Self::Root {
{
self.0.get_root() self.0.get_root()
} }
} }
@@ -380,54 +490,48 @@ impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs
CS::one() CS::one()
} }
fn alloc<F, A, AR>( fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
(**self).alloc(annotation, f) (**self).alloc(annotation, f)
} }
fn alloc_input<F, A, AR>( fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
&mut self, where
annotation: A, F: FnOnce() -> Result<E::Fr, SynthesisError>,
f: F A: FnOnce() -> AR,
) -> Result<Variable, SynthesisError> AR: Into<String>,
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{ {
(**self).alloc_input(annotation, f) (**self).alloc_input(annotation, f)
} }
fn enforce<A, AR, LA, LB, LC>( fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
&mut self, where
annotation: A, A: FnOnce() -> AR,
a: LA, AR: Into<String>,
b: LB, LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
c: LC LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
) LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>
{ {
(**self).enforce(annotation, a, b, c) (**self).enforce(annotation, a, b, c)
} }
fn push_namespace<NR, N>(&mut self, name_fn: N) fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR where
NR: Into<String>,
N: FnOnce() -> NR,
{ {
(**self).push_namespace(name_fn) (**self).push_namespace(name_fn)
} }
fn pop_namespace(&mut self) fn pop_namespace(&mut self) {
{
(**self).pop_namespace() (**self).pop_namespace()
} }
fn get_root(&mut self) -> &mut Self::Root fn get_root(&mut self) -> &mut Self::Root {
{
(**self).get_root() (**self).get_root()
} }
} }

View File

@@ -1,20 +1,21 @@
//! This is an interface for dealing with the kinds of //! An interface for dealing with the kinds of parallel computations involved in
//! parallel computations involved in bellman. It's //! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and
//! currently just a thin wrapper around CpuPool and //! [`crossbeam`] but may be extended in the future to allow for various
//! crossbeam but may be extended in the future to //! parallelism strategies.
//! allow for various parallelism strategies. //!
//! [`CpuPool`]: futures_cpupool::CpuPool
#[cfg(feature = "multicore")] #[cfg(feature = "multicore")]
mod implementation { mod implementation {
use num_cpus; use crossbeam::{self, thread::Scope};
use futures::{Future, IntoFuture, Poll}; use futures::{Future, IntoFuture, Poll};
use futures_cpupool::{CpuPool, CpuFuture}; use futures_cpupool::{CpuFuture, CpuPool};
use crossbeam::{self, Scope}; use num_cpus;
#[derive(Clone)] #[derive(Clone)]
pub struct Worker { pub struct Worker {
cpus: usize, cpus: usize,
pool: CpuPool pool: CpuPool,
} }
impl Worker { impl Worker {
@@ -23,8 +24,8 @@ mod implementation {
// CPUs configured. // CPUs configured.
pub(crate) fn new_with_cpus(cpus: usize) -> Worker { pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
Worker { Worker {
cpus: cpus, cpus,
pool: CpuPool::new(cpus) pool: CpuPool::new(cpus),
} }
} }
@@ -36,26 +37,22 @@ mod implementation {
log2_floor(self.cpus) log2_floor(self.cpus)
} }
pub fn compute<F, R>( pub fn compute<F, R>(&self, f: F) -> WorkerFuture<R::Item, R::Error>
&self, f: F where
) -> WorkerFuture<R::Item, R::Error> F: FnOnce() -> R + Send + 'static,
where F: FnOnce() -> R + Send + 'static, R: IntoFuture + 'static,
R: IntoFuture + 'static, R::Future: Send + 'static,
R::Future: Send + 'static, R::Item: Send + 'static,
R::Item: Send + 'static, R::Error: Send + 'static,
R::Error: Send + 'static
{ {
WorkerFuture { WorkerFuture {
future: self.pool.spawn_fn(f) future: self.pool.spawn_fn(f),
} }
} }
pub fn scope<'a, F, R>( pub fn scope<'a, F, R>(&self, elements: usize, f: F) -> R
&self, where
elements: usize, F: FnOnce(&Scope<'a>, usize) -> R,
f: F
) -> R
where F: FnOnce(&Scope<'a>, usize) -> R
{ {
let chunk_size = if elements < self.cpus { let chunk_size = if elements < self.cpus {
1 1
@@ -63,22 +60,21 @@ mod implementation {
elements / self.cpus elements / self.cpus
}; };
crossbeam::scope(|scope| { // TODO: Handle case where threads fail
f(scope, chunk_size) crossbeam::scope(|scope| f(scope, chunk_size))
}) .expect("Threads aren't allowed to fail yet")
} }
} }
pub struct WorkerFuture<T, E> { pub struct WorkerFuture<T, E> {
future: CpuFuture<T, E> future: CpuFuture<T, E>,
} }
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> { impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
type Item = T; type Item = T;
type Error = E; type Error = E;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
{
self.future.poll() self.future.poll()
} }
} }
@@ -88,7 +84,7 @@ mod implementation {
let mut pow = 0; let mut pow = 0;
while (1 << (pow+1)) <= num { while (1 << (pow + 1)) <= num {
pow += 1; pow += 1;
} }
@@ -159,8 +155,8 @@ mod implementation {
pub struct DummyScope; pub struct DummyScope;
impl DummyScope { impl DummyScope {
pub fn spawn<F: FnOnce()>(&self, f: F) { pub fn spawn<F: FnOnce(&DummyScope)>(&self, f: F) {
f(); f(self);
} }
} }
} }

View File

@@ -1,11 +1,11 @@
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use group::{CurveAffine, CurveProjective};
use std::sync::Arc;
use std::io;
use bit_vec::{self, BitVec};
use std::iter;
use futures::{Future};
use super::multicore::Worker; use super::multicore::Worker;
use bit_vec::{self, BitVec};
use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine};
use futures::Future;
use group::{CurveAffine, CurveProjective};
use std::io;
use std::iter;
use std::sync::Arc;
use super::SynthesisError; use super::SynthesisError;
@@ -19,7 +19,10 @@ pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
/// A source of bases, like an iterator. /// A source of bases, like an iterator.
pub trait Source<G: CurveAffine> { pub trait Source<G: CurveAffine> {
/// Parses the element from the source. Fails if the point is at infinity. /// Parses the element from the source. Fails if the point is at infinity.
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError>; fn add_assign_mixed(
&mut self,
to: &mut <G as CurveAffine>::Projective,
) -> Result<(), SynthesisError>;
/// Skips `amt` elements from the source, avoiding deserialization. /// Skips `amt` elements from the source, avoiding deserialization.
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
@@ -34,13 +37,20 @@ impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
} }
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) { impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError> { fn add_assign_mixed(
&mut self,
to: &mut <G as CurveAffine>::Projective,
) -> Result<(), SynthesisError> {
if self.0.len() <= self.1 { if self.0.len() <= self.1 {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into()); return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"expected more bases from source",
)
.into());
} }
if self.0[self.1].is_zero() { if self.0[self.1].is_zero() {
return Err(SynthesisError::UnexpectedIdentity) return Err(SynthesisError::UnexpectedIdentity);
} }
to.add_assign_mixed(&self.0[self.1]); to.add_assign_mixed(&self.0[self.1]);
@@ -52,7 +62,11 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
if self.0.len() <= self.1 { if self.0.len() <= self.1 {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into()); return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"expected more bases from source",
)
.into());
} }
self.1 += amt; self.1 += amt;
@@ -63,7 +77,7 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
pub trait QueryDensity { pub trait QueryDensity {
/// Returns whether the base exists. /// Returns whether the base exists.
type Iter: Iterator<Item=bool>; type Iter: Iterator<Item = bool>;
fn iter(self) -> Self::Iter; fn iter(self) -> Self::Iter;
fn get_query_size(self) -> Option<usize>; fn get_query_size(self) -> Option<usize>;
@@ -92,7 +106,7 @@ impl<'a> QueryDensity for &'a FullDensity {
pub struct DensityTracker { pub struct DensityTracker {
bv: BitVec, bv: BitVec,
total_density: usize total_density: usize,
} }
impl<'a> QueryDensity for &'a DensityTracker { impl<'a> QueryDensity for &'a DensityTracker {
@@ -111,7 +125,7 @@ impl DensityTracker {
pub fn new() -> DensityTracker { pub fn new() -> DensityTracker {
DensityTracker { DensityTracker {
bv: BitVec::new(), bv: BitVec::new(),
total_density: 0 total_density: 0,
} }
} }
@@ -138,12 +152,13 @@ fn multiexp_inner<Q, D, G, S>(
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>, exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
mut skip: u32, mut skip: u32,
c: u32, c: u32,
handle_trivial: bool handle_trivial: bool,
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>> ) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
where for<'a> &'a Q: QueryDensity, where
D: Send + Sync + 'static + Clone + AsRef<Q>, for<'a> &'a Q: QueryDensity,
G: CurveAffine, D: Send + Sync + 'static + Clone + AsRef<Q>,
S: SourceBuilder<G> G: CurveAffine,
S: SourceBuilder<G>,
{ {
// Perform this region of the multiexp // Perform this region of the multiexp
let this = { let this = {
@@ -212,16 +227,24 @@ fn multiexp_inner<Q, D, G, S>(
// There's another region more significant. Calculate and join it with // There's another region more significant. Calculate and join it with
// this region recursively. // this region recursively.
Box::new( Box::new(
this.join(multiexp_inner(pool, bases, density_map, exponents, skip, c, false)) this.join(multiexp_inner(
.map(move |(this, mut higher)| { pool,
for _ in 0..c { bases,
higher.double(); density_map,
} exponents,
skip,
c,
false,
))
.map(move |(this, mut higher)| {
for _ in 0..c {
higher.double();
}
higher.add_assign(&this); higher.add_assign(&this);
higher higher
}) }),
) )
} }
} }
@@ -232,12 +255,13 @@ pub fn multiexp<Q, D, G, S>(
pool: &Worker, pool: &Worker,
bases: S, bases: S,
density_map: D, density_map: D,
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>> exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>> ) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
where for<'a> &'a Q: QueryDensity, where
D: Send + Sync + 'static + Clone + AsRef<Q>, for<'a> &'a Q: QueryDensity,
G: CurveAffine, D: Send + Sync + 'static + Clone + AsRef<Q>,
S: SourceBuilder<G> G: CurveAffine,
S: SourceBuilder<G>,
{ {
let c = if exponents.len() < 32 { let c = if exponents.len() < 32 {
3u32 3u32
@@ -260,9 +284,8 @@ pub fn multiexp<Q, D, G, S>(
fn test_with_bls12() { fn test_with_bls12() {
fn naive_multiexp<G: CurveAffine>( fn naive_multiexp<G: CurveAffine>(
bases: Arc<Vec<G>>, bases: Arc<Vec<G>>,
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>> exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
) -> G::Projective ) -> G::Projective {
{
assert_eq!(bases.len(), exponents.len()); assert_eq!(bases.len(), exponents.len());
let mut acc = G::Projective::zero(); let mut acc = G::Projective::zero();
@@ -274,25 +297,28 @@ fn test_with_bls12() {
acc acc
} }
use rand::{self, Rand};
use pairing::{bls12_381::Bls12, Engine}; use pairing::{bls12_381::Bls12, Engine};
use rand;
const SAMPLES: usize = 1 << 14; const SAMPLES: usize = 1 << 14;
let rng = &mut rand::thread_rng(); let rng = &mut rand::thread_rng();
let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as ScalarEngine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>()); let v = Arc::new(
let g = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::G1::rand(rng).into_affine()).collect::<Vec<_>>()); (0..SAMPLES)
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng).into_repr())
.collect::<Vec<_>>(),
);
let g = Arc::new(
(0..SAMPLES)
.map(|_| <Bls12 as Engine>::G1::random(rng).into_affine())
.collect::<Vec<_>>(),
);
let naive = naive_multiexp(g.clone(), v.clone()); let naive = naive_multiexp(g.clone(), v.clone());
let pool = Worker::new(); let pool = Worker::new();
let fast = multiexp( let fast = multiexp(&pool, (g, 0), FullDensity, v).wait().unwrap();
&pool,
(g, 0),
FullDensity,
v
).wait().unwrap();
assert_eq!(naive, fast); assert_eq!(naive, fast);
} }

View File

@@ -1,44 +1,29 @@
extern crate bellman;
extern crate ff;
extern crate pairing;
extern crate rand;
// For randomness (during paramgen and proof generation) // For randomness (during paramgen and proof generation)
use rand::{thread_rng, Rng}; use rand::thread_rng;
// For benchmarking // For benchmarking
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
// Bring in some tools for using pairing-friendly curves // Bring in some tools for using pairing-friendly curves
use ff::Field; use ff::{Field, ScalarEngine};
use pairing::Engine; use pairing::Engine;
// We're going to use the BLS12-381 pairing-friendly elliptic curve. // We're going to use the BLS12-381 pairing-friendly elliptic curve.
use pairing::bls12_381::{ use pairing::bls12_381::Bls12;
Bls12
};
// We'll use these interfaces to construct our circuit. // We'll use these interfaces to construct our circuit.
use bellman::{ use bellman::{Circuit, ConstraintSystem, SynthesisError};
Circuit,
ConstraintSystem,
SynthesisError
};
// We're going to use the Groth16 proving system. // We're going to use the Groth16 proving system.
use bellman::groth16::{ use bellman::groth16::{
Proof, create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, Proof,
generate_random_parameters,
prepare_verifying_key,
create_random_proof,
verify_proof,
}; };
const MIMC_ROUNDS: usize = 322; const MIMC_ROUNDS: usize = 322;
/// This is an implementation of MiMC, specifically a /// This is an implementation of MiMC, specifically a
/// variant named `LongsightF322p3` for BLS12-381. /// variant named `LongsightF322p3` for BLS12-381.
/// See http://eprint.iacr.org/2016/492 for more /// See http://eprint.iacr.org/2016/492 for more
/// information about this construction. /// information about this construction.
/// ///
/// ``` /// ```
@@ -49,12 +34,7 @@ const MIMC_ROUNDS: usize = 322;
/// return xL /// return xL
/// } /// }
/// ``` /// ```
fn mimc<E: Engine>( fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr {
mut xl: E::Fr,
mut xr: E::Fr,
constants: &[E::Fr]
) -> E::Fr
{
assert_eq!(constants.len(), MIMC_ROUNDS); assert_eq!(constants.len(), MIMC_ROUNDS);
for i in 0..MIMC_ROUNDS { for i in 0..MIMC_ROUNDS {
@@ -76,80 +56,81 @@ fn mimc<E: Engine>(
struct MiMCDemo<'a, E: Engine> { struct MiMCDemo<'a, E: Engine> {
xl: Option<E::Fr>, xl: Option<E::Fr>,
xr: Option<E::Fr>, xr: Option<E::Fr>,
constants: &'a [E::Fr] constants: &'a [E::Fr],
} }
/// Our demo circuit implements this `Circuit` trait which /// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to /// is used during paramgen and proving in order to
/// synthesize the constraint system. /// synthesize the constraint system.
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> { impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
assert_eq!(self.constants.len(), MIMC_ROUNDS); assert_eq!(self.constants.len(), MIMC_ROUNDS);
// Allocate the first component of the preimage. // Allocate the first component of the preimage.
let mut xl_value = self.xl; let mut xl_value = self.xl;
let mut xl = cs.alloc(|| "preimage xl", || { let mut xl = cs.alloc(
xl_value.ok_or(SynthesisError::AssignmentMissing) || "preimage xl",
})?; || xl_value.ok_or(SynthesisError::AssignmentMissing),
)?;
// Allocate the second component of the preimage. // Allocate the second component of the preimage.
let mut xr_value = self.xr; let mut xr_value = self.xr;
let mut xr = cs.alloc(|| "preimage xr", || { let mut xr = cs.alloc(
xr_value.ok_or(SynthesisError::AssignmentMissing) || "preimage xr",
})?; || xr_value.ok_or(SynthesisError::AssignmentMissing),
)?;
for i in 0..MIMC_ROUNDS { for i in 0..MIMC_ROUNDS {
// xL, xR := xR + (xL + Ci)^3, xL // xL, xR := xR + (xL + Ci)^3, xL
let cs = &mut cs.namespace(|| format!("round {}", i)); let cs = &mut cs.namespace(|| format!("round {}", i));
// tmp = (xL + Ci)^2 // tmp = (xL + Ci)^2
let mut tmp_value = xl_value.map(|mut e| { let tmp_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]); e.add_assign(&self.constants[i]);
e.square(); e.square();
e e
}); });
let mut tmp = cs.alloc(|| "tmp", || { let tmp = cs.alloc(
tmp_value.ok_or(SynthesisError::AssignmentMissing) || "tmp",
})?; || tmp_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce( cs.enforce(
|| "tmp = (xL + Ci)^2", || "tmp = (xL + Ci)^2",
|lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + tmp |lc| lc + tmp,
); );
// new_xL = xR + (xL + Ci)^3 // new_xL = xR + (xL + Ci)^3
// new_xL = xR + tmp * (xL + Ci) // new_xL = xR + tmp * (xL + Ci)
// new_xL - xR = tmp * (xL + Ci) // new_xL - xR = tmp * (xL + Ci)
let mut new_xl_value = xl_value.map(|mut e| { let new_xl_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]); e.add_assign(&self.constants[i]);
e.mul_assign(&tmp_value.unwrap()); e.mul_assign(&tmp_value.unwrap());
e.add_assign(&xr_value.unwrap()); e.add_assign(&xr_value.unwrap());
e e
}); });
let mut new_xl = if i == (MIMC_ROUNDS-1) { let new_xl = if i == (MIMC_ROUNDS - 1) {
// This is the last round, xL is our image and so // This is the last round, xL is our image and so
// we allocate a public input. // we allocate a public input.
cs.alloc_input(|| "image", || { cs.alloc_input(
new_xl_value.ok_or(SynthesisError::AssignmentMissing) || "image",
})? || new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
} else { } else {
cs.alloc(|| "new_xl", || { cs.alloc(
new_xl_value.ok_or(SynthesisError::AssignmentMissing) || "new_xl",
})? || new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
}; };
cs.enforce( cs.enforce(
|| "new_xL = xR + (xL + Ci)^3", || "new_xL = xR + (xL + Ci)^3",
|lc| lc + tmp, |lc| lc + tmp,
|lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + new_xl - xr |lc| lc + new_xl - xr,
); );
// xR = xL // xR = xL
@@ -172,7 +153,9 @@ fn test_mimc() {
let rng = &mut thread_rng(); let rng = &mut thread_rng();
// Generate the MiMC round constants // Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>(); let constants = (0..MIMC_ROUNDS)
.map(|_| <Bls12 as ScalarEngine>::Fr::random(rng))
.collect::<Vec<_>>();
println!("Creating parameters..."); println!("Creating parameters...");
@@ -181,7 +164,7 @@ fn test_mimc() {
let c = MiMCDemo::<Bls12> { let c = MiMCDemo::<Bls12> {
xl: None, xl: None,
xr: None, xr: None,
constants: &constants constants: &constants,
}; };
generate_random_parameters(c, rng).unwrap() generate_random_parameters(c, rng).unwrap()
@@ -203,8 +186,8 @@ fn test_mimc() {
for _ in 0..SAMPLES { for _ in 0..SAMPLES {
// Generate a random preimage and compute the image // Generate a random preimage and compute the image
let xl = rng.gen(); let xl = <Bls12 as ScalarEngine>::Fr::random(rng);
let xr = rng.gen(); let xr = <Bls12 as ScalarEngine>::Fr::random(rng);
let image = mimc::<Bls12>(xl, xr, &constants); let image = mimc::<Bls12>(xl, xr, &constants);
proof_vec.truncate(0); proof_vec.truncate(0);
@@ -216,7 +199,7 @@ fn test_mimc() {
let c = MiMCDemo { let c = MiMCDemo {
xl: Some(xl), xl: Some(xl),
xr: Some(xr), xr: Some(xr),
constants: &constants constants: &constants,
}; };
// Create a groth16 proof with our parameters. // Create a groth16 proof with our parameters.
@@ -230,20 +213,16 @@ fn test_mimc() {
let start = Instant::now(); let start = Instant::now();
let proof = Proof::read(&proof_vec[..]).unwrap(); let proof = Proof::read(&proof_vec[..]).unwrap();
// Check the proof // Check the proof
assert!(verify_proof( assert!(verify_proof(&pvk, &proof, &[image]).unwrap());
&pvk,
&proof,
&[image]
).unwrap());
total_verifying += start.elapsed(); total_verifying += start.elapsed();
} }
let proving_avg = total_proving / SAMPLES; let proving_avg = total_proving / SAMPLES;
let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 let proving_avg =
+ (proving_avg.as_secs() as f64); proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64);
let verifying_avg = total_verifying / SAMPLES; let verifying_avg = total_verifying / SAMPLES;
let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 let verifying_avg =
+ (verifying_avg.as_secs() as f64); verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64);
println!("Average proving time: {:?} seconds", proving_avg); println!("Average proving time: {:?} seconds", proving_avg);
println!("Average verifying time: {:?} seconds", verifying_avg); println!("Average verifying time: {:?} seconds", verifying_avg);

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"
rand = "0.4" ff_derive = { version = "^0.4.1", path = "ff_derive", optional = true }
ff_derive = { version = "0.3.0", path = "ff_derive", optional = true } rand_core = "0.5"
[features] [features]
default = [] default = []
derive = ["ff_derive"] derive = ["ff_derive"]
[badges]
maintenance = { status = "actively-developed" }

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;
@@ -52,13 +46,8 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut gen = proc_macro2::TokenStream::new(); let mut gen = proc_macro2::TokenStream::new();
let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt( let (constants_impl, sqrt_impl) =
&ast.ident, prime_field_constants_and_sqrt(&ast.ident, &repr_ident, modulus, limbs, generator);
&repr_ident,
modulus,
limbs,
generator,
);
gen.extend(constants_impl); gen.extend(constants_impl);
gen.extend(prime_field_repr_impl(&repr_ident, limbs)); gen.extend(prime_field_repr_impl(&repr_ident, limbs));
@@ -96,10 +85,10 @@ fn fetch_wrapped_ident(body: &syn::Data) -> Option<syn::Ident> {
/// Fetch an attribute string from the derived struct. /// Fetch an attribute string from the derived struct.
fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> { fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option<String> {
for attr in attrs { for attr in attrs {
if let Some(meta) = attr.interpret_meta() { if let Ok(meta) = attr.parse_meta() {
match meta { match meta {
syn::Meta::NameValue(nv) => { syn::Meta::NameValue(nv) => {
if nv.ident.to_string() == name { if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) {
match nv.lit { match nv.lit {
syn::Lit::Str(ref s) => return Some(s.value()), syn::Lit::Str(ref s) => return Some(s.value()),
_ => { _ => {
@@ -127,27 +116,20 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
impl ::std::fmt::Debug for #repr impl ::std::fmt::Debug for #repr
{ {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
try!(write!(f, "0x")); write!(f, "0x")?;
for i in self.0.iter().rev() { for i in self.0.iter().rev() {
try!(write!(f, "{:016x}", *i)); write!(f, "{:016x}", *i)?;
} }
Ok(()) Ok(())
} }
} }
impl ::rand::Rand for #repr {
#[inline(always)]
fn rand<R: ::rand::Rng>(rng: &mut R) -> Self {
#repr(rng.gen())
}
}
impl ::std::fmt::Display for #repr { impl ::std::fmt::Display for #repr {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
try!(write!(f, "0x")); write!(f, "0x")?;
for i in self.0.iter().rev() { for i in self.0.iter().rev() {
try!(write!(f, "{:016x}", *i)); write!(f, "{:016x}", *i)?;
} }
Ok(()) Ok(())
@@ -366,7 +348,8 @@ fn biguint_num_bits(mut v: BigUint) -> u32 {
fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
let mut ret = BigUint::one(); let mut ret = BigUint::one();
for i in exp.to_bytes_be() for i in exp
.to_bytes_be()
.into_iter() .into_iter()
.flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd())) .flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd()))
{ {
@@ -387,11 +370,13 @@ fn test_exp() {
&BigUint::from_str("5489673498567349856734895").unwrap(), &BigUint::from_str("5489673498567349856734895").unwrap(),
&BigUint::from_str( &BigUint::from_str(
"52435875175126190479447740508185965837690552500527637822603658699938581184513" "52435875175126190479447740508185965837690552500527637822603658699938581184513"
).unwrap() )
.unwrap()
), ),
BigUint::from_str( BigUint::from_str(
"4371221214068404307866768905142520595925044802278091865033317963560480051536" "4371221214068404307866768905142520595925044802278091865033317963560480051536"
).unwrap() )
.unwrap()
); );
} }
@@ -430,7 +415,7 @@ fn prime_field_constants_and_sqrt(
let mod_minus_1_over_2 = let mod_minus_1_over_2 =
biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs); biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs);
let legendre_impl = quote!{ let legendre_impl = quote! {
fn legendre(&self) -> ::ff::LegendreSymbol { fn legendre(&self) -> ::ff::LegendreSymbol {
// s = self^((modulus - 1) // 2) // s = self^((modulus - 1) // 2)
let s = self.pow(#mod_minus_1_over_2); let s = self.pow(#mod_minus_1_over_2);
@@ -452,7 +437,7 @@ fn prime_field_constants_and_sqrt(
// Compute -R as (m - r) // Compute -R as (m - r)
let rneg = biguint_to_u64_vec(&modulus - &r, limbs); let rneg = biguint_to_u64_vec(&modulus - &r, limbs);
quote!{ quote! {
impl ::ff::SqrtField for #name { impl ::ff::SqrtField for #name {
#legendre_impl #legendre_impl
@@ -479,7 +464,7 @@ fn prime_field_constants_and_sqrt(
let t_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs); let t_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs);
let t = biguint_to_u64_vec(t.clone(), limbs); let t = biguint_to_u64_vec(t.clone(), limbs);
quote!{ quote! {
impl ::ff::SqrtField for #name { impl ::ff::SqrtField for #name {
#legendre_impl #legendre_impl
@@ -526,7 +511,7 @@ fn prime_field_constants_and_sqrt(
} }
} }
} else { } else {
quote!{} quote! {}
}; };
// Compute R^2 mod m // Compute R^2 mod m
@@ -543,36 +528,39 @@ fn prime_field_constants_and_sqrt(
} }
inv = inv.wrapping_neg(); inv = inv.wrapping_neg();
(quote! { (
/// This is the modulus m of the prime field quote! {
const MODULUS: #repr = #repr([#(#modulus,)*]); /// This is the modulus m of the prime field
const MODULUS: #repr = #repr([#(#modulus,)*]);
/// The number of bits needed to represent the modulus. /// The number of bits needed to represent the modulus.
const MODULUS_BITS: u32 = #modulus_num_bits; const MODULUS_BITS: u32 = #modulus_num_bits;
/// The number of bits that must be shaved from the beginning of /// The number of bits that must be shaved from the beginning of
/// the representation when randomly sampling. /// the representation when randomly sampling.
const REPR_SHAVE_BITS: u32 = #repr_shave_bits; const REPR_SHAVE_BITS: u32 = #repr_shave_bits;
/// 2^{limbs*64} mod m /// 2^{limbs*64} mod m
const R: #repr = #repr(#r); const R: #repr = #repr(#r);
/// 2^{limbs*64*2} mod m /// 2^{limbs*64*2} mod m
const R2: #repr = #repr(#r2); const R2: #repr = #repr(#r2);
/// -(m^{-1} mod m) mod m /// -(m^{-1} mod m) mod m
const INV: u64 = #inv; const INV: u64 = #inv;
/// Multiplicative generator of `MODULUS` - 1 order, also quadratic /// Multiplicative generator of `MODULUS` - 1 order, also quadratic
/// nonresidue. /// nonresidue.
const GENERATOR: #repr = #repr(#generator); const GENERATOR: #repr = #repr(#generator);
/// 2^s * t = MODULUS - 1 with t odd /// 2^s * t = MODULUS - 1 with t odd
const S: u32 = #s; const S: u32 = #s;
/// 2^s root of unity computed by GENERATOR^t /// 2^s root of unity computed by GENERATOR^t
const ROOT_OF_UNITY: #repr = #repr(#root_of_unity); const ROOT_OF_UNITY: #repr = #repr(#root_of_unity);
}, sqrt_impl) },
sqrt_impl,
)
} }
/// Implement PrimeField for the derived type. /// Implement PrimeField for the derived type.
@@ -592,9 +580,9 @@ fn prime_field_impl(
mont_paramlist.append_separated( mont_paramlist.append_separated(
(0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| { (0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| {
if i != 0 { if i != 0 {
quote!{mut #x: u64} quote! {mut #x: u64}
} else { } else {
quote!{#x: u64} quote! {#x: u64}
} }
}), }),
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
@@ -607,7 +595,7 @@ fn prime_field_impl(
for i in 0..limbs { for i in 0..limbs {
{ {
let temp = get_temp(i); let temp = get_temp(i);
gen.extend(quote!{ gen.extend(quote! {
let k = #temp.wrapping_mul(INV); let k = #temp.wrapping_mul(INV);
let mut carry = 0; let mut carry = 0;
::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry); ::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry);
@@ -616,7 +604,7 @@ fn prime_field_impl(
for j in 1..limbs { for j in 1..limbs {
let temp = get_temp(i + j); let temp = get_temp(i + j);
gen.extend(quote!{ gen.extend(quote! {
#temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry); #temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry);
}); });
} }
@@ -624,17 +612,17 @@ fn prime_field_impl(
let temp = get_temp(i + limbs); let temp = get_temp(i + limbs);
if i == 0 { if i == 0 {
gen.extend(quote!{ gen.extend(quote! {
#temp = ::ff::adc(#temp, 0, &mut carry); #temp = ::ff::adc(#temp, 0, &mut carry);
}); });
} else { } else {
gen.extend(quote!{ gen.extend(quote! {
#temp = ::ff::adc(#temp, carry2, &mut carry); #temp = ::ff::adc(#temp, carry2, &mut carry);
}); });
} }
if i != (limbs - 1) { if i != (limbs - 1) {
gen.extend(quote!{ gen.extend(quote! {
let carry2 = carry; let carry2 = carry;
}); });
} }
@@ -643,7 +631,7 @@ fn prime_field_impl(
for i in 0..limbs { for i in 0..limbs {
let temp = get_temp(limbs + i); let temp = get_temp(limbs + i);
gen.extend(quote!{ gen.extend(quote! {
(self.0).0[#i] = #temp; (self.0).0[#i] = #temp;
}); });
} }
@@ -655,14 +643,14 @@ fn prime_field_impl(
let mut gen = proc_macro2::TokenStream::new(); let mut gen = proc_macro2::TokenStream::new();
for i in 0..(limbs - 1) { for i in 0..(limbs - 1) {
gen.extend(quote!{ gen.extend(quote! {
let mut carry = 0; let mut carry = 0;
}); });
for j in (i + 1)..limbs { for j in (i + 1)..limbs {
let temp = get_temp(i + j); let temp = get_temp(i + j);
if i == 0 { if i == 0 {
gen.extend(quote!{ gen.extend(quote! {
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry); let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry);
}); });
} else { } else {
@@ -674,7 +662,7 @@ fn prime_field_impl(
let temp = get_temp(i + limbs); let temp = get_temp(i + limbs);
gen.extend(quote!{ gen.extend(quote! {
let #temp = carry; let #temp = carry;
}); });
} }
@@ -684,21 +672,21 @@ fn prime_field_impl(
let temp1 = get_temp(limbs * 2 - i - 1); let temp1 = get_temp(limbs * 2 - i - 1);
if i == 1 { if i == 1 {
gen.extend(quote!{ gen.extend(quote! {
let #temp0 = #temp1 >> 63; let #temp0 = #temp1 >> 63;
}); });
} else if i == (limbs * 2 - 1) { } else if i == (limbs * 2 - 1) {
gen.extend(quote!{ gen.extend(quote! {
let #temp0 = #temp0 << 1; let #temp0 = #temp0 << 1;
}); });
} else { } else {
gen.extend(quote!{ gen.extend(quote! {
let #temp0 = (#temp0 << 1) | (#temp1 >> 63); let #temp0 = (#temp0 << 1) | (#temp1 >> 63);
}); });
} }
} }
gen.extend(quote!{ gen.extend(quote! {
let mut carry = 0; let mut carry = 0;
}); });
@@ -706,7 +694,7 @@ fn prime_field_impl(
let temp0 = get_temp(i * 2); let temp0 = get_temp(i * 2);
let temp1 = get_temp(i * 2 + 1); let temp1 = get_temp(i * 2 + 1);
if i == 0 { if i == 0 {
gen.extend(quote!{ gen.extend(quote! {
let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry); let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry);
}); });
} else { } else {
@@ -715,7 +703,7 @@ fn prime_field_impl(
}); });
} }
gen.extend(quote!{ gen.extend(quote! {
let #temp1 = ::ff::adc(#temp1, 0, &mut carry); let #temp1 = ::ff::adc(#temp1, 0, &mut carry);
}); });
} }
@@ -726,7 +714,7 @@ fn prime_field_impl(
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
); );
gen.extend(quote!{ gen.extend(quote! {
self.mont_reduce(#mont_calling); self.mont_reduce(#mont_calling);
}); });
@@ -741,7 +729,7 @@ fn prime_field_impl(
let mut gen = proc_macro2::TokenStream::new(); let mut gen = proc_macro2::TokenStream::new();
for i in 0..limbs { for i in 0..limbs {
gen.extend(quote!{ gen.extend(quote! {
let mut carry = 0; let mut carry = 0;
}); });
@@ -749,7 +737,7 @@ fn prime_field_impl(
let temp = get_temp(i + j); let temp = get_temp(i + j);
if i == 0 { if i == 0 {
gen.extend(quote!{ gen.extend(quote! {
let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry); let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry);
}); });
} else { } else {
@@ -761,7 +749,7 @@ fn prime_field_impl(
let temp = get_temp(i + limbs); let temp = get_temp(i + limbs);
gen.extend(quote!{ gen.extend(quote! {
let #temp = carry; let #temp = carry;
}); });
} }
@@ -772,29 +760,29 @@ fn prime_field_impl(
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
); );
gen.extend(quote!{ gen.extend(quote! {
self.mont_reduce(#mont_calling); self.mont_reduce(#mont_calling);
}); });
gen gen
} }
let squaring_impl = sqr_impl(quote!{self}, limbs); let squaring_impl = sqr_impl(quote! {self}, limbs);
let multiply_impl = mul_impl(quote!{self}, quote!{other}, limbs); let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs);
let montgomery_impl = mont_impl(limbs); let montgomery_impl = mont_impl(limbs);
// (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ... // (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ...
let mut into_repr_params = proc_macro2::TokenStream::new(); let mut into_repr_params = proc_macro2::TokenStream::new();
into_repr_params.append_separated( into_repr_params.append_separated(
(0..limbs) (0..limbs)
.map(|i| quote!{ (self.0).0[#i] }) .map(|i| quote! { (self.0).0[#i] })
.chain((0..limbs).map(|_| quote!{0})), .chain((0..limbs).map(|_| quote! {0})),
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
); );
let top_limb_index = limbs - 1; let top_limb_index = limbs - 1;
quote!{ quote! {
impl ::std::marker::Copy for #name { } impl ::std::marker::Copy for #name { }
impl ::std::clone::Clone for #name { impl ::std::clone::Clone for #name {
@@ -839,22 +827,6 @@ fn prime_field_impl(
} }
} }
impl ::rand::Rand for #name {
/// Computes a uniformly random element using rejection sampling.
fn rand<R: ::rand::Rng>(rng: &mut R) -> Self {
loop {
let mut tmp = #name(#repr::rand(rng));
// Mask away the unused bits at the beginning.
tmp.0.as_mut()[#top_limb_index] &= 0xffffffffffffffff >> REPR_SHAVE_BITS;
if tmp.is_valid() {
return tmp
}
}
}
}
impl From<#name> for #repr { impl From<#name> for #repr {
fn from(e: #name) -> #repr { fn from(e: #name) -> #repr {
e.into_repr() e.into_repr()
@@ -904,6 +876,26 @@ fn prime_field_impl(
} }
impl ::ff::Field for #name { impl ::ff::Field for #name {
/// Computes a uniformly random element using rejection sampling.
fn random<R: ::rand_core::RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
loop {
let mut tmp = {
let mut repr = [0u64; #limbs];
for i in 0..#limbs {
repr[i] = rng.next_u64();
}
#name(#repr(repr))
};
// Mask away the unused most-significant bits.
tmp.0.as_mut()[#top_limb_index] &= 0xffffffffffffffff >> REPR_SHAVE_BITS;
if tmp.is_valid() {
return tmp
}
}
}
#[inline] #[inline]
fn zero() -> Self { fn zero() -> Self {
#name(#repr::from(0)) #name(#repr::from(0))

View File

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

View File

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

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,7 +1,8 @@
extern crate ff; // Catch documentation errors caused by code changes.
extern crate rand; #![deny(intra_doc_link_resolution_failure)]
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField}; use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
use rand::RngCore;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
@@ -13,23 +14,16 @@ pub use self::wnaf::Wnaf;
/// Projective representation of an elliptic curve point guaranteed to be /// Projective representation of an elliptic curve point guaranteed to be
/// in the correct prime order subgroup. /// in the correct prime order subgroup.
pub trait CurveProjective: pub trait CurveProjective:
PartialEq PartialEq + Eq + Sized + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
+ Eq
+ Sized
+ Copy
+ Clone
+ Send
+ Sync
+ fmt::Debug
+ fmt::Display
+ rand::Rand
+ 'static
{ {
type Engine: ScalarEngine<Fr = Self::Scalar>; type Engine: ScalarEngine<Fr = Self::Scalar>;
type Scalar: PrimeField + SqrtField; type Scalar: PrimeField + SqrtField;
type Base: SqrtField; type Base: SqrtField;
type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>; type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>;
/// Returns an element chosen uniformly at random using a user-provided RNG.
fn random<R: RngCore>(rng: &mut R) -> Self;
/// Returns the additive identity. /// Returns the additive identity.
fn zero() -> Self; fn zero() -> Self;
@@ -185,7 +179,7 @@ impl Error for GroupDecodingError {
} }
impl fmt::Display for GroupDecodingError { impl fmt::Display for GroupDecodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self { match *self {
GroupDecodingError::CoordinateDecodingError(description, ref err) => { GroupDecodingError::CoordinateDecodingError(description, ref err) => {
write!(f, "{} decoding error: {}", description, err) write!(f, "{} decoding error: {}", description, err)

View File

@@ -1,9 +1,14 @@
use rand::{Rand, Rng, SeedableRng, XorShiftRng}; use ff::{Field, PrimeField};
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use {CurveAffine, CurveProjective, EncodedPoint}; use crate::{CurveAffine, CurveProjective, EncodedPoint};
pub fn curve_tests<G: CurveProjective>() { pub fn curve_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
// Negation edge case with zero. // Negation edge case with zero.
{ {
@@ -21,7 +26,7 @@ pub fn curve_tests<G: CurveProjective>() {
// Addition edge cases with zero // Addition edge cases with zero
{ {
let mut r = G::rand(&mut rng); let mut r = G::random(&mut rng);
let rcopy = r; let rcopy = r;
r.add_assign(&G::zero()); r.add_assign(&G::zero());
assert_eq!(r, rcopy); assert_eq!(r, rcopy);
@@ -45,9 +50,10 @@ pub fn curve_tests<G: CurveProjective>() {
// Transformations // Transformations
{ {
let a = G::rand(&mut rng); let a = G::random(&mut rng);
let b = a.into_affine().into_projective(); let b = a.into_affine().into_projective();
let c = a.into_affine() let c = a
.into_affine()
.into_projective() .into_projective()
.into_affine() .into_affine()
.into_projective(); .into_projective();
@@ -65,11 +71,12 @@ pub fn curve_tests<G: CurveProjective>() {
} }
fn random_wnaf_tests<G: CurveProjective>() { fn random_wnaf_tests<G: CurveProjective>() {
use ff::PrimeField; use crate::wnaf::*;
use wnaf::*; let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); 0xe5,
]);
{ {
let mut table = vec![]; let mut table = vec![];
@@ -77,8 +84,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
for w in 2..14 { for w in 2..14 {
for _ in 0..100 { for _ in 0..100 {
let g = G::rand(&mut rng); let g = G::random(&mut rng);
let s = G::Scalar::rand(&mut rng).into_repr(); let s = G::Scalar::random(&mut rng).into_repr();
let mut g1 = g; let mut g1 = g;
g1.mul_assign(s); g1.mul_assign(s);
@@ -95,8 +102,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
fn only_compiles_if_send<S: Send>(_: &S) {} fn only_compiles_if_send<S: Send>(_: &S) {}
for _ in 0..100 { for _ in 0..100 {
let g = G::rand(&mut rng); let g = G::random(&mut rng);
let s = G::Scalar::rand(&mut rng).into_repr(); let s = G::Scalar::random(&mut rng).into_repr();
let mut g1 = g; let mut g1 = g;
g1.mul_assign(s); g1.mul_assign(s);
@@ -129,7 +136,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(rng.gen(), 1).scalar(rng.gen()); wnaf.base(G::random(&mut rng), 1)
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
wnaf.base(g, 1).scalar(s) wnaf.base(g, 1).scalar(s)
}; };
@@ -137,7 +145,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(rng.gen(), 1).scalar(rng.gen()); wnaf.base(G::random(&mut rng), 1)
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
wnaf.scalar(s).base(g) wnaf.scalar(s).base(g)
}; };
@@ -145,7 +154,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(rng.gen(), 1).scalar(rng.gen()); wnaf.base(G::random(&mut rng), 1)
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
let mut shared = wnaf.base(g, 1).shared(); let mut shared = wnaf.base(g, 1).shared();
@@ -157,7 +167,8 @@ fn random_wnaf_tests<G: CurveProjective>() {
let mut wnaf = Wnaf::new(); let mut wnaf = Wnaf::new();
{ {
// Populate the vectors. // Populate the vectors.
wnaf.base(rng.gen(), 1).scalar(rng.gen()); wnaf.base(G::random(&mut rng), 1)
.scalar(G::Scalar::random(&mut rng).into_repr());
} }
let mut shared = wnaf.scalar(s).shared(); let mut shared = wnaf.scalar(s).shared();
@@ -179,14 +190,15 @@ fn random_wnaf_tests<G: CurveProjective>() {
} }
fn random_negation_tests<G: CurveProjective>() { fn random_negation_tests<G: CurveProjective>() {
use ff::Field; let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let r = G::rand(&mut rng); let r = G::random(&mut rng);
let s = G::Scalar::rand(&mut rng); let s = G::Scalar::random(&mut rng);
let mut sneg = s; let mut sneg = s;
sneg.negate(); sneg.negate();
@@ -210,11 +222,14 @@ fn random_negation_tests<G: CurveProjective>() {
} }
fn random_doubling_tests<G: CurveProjective>() { fn random_doubling_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut a = G::rand(&mut rng); let mut a = G::random(&mut rng);
let mut b = G::rand(&mut rng); let mut b = G::random(&mut rng);
// 2(a + b) // 2(a + b)
let mut tmp1 = a; let mut tmp1 = a;
@@ -237,15 +252,18 @@ fn random_doubling_tests<G: CurveProjective>() {
} }
fn random_multiplication_tests<G: CurveProjective>() { fn random_multiplication_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let mut a = G::rand(&mut rng); let mut a = G::random(&mut rng);
let mut b = G::rand(&mut rng); let mut b = G::random(&mut rng);
let a_affine = a.into_affine(); let a_affine = a.into_affine();
let b_affine = b.into_affine(); let b_affine = b.into_affine();
let s = G::Scalar::rand(&mut rng); let s = G::Scalar::random(&mut rng);
// s ( a + b ) // s ( a + b )
let mut tmp1 = a; let mut tmp1 = a;
@@ -269,12 +287,15 @@ fn random_multiplication_tests<G: CurveProjective>() {
} }
fn random_addition_tests<G: CurveProjective>() { fn random_addition_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = G::rand(&mut rng); let a = G::random(&mut rng);
let b = G::rand(&mut rng); let b = G::random(&mut rng);
let c = G::rand(&mut rng); let c = G::random(&mut rng);
let a_affine = a.into_affine(); let a_affine = a.into_affine();
let b_affine = b.into_affine(); let b_affine = b.into_affine();
let c_affine = c.into_affine(); let c_affine = c.into_affine();
@@ -347,10 +368,13 @@ fn random_addition_tests<G: CurveProjective>() {
} }
fn random_transformation_tests<G: CurveProjective>() { fn random_transformation_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let g = G::rand(&mut rng); let g = G::random(&mut rng);
let g_affine = g.into_affine(); let g_affine = g.into_affine();
let g_projective = g_affine.into_projective(); let g_projective = g_affine.into_projective();
assert_eq!(g, g_projective); assert_eq!(g, g_projective);
@@ -358,24 +382,25 @@ fn random_transformation_tests<G: CurveProjective>() {
// Batch normalization // Batch normalization
for _ in 0..10 { for _ in 0..10 {
let mut v = (0..1000).map(|_| G::rand(&mut rng)).collect::<Vec<_>>(); let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::<Vec<_>>();
for i in &v { for i in &v {
assert!(!i.is_normalized()); assert!(!i.is_normalized());
} }
use rand::distributions::{IndependentSample, Range}; use rand::distributions::{Distribution, Uniform};
let between = Range::new(0, 1000); let between = Uniform::new(0, 1000);
// Sprinkle in some normalized points // Sprinkle in some normalized points
for _ in 0..5 { for _ in 0..5 {
v[between.ind_sample(&mut rng)] = G::zero(); v[between.sample(&mut rng)] = G::zero();
} }
for _ in 0..5 { for _ in 0..5 {
let s = between.ind_sample(&mut rng); let s = between.sample(&mut rng);
v[s] = v[s].into_affine().into_projective(); v[s] = v[s].into_affine().into_projective();
} }
let expected_v = v.iter() let expected_v = v
.iter()
.map(|v| v.into_affine().into_projective()) .map(|v| v.into_affine().into_projective())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
G::batch_normalization(&mut v); G::batch_normalization(&mut v);
@@ -389,7 +414,10 @@ fn random_transformation_tests<G: CurveProjective>() {
} }
fn random_encoding_tests<G: CurveAffine>() { fn random_encoding_tests<G: CurveAffine>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
assert_eq!( assert_eq!(
G::zero().into_uncompressed().into_affine().unwrap(), G::zero().into_uncompressed().into_affine().unwrap(),
@@ -402,7 +430,7 @@ fn random_encoding_tests<G: CurveAffine>() {
); );
for _ in 0..1000 { for _ in 0..1000 {
let mut r = G::Projective::rand(&mut rng).into_affine(); let mut r = G::Projective::random(&mut rng).into_affine();
let uncompressed = r.into_uncompressed(); let uncompressed = r.into_uncompressed();
let de_uncompressed = uncompressed.into_affine().unwrap(); let de_uncompressed = uncompressed.into_affine().unwrap();

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,18 +20,17 @@ path = "src/rustzcash.rs"
crate-type = ["staticlib"] crate-type = ["staticlib"]
[dependencies] [dependencies]
bellman = { path = "../bellman" } bellman = { version = "0.2.0", path = "../bellman" }
ff = { path = "../ff" } blake2b_simd = "0.5"
blake2s_simd = "0.5"
ff = { version = "0.5.0", path = "../ff" }
libc = "0.2" libc = "0.2"
pairing = { path = "../pairing" } pairing = { version = "0.15.0", path = "../pairing" }
lazy_static = "1" lazy_static = "1"
byteorder = "1" rand_core = "0.5.1"
rand = "0.4" zcash_history = { version = "0.0.1", path = "../zcash_history" }
sapling-crypto = { path = "../sapling-crypto" } zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
zcash_primitives = { path = "../zcash_primitives" } zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
zcash_proofs = { path = "../zcash_proofs" }
zip32 = { path = "../zip32" }
[dependencies.blake2-rfc] [badges]
git = "https://github.com/gtank/blake2-rfc" maintenance = { status = "deprecated" }
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"

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,10 +1,10 @@
use ff::{PrimeField, PrimeFieldRepr}; use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use rand::{OsRng, Rng}; use rand_core::{OsRng, RngCore};
use sapling_crypto::jubjub::{edwards, JubjubBls12}; use zcash_primitives::jubjub::{edwards, JubjubBls12};
use sapling_crypto::primitives::{Diversifier, ViewingKey}; use zcash_primitives::primitives::{Diversifier, ViewingKey};
use { use crate::{
librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree, librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree,
librustzcash_sapling_ka_derivepublic, librustzcash_sapling_ka_derivepublic,
}; };
@@ -12,7 +12,7 @@ use {
#[test] #[test]
fn test_key_agreement() { fn test_key_agreement() {
let params = JubjubBls12::new(); let params = JubjubBls12::new();
let mut rng = OsRng::new().unwrap(); let mut rng = OsRng;
// Create random viewing key // Create random viewing key
let vk = ViewingKey::<Bls12> { let vk = ViewingKey::<Bls12> {
@@ -22,7 +22,9 @@ fn test_key_agreement() {
// Create a random address with the viewing key // Create a random address with the viewing key
let addr = loop { let addr = loop {
match vk.into_payment_address(Diversifier(rng.gen()), &params) { let mut d = [0; 11];
rng.fill_bytes(&mut d);
match vk.to_payment_address(Diversifier(d), &params) {
Some(a) => break a, Some(a) => break a,
None => {} None => {}
} }
@@ -44,7 +46,7 @@ fn test_key_agreement() {
// Serialize pk_d for the call to librustzcash_sapling_ka_agree // Serialize pk_d for the call to librustzcash_sapling_ka_agree
let mut addr_pk_d = [0u8; 32]; let mut addr_pk_d = [0u8; 32];
addr.pk_d.write(&mut addr_pk_d[..]).unwrap(); addr.pk_d().write(&mut addr_pk_d[..]).unwrap();
assert!(librustzcash_sapling_ka_agree( assert!(librustzcash_sapling_ka_agree(
&addr_pk_d, &addr_pk_d,
@@ -56,7 +58,7 @@ fn test_key_agreement() {
// using the diversifier and esk. // using the diversifier and esk.
let mut epk = [0u8; 32]; let mut epk = [0u8; 32];
assert!(librustzcash_sapling_ka_derivepublic( assert!(librustzcash_sapling_ka_derivepublic(
&addr.diversifier.0, &addr.diversifier().0,
&esk, &esk,
&mut epk &mut epk
)); ));

View File

@@ -1,13 +1,13 @@
use ff::{PrimeField, PrimeFieldRepr}; use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use sapling_crypto::{ use zcash_primitives::{
jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams}, jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams},
primitives::{Diversifier, ProofGenerationKey}, primitives::{Diversifier, ProofGenerationKey},
}; };
use super::JUBJUB; use super::JUBJUB;
use { use crate::{
librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk, librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk,
librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk, librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk,
}; };
@@ -28,6 +28,8 @@ fn key_components() {
note_v: u64, note_v: u64,
note_r: [u8; 32], note_r: [u8; 32],
note_cm: [u8; 32], note_cm: [u8; 32],
note_pos: u64,
note_nf: [u8; 32],
}; };
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
@@ -87,6 +89,12 @@ fn key_components() {
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2, 0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
0xdd, 0x07, 0x64, 0x39, 0xdd, 0x07, 0x64, 0x39,
], ],
note_pos: 0,
note_nf: [
0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86,
0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27,
0x47, 0xab, 0x40, 0x63,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -143,6 +151,12 @@ fn key_components() {
0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72, 0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72,
0xca, 0xd4, 0x69, 0x50, 0xca, 0xd4, 0x69, 0x50,
], ],
note_pos: 763714296,
note_nf: [
0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2,
0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17,
0x4c, 0x2e, 0x9d, 0x93,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -199,6 +213,12 @@ fn key_components() {
0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f, 0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f,
0x4e, 0x55, 0xf1, 0x51, 0x4e, 0x55, 0xf1, 0x51,
], ],
note_pos: 1527428592,
note_nf: [
0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f,
0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f,
0x59, 0xae, 0x1d, 0x14,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -255,6 +275,12 @@ fn key_components() {
0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa, 0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa,
0x60, 0xd1, 0x9b, 0x6c, 0x60, 0xd1, 0x9b, 0x6c,
], ],
note_pos: 2291142888,
note_nf: [
0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56,
0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75,
0x8b, 0xec, 0x47, 0xa1,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -311,6 +337,12 @@ fn key_components() {
0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b, 0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b,
0xc7, 0x1b, 0x7f, 0x36, 0xc7, 0x1b, 0x7f, 0x36,
], ],
note_pos: 3054857184,
note_nf: [
0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f,
0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03,
0x01, 0xbf, 0x3d, 0x13,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -367,6 +399,12 @@ fn key_components() {
0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69, 0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69,
0xde, 0x1a, 0x5b, 0x4c, 0xde, 0x1a, 0x5b, 0x4c,
], ],
note_pos: 3818571480,
note_nf: [
0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2,
0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20,
0xb6, 0xc9, 0xa7, 0xe1,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -423,6 +461,12 @@ fn key_components() {
0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b, 0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b,
0x78, 0x3a, 0x1e, 0x55, 0x78, 0x3a, 0x1e, 0x55,
], ],
note_pos: 287318480,
note_nf: [
0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae,
0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4,
0x07, 0xd5, 0x1e, 0x11,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -479,6 +523,12 @@ fn key_components() {
0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36, 0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36,
0xf7, 0x8c, 0x2b, 0x23, 0xf7, 0x8c, 0x2b, 0x23,
], ],
note_pos: 1051032776,
note_nf: [
0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f,
0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc,
0x8b, 0xb6, 0x98, 0x90,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -535,6 +585,12 @@ fn key_components() {
0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07, 0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07,
0x8f, 0xea, 0x4d, 0x04, 0x8f, 0xea, 0x4d, 0x04,
], ],
note_pos: 1814747072,
note_nf: [
0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20,
0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe,
0xb1, 0x7c, 0xd6, 0x20,
],
}, },
TestVector { TestVector {
sk: [ sk: [
@@ -591,6 +647,12 @@ fn key_components() {
0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63, 0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63,
0xe4, 0x1d, 0xeb, 0x37, 0xe4, 0x1d, 0xeb, 0x37,
], ],
note_pos: 2578461368,
note_nf: [
0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe,
0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71,
0x0d, 0x93, 0xc9, 0xe9,
],
}, },
]; ];
@@ -616,7 +678,7 @@ fn key_components() {
} }
let pgk = ProofGenerationKey { ak, nsk }; let pgk = ProofGenerationKey { ak, nsk };
let fvk = pgk.into_viewing_key(&JUBJUB); let fvk = pgk.to_viewing_key(&JUBJUB);
{ {
let mut vec = Vec::new(); let mut vec = Vec::new();
fvk.nk.write(&mut vec).unwrap(); fvk.nk.write(&mut vec).unwrap();
@@ -642,10 +704,10 @@ fn key_components() {
let diversifier = Diversifier(tv.default_d); let diversifier = Diversifier(tv.default_d);
assert!(librustzcash_check_diversifier(&tv.default_d)); assert!(librustzcash_check_diversifier(&tv.default_d));
let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap(); let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
{ {
let mut vec = Vec::new(); let mut vec = Vec::new();
addr.pk_d.write(&mut vec).unwrap(); addr.pk_d().write(&mut vec).unwrap();
assert_eq!(&vec, &tv.default_pk_d); assert_eq!(&vec, &tv.default_pk_d);
} }
{ {
@@ -663,5 +725,7 @@ fn key_components() {
note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap(); note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap();
assert_eq!(&vec, &tv.note_cm); assert_eq!(&vec, &tv.note_cm);
} }
assert_eq!(note.nf(&fvk, tv.note_pos, &JUBJUB), tv.note_nf);
} }
} }

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

@@ -1,9 +1,10 @@
use sapling_crypto::jubjub::{FixedGenerators, JubjubParams}; use zcash_primitives::jubjub::{FixedGenerators, JubjubParams};
use super::JUBJUB; use super::JUBJUB;
mod key_agreement; mod key_agreement;
mod key_components; mod key_components;
mod mmr;
mod notes; mod notes;
mod signatures; mod signatures;

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

@@ -1,9 +1,7 @@
use ff::{PrimeField, PrimeFieldRepr}; use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12; use pairing::bls12_381::Bls12;
use sapling_crypto::{ use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine};
jubjub::{FixedGenerators, JubjubEngine}, use zcash_primitives::redjubjub::{PrivateKey, PublicKey, Signature};
redjubjub::{PrivateKey, PublicKey, Signature},
};
use super::JUBJUB; use super::JUBJUB;

View File

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

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

@@ -14,12 +14,11 @@ macro_rules! curve_impl {
pub struct $affine { pub struct $affine {
pub(crate) x: $basefield, pub(crate) x: $basefield,
pub(crate) y: $basefield, pub(crate) y: $basefield,
pub(crate) infinity: bool pub(crate) infinity: bool,
} }
impl ::std::fmt::Display for $affine impl ::std::fmt::Display for $affine {
{ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
if self.infinity { if self.infinity {
write!(f, "{}(Infinity)", $name) write!(f, "{}(Infinity)", $name)
} else { } else {
@@ -30,14 +29,13 @@ macro_rules! curve_impl {
#[derive(Copy, Clone, Debug, Eq)] #[derive(Copy, Clone, Debug, Eq)]
pub struct $projective { pub struct $projective {
pub(crate) x: $basefield, pub(crate) x: $basefield,
pub(crate) y: $basefield, pub(crate) y: $basefield,
pub(crate) z: $basefield pub(crate) z: $basefield,
} }
impl ::std::fmt::Display for $projective impl ::std::fmt::Display for $projective {
{ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.into_affine()) write!(f, "{}", self.into_affine())
} }
} }
@@ -89,7 +87,9 @@ macro_rules! curve_impl {
let mut res = $projective::zero(); let mut res = $projective::zero();
for i in bits { for i in bits {
res.double(); res.double();
if i { res.add_assign_mixed(self) } if i {
res.add_assign_mixed(self)
}
} }
res res
} }
@@ -112,12 +112,8 @@ macro_rules! curve_impl {
$affine { $affine {
x: x, x: x,
y: if (y < negy) ^ greatest { y: if (y < negy) ^ greatest { y } else { negy },
y infinity: false,
} else {
negy
},
infinity: false
} }
}) })
} }
@@ -156,7 +152,7 @@ macro_rules! curve_impl {
$affine { $affine {
x: $basefield::zero(), x: $basefield::zero(),
y: $basefield::one(), y: $basefield::one(),
infinity: true infinity: true,
} }
} }
@@ -182,7 +178,6 @@ macro_rules! curve_impl {
fn into_projective(&self) -> $projective { fn into_projective(&self) -> $projective {
(*self).into() (*self).into()
} }
} }
impl PairingCurveAffine for $affine { impl PairingCurveAffine for $affine {
@@ -197,14 +192,18 @@ macro_rules! curve_impl {
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
self.perform_pairing(other) self.perform_pairing(other)
} }
} }
impl Rand for $projective { impl CurveProjective for $projective {
fn rand<R: Rng>(rng: &mut R) -> Self { type Engine = Bls12;
type Scalar = $scalarfield;
type Base = $basefield;
type Affine = $affine;
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
loop { loop {
let x = rng.gen(); let x = $basefield::random(rng);
let greatest = rng.gen(); let greatest = rng.next_u32() % 2 != 0;
if let Some(p) = $affine::get_point_from_x(x, greatest) { if let Some(p) = $affine::get_point_from_x(x, greatest) {
let p = p.scale_by_cofactor(); let p = p.scale_by_cofactor();
@@ -215,13 +214,6 @@ macro_rules! curve_impl {
} }
} }
} }
}
impl CurveProjective for $projective {
type Engine = Bls12;
type Scalar = $scalarfield;
type Base = $basefield;
type Affine = $affine;
// The point at infinity is always represented by // The point at infinity is always represented by
// Z = 0. // Z = 0.
@@ -229,7 +221,7 @@ macro_rules! curve_impl {
$projective { $projective {
x: $basefield::zero(), x: $basefield::zero(),
y: $basefield::one(), y: $basefield::one(),
z: $basefield::zero() z: $basefield::zero(),
} }
} }
@@ -247,8 +239,7 @@ macro_rules! curve_impl {
self.is_zero() || self.z == $basefield::one() self.is_zero() || self.z == $basefield::one()
} }
fn batch_normalization(v: &mut [Self]) fn batch_normalization(v: &mut [Self]) {
{
// Montgomerys Trick and Fast Implementation of Masked AES // Montgomerys Trick and Fast Implementation of Masked AES
// Genelle, Prouff and Quisquater // Genelle, Prouff and Quisquater
// Section 3.2 // Section 3.2
@@ -256,9 +247,10 @@ macro_rules! curve_impl {
// First pass: compute [a, ab, abc, ...] // First pass: compute [a, ab, abc, ...]
let mut prod = Vec::with_capacity(v.len()); let mut prod = Vec::with_capacity(v.len());
let mut tmp = $basefield::one(); let mut tmp = $basefield::one();
for g in v.iter_mut() for g in v
// Ignore normalized elements .iter_mut()
.filter(|g| !g.is_normalized()) // Ignore normalized elements
.filter(|g| !g.is_normalized())
{ {
tmp.mul_assign(&g.z); tmp.mul_assign(&g.z);
prod.push(tmp); prod.push(tmp);
@@ -268,13 +260,19 @@ macro_rules! curve_impl {
tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero. tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero.
// Second pass: iterate backwards to compute inverses // Second pass: iterate backwards to compute inverses
for (g, s) in v.iter_mut() for (g, s) in v
// Backwards .iter_mut()
.rev() // Backwards
// Ignore normalized elements .rev()
.filter(|g| !g.is_normalized()) // Ignore normalized elements
// Backwards, skip last element, fill in one for last term. .filter(|g| !g.is_normalized())
.zip(prod.into_iter().rev().skip(1).chain(Some($basefield::one()))) // Backwards, skip last element, fill in one for last term.
.zip(
prod.into_iter()
.rev()
.skip(1)
.chain(Some($basefield::one())),
)
{ {
// tmp := tmp * g.z; g.z := tmp * s = 1/z // tmp := tmp * g.z; g.z := tmp * s = 1/z
let mut newtmp = tmp; let mut newtmp = tmp;
@@ -285,9 +283,7 @@ macro_rules! curve_impl {
} }
// Perform affine transformations // Perform affine transformations
for g in v.iter_mut() for g in v.iter_mut().filter(|g| !g.is_normalized()) {
.filter(|g| !g.is_normalized())
{
let mut z = g.z; // 1/z let mut z = g.z; // 1/z
z.square(); // 1/z^2 z.square(); // 1/z^2
g.x.mul_assign(&z); // x/z^2 g.x.mul_assign(&z); // x/z^2
@@ -540,8 +536,7 @@ macro_rules! curve_impl {
let mut found_one = false; let mut found_one = false;
for i in BitIterator::new(other.into()) for i in BitIterator::new(other.into()) {
{
if found_one { if found_one {
res.double(); res.double();
} else { } else {
@@ -579,7 +574,7 @@ macro_rules! curve_impl {
$projective { $projective {
x: p.x, x: p.x,
y: p.y, y: p.y,
z: $basefield::one() z: $basefield::one(),
} }
} }
} }
@@ -596,7 +591,7 @@ macro_rules! curve_impl {
$affine { $affine {
x: p.x, x: p.x,
y: p.y, y: p.y,
infinity: false infinity: false,
} }
} else { } else {
// Z is nonzero, so it must have an inverse in a field. // Z is nonzero, so it must have an inverse in a field.
@@ -616,22 +611,22 @@ macro_rules! curve_impl {
$affine { $affine {
x: x, x: x,
y: y, y: y,
infinity: false infinity: false,
} }
} }
} }
} }
} };
} }
pub mod g1 { pub mod g1 {
use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr}; use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
use super::g2::G2Affine; use super::g2::G2Affine;
use crate::{Engine, PairingCurveAffine};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand::{Rand, Rng}; use rand_core::RngCore;
use std::fmt; use std::fmt;
use {Engine, PairingCurveAffine};
curve_impl!( curve_impl!(
"G1", "G1",
@@ -661,7 +656,7 @@ pub mod g1 {
} }
impl fmt::Debug for G1Uncompressed { impl fmt::Debug for G1Uncompressed {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -771,7 +766,7 @@ pub mod g1 {
} }
impl fmt::Debug for G1Compressed { impl fmt::Debug for G1Compressed {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -939,7 +934,7 @@ pub mod g1 {
#[test] #[test]
fn g1_generator() { fn g1_generator() {
use SqrtField; use crate::SqrtField;
let mut x = Fq::zero(); let mut x = Fq::zero();
let mut i = 0; let mut i = 0;
@@ -957,7 +952,7 @@ pub mod g1 {
let negyrepr = negy.into_repr(); let negyrepr = negy.into_repr();
let p = G1Affine { let p = G1Affine {
x: x, x,
y: if yrepr < negyrepr { y } else { negy }, y: if yrepr < negyrepr { y } else { negy },
infinity: false, infinity: false,
}; };
@@ -992,7 +987,8 @@ pub mod g1 {
0x9fe83b1b4a5d648d, 0x9fe83b1b4a5d648d,
0xf583cc5a508f6a40, 0xf583cc5a508f6a40,
0xc3ad2aefde0bb13, 0xc3ad2aefde0bb13,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x60aa6f9552f03aae, 0x60aa6f9552f03aae,
0xecd01d5181300d35, 0xecd01d5181300d35,
@@ -1000,7 +996,8 @@ pub mod g1 {
0xe760f57922998c9d, 0xe760f57922998c9d,
0x953703f5795a39e5, 0x953703f5795a39e5,
0xfe3ae0922df702c, 0xfe3ae0922df702c,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
assert!(!p.is_on_curve()); assert!(!p.is_on_curve());
@@ -1017,7 +1014,8 @@ pub mod g1 {
0xea034ee2928b30a8, 0xea034ee2928b30a8,
0xbd8833dc7c79a7f7, 0xbd8833dc7c79a7f7,
0xe45c9f0c0438675, 0xe45c9f0c0438675,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x3b450eb1ab7b5dad, 0x3b450eb1ab7b5dad,
0xa65cb81e975e8675, 0xa65cb81e975e8675,
@@ -1025,7 +1023,8 @@ pub mod g1 {
0x753ddf21a2601d20, 0x753ddf21a2601d20,
0x532d0b640bd3ff8b, 0x532d0b640bd3ff8b,
0x118d2c543f031102, 0x118d2c543f031102,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
assert!(!p.is_on_curve()); assert!(!p.is_on_curve());
@@ -1043,7 +1042,8 @@ pub mod g1 {
0xf35de9ce0d6b4e84, 0xf35de9ce0d6b4e84,
0x265bddd23d1dec54, 0x265bddd23d1dec54,
0x12a8778088458308, 0x12a8778088458308,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x8a22defa0d526256, 0x8a22defa0d526256,
0xc57ca55456fcb9ae, 0xc57ca55456fcb9ae,
@@ -1051,7 +1051,8 @@ pub mod g1 {
0x921beef89d4f29df, 0x921beef89d4f29df,
0x5b6fda44ad85fa78, 0x5b6fda44ad85fa78,
0xed74ab9f302cbe0, 0xed74ab9f302cbe0,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
assert!(p.is_on_curve()); assert!(p.is_on_curve());
@@ -1069,7 +1070,8 @@ pub mod g1 {
0x485e77d50a5df10d, 0x485e77d50a5df10d,
0x4c6fcac4b55fd479, 0x4c6fcac4b55fd479,
0x86ed4d9906fb064, 0x86ed4d9906fb064,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xd25ee6461538c65, 0xd25ee6461538c65,
0x9f3bbb2ecd3719b9, 0x9f3bbb2ecd3719b9,
@@ -1077,7 +1079,8 @@ pub mod g1 {
0xcefca68333c35288, 0xcefca68333c35288,
0x570c8005f8573fa6, 0x570c8005f8573fa6,
0x152ca696fe034442, 0x152ca696fe034442,
])).unwrap(), ]))
.unwrap(),
z: Fq::one(), z: Fq::one(),
}; };
@@ -1089,7 +1092,8 @@ pub mod g1 {
0x5f44314ec5e3fb03, 0x5f44314ec5e3fb03,
0x24e8538737c6e675, 0x24e8538737c6e675,
0x8abd623a594fba8, 0x8abd623a594fba8,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x6b0528f088bb7044, 0x6b0528f088bb7044,
0x2fdeb5c82917ff9e, 0x2fdeb5c82917ff9e,
@@ -1097,7 +1101,8 @@ pub mod g1 {
0xd65104c6f95a872a, 0xd65104c6f95a872a,
0x1f2998a5a9c61253, 0x1f2998a5a9c61253,
0xe74846154a9e44, 0xe74846154a9e44,
])).unwrap(), ]))
.unwrap(),
z: Fq::one(), z: Fq::one(),
}); });
@@ -1113,7 +1118,8 @@ pub mod g1 {
0xc4f9a52a428e23bb, 0xc4f9a52a428e23bb,
0xd178b28dd4f407ef, 0xd178b28dd4f407ef,
0x17fb8905e9183c69 0x17fb8905e9183c69
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xd0de9d65292b7710, 0xd0de9d65292b7710,
0xf6a05f2bcf1d9ca7, 0xf6a05f2bcf1d9ca7,
@@ -1121,7 +1127,8 @@ pub mod g1 {
0xeec8d1a5b7466c58, 0xeec8d1a5b7466c58,
0x4bc362649dce6376, 0x4bc362649dce6376,
0x430cbdc5455b00a 0x430cbdc5455b00a
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
} }
); );
@@ -1137,7 +1144,8 @@ pub mod g1 {
0x485e77d50a5df10d, 0x485e77d50a5df10d,
0x4c6fcac4b55fd479, 0x4c6fcac4b55fd479,
0x86ed4d9906fb064, 0x86ed4d9906fb064,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xd25ee6461538c65, 0xd25ee6461538c65,
0x9f3bbb2ecd3719b9, 0x9f3bbb2ecd3719b9,
@@ -1145,7 +1153,8 @@ pub mod g1 {
0xcefca68333c35288, 0xcefca68333c35288,
0x570c8005f8573fa6, 0x570c8005f8573fa6,
0x152ca696fe034442, 0x152ca696fe034442,
])).unwrap(), ]))
.unwrap(),
z: Fq::one(), z: Fq::one(),
}; };
@@ -1163,7 +1172,8 @@ pub mod g1 {
0x4b914c16687dcde0, 0x4b914c16687dcde0,
0x66c8baf177d20533, 0x66c8baf177d20533,
0xaf960cff3d83833 0xaf960cff3d83833
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x3f0675695f5177a8, 0x3f0675695f5177a8,
0x2b6d82ae178a1ba0, 0x2b6d82ae178a1ba0,
@@ -1171,7 +1181,8 @@ pub mod g1 {
0x1771a65b60572f4e, 0x1771a65b60572f4e,
0x8b547c1313b27555, 0x8b547c1313b27555,
0x135075589a687b1e 0x135075589a687b1e
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
} }
); );
@@ -1194,7 +1205,8 @@ pub mod g1 {
0x71ffa8021531705, 0x71ffa8021531705,
0x7418d484386d267, 0x7418d484386d267,
0xd5108d8ff1fbd6, 0xd5108d8ff1fbd6,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xa776ccbfe9981766, 0xa776ccbfe9981766,
0x255632964ff40f4a, 0x255632964ff40f4a,
@@ -1202,7 +1214,8 @@ pub mod g1 {
0x520f74773e74c8c3, 0x520f74773e74c8c3,
0x484c8fc982008f0, 0x484c8fc982008f0,
0xee2c3d922008cc6, 0xee2c3d922008cc6,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
@@ -1214,7 +1227,8 @@ pub mod g1 {
0xc6e05201e5f83991, 0xc6e05201e5f83991,
0xf7c75910816f207c, 0xf7c75910816f207c,
0x18d4043e78103106, 0x18d4043e78103106,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0xa776ccbfe9981766, 0xa776ccbfe9981766,
0x255632964ff40f4a, 0x255632964ff40f4a,
@@ -1222,7 +1236,8 @@ pub mod g1 {
0x520f74773e74c8c3, 0x520f74773e74c8c3,
0x484c8fc982008f0, 0x484c8fc982008f0,
0xee2c3d922008cc6, 0xee2c3d922008cc6,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
@@ -1237,7 +1252,8 @@ pub mod g1 {
0x9676ff02ec39c227, 0x9676ff02ec39c227,
0x4c12c15d7e55b9f3, 0x4c12c15d7e55b9f3,
0x57fd1e317db9bd, 0x57fd1e317db9bd,
])).unwrap(), ]))
.unwrap(),
y: Fq::from_repr(FqRepr([ y: Fq::from_repr(FqRepr([
0x1288334016679345, 0x1288334016679345,
0xf955cd68615ff0b5, 0xf955cd68615ff0b5,
@@ -1245,7 +1261,8 @@ pub mod g1 {
0x1267d70db51049fb, 0x1267d70db51049fb,
0x4696deb9ab2ba3e7, 0x4696deb9ab2ba3e7,
0xb1e4e11177f59d4, 0xb1e4e11177f59d4,
])).unwrap(), ]))
.unwrap(),
infinity: false, infinity: false,
}; };
@@ -1274,11 +1291,11 @@ pub mod g1 {
pub mod g2 { pub mod g2 {
use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr}; use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
use super::g1::G1Affine; use super::g1::G1Affine;
use crate::{Engine, PairingCurveAffine};
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use rand::{Rand, Rng}; use rand_core::RngCore;
use std::fmt; use std::fmt;
use {Engine, PairingCurveAffine};
curve_impl!( curve_impl!(
"G2", "G2",
@@ -1308,7 +1325,7 @@ pub mod g2 {
} }
impl fmt::Debug for G2Uncompressed { impl fmt::Debug for G2Uncompressed {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -1434,7 +1451,7 @@ pub mod g2 {
} }
impl fmt::Debug for G2Compressed { impl fmt::Debug for G2Compressed {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.0[..].fmt(formatter) self.0[..].fmt(formatter)
} }
} }
@@ -1623,7 +1640,7 @@ pub mod g2 {
#[test] #[test]
fn g2_generator() { fn g2_generator() {
use SqrtField; use crate::SqrtField;
let mut x = Fq2::zero(); let mut x = Fq2::zero();
let mut i = 0; let mut i = 0;
@@ -1639,7 +1656,7 @@ pub mod g2 {
negy.negate(); negy.negate();
let p = G2Affine { let p = G2Affine {
x: x, x,
y: if y < negy { y } else { negy }, y: if y < negy { y } else { negy },
infinity: false, infinity: false,
}; };
@@ -1675,7 +1692,8 @@ pub mod g2 {
0x7a17a004747e3dbe, 0x7a17a004747e3dbe,
0xcc65406a7c2e5a73, 0xcc65406a7c2e5a73,
0x10b8c03d64db4d0c, 0x10b8c03d64db4d0c,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xd30e70fe2f029778, 0xd30e70fe2f029778,
0xda30772df0f5212e, 0xda30772df0f5212e,
@@ -1683,7 +1701,8 @@ pub mod g2 {
0xfb777e5b9b568608, 0xfb777e5b9b568608,
0x789bac1fec71a2b9, 0x789bac1fec71a2b9,
0x1342f02e2da54405, 0x1342f02e2da54405,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1693,7 +1712,8 @@ pub mod g2 {
0x663015d9410eb608, 0x663015d9410eb608,
0x78e82a79d829a544, 0x78e82a79d829a544,
0x40a00545bb3c1e, 0x40a00545bb3c1e,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4709802348e79377, 0x4709802348e79377,
0xb5ac4dc9204bcfbd, 0xb5ac4dc9204bcfbd,
@@ -1701,7 +1721,8 @@ pub mod g2 {
0x15008b1dc399e8df, 0x15008b1dc399e8df,
0x68128fd0548a3829, 0x68128fd0548a3829,
0x16a613db5c873aaa, 0x16a613db5c873aaa,
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@@ -1720,7 +1741,8 @@ pub mod g2 {
0x41abba710d6c692c, 0x41abba710d6c692c,
0xffcc4b2b62ce8484, 0xffcc4b2b62ce8484,
0x6993ec01b8934ed, 0x6993ec01b8934ed,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xb94e92d5f874e26, 0xb94e92d5f874e26,
0x44516408bc115d95, 0x44516408bc115d95,
@@ -1728,7 +1750,8 @@ pub mod g2 {
0xa5a0c2b7131f3555, 0xa5a0c2b7131f3555,
0x83800965822367e7, 0x83800965822367e7,
0x10cf1d3ad8d90bfa, 0x10cf1d3ad8d90bfa,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1738,7 +1761,8 @@ pub mod g2 {
0x5a9171720e73eb51, 0x5a9171720e73eb51,
0x38eb4fd8d658adb7, 0x38eb4fd8d658adb7,
0xb649051bbc1164d, 0xb649051bbc1164d,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x9225814253d7df75, 0x9225814253d7df75,
0xc196c2513477f887, 0xc196c2513477f887,
@@ -1746,7 +1770,8 @@ pub mod g2 {
0x55f2b8efad953e04, 0x55f2b8efad953e04,
0x7379345eda55265e, 0x7379345eda55265e,
0x377f2e6208fd4cb, 0x377f2e6208fd4cb,
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@@ -1766,7 +1791,8 @@ pub mod g2 {
0x2199bc19c48c393d, 0x2199bc19c48c393d,
0x4a151b732a6075bf, 0x4a151b732a6075bf,
0x17762a3b9108c4a7, 0x17762a3b9108c4a7,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x26f461e944bbd3d1, 0x26f461e944bbd3d1,
0x298f3189a9cf6ed6, 0x298f3189a9cf6ed6,
@@ -1774,7 +1800,8 @@ pub mod g2 {
0x7e147f3f9e6e241, 0x7e147f3f9e6e241,
0x72a9b63583963fff, 0x72a9b63583963fff,
0x158b0083c000462, 0x158b0083c000462,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1784,7 +1811,8 @@ pub mod g2 {
0x68cad19430706b4d, 0x68cad19430706b4d,
0x3ccfb97b924dcea8, 0x3ccfb97b924dcea8,
0x1660f93434588f8d, 0x1660f93434588f8d,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xaaed3985b6dcb9c7, 0xaaed3985b6dcb9c7,
0xc1e985d6d898d9f4, 0xc1e985d6d898d9f4,
@@ -1792,7 +1820,8 @@ pub mod g2 {
0x3940a2dbb914b529, 0x3940a2dbb914b529,
0xbeb88137cf34f3e7, 0xbeb88137cf34f3e7,
0x1699ee577c61b694, 0x1699ee577c61b694,
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
}; };
@@ -1812,7 +1841,8 @@ pub mod g2 {
0x72556c999f3707ac, 0x72556c999f3707ac,
0x4617f2e6774e9711, 0x4617f2e6774e9711,
0x100b2fe5bffe030b, 0x100b2fe5bffe030b,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x7a33555977ec608, 0x7a33555977ec608,
0xe23039d1fe9c0881, 0xe23039d1fe9c0881,
@@ -1820,7 +1850,8 @@ pub mod g2 {
0x4637c4f417667e2e, 0x4637c4f417667e2e,
0x93ebe7c3e41f6acc, 0x93ebe7c3e41f6acc,
0xde884f89a9a371b, 0xde884f89a9a371b,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1830,7 +1861,8 @@ pub mod g2 {
0x25fd427b4122f231, 0x25fd427b4122f231,
0xd83112aace35cae, 0xd83112aace35cae,
0x191b2432407cbb7f, 0x191b2432407cbb7f,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xf68ae82fe97662f5, 0xf68ae82fe97662f5,
0xe986057068b50b7d, 0xe986057068b50b7d,
@@ -1838,7 +1870,8 @@ pub mod g2 {
0x9eaa6d19de569196, 0x9eaa6d19de569196,
0xf6a03d31e2ec2183, 0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b, 0x3bdafaf7ca9b39b,
])).unwrap(), ]))
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}; };
@@ -1852,7 +1885,8 @@ pub mod g2 {
0x8e73a96b329ad190, 0x8e73a96b329ad190,
0x27c546f75ee1f3ab, 0x27c546f75ee1f3ab,
0xa33d27add5e7e82, 0xa33d27add5e7e82,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x93b1ebcd54870dfe, 0x93b1ebcd54870dfe,
0xf1578300e1342e11, 0xf1578300e1342e11,
@@ -1860,7 +1894,8 @@ pub mod g2 {
0x2089faf462438296, 0x2089faf462438296,
0x828e5848cd48ea66, 0x828e5848cd48ea66,
0x141ecbac1deb038b, 0x141ecbac1deb038b,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1870,7 +1905,8 @@ pub mod g2 {
0x2767032fc37cc31d, 0x2767032fc37cc31d,
0xd5ee2aba84fd10fe, 0xd5ee2aba84fd10fe,
0x16576ccd3dd0a4e8, 0x16576ccd3dd0a4e8,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4da9b6f6a96d1dd2, 0x4da9b6f6a96d1dd2,
0x9657f7da77f1650e, 0x9657f7da77f1650e,
@@ -1878,7 +1914,8 @@ pub mod g2 {
0x31898db63f87363a, 0x31898db63f87363a,
0xabab040ddbd097cc, 0xabab040ddbd097cc,
0x11ad236b9ba02990, 0x11ad236b9ba02990,
])).unwrap(), ]))
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}); });
@@ -1896,7 +1933,8 @@ pub mod g2 {
0xf1273e6406eef9cc, 0xf1273e6406eef9cc,
0xababd760ff05cb92, 0xababd760ff05cb92,
0xd7c20456617e89 0xd7c20456617e89
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xd1a50b8572cbd2b8, 0xd1a50b8572cbd2b8,
0x238f0ac6119d07df, 0x238f0ac6119d07df,
@@ -1904,7 +1942,8 @@ pub mod g2 {
0x8b203284c51edf6b, 0x8b203284c51edf6b,
0xc8a0b730bbb21f5e, 0xc8a0b730bbb21f5e,
0x1a3b59d29a31274 0x1a3b59d29a31274
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1914,7 +1953,8 @@ pub mod g2 {
0x64528ab3863633dc, 0x64528ab3863633dc,
0x159384333d7cba97, 0x159384333d7cba97,
0x4cb84741f3cafe8 0x4cb84741f3cafe8
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x242af0dc3640e1a4, 0x242af0dc3640e1a4,
0xe90a73ad65c66919, 0xe90a73ad65c66919,
@@ -1922,7 +1962,8 @@ pub mod g2 {
0x38528f92b689644d, 0x38528f92b689644d,
0xb6884deec59fb21f, 0xb6884deec59fb21f,
0x3c075d3ec52ba90 0x3c075d3ec52ba90
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
} }
@@ -1940,7 +1981,8 @@ pub mod g2 {
0x72556c999f3707ac, 0x72556c999f3707ac,
0x4617f2e6774e9711, 0x4617f2e6774e9711,
0x100b2fe5bffe030b, 0x100b2fe5bffe030b,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x7a33555977ec608, 0x7a33555977ec608,
0xe23039d1fe9c0881, 0xe23039d1fe9c0881,
@@ -1948,7 +1990,8 @@ pub mod g2 {
0x4637c4f417667e2e, 0x4637c4f417667e2e,
0x93ebe7c3e41f6acc, 0x93ebe7c3e41f6acc,
0xde884f89a9a371b, 0xde884f89a9a371b,
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -1958,7 +2001,8 @@ pub mod g2 {
0x25fd427b4122f231, 0x25fd427b4122f231,
0xd83112aace35cae, 0xd83112aace35cae,
0x191b2432407cbb7f, 0x191b2432407cbb7f,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xf68ae82fe97662f5, 0xf68ae82fe97662f5,
0xe986057068b50b7d, 0xe986057068b50b7d,
@@ -1966,7 +2010,8 @@ pub mod g2 {
0x9eaa6d19de569196, 0x9eaa6d19de569196,
0xf6a03d31e2ec2183, 0xf6a03d31e2ec2183,
0x3bdafaf7ca9b39b, 0x3bdafaf7ca9b39b,
])).unwrap(), ]))
.unwrap(),
}, },
z: Fq2::one(), z: Fq2::one(),
}; };
@@ -1986,7 +2031,8 @@ pub mod g2 {
0xbcedcfce1e52d986, 0xbcedcfce1e52d986,
0x9755d4a3926e9862, 0x9755d4a3926e9862,
0x18bab73760fd8024 0x18bab73760fd8024
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4e7c5e0a2ae5b99e, 0x4e7c5e0a2ae5b99e,
0x96e582a27f028961, 0x96e582a27f028961,
@@ -1994,7 +2040,8 @@ pub mod g2 {
0xeb0cf5e610ef4fe7, 0xeb0cf5e610ef4fe7,
0x7b4c2bae8db6e70b, 0x7b4c2bae8db6e70b,
0xf136e43909fca0 0xf136e43909fca0
])).unwrap(), ]))
.unwrap(),
}, },
y: Fq2 { y: Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -2004,7 +2051,8 @@ pub mod g2 {
0xa5a2a51f7fde787b, 0xa5a2a51f7fde787b,
0x8b92866bc6384188, 0x8b92866bc6384188,
0x81a53fe531d64ef 0x81a53fe531d64ef
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4c5d607666239b34, 0x4c5d607666239b34,
0xeddb5f48304d14b3, 0xeddb5f48304d14b3,
@@ -2012,7 +2060,8 @@ pub mod g2 {
0xb271f52f12ead742, 0xb271f52f12ead742,
0x244e6c2015c83348, 0x244e6c2015c83348,
0x19e2deae6eb9b441 0x19e2deae6eb9b441
])).unwrap(), ]))
.unwrap(),
}, },
infinity: false, infinity: false,
} }

View File

@@ -1173,7 +1173,9 @@ fn test_neg_one() {
} }
#[cfg(test)] #[cfg(test)]
use rand::{Rand, SeedableRng, XorShiftRng}; use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq_repr_ordering() { fn test_fq_repr_ordering() {
@@ -1396,7 +1398,10 @@ fn test_fq_repr_num_bits() {
#[test] #[test]
fn test_fq_repr_sub_noborrow() { fn test_fq_repr_sub_noborrow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FqRepr([ let mut t = FqRepr([
0x827a4a08041ebd9, 0x827a4a08041ebd9,
@@ -1426,7 +1431,7 @@ fn test_fq_repr_sub_noborrow() {
); );
for _ in 0..1000 { for _ in 0..1000 {
let mut a = FqRepr::rand(&mut rng); let mut a = Fq::random(&mut rng).into_repr();
a.0[5] >>= 30; a.0[5] >>= 30;
let mut b = a; let mut b = a;
for _ in 0..10 { for _ in 0..10 {
@@ -1483,7 +1488,10 @@ fn test_fq_repr_sub_noborrow() {
#[test] #[test]
fn test_fq_repr_add_nocarry() { fn test_fq_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FqRepr([ let mut t = FqRepr([
0x827a4a08041ebd9, 0x827a4a08041ebd9,
@@ -1514,9 +1522,9 @@ fn test_fq_repr_add_nocarry() {
// Test for the associativity of addition. // Test for the associativity of addition.
for _ in 0..1000 { for _ in 0..1000 {
let mut a = FqRepr::rand(&mut rng); let mut a = Fq::random(&mut rng).into_repr();
let mut b = FqRepr::rand(&mut rng); let mut b = Fq::random(&mut rng).into_repr();
let mut c = FqRepr::rand(&mut rng); let mut c = Fq::random(&mut rng).into_repr();
// Unset the first few bits, so that overflow won't occur. // Unset the first few bits, so that overflow won't occur.
a.0[5] >>= 3; a.0[5] >>= 3;
@@ -1574,31 +1582,32 @@ fn test_fq_is_valid() {
a.0.sub_noborrow(&FqRepr::from(1)); a.0.sub_noborrow(&FqRepr::from(1));
assert!(a.is_valid()); assert!(a.is_valid());
assert!(Fq(FqRepr::from(0)).is_valid()); assert!(Fq(FqRepr::from(0)).is_valid());
assert!( assert!(Fq(FqRepr([
Fq(FqRepr([ 0xdf4671abd14dab3e,
0xdf4671abd14dab3e, 0xe2dc0c9f534fbd33,
0xe2dc0c9f534fbd33, 0x31ca6c880cc444a6,
0x31ca6c880cc444a6, 0x257a67e70ef33359,
0x257a67e70ef33359, 0xf9b29e493f899b36,
0xf9b29e493f899b36, 0x17c8be1800b9f059
0x17c8be1800b9f059 ]))
])).is_valid() .is_valid());
); assert!(!Fq(FqRepr([
assert!( 0xffffffffffffffff,
!Fq(FqRepr([ 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff
0xffffffffffffffff, ]))
0xffffffffffffffff .is_valid());
])).is_valid()
);
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
assert!(a.is_valid()); assert!(a.is_valid());
} }
} }
@@ -1708,13 +1717,16 @@ fn test_fq_add_assign() {
// Test associativity // Test associativity
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Generate a, b, c and ensure (a + b) + c == a + (b + c). // Generate a, b, c and ensure (a + b) + c == a + (b + c).
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
let b = Fq::rand(&mut rng); let b = Fq::random(&mut rng);
let c = Fq::rand(&mut rng); let c = Fq::random(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.add_assign(&b); tmp1.add_assign(&b);
@@ -1818,12 +1830,15 @@ fn test_fq_sub_assign() {
); );
} }
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure that (a - b) + (b - a) = 0. // Ensure that (a - b) + (b - a) = 0.
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
let b = Fq::rand(&mut rng); let b = Fq::random(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.sub_assign(&b); tmp1.sub_assign(&b);
@@ -1865,13 +1880,16 @@ fn test_fq_mul_assign() {
])) ]))
); );
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that (a * b) * c = a * (b * c) // Ensure that (a * b) * c = a * (b * c)
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
let b = Fq::rand(&mut rng); let b = Fq::random(&mut rng);
let c = Fq::rand(&mut rng); let c = Fq::random(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.mul_assign(&b); tmp1.mul_assign(&b);
@@ -1887,10 +1905,10 @@ fn test_fq_mul_assign() {
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that r * (a + b + c) = r*a + r*b + r*c // Ensure that r * (a + b + c) = r*a + r*b + r*c
let r = Fq::rand(&mut rng); let r = Fq::random(&mut rng);
let mut a = Fq::rand(&mut rng); let mut a = Fq::random(&mut rng);
let mut b = Fq::rand(&mut rng); let mut b = Fq::random(&mut rng);
let mut c = Fq::rand(&mut rng); let mut c = Fq::random(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.add_assign(&b); tmp1.add_assign(&b);
@@ -1929,14 +1947,18 @@ fn test_fq_squaring() {
0xdc05c659b4e15b27, 0xdc05c659b4e15b27,
0x79361e5a802c6a23, 0x79361e5a802c6a23,
0x24bcbe5d51b9a6f 0x24bcbe5d51b9a6f
])).unwrap() ]))
.unwrap()
); );
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that (a * a) = a^2 // Ensure that (a * a) = a^2
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
let mut tmp = a; let mut tmp = a;
tmp.square(); tmp.square();
@@ -1952,13 +1974,16 @@ fn test_fq_squaring() {
fn test_fq_inverse() { fn test_fq_inverse() {
assert!(Fq::zero().inverse().is_none()); assert!(Fq::zero().inverse().is_none());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let one = Fq::one(); let one = Fq::one();
for _ in 0..1000 { for _ in 0..1000 {
// Ensure that a * a^-1 = 1 // Ensure that a * a^-1 = 1
let mut a = Fq::rand(&mut rng); let mut a = Fq::random(&mut rng);
let ainv = a.inverse().unwrap(); let ainv = a.inverse().unwrap();
a.mul_assign(&ainv); a.mul_assign(&ainv);
assert_eq!(a, one); assert_eq!(a, one);
@@ -1967,11 +1992,14 @@ fn test_fq_inverse() {
#[test] #[test]
fn test_fq_double() { fn test_fq_double() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure doubling a is equivalent to adding a to itself. // Ensure doubling a is equivalent to adding a to itself.
let mut a = Fq::rand(&mut rng); let mut a = Fq::random(&mut rng);
let mut b = a; let mut b = a;
b.add_assign(&a); b.add_assign(&a);
a.double(); a.double();
@@ -1988,11 +2016,14 @@ fn test_fq_negate() {
assert!(a.is_zero()); assert!(a.is_zero());
} }
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure (a - (-a)) = 0. // Ensure (a - (-a)) = 0.
let mut a = Fq::rand(&mut rng); let mut a = Fq::random(&mut rng);
let mut b = a; let mut b = a;
b.negate(); b.negate();
a.add_assign(&b); a.add_assign(&b);
@@ -2003,12 +2034,15 @@ fn test_fq_negate() {
#[test] #[test]
fn test_fq_pow() { fn test_fq_pow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for i in 0..1000 { for i in 0..1000 {
// Exponentiate by various small numbers and ensure it consists with repeated // Exponentiate by various small numbers and ensure it consists with repeated
// multiplication. // multiplication.
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
let target = a.pow(&[i]); let target = a.pow(&[i]);
let mut c = Fq::one(); let mut c = Fq::one();
for _ in 0..i { for _ in 0..i {
@@ -2019,7 +2053,7 @@ fn test_fq_pow() {
for _ in 0..1000 { for _ in 0..1000 {
// Exponentiating by the modulus should have no effect in a prime field. // Exponentiating by the modulus should have no effect in a prime field.
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
assert_eq!(a, a.pow(Fq::char())); assert_eq!(a, a.pow(Fq::char()));
} }
@@ -2029,13 +2063,16 @@ fn test_fq_pow() {
fn test_fq_sqrt() { fn test_fq_sqrt() {
use ff::SqrtField; use ff::SqrtField;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero()); assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero());
for _ in 0..1000 { for _ in 0..1000 {
// Ensure sqrt(a^2) = a or -a // Ensure sqrt(a^2) = a or -a
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
let mut nega = a; let mut nega = a;
nega.negate(); nega.negate();
let mut b = a; let mut b = a;
@@ -2048,7 +2085,7 @@ fn test_fq_sqrt() {
for _ in 0..1000 { for _ in 0..1000 {
// Ensure sqrt(a)^2 = a for random a // Ensure sqrt(a)^2 = a for random a
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
if let Some(mut tmp) = a.sqrt() { if let Some(mut tmp) = a.sqrt() {
tmp.square(); tmp.square();
@@ -2061,16 +2098,15 @@ fn test_fq_sqrt() {
#[test] #[test]
fn test_fq_from_into_repr() { fn test_fq_from_into_repr() {
// q + 1 should not be in the field // q + 1 should not be in the field
assert!( assert!(Fq::from_repr(FqRepr([
Fq::from_repr(FqRepr([ 0xb9feffffffffaaac,
0xb9feffffffffaaac, 0x1eabfffeb153ffff,
0x1eabfffeb153ffff, 0x6730d2a0f6b0f624,
0x6730d2a0f6b0f624, 0x64774b84f38512bf,
0x64774b84f38512bf, 0x4b1ba7b6434bacd7,
0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a
0x1a0111ea397fe69a ]))
])).is_err() .is_err());
);
// q should not be in the field // q should not be in the field
assert!(Fq::from_repr(Fq::char()).is_err()); assert!(Fq::from_repr(Fq::char()).is_err());
@@ -2108,11 +2144,14 @@ fn test_fq_from_into_repr() {
// Zero should be in the field. // Zero should be in the field.
assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero()); assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Try to turn Fq elements into representations and back again, and compare. // Try to turn Fq elements into representations and back again, and compare.
let a = Fq::rand(&mut rng); let a = Fq::random(&mut rng);
let a_repr = a.into_repr(); let a_repr = a.into_repr();
let b_repr = FqRepr::from(a); let b_repr = FqRepr::from(a);
assert_eq!(a_repr, b_repr); assert_eq!(a_repr, b_repr);
@@ -2186,10 +2225,10 @@ fn test_fq_root_of_unity() {
#[test] #[test]
fn fq_field_tests() { fn fq_field_tests() {
::tests::field::random_field_tests::<Fq>(); crate::tests::field::random_field_tests::<Fq>();
::tests::field::random_sqrt_tests::<Fq>(); crate::tests::field::random_sqrt_tests::<Fq>();
::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13); crate::tests::field::random_frobenius_tests::<Fq, _>(Fq::char(), 13);
::tests::field::from_str_tests::<Fq>(); crate::tests::field::from_str_tests::<Fq>();
} }
#[test] #[test]
@@ -2205,7 +2244,7 @@ fn test_fq_ordering() {
#[test] #[test]
fn fq_repr_tests() { fn fq_repr_tests() {
::tests::repr::random_repr_tests::<FqRepr>(); crate::tests::repr::random_repr_tests::<Fq>();
} }
#[test] #[test]

View File

@@ -2,7 +2,7 @@ use super::fq::FROBENIUS_COEFF_FQ12_C1;
use super::fq2::Fq2; use super::fq2::Fq2;
use super::fq6::Fq6; use super::fq6::Fq6;
use ff::Field; use ff::Field;
use rand::{Rand, Rng}; use rand_core::RngCore;
/// An element of Fq12, represented by c0 + c1 * w. /// An element of Fq12, represented by c0 + c1 * w.
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -12,20 +12,11 @@ pub struct Fq12 {
} }
impl ::std::fmt::Display for Fq12 { impl ::std::fmt::Display for Fq12 {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "Fq12({} + {} * w)", self.c0, self.c1) write!(f, "Fq12({} + {} * w)", self.c0, self.c1)
} }
} }
impl Rand for Fq12 {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fq12 {
c0: rng.gen(),
c1: rng.gen(),
}
}
}
impl Fq12 { impl Fq12 {
pub fn conjugate(&mut self) { pub fn conjugate(&mut self) {
self.c1.negate(); self.c1.negate();
@@ -49,6 +40,13 @@ impl Fq12 {
} }
impl Field for Fq12 { impl Field for Fq12 {
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fq12 {
c0: Fq6::random(rng),
c1: Fq6::random(rng),
}
}
fn zero() -> Self { fn zero() -> Self {
Fq12 { Fq12 {
c0: Fq6::zero(), c0: Fq6::zero(),
@@ -149,24 +147,29 @@ impl Field for Fq12 {
} }
#[cfg(test)] #[cfg(test)]
use rand::{SeedableRng, XorShiftRng}; use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq12_mul_by_014() { fn test_fq12_mul_by_014() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let c0 = Fq2::rand(&mut rng); let c0 = Fq2::random(&mut rng);
let c1 = Fq2::rand(&mut rng); let c1 = Fq2::random(&mut rng);
let c5 = Fq2::rand(&mut rng); let c5 = Fq2::random(&mut rng);
let mut a = Fq12::rand(&mut rng); let mut a = Fq12::random(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_014(&c0, &c1, &c5); a.mul_by_014(&c0, &c1, &c5);
b.mul_assign(&Fq12 { b.mul_assign(&Fq12 {
c0: Fq6 { c0: Fq6 {
c0: c0, c0,
c1: c1, c1,
c2: Fq2::zero(), c2: Fq2::zero(),
}, },
c1: Fq6 { c1: Fq6 {
@@ -184,6 +187,6 @@ fn test_fq12_mul_by_014() {
fn fq12_field_tests() { fn fq12_field_tests() {
use ff::PrimeField; use ff::PrimeField;
::tests::field::random_field_tests::<Fq12>(); crate::tests::field::random_field_tests::<Fq12>();
::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13); crate::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
} }

View File

@@ -1,6 +1,6 @@
use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE}; use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE};
use ff::{Field, SqrtField}; use ff::{Field, SqrtField};
use rand::{Rand, Rng}; use rand_core::RngCore;
use std::cmp::Ordering; use std::cmp::Ordering;
@@ -12,7 +12,7 @@ pub struct Fq2 {
} }
impl ::std::fmt::Display for Fq2 { impl ::std::fmt::Display for Fq2 {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "Fq2({} + {} * u)", self.c0, self.c1) write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
} }
} }
@@ -56,16 +56,14 @@ impl Fq2 {
} }
} }
impl Rand for Fq2 { impl Field for Fq2 {
fn rand<R: Rng>(rng: &mut R) -> Self { fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fq2 { Fq2 {
c0: rng.gen(), c0: Fq::random(rng),
c1: rng.gen(), c1: Fq::random(rng),
} }
} }
}
impl Field for Fq2 {
fn zero() -> Self { fn zero() -> Self {
Fq2 { Fq2 {
c0: Fq::zero(), c0: Fq::zero(),
@@ -263,12 +261,11 @@ fn test_fq2_basics() {
); );
assert!(Fq2::zero().is_zero()); assert!(Fq2::zero().is_zero());
assert!(!Fq2::one().is_zero()); assert!(!Fq2::one().is_zero());
assert!( assert!(!Fq2 {
!Fq2 { c0: Fq::zero(),
c0: Fq::zero(), c1: Fq::one(),
c1: Fq::one(), }
}.is_zero() .is_zero());
);
} }
#[test] #[test]
@@ -311,7 +308,8 @@ fn test_fq2_squaring() {
0xf7f295a94e58ae7c, 0xf7f295a94e58ae7c,
0x41b76dcc1c3fbe5e, 0x41b76dcc1c3fbe5e,
0x7080c5fa1d8e042, 0x7080c5fa1d8e042,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x38f473b3c870a4ab, 0x38f473b3c870a4ab,
0x6ad3291177c8c7e5, 0x6ad3291177c8c7e5,
@@ -319,7 +317,8 @@ fn test_fq2_squaring() {
0xbfb99020604137a0, 0xbfb99020604137a0,
0xfc58a7b7be815407, 0xfc58a7b7be815407,
0x10d1615e75250a21, 0x10d1615e75250a21,
])).unwrap(), ]))
.unwrap(),
}; };
a.square(); a.square();
assert_eq!( assert_eq!(
@@ -332,7 +331,8 @@ fn test_fq2_squaring() {
0xcb674157618da176, 0xcb674157618da176,
0x4cf17b5893c3d327, 0x4cf17b5893c3d327,
0x7eac81369c43361 0x7eac81369c43361
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xc1579cf58e980cf8, 0xc1579cf58e980cf8,
0xa23eb7e12dd54d98, 0xa23eb7e12dd54d98,
@@ -340,7 +340,8 @@ fn test_fq2_squaring() {
0x38d0d7275a9689e1, 0x38d0d7275a9689e1,
0x739c983042779a65, 0x739c983042779a65,
0x1542a61c8a8db994 0x1542a61c8a8db994
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@@ -358,7 +359,8 @@ fn test_fq2_mul() {
0x9ee53e7e84d7532e, 0x9ee53e7e84d7532e,
0x1c202d8ed97afb45, 0x1c202d8ed97afb45,
0x51d3f9253e2516f, 0x51d3f9253e2516f,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf, 0xa7348a8b511aedcf,
0x143c215d8176b319, 0x143c215d8176b319,
@@ -366,7 +368,8 @@ fn test_fq2_mul() {
0x9533e4a9a5158be, 0x9533e4a9a5158be,
0x7a5e1ecb676d65f9, 0x7a5e1ecb676d65f9,
0x180c3ee46656b008, 0x180c3ee46656b008,
])).unwrap(), ]))
.unwrap(),
}; };
a.mul_assign(&Fq2 { a.mul_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -376,7 +379,8 @@ fn test_fq2_mul() {
0xcd460f9f0c23e430, 0xcd460f9f0c23e430,
0x6c9110292bfa409, 0x6c9110292bfa409,
0x2c93a72eb8af83e, 0x2c93a72eb8af83e,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x4b1c3f936d8992d4, 0x4b1c3f936d8992d4,
0x1d2a72916dba4c8a, 0x1d2a72916dba4c8a,
@@ -384,7 +388,8 @@ fn test_fq2_mul() {
0x57a06d3135a752ae, 0x57a06d3135a752ae,
0x634cd3c6c565096d, 0x634cd3c6c565096d,
0x19e17334d4e93558, 0x19e17334d4e93558,
])).unwrap(), ]))
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@@ -396,7 +401,8 @@ fn test_fq2_mul() {
0x5511fe4d84ee5f78, 0x5511fe4d84ee5f78,
0x5310a202d92f9963, 0x5310a202d92f9963,
0x1751afbe166e5399 0x1751afbe166e5399
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x84af0e1bd630117a, 0x84af0e1bd630117a,
0x6c63cd4da2c2aa7, 0x6c63cd4da2c2aa7,
@@ -404,7 +410,8 @@ fn test_fq2_mul() {
0xc975106579c275ee, 0xc975106579c275ee,
0x33a9ac82ce4c5083, 0x33a9ac82ce4c5083,
0x1ef1a36c201589d 0x1ef1a36c201589d
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@@ -424,7 +431,8 @@ fn test_fq2_inverse() {
0x9ee53e7e84d7532e, 0x9ee53e7e84d7532e,
0x1c202d8ed97afb45, 0x1c202d8ed97afb45,
0x51d3f9253e2516f, 0x51d3f9253e2516f,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf, 0xa7348a8b511aedcf,
0x143c215d8176b319, 0x143c215d8176b319,
@@ -432,7 +440,8 @@ fn test_fq2_inverse() {
0x9533e4a9a5158be, 0x9533e4a9a5158be,
0x7a5e1ecb676d65f9, 0x7a5e1ecb676d65f9,
0x180c3ee46656b008, 0x180c3ee46656b008,
])).unwrap(), ]))
.unwrap(),
}; };
let a = a.inverse().unwrap(); let a = a.inverse().unwrap();
assert_eq!( assert_eq!(
@@ -445,7 +454,8 @@ fn test_fq2_inverse() {
0xdfba703293941c30, 0xdfba703293941c30,
0xa6c3d8f9586f2636, 0xa6c3d8f9586f2636,
0x1351ef01941b70c4 0x1351ef01941b70c4
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x8c39fd76a8312cb4, 0x8c39fd76a8312cb4,
0x15d7b6b95defbff0, 0x15d7b6b95defbff0,
@@ -453,7 +463,8 @@ fn test_fq2_inverse() {
0xcbf651a0f367afb2, 0xcbf651a0f367afb2,
0xdf4e54f0d3ef15a6, 0xdf4e54f0d3ef15a6,
0x103bdf241afb0019 0x103bdf241afb0019
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@@ -471,7 +482,8 @@ fn test_fq2_addition() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -479,7 +491,8 @@ fn test_fq2_addition() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.add_assign(&Fq2 { a.add_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -489,7 +502,8 @@ fn test_fq2_addition() {
0x3b88899a42a6318f, 0x3b88899a42a6318f,
0x986a4a62fa82a49d, 0x986a4a62fa82a49d,
0x13ce433fa26027f5, 0x13ce433fa26027f5,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9, 0x66323bf80b58b9b9,
0xa1379b6facf6e596, 0xa1379b6facf6e596,
@@ -497,7 +511,8 @@ fn test_fq2_addition() {
0x2236f55246d0d44d, 0x2236f55246d0d44d,
0x4c8c1800eb104566, 0x4c8c1800eb104566,
0x11d6e20e986c2085, 0x11d6e20e986c2085,
])).unwrap(), ]))
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@@ -509,7 +524,8 @@ fn test_fq2_addition() {
0xf4ef57d604b6bca2, 0xf4ef57d604b6bca2,
0x65309427b3d5d090, 0x65309427b3d5d090,
0x14c715d5553f01d2 0x14c715d5553f01d2
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xfdb032e7d9079a94, 0xfdb032e7d9079a94,
0x35a2809d15468d83, 0x35a2809d15468d83,
@@ -517,7 +533,8 @@ fn test_fq2_addition() {
0xd62fa51334f560fa, 0xd62fa51334f560fa,
0x9ad265eb46e01984, 0x9ad265eb46e01984,
0x1303f3465112c8bc 0x1303f3465112c8bc
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@@ -535,7 +552,8 @@ fn test_fq2_subtraction() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -543,7 +561,8 @@ fn test_fq2_subtraction() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.sub_assign(&Fq2 { a.sub_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@@ -553,7 +572,8 @@ fn test_fq2_subtraction() {
0x3b88899a42a6318f, 0x3b88899a42a6318f,
0x986a4a62fa82a49d, 0x986a4a62fa82a49d,
0x13ce433fa26027f5, 0x13ce433fa26027f5,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9, 0x66323bf80b58b9b9,
0xa1379b6facf6e596, 0xa1379b6facf6e596,
@@ -561,7 +581,8 @@ fn test_fq2_subtraction() {
0x2236f55246d0d44d, 0x2236f55246d0d44d,
0x4c8c1800eb104566, 0x4c8c1800eb104566,
0x11d6e20e986c2085, 0x11d6e20e986c2085,
])).unwrap(), ]))
.unwrap(),
}); });
assert_eq!( assert_eq!(
a, a,
@@ -573,7 +594,8 @@ fn test_fq2_subtraction() {
0xe255902672ef6c43, 0xe255902672ef6c43,
0x7f77a718021c342d, 0x7f77a718021c342d,
0x72ba14049fe9881 0x72ba14049fe9881
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xeb4abaf7c255d1cd, 0xeb4abaf7c255d1cd,
0x11df49bc6cacc256, 0x11df49bc6cacc256,
@@ -581,7 +603,8 @@ fn test_fq2_subtraction() {
0xf63905f39ad8cb1f, 0xf63905f39ad8cb1f,
0x4cd5dd9fb40b3b8f, 0x4cd5dd9fb40b3b8f,
0x957411359ba6e4c 0x957411359ba6e4c
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@@ -599,7 +622,8 @@ fn test_fq2_negation() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -607,7 +631,8 @@ fn test_fq2_negation() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.negate(); a.negate();
assert_eq!( assert_eq!(
@@ -620,7 +645,8 @@ fn test_fq2_negation() {
0xab107d49317487ab, 0xab107d49317487ab,
0x7e555df189f880e3, 0x7e555df189f880e3,
0x19083f5486a10cbd 0x19083f5486a10cbd
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x228109103250c9d0, 0x228109103250c9d0,
0x8a411ad149045812, 0x8a411ad149045812,
@@ -628,7 +654,8 @@ fn test_fq2_negation() {
0xb07e9bc405608611, 0xb07e9bc405608611,
0xfcd559cbe77bd8b8, 0xfcd559cbe77bd8b8,
0x18d400b280d93e62 0x18d400b280d93e62
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@@ -646,7 +673,8 @@ fn test_fq2_doubling() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -654,7 +682,8 @@ fn test_fq2_doubling() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.double(); a.double();
assert_eq!( assert_eq!(
@@ -667,7 +696,8 @@ fn test_fq2_doubling() {
0x72cd9c7784211627, 0x72cd9c7784211627,
0x998c938972a657e7, 0x998c938972a657e7,
0x1f1a52b65bdb3b9 0x1f1a52b65bdb3b9
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x2efbeddf9b5dc1b6, 0x2efbeddf9b5dc1b6,
0x28d5ca5ad09f4fdb, 0x28d5ca5ad09f4fdb,
@@ -675,7 +705,8 @@ fn test_fq2_doubling() {
0x67f15f81dc49195b, 0x67f15f81dc49195b,
0x9c8c9bd4b79fa83d, 0x9c8c9bd4b79fa83d,
0x25a226f714d506e 0x25a226f714d506e
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@@ -693,7 +724,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc, 0xf8d295b2ded9dc,
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -701,7 +733,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837, 0x12d1137b8a6a837,
])).unwrap(), ]))
.unwrap(),
}; };
a.frobenius_map(0); a.frobenius_map(0);
assert_eq!( assert_eq!(
@@ -714,7 +747,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -722,7 +756,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])).unwrap(), ]))
.unwrap(),
} }
); );
a.frobenius_map(1); a.frobenius_map(1);
@@ -736,7 +771,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x228109103250c9d0, 0x228109103250c9d0,
0x8a411ad149045812, 0x8a411ad149045812,
@@ -744,7 +780,8 @@ fn test_fq2_frobenius_map() {
0xb07e9bc405608611, 0xb07e9bc405608611,
0xfcd559cbe77bd8b8, 0xfcd559cbe77bd8b8,
0x18d400b280d93e62 0x18d400b280d93e62
])).unwrap(), ]))
.unwrap(),
} }
); );
a.frobenius_map(1); a.frobenius_map(1);
@@ -758,7 +795,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -766,7 +804,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])).unwrap(), ]))
.unwrap(),
} }
); );
a.frobenius_map(2); a.frobenius_map(2);
@@ -780,7 +819,8 @@ fn test_fq2_frobenius_map() {
0xb966ce3bc2108b13, 0xb966ce3bc2108b13,
0xccc649c4b9532bf3, 0xccc649c4b9532bf3,
0xf8d295b2ded9dc 0xf8d295b2ded9dc
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db, 0x977df6efcdaee0db,
0x946ae52d684fa7ed, 0x946ae52d684fa7ed,
@@ -788,7 +828,8 @@ fn test_fq2_frobenius_map() {
0xb3f8afc0ee248cad, 0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e, 0x4e464dea5bcfd41e,
0x12d1137b8a6a837 0x12d1137b8a6a837
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@@ -807,7 +848,8 @@ fn test_fq2_sqrt() {
0xdb4a116b5bf74aa1, 0xdb4a116b5bf74aa1,
0x1e58b2159dfe10e2, 0x1e58b2159dfe10e2,
0x7ca7da1f13606ac 0x7ca7da1f13606ac
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xfa8de88b7516d2c3, 0xfa8de88b7516d2c3,
0x371a75ed14f41629, 0x371a75ed14f41629,
@@ -815,9 +857,11 @@ fn test_fq2_sqrt() {
0x212611bca4e99121, 0x212611bca4e99121,
0x8ee5394d77afb3d, 0x8ee5394d77afb3d,
0xec92336650e49d5 0xec92336650e49d5
])).unwrap(), ]))
}.sqrt()
.unwrap(), .unwrap(),
}
.sqrt()
.unwrap(),
Fq2 { Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
0x40b299b2704258c5, 0x40b299b2704258c5,
@@ -826,7 +870,8 @@ fn test_fq2_sqrt() {
0x8d7f1f723d02c1d3, 0x8d7f1f723d02c1d3,
0x881b3e01b611c070, 0x881b3e01b611c070,
0x10f6963bbad2ebc5 0x10f6963bbad2ebc5
])).unwrap(), ]))
.unwrap(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
0xc099534fc209e752, 0xc099534fc209e752,
0x7670594665676447, 0x7670594665676447,
@@ -834,7 +879,8 @@ fn test_fq2_sqrt() {
0x6b852aeaf2afcb1b, 0x6b852aeaf2afcb1b,
0xa4c93b08105d71a9, 0xa4c93b08105d71a9,
0x8d7cfff94216330 0x8d7cfff94216330
])).unwrap(), ]))
.unwrap(),
} }
); );
@@ -847,10 +893,12 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf, 0x64774b84f38512bf,
0x4b1ba7b6434bacd7, 0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a 0x1a0111ea397fe69a
])).unwrap(), ]))
c1: Fq::zero(),
}.sqrt()
.unwrap(), .unwrap(),
c1: Fq::zero(),
}
.sqrt()
.unwrap(),
Fq2 { Fq2 {
c0: Fq::zero(), c0: Fq::zero(),
c1: Fq::from_repr(FqRepr([ c1: Fq::from_repr(FqRepr([
@@ -860,7 +908,8 @@ fn test_fq2_sqrt() {
0x64774b84f38512bf, 0x64774b84f38512bf,
0x4b1ba7b6434bacd7, 0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a 0x1a0111ea397fe69a
])).unwrap(), ]))
.unwrap(),
} }
); );
} }
@@ -879,11 +928,16 @@ fn test_fq2_legendre() {
} }
#[cfg(test)] #[cfg(test)]
use rand::{SeedableRng, XorShiftRng}; use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq2_mul_nonresidue() { fn test_fq2_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let nqr = Fq2 { let nqr = Fq2 {
c0: Fq::one(), c0: Fq::one(),
@@ -891,7 +945,7 @@ fn test_fq2_mul_nonresidue() {
}; };
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fq2::rand(&mut rng); let mut a = Fq2::random(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_nonresidue(); a.mul_by_nonresidue();
b.mul_assign(&nqr); b.mul_assign(&nqr);
@@ -904,7 +958,7 @@ fn test_fq2_mul_nonresidue() {
fn fq2_field_tests() { fn fq2_field_tests() {
use ff::PrimeField; use ff::PrimeField;
::tests::field::random_field_tests::<Fq2>(); crate::tests::field::random_field_tests::<Fq2>();
::tests::field::random_sqrt_tests::<Fq2>(); crate::tests::field::random_sqrt_tests::<Fq2>();
::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13); crate::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
} }

View File

@@ -1,7 +1,7 @@
use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2};
use super::fq2::Fq2; use super::fq2::Fq2;
use ff::Field; use ff::Field;
use rand::{Rand, Rng}; use rand_core::RngCore;
/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). /// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2).
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -12,21 +12,11 @@ pub struct Fq6 {
} }
impl ::std::fmt::Display for Fq6 { impl ::std::fmt::Display for Fq6 {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2) write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2)
} }
} }
impl Rand for Fq6 {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fq6 {
c0: rng.gen(),
c1: rng.gen(),
c2: rng.gen(),
}
}
}
impl Fq6 { impl Fq6 {
/// Multiply by quadratic nonresidue v. /// Multiply by quadratic nonresidue v.
pub fn mul_by_nonresidue(&mut self) { pub fn mul_by_nonresidue(&mut self) {
@@ -110,6 +100,14 @@ impl Fq6 {
} }
impl Field for Fq6 { impl Field for Fq6 {
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
Fq6 {
c0: Fq2::random(rng),
c1: Fq2::random(rng),
c2: Fq2::random(rng),
}
}
fn zero() -> Self { fn zero() -> Self {
Fq6 { Fq6 {
c0: Fq2::zero(), c0: Fq2::zero(),
@@ -302,11 +300,16 @@ impl Field for Fq6 {
} }
#[cfg(test)] #[cfg(test)]
use rand::{SeedableRng, XorShiftRng}; use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fq6_mul_nonresidue() { fn test_fq6_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let nqr = Fq6 { let nqr = Fq6 {
c0: Fq2::zero(), c0: Fq2::zero(),
@@ -315,7 +318,7 @@ fn test_fq6_mul_nonresidue() {
}; };
for _ in 0..1000 { for _ in 0..1000 {
let mut a = Fq6::rand(&mut rng); let mut a = Fq6::random(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_nonresidue(); a.mul_by_nonresidue();
b.mul_assign(&nqr); b.mul_assign(&nqr);
@@ -326,17 +329,20 @@ fn test_fq6_mul_nonresidue() {
#[test] #[test]
fn test_fq6_mul_by_1() { fn test_fq6_mul_by_1() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let c1 = Fq2::rand(&mut rng); let c1 = Fq2::random(&mut rng);
let mut a = Fq6::rand(&mut rng); let mut a = Fq6::random(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_1(&c1); a.mul_by_1(&c1);
b.mul_assign(&Fq6 { b.mul_assign(&Fq6 {
c0: Fq2::zero(), c0: Fq2::zero(),
c1: c1, c1,
c2: Fq2::zero(), c2: Fq2::zero(),
}); });
@@ -346,18 +352,21 @@ fn test_fq6_mul_by_1() {
#[test] #[test]
fn test_fq6_mul_by_01() { fn test_fq6_mul_by_01() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let c0 = Fq2::rand(&mut rng); let c0 = Fq2::random(&mut rng);
let c1 = Fq2::rand(&mut rng); let c1 = Fq2::random(&mut rng);
let mut a = Fq6::rand(&mut rng); let mut a = Fq6::random(&mut rng);
let mut b = a; let mut b = a;
a.mul_by_01(&c0, &c1); a.mul_by_01(&c0, &c1);
b.mul_assign(&Fq6 { b.mul_assign(&Fq6 {
c0: c0, c0,
c1: c1, c1,
c2: Fq2::zero(), c2: Fq2::zero(),
}); });
@@ -369,6 +378,6 @@ fn test_fq6_mul_by_01() {
fn fq6_field_tests() { fn fq6_field_tests() {
use ff::PrimeField; use ff::PrimeField;
::tests::field::random_field_tests::<Fq6>(); crate::tests::field::random_field_tests::<Fq6>();
::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13); crate::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
} }

View File

@@ -6,7 +6,9 @@ use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr};
pub struct Fr(FrRepr); pub struct Fr(FrRepr);
#[cfg(test)] #[cfg(test)]
use rand::{Rand, SeedableRng, XorShiftRng}; use rand_core::SeedableRng;
#[cfg(test)]
use rand_xorshift::XorShiftRng;
#[test] #[test]
fn test_fr_repr_ordering() { fn test_fr_repr_ordering() {
@@ -197,7 +199,10 @@ fn test_fr_repr_num_bits() {
#[test] #[test]
fn test_fr_repr_sub_noborrow() { fn test_fr_repr_sub_noborrow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FrRepr([ let mut t = FrRepr([
0x8e62a7e85264e2c3, 0x8e62a7e85264e2c3,
@@ -221,7 +226,7 @@ fn test_fr_repr_sub_noborrow() {
); );
for _ in 0..1000 { for _ in 0..1000 {
let mut a = FrRepr::rand(&mut rng); let mut a = Fr::random(&mut rng).into_repr();
a.0[3] >>= 30; a.0[3] >>= 30;
let mut b = a; let mut b = a;
for _ in 0..10 { for _ in 0..10 {
@@ -296,7 +301,10 @@ fn test_fr_legendre() {
#[test] #[test]
fn test_fr_repr_add_nocarry() { fn test_fr_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let mut t = FrRepr([ let mut t = FrRepr([
0xd64f669809cbc6a4, 0xd64f669809cbc6a4,
@@ -322,9 +330,9 @@ fn test_fr_repr_add_nocarry() {
// Test for the associativity of addition. // Test for the associativity of addition.
for _ in 0..1000 { for _ in 0..1000 {
let mut a = FrRepr::rand(&mut rng); let mut a = Fr::random(&mut rng).into_repr();
let mut b = FrRepr::rand(&mut rng); let mut b = Fr::random(&mut rng).into_repr();
let mut c = FrRepr::rand(&mut rng); let mut c = Fr::random(&mut rng).into_repr();
// Unset the first few bits, so that overflow won't occur. // Unset the first few bits, so that overflow won't occur.
a.0[3] >>= 3; a.0[3] >>= 3;
@@ -380,27 +388,28 @@ fn test_fr_is_valid() {
a.0.sub_noborrow(&FrRepr::from(1)); a.0.sub_noborrow(&FrRepr::from(1));
assert!(a.is_valid()); assert!(a.is_valid());
assert!(Fr(FrRepr::from(0)).is_valid()); assert!(Fr(FrRepr::from(0)).is_valid());
assert!( assert!(Fr(FrRepr([
Fr(FrRepr([ 0xffffffff00000000,
0xffffffff00000000, 0x53bda402fffe5bfe,
0x53bda402fffe5bfe, 0x3339d80809a1d805,
0x3339d80809a1d805, 0x73eda753299d7d48
0x73eda753299d7d48 ]))
])).is_valid() .is_valid());
); assert!(!Fr(FrRepr([
assert!( 0xffffffffffffffff,
!Fr(FrRepr([ 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff
0xffffffffffffffff, ]))
0xffffffffffffffff .is_valid());
])).is_valid()
);
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
assert!(a.is_valid()); assert!(a.is_valid());
} }
} }
@@ -492,13 +501,16 @@ fn test_fr_add_assign() {
// Test associativity // Test associativity
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Generate a, b, c and ensure (a + b) + c == a + (b + c). // Generate a, b, c and ensure (a + b) + c == a + (b + c).
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
let b = Fr::rand(&mut rng); let b = Fr::random(&mut rng);
let c = Fr::rand(&mut rng); let c = Fr::random(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.add_assign(&b); tmp1.add_assign(&b);
@@ -586,12 +598,15 @@ fn test_fr_sub_assign() {
); );
} }
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure that (a - b) + (b - a) = 0. // Ensure that (a - b) + (b - a) = 0.
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
let b = Fr::rand(&mut rng); let b = Fr::random(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.sub_assign(&b); tmp1.sub_assign(&b);
@@ -627,13 +642,16 @@ fn test_fr_mul_assign() {
])) ]))
); );
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that (a * b) * c = a * (b * c) // Ensure that (a * b) * c = a * (b * c)
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
let b = Fr::rand(&mut rng); let b = Fr::random(&mut rng);
let c = Fr::rand(&mut rng); let c = Fr::random(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.mul_assign(&b); tmp1.mul_assign(&b);
@@ -649,10 +667,10 @@ fn test_fr_mul_assign() {
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that r * (a + b + c) = r*a + r*b + r*c // Ensure that r * (a + b + c) = r*a + r*b + r*c
let r = Fr::rand(&mut rng); let r = Fr::random(&mut rng);
let mut a = Fr::rand(&mut rng); let mut a = Fr::random(&mut rng);
let mut b = Fr::rand(&mut rng); let mut b = Fr::random(&mut rng);
let mut c = Fr::rand(&mut rng); let mut c = Fr::random(&mut rng);
let mut tmp1 = a; let mut tmp1 = a;
tmp1.add_assign(&b); tmp1.add_assign(&b);
@@ -687,14 +705,18 @@ fn test_fr_squaring() {
0xb79a310579e76ec2, 0xb79a310579e76ec2,
0xac1da8d0a9af4e5f, 0xac1da8d0a9af4e5f,
0x13f629c49bf23e97 0x13f629c49bf23e97
])).unwrap() ]))
.unwrap()
); );
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000000 { for _ in 0..1000000 {
// Ensure that (a * a) = a^2 // Ensure that (a * a) = a^2
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
let mut tmp = a; let mut tmp = a;
tmp.square(); tmp.square();
@@ -710,13 +732,16 @@ fn test_fr_squaring() {
fn test_fr_inverse() { fn test_fr_inverse() {
assert!(Fr::zero().inverse().is_none()); assert!(Fr::zero().inverse().is_none());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let one = Fr::one(); let one = Fr::one();
for _ in 0..1000 { for _ in 0..1000 {
// Ensure that a * a^-1 = 1 // Ensure that a * a^-1 = 1
let mut a = Fr::rand(&mut rng); let mut a = Fr::random(&mut rng);
let ainv = a.inverse().unwrap(); let ainv = a.inverse().unwrap();
a.mul_assign(&ainv); a.mul_assign(&ainv);
assert_eq!(a, one); assert_eq!(a, one);
@@ -725,11 +750,14 @@ fn test_fr_inverse() {
#[test] #[test]
fn test_fr_double() { fn test_fr_double() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure doubling a is equivalent to adding a to itself. // Ensure doubling a is equivalent to adding a to itself.
let mut a = Fr::rand(&mut rng); let mut a = Fr::random(&mut rng);
let mut b = a; let mut b = a;
b.add_assign(&a); b.add_assign(&a);
a.double(); a.double();
@@ -746,11 +774,14 @@ fn test_fr_negate() {
assert!(a.is_zero()); assert!(a.is_zero());
} }
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Ensure (a - (-a)) = 0. // Ensure (a - (-a)) = 0.
let mut a = Fr::rand(&mut rng); let mut a = Fr::random(&mut rng);
let mut b = a; let mut b = a;
b.negate(); b.negate();
a.add_assign(&b); a.add_assign(&b);
@@ -761,12 +792,15 @@ fn test_fr_negate() {
#[test] #[test]
fn test_fr_pow() { fn test_fr_pow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for i in 0..1000 { for i in 0..1000 {
// Exponentiate by various small numbers and ensure it consists with repeated // Exponentiate by various small numbers and ensure it consists with repeated
// multiplication. // multiplication.
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
let target = a.pow(&[i]); let target = a.pow(&[i]);
let mut c = Fr::one(); let mut c = Fr::one();
for _ in 0..i { for _ in 0..i {
@@ -777,7 +811,7 @@ fn test_fr_pow() {
for _ in 0..1000 { for _ in 0..1000 {
// Exponentiating by the modulus should have no effect in a prime field. // Exponentiating by the modulus should have no effect in a prime field.
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
assert_eq!(a, a.pow(Fr::char())); assert_eq!(a, a.pow(Fr::char()));
} }
@@ -787,13 +821,16 @@ fn test_fr_pow() {
fn test_fr_sqrt() { fn test_fr_sqrt() {
use ff::SqrtField; use ff::SqrtField;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero()); assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero());
for _ in 0..1000 { for _ in 0..1000 {
// Ensure sqrt(a^2) = a or -a // Ensure sqrt(a^2) = a or -a
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
let mut nega = a; let mut nega = a;
nega.negate(); nega.negate();
let mut b = a; let mut b = a;
@@ -806,7 +843,7 @@ fn test_fr_sqrt() {
for _ in 0..1000 { for _ in 0..1000 {
// Ensure sqrt(a)^2 = a for random a // Ensure sqrt(a)^2 = a for random a
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
if let Some(mut tmp) = a.sqrt() { if let Some(mut tmp) = a.sqrt() {
tmp.square(); tmp.square();
@@ -819,14 +856,13 @@ fn test_fr_sqrt() {
#[test] #[test]
fn test_fr_from_into_repr() { fn test_fr_from_into_repr() {
// r + 1 should not be in the field // r + 1 should not be in the field
assert!( assert!(Fr::from_repr(FrRepr([
Fr::from_repr(FrRepr([ 0xffffffff00000002,
0xffffffff00000002, 0x53bda402fffe5bfe,
0x53bda402fffe5bfe, 0x3339d80809a1d805,
0x3339d80809a1d805, 0x73eda753299d7d48
0x73eda753299d7d48 ]))
])).is_err() .is_err());
);
// r should not be in the field // r should not be in the field
assert!(Fr::from_repr(Fr::char()).is_err()); assert!(Fr::from_repr(Fr::char()).is_err());
@@ -858,11 +894,14 @@ fn test_fr_from_into_repr() {
// Zero should be in the field. // Zero should be in the field.
assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero()); assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
// Try to turn Fr elements into representations and back again, and compare. // Try to turn Fr elements into representations and back again, and compare.
let a = Fr::rand(&mut rng); let a = Fr::random(&mut rng);
let a_repr = a.into_repr(); let a_repr = a.into_repr();
let b_repr = FrRepr::from(a); let b_repr = FrRepr::from(a);
assert_eq!(a_repr, b_repr); assert_eq!(a_repr, b_repr);
@@ -926,7 +965,8 @@ fn test_fr_display() {
0x185ec8eb3f5b5aee, 0x185ec8eb3f5b5aee,
0x684499ffe4b9dd99, 0x684499ffe4b9dd99,
0x7c9bba7afb68faa 0x7c9bba7afb68faa
])).unwrap() ]))
.unwrap()
), ),
"Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string() "Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string()
); );
@@ -938,7 +978,8 @@ fn test_fr_display() {
0xb0ad10817df79b6a, 0xb0ad10817df79b6a,
0xd034a80a2b74132b, 0xd034a80a2b74132b,
0x41cf9a1336f50719 0x41cf9a1336f50719
])).unwrap() ]))
.unwrap()
), ),
"Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string() "Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string()
); );
@@ -974,13 +1015,13 @@ fn test_fr_root_of_unity() {
#[test] #[test]
fn fr_field_tests() { fn fr_field_tests() {
::tests::field::random_field_tests::<Fr>(); crate::tests::field::random_field_tests::<Fr>();
::tests::field::random_sqrt_tests::<Fr>(); crate::tests::field::random_sqrt_tests::<Fr>();
::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13); crate::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
::tests::field::from_str_tests::<Fr>(); crate::tests::field::from_str_tests::<Fr>();
} }
#[test] #[test]
fn fr_repr_tests() { fn fr_repr_tests() {
::tests::repr::random_repr_tests::<FrRepr>(); crate::tests::repr::random_repr_tests::<Fr>();
} }

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;
@@ -9,8 +12,8 @@ mod fr;
mod tests; mod tests;
pub use self::ec::{ pub use self::ec::{
G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2, G2Affine, G2Compressed, G2Prepared, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared,
G2Uncompressed, G2Uncompressed, G1, G2,
}; };
pub use self::fq::{Fq, FqRepr}; pub use self::fq::{Fq, FqRepr};
pub use self::fq12::Fq12; pub use self::fq12::Fq12;
@@ -366,5 +369,5 @@ impl G2Prepared {
#[test] #[test]
fn bls12_engine_tests() { fn bls12_engine_tests() {
::tests::engine::engine_tests::<Bls12>(); crate::tests::engine::engine_tests::<Bls12>();
} }

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,21 +1,20 @@
//! A library for working with pairing-friendly curves.
// `clippy` is a code linting tool for improving code quality by catching // `clippy` is a code linting tool for improving code quality by catching
// common mistakes or strange code patterns. If the `cargo-clippy` feature // common mistakes or strange code patterns. If the `cargo-clippy` feature
// is provided, all compiler warnings are prohibited. // is provided, all compiler warnings are prohibited.
#![cfg_attr(feature = "cargo-clippy", deny(warnings))] #![cfg_attr(feature = "cargo-clippy", deny(warnings))]
#![cfg_attr(feature = "cargo-clippy", allow(inline_always))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))]
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))]
#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
#![cfg_attr(feature = "cargo-clippy", allow(write_literal))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))]
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
// Force public structures to implement Debug // Force public structures to implement Debug
#![deny(missing_debug_implementations)] #![deny(missing_debug_implementations)]
extern crate byteorder;
extern crate ff;
extern crate group;
extern crate rand;
#[cfg(test)] #[cfg(test)]
pub mod tests; pub mod tests;
@@ -34,8 +33,7 @@ pub trait Engine: ScalarEngine {
Base = Self::Fq, Base = Self::Fq,
Scalar = Self::Fr, Scalar = Self::Fr,
Affine = Self::G1Affine, Affine = Self::G1Affine,
> > + From<Self::G1Affine>;
+ From<Self::G1Affine>;
/// The affine representation of an element in G1. /// The affine representation of an element in G1.
type G1Affine: PairingCurveAffine< type G1Affine: PairingCurveAffine<
@@ -45,8 +43,7 @@ pub trait Engine: ScalarEngine {
Projective = Self::G1, Projective = Self::G1,
Pair = Self::G2Affine, Pair = Self::G2Affine,
PairingResult = Self::Fqk, PairingResult = Self::Fqk,
> > + From<Self::G1>;
+ From<Self::G1>;
/// The projective representation of an element in G2. /// The projective representation of an element in G2.
type G2: CurveProjective< type G2: CurveProjective<
@@ -54,8 +51,7 @@ pub trait Engine: ScalarEngine {
Base = Self::Fqe, Base = Self::Fqe,
Scalar = Self::Fr, Scalar = Self::Fr,
Affine = Self::G2Affine, Affine = Self::G2Affine,
> > + From<Self::G2Affine>;
+ From<Self::G2Affine>;
/// The affine representation of an element in G2. /// The affine representation of an element in G2.
type G2Affine: PairingCurveAffine< type G2Affine: PairingCurveAffine<
@@ -65,8 +61,7 @@ pub trait Engine: ScalarEngine {
Projective = Self::G2, Projective = Self::G2,
Pair = Self::G1Affine, Pair = Self::G1Affine,
PairingResult = Self::Fqk, PairingResult = Self::Fqk,
> > + From<Self::G2>;
+ From<Self::G2>;
/// The base field that hosts G1. /// The base field that hosts G1.
type Fq: PrimeField + SqrtField; type Fq: PrimeField + SqrtField;
@@ -88,7 +83,7 @@ pub trait Engine: ScalarEngine {
>; >;
/// Perform final exponentiation of the result of a miller loop. /// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(&Self::Fqk) -> Option<Self::Fqk>; fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk>;
/// Performs a complete pairing operation `(p, q)`. /// Performs a complete pairing operation `(p, q)`.
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
@@ -97,8 +92,9 @@ pub trait Engine: ScalarEngine {
G2: Into<Self::G2Affine>, G2: Into<Self::G2Affine>,
{ {
Self::final_exponentiation(&Self::miller_loop( Self::final_exponentiation(&Self::miller_loop(
[(&(p.into().prepare()), &(q.into().prepare()))].into_iter(), [(&(p.into().prepare()), &(q.into().prepare()))].iter(),
)).unwrap() ))
.unwrap()
} }
} }

View File

@@ -1,14 +1,18 @@
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use rand::{Rand, SeedableRng, XorShiftRng}; use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use {Engine, Field, PairingCurveAffine, PrimeField}; use crate::{Engine, Field, PairingCurveAffine, PrimeField};
pub fn engine_tests<E: Engine>() { pub fn engine_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..10 { for _ in 0..10 {
let a = E::G1::rand(&mut rng).into_affine(); let a = E::G1::random(&mut rng).into_affine();
let b = E::G2::rand(&mut rng).into_affine(); let b = E::G2::random(&mut rng).into_affine();
assert!(a.pairing_with(&b) == b.pairing_with(&a)); assert!(a.pairing_with(&b) == b.pairing_with(&a));
assert!(a.pairing_with(&b) == E::pairing(a, b)); assert!(a.pairing_with(&b) == E::pairing(a, b));
@@ -18,10 +22,10 @@ pub fn engine_tests<E: Engine>() {
let z1 = E::G1Affine::zero().prepare(); let z1 = E::G1Affine::zero().prepare();
let z2 = E::G2Affine::zero().prepare(); let z2 = E::G2Affine::zero().prepare();
let a = E::G1::rand(&mut rng).into_affine().prepare(); let a = E::G1::random(&mut rng).into_affine().prepare();
let b = E::G2::rand(&mut rng).into_affine().prepare(); let b = E::G2::random(&mut rng).into_affine().prepare();
let c = E::G1::rand(&mut rng).into_affine().prepare(); let c = E::G1::random(&mut rng).into_affine().prepare();
let d = E::G2::rand(&mut rng).into_affine().prepare(); let d = E::G2::random(&mut rng).into_affine().prepare();
assert_eq!( assert_eq!(
E::Fqk::one(), E::Fqk::one(),
@@ -49,12 +53,15 @@ pub fn engine_tests<E: Engine>() {
} }
fn random_miller_loop_tests<E: Engine>() { fn random_miller_loop_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
// Exercise the miller loop for a reduced pairing // Exercise the miller loop for a reduced pairing
for _ in 0..1000 { for _ in 0..1000 {
let a = E::G1::rand(&mut rng); let a = E::G1::random(&mut rng);
let b = E::G2::rand(&mut rng); let b = E::G2::random(&mut rng);
let p2 = E::pairing(a, b); let p2 = E::pairing(a, b);
@@ -68,10 +75,10 @@ fn random_miller_loop_tests<E: Engine>() {
// Exercise a double miller loop // Exercise a double miller loop
for _ in 0..1000 { for _ in 0..1000 {
let a = E::G1::rand(&mut rng); let a = E::G1::random(&mut rng);
let b = E::G2::rand(&mut rng); let b = E::G2::random(&mut rng);
let c = E::G1::rand(&mut rng); let c = E::G1::random(&mut rng);
let d = E::G2::rand(&mut rng); let d = E::G2::random(&mut rng);
let ab = E::pairing(a, b); let ab = E::pairing(a, b);
let cd = E::pairing(c, d); let cd = E::pairing(c, d);
@@ -92,14 +99,17 @@ fn random_miller_loop_tests<E: Engine>() {
} }
fn random_bilinearity_tests<E: Engine>() { fn random_bilinearity_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let a = E::G1::rand(&mut rng); let a = E::G1::random(&mut rng);
let b = E::G2::rand(&mut rng); let b = E::G2::random(&mut rng);
let c = E::Fr::rand(&mut rng); let c = E::Fr::random(&mut rng);
let d = E::Fr::rand(&mut rng); let d = E::Fr::random(&mut rng);
let mut ac = a; let mut ac = a;
ac.mul_assign(c); ac.mul_assign(c);

View File

@@ -1,12 +1,16 @@
use ff::{Field, LegendreSymbol, PrimeField, SqrtField}; use ff::{Field, LegendreSymbol, PrimeField, SqrtField};
use rand::{Rng, SeedableRng, XorShiftRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) { pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
for i in 0..(maxpower + 1) { for i in 0..(maxpower + 1) {
let mut a = F::rand(&mut rng); let mut a = F::random(&mut rng);
let mut b = a; let mut b = a;
for _ in 0..i { for _ in 0..i {
@@ -20,10 +24,13 @@ pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxp
} }
pub fn random_sqrt_tests<F: SqrtField>() { pub fn random_sqrt_tests<F: SqrtField>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..10000 { for _ in 0..10000 {
let a = F::rand(&mut rng); let a = F::random(&mut rng);
let mut b = a; let mut b = a;
b.square(); b.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
@@ -54,7 +61,10 @@ pub fn random_sqrt_tests<F: SqrtField>() {
} }
pub fn random_field_tests<F: Field>() { pub fn random_field_tests<F: Field>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
random_multiplication_tests::<F, _>(&mut rng); random_multiplication_tests::<F, _>(&mut rng);
random_addition_tests::<F, _>(&mut rng); random_addition_tests::<F, _>(&mut rng);
@@ -76,14 +86,14 @@ pub fn random_field_tests<F: Field>() {
// Multiplication by zero // Multiplication by zero
{ {
let mut a = F::rand(&mut rng); let mut a = F::random(&mut rng);
a.mul_assign(&F::zero()); a.mul_assign(&F::zero());
assert!(a.is_zero()); assert!(a.is_zero());
} }
// Addition by zero // Addition by zero
{ {
let mut a = F::rand(&mut rng); let mut a = F::random(&mut rng);
let copy = a; let copy = a;
a.add_assign(&F::zero()); a.add_assign(&F::zero());
assert_eq!(a, copy); assert_eq!(a, copy);
@@ -106,10 +116,13 @@ pub fn from_str_tests<F: PrimeField>() {
} }
{ {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let n: u64 = rng.gen(); let n = rng.next_u64();
let a = F::from_str(&format!("{}", n)).unwrap(); let a = F::from_str(&format!("{}", n)).unwrap();
let b = F::from_repr(n.into()).unwrap(); let b = F::from_repr(n.into()).unwrap();
@@ -124,11 +137,11 @@ pub fn from_str_tests<F: PrimeField>() {
assert!(F::from_str("00000000000").is_none()); assert!(F::from_str("00000000000").is_none());
} }
fn random_multiplication_tests<F: Field, R: Rng>(rng: &mut R) { fn random_multiplication_tests<F: Field, R: RngCore>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::rand(rng); let a = F::random(rng);
let b = F::rand(rng); let b = F::random(rng);
let c = F::rand(rng); let c = F::random(rng);
let mut t0 = a; // (a * b) * c let mut t0 = a; // (a * b) * c
t0.mul_assign(&b); t0.mul_assign(&b);
@@ -147,11 +160,11 @@ fn random_multiplication_tests<F: Field, R: Rng>(rng: &mut R) {
} }
} }
fn random_addition_tests<F: Field, R: Rng>(rng: &mut R) { fn random_addition_tests<F: Field, R: RngCore>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::rand(rng); let a = F::random(rng);
let b = F::rand(rng); let b = F::random(rng);
let c = F::rand(rng); let c = F::random(rng);
let mut t0 = a; // (a + b) + c let mut t0 = a; // (a + b) + c
t0.add_assign(&b); t0.add_assign(&b);
@@ -170,10 +183,10 @@ fn random_addition_tests<F: Field, R: Rng>(rng: &mut R) {
} }
} }
fn random_subtraction_tests<F: Field, R: Rng>(rng: &mut R) { fn random_subtraction_tests<F: Field, R: RngCore>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::rand(rng); let b = F::random(rng);
let b = F::rand(rng); let a = F::random(rng);
let mut t0 = a; // (a - b) let mut t0 = a; // (a - b)
t0.sub_assign(&b); t0.sub_assign(&b);
@@ -188,9 +201,9 @@ fn random_subtraction_tests<F: Field, R: Rng>(rng: &mut R) {
} }
} }
fn random_negation_tests<F: Field, R: Rng>(rng: &mut R) { fn random_negation_tests<F: Field, R: RngCore>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::rand(rng); let a = F::random(rng);
let mut b = a; let mut b = a;
b.negate(); b.negate();
b.add_assign(&a); b.add_assign(&a);
@@ -199,9 +212,9 @@ fn random_negation_tests<F: Field, R: Rng>(rng: &mut R) {
} }
} }
fn random_doubling_tests<F: Field, R: Rng>(rng: &mut R) { fn random_doubling_tests<F: Field, R: RngCore>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::rand(rng); let mut a = F::random(rng);
let mut b = a; let mut b = a;
a.add_assign(&b); a.add_assign(&b);
b.double(); b.double();
@@ -210,9 +223,9 @@ fn random_doubling_tests<F: Field, R: Rng>(rng: &mut R) {
} }
} }
fn random_squaring_tests<F: Field, R: Rng>(rng: &mut R) { fn random_squaring_tests<F: Field, R: RngCore>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::rand(rng); let mut a = F::random(rng);
let mut b = a; let mut b = a;
a.mul_assign(&b); a.mul_assign(&b);
b.square(); b.square();
@@ -221,11 +234,11 @@ fn random_squaring_tests<F: Field, R: Rng>(rng: &mut R) {
} }
} }
fn random_inversion_tests<F: Field, R: Rng>(rng: &mut R) { fn random_inversion_tests<F: Field, R: RngCore>(rng: &mut R) {
assert!(F::zero().inverse().is_none()); assert!(F::zero().inverse().is_none());
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::rand(rng); let mut a = F::random(rng);
let b = a.inverse().unwrap(); // probablistically nonzero let b = a.inverse().unwrap(); // probablistically nonzero
a.mul_assign(&b); a.mul_assign(&b);
@@ -233,14 +246,14 @@ fn random_inversion_tests<F: Field, R: Rng>(rng: &mut R) {
} }
} }
fn random_expansion_tests<F: Field, R: Rng>(rng: &mut R) { fn random_expansion_tests<F: Field, R: RngCore>(rng: &mut R) {
for _ in 0..10000 { for _ in 0..10000 {
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
let a = F::rand(rng); let a = F::random(rng);
let b = F::rand(rng); let b = F::random(rng);
let c = F::rand(rng); let c = F::random(rng);
let d = F::rand(rng); let d = F::random(rng);
let mut t0 = a; let mut t0 = a;
t0.add_assign(&b); t0.add_assign(&b);

View File

@@ -1,21 +1,25 @@
use ff::PrimeFieldRepr; use ff::{PrimeField, PrimeFieldRepr};
use rand::{SeedableRng, XorShiftRng}; use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
pub fn random_repr_tests<R: PrimeFieldRepr>() { pub fn random_repr_tests<P: PrimeField>() {
random_encoding_tests::<R>(); random_encoding_tests::<P>();
random_shl_tests::<R>(); random_shl_tests::<P>();
random_shr_tests::<R>(); random_shr_tests::<P>();
} }
fn random_encoding_tests<R: PrimeFieldRepr>() { fn random_encoding_tests<P: PrimeField>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..1000 { for _ in 0..1000 {
let r = R::rand(&mut rng); let r = P::random(&mut rng).into_repr();
// Big endian // Big endian
{ {
let mut rdecoded = R::default(); let mut rdecoded = <P as PrimeField>::Repr::default();
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
r.write_be(&mut v).unwrap(); r.write_be(&mut v).unwrap();
@@ -26,7 +30,7 @@ fn random_encoding_tests<R: PrimeFieldRepr>() {
// Little endian // Little endian
{ {
let mut rdecoded = R::default(); let mut rdecoded = <P as PrimeField>::Repr::default();
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
r.write_le(&mut v).unwrap(); r.write_le(&mut v).unwrap();
@@ -36,8 +40,8 @@ fn random_encoding_tests<R: PrimeFieldRepr>() {
} }
{ {
let mut rdecoded_le = R::default(); let mut rdecoded_le = <P as PrimeField>::Repr::default();
let mut rdecoded_be_flip = R::default(); let mut rdecoded_be_flip = <P as PrimeField>::Repr::default();
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
r.write_le(&mut v).unwrap(); r.write_le(&mut v).unwrap();
@@ -55,11 +59,14 @@ fn random_encoding_tests<R: PrimeFieldRepr>() {
} }
} }
fn random_shl_tests<R: PrimeFieldRepr>() { fn random_shl_tests<P: PrimeField>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let r = R::rand(&mut rng); let r = P::random(&mut rng).into_repr();
for shift in 0..(r.num_bits() + 1) { for shift in 0..(r.num_bits() + 1) {
let mut r1 = r; let mut r1 = r;
@@ -76,11 +83,14 @@ fn random_shl_tests<R: PrimeFieldRepr>() {
} }
} }
fn random_shr_tests<R: PrimeFieldRepr>() { fn random_shr_tests<P: PrimeField>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 { for _ in 0..100 {
let r = R::rand(&mut rng); let r = P::random(&mut rng).into_repr();
for shift in 0..(r.num_bits() + 1) { for shift in 0..(r.num_bits() + 1) {
let mut r1 = r; let mut r1 = r;

View File

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

View File

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

View File

@@ -1,28 +0,0 @@
[package]
authors = ["Sean Bowe <sean@z.cash>"]
description = "Cryptographic library for Zcash Sapling"
documentation = "https://github.com/zcash-hackworks/sapling"
homepage = "https://github.com/zcash-hackworks/sapling"
license = "MIT/Apache-2.0"
name = "sapling-crypto"
repository = "https://github.com/zcash-hackworks/sapling"
version = "0.0.1"
[dependencies.pairing]
path = "../pairing"
features = ["expose-arith"]
[dependencies]
bellman = { path = "../bellman" }
ff = { path = "../ff" }
rand = "0.4"
digest = "0.7"
byteorder = "1"
[dependencies.blake2-rfc]
git = "https://github.com/gtank/blake2-rfc"
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
[dev-dependencies]
hex-literal = "0.1"
rust-crypto = "0.2"

View File

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

View File

@@ -1,23 +0,0 @@
# sapling-crypto
This repository contains a (work-in-progress) implementation of Zcash's "Sapling" cryptography.
## Security Warnings
This library is currently under development and has not been reviewed.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.

View File

@@ -1,23 +0,0 @@
#![feature(test)]
extern crate rand;
extern crate test;
extern crate pairing;
extern crate sapling_crypto;
use rand::{Rand, thread_rng};
use pairing::bls12_381::Bls12;
use sapling_crypto::jubjub::JubjubBls12;
use sapling_crypto::pedersen_hash::{pedersen_hash, Personalization};
#[bench]
fn bench_pedersen_hash(b: &mut test::Bencher) {
let params = JubjubBls12::new();
let rng = &mut thread_rng();
let bits = (0..510).map(|_| bool::rand(rng)).collect::<Vec<_>>();
let personalization = Personalization::MerkleTree(31);
b.iter(|| {
pedersen_hash::<Bls12, _>(personalization, bits.clone(), &params)
});
}

View File

@@ -1,102 +0,0 @@
extern crate sapling_crypto;
extern crate bellman;
extern crate rand;
extern crate pairing;
use std::time::{Duration, Instant};
use sapling_crypto::jubjub::{
JubjubBls12,
edwards,
fs,
};
use sapling_crypto::circuit::sapling::{
Spend
};
use sapling_crypto::primitives::{
Diversifier,
ProofGenerationKey,
ValueCommitment
};
use bellman::groth16::*;
use rand::{XorShiftRng, SeedableRng, Rng};
use pairing::bls12_381::{Bls12, Fr};
const TREE_DEPTH: usize = 32;
fn main() {
let jubjub_params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
println!("Creating sample parameters...");
let groth_params = generate_random_parameters::<Bls12, _, _>(
Spend {
params: jubjub_params,
value_commitment: None,
proof_generation_key: None,
payment_address: None,
commitment_randomness: None,
ar: None,
auth_path: vec![None; TREE_DEPTH],
anchor: None
},
rng
).unwrap();
const SAMPLES: u32 = 50;
let mut total_time = Duration::new(0, 0);
for _ in 0..SAMPLES {
let value_commitment = ValueCommitment {
value: 1,
randomness: rng.gen()
};
let nsk: fs::Fs = rng.gen();
let ak = edwards::Point::rand(rng, jubjub_params).mul_by_cofactor(jubjub_params);
let proof_generation_key = ProofGenerationKey {
ak: ak.clone(),
nsk: nsk.clone()
};
let viewing_key = proof_generation_key.into_viewing_key(jubjub_params);
let payment_address;
loop {
let diversifier = Diversifier(rng.gen());
if let Some(p) = viewing_key.into_payment_address(
diversifier,
jubjub_params
)
{
payment_address = p;
break;
}
}
let commitment_randomness: fs::Fs = rng.gen();
let auth_path = vec![Some((rng.gen(), rng.gen())); TREE_DEPTH];
let ar: fs::Fs = rng.gen();
let anchor: Fr = rng.gen();
let start = Instant::now();
let _ = create_random_proof(Spend {
params: jubjub_params,
value_commitment: Some(value_commitment),
proof_generation_key: Some(proof_generation_key),
payment_address: Some(payment_address),
commitment_randomness: Some(commitment_randomness),
ar: Some(ar),
auth_path: auth_path,
anchor: Some(anchor)
}, &groth_params, rng).unwrap();
total_time += start.elapsed();
}
let avg = total_time / SAMPLES;
let avg = avg.subsec_nanos() as f64 / 1_000_000_000f64
+ (avg.as_secs() as f64);
println!("Average proving time (in seconds): {}", avg);
}

View File

@@ -1,438 +0,0 @@
use pairing::{
Engine,
};
use bellman::{
SynthesisError,
ConstraintSystem
};
use super::boolean::{
Boolean
};
use super::uint32::{
UInt32
};
use super::multieq::MultiEq;
/*
2.1. Parameters
The following table summarizes various parameters and their ranges:
| BLAKE2b | BLAKE2s |
--------------+------------------+------------------+
Bits in word | w = 64 | w = 32 |
Rounds in F | r = 12 | r = 10 |
Block bytes | bb = 128 | bb = 64 |
Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 |
Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 |
Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 |
--------------+------------------+------------------+
G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
constants = | (32, 24, 16, 63) | (16, 12, 8, 7) |
--------------+------------------+------------------+
*/
const R1: usize = 16;
const R2: usize = 12;
const R3: usize = 8;
const R4: usize = 7;
/*
Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
----------+-------------------------------------------------+
SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 |
SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 |
SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 |
SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 |
SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 |
SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 |
SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 |
SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 |
SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 |
----------+-------------------------------------------------+
*/
const SIGMA: [[usize; 16]; 10] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]
];
/*
3.1. Mixing Function G
The G primitive function mixes two input words, "x" and "y", into
four words indexed by "a", "b", "c", and "d" in the working vector
v[0..15]. The full modified vector is returned. The rotation
constants (R1, R2, R3, R4) are given in Section 2.1.
FUNCTION G( v[0..15], a, b, c, d, x, y )
|
| v[a] := (v[a] + v[b] + x) mod 2**w
| v[d] := (v[d] ^ v[a]) >>> R1
| v[c] := (v[c] + v[d]) mod 2**w
| v[b] := (v[b] ^ v[c]) >>> R2
| v[a] := (v[a] + v[b] + y) mod 2**w
| v[d] := (v[d] ^ v[a]) >>> R3
| v[c] := (v[c] + v[d]) mod 2**w
| v[b] := (v[b] ^ v[c]) >>> R4
|
| RETURN v[0..15]
|
END FUNCTION.
*/
fn mixing_g<E: Engine, CS: ConstraintSystem<E>, M>(
mut cs: M,
v: &mut [UInt32],
a: usize,
b: usize,
c: usize,
d: usize,
x: &UInt32,
y: &UInt32
) -> Result<(), SynthesisError>
where M: ConstraintSystem<E, Root=MultiEq<E, CS>>
{
v[a] = UInt32::addmany(cs.namespace(|| "mixing step 1"), &[v[a].clone(), v[b].clone(), x.clone()])?;
v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1);
v[c] = UInt32::addmany(cs.namespace(|| "mixing step 3"), &[v[c].clone(), v[d].clone()])?;
v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2);
v[a] = UInt32::addmany(cs.namespace(|| "mixing step 5"), &[v[a].clone(), v[b].clone(), y.clone()])?;
v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3);
v[c] = UInt32::addmany(cs.namespace(|| "mixing step 7"), &[v[c].clone(), v[d].clone()])?;
v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4);
Ok(())
}
/*
3.2. Compression Function F
Compression function F takes as an argument the state vector "h",
message block vector "m" (last block is padded with zeros to full
block size, if required), 2w-bit offset counter "t", and final block
indicator flag "f". Local vector v[0..15] is used in processing. F
returns a new state vector. The number of rounds, "r", is 12 for
BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
FUNCTION F( h[0..7], m[0..15], t, f )
|
| // Initialize local work vector v[0..15]
| v[0..7] := h[0..7] // First half from state.
| v[8..15] := IV[0..7] // Second half from IV.
|
| v[12] := v[12] ^ (t mod 2**w) // Low word of the offset.
| v[13] := v[13] ^ (t >> w) // High word.
|
| IF f = TRUE THEN // last block flag?
| | v[14] := v[14] ^ 0xFF..FF // Invert all bits.
| END IF.
|
| // Cryptographic mixing
| FOR i = 0 TO r - 1 DO // Ten or twelve rounds.
| |
| | // Message word selection permutation for this round.
| | s[0..15] := SIGMA[i mod 10][0..15]
| |
| | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] )
| | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] )
| | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
| | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
| |
| | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
| | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
| | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] )
| | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] )
| |
| END FOR
|
| FOR i = 0 TO 7 DO // XOR the two halves.
| | h[i] := h[i] ^ v[i] ^ v[i + 8]
| END FOR.
|
| RETURN h[0..7] // New state.
|
END FUNCTION.
*/
fn blake2s_compression<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS,
h: &mut [UInt32],
m: &[UInt32],
t: u64,
f: bool
) -> Result<(), SynthesisError>
{
assert_eq!(h.len(), 8);
assert_eq!(m.len(), 16);
/*
static const uint32_t blake2s_iv[8] =
{
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
*/
let mut v = Vec::with_capacity(16);
v.extend_from_slice(h);
v.push(UInt32::constant(0x6A09E667));
v.push(UInt32::constant(0xBB67AE85));
v.push(UInt32::constant(0x3C6EF372));
v.push(UInt32::constant(0xA54FF53A));
v.push(UInt32::constant(0x510E527F));
v.push(UInt32::constant(0x9B05688C));
v.push(UInt32::constant(0x1F83D9AB));
v.push(UInt32::constant(0x5BE0CD19));
assert_eq!(v.len(), 16);
v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?;
v[13] = v[13].xor(cs.namespace(|| "second xor"), &UInt32::constant((t >> 32) as u32))?;
if f {
v[14] = v[14].xor(cs.namespace(|| "third xor"), &UInt32::constant(u32::max_value()))?;
}
{
let mut cs = MultiEq::new(&mut cs);
for i in 0..10 {
let mut cs = cs.namespace(|| format!("round {}", i));
let s = SIGMA[i % 10];
mixing_g(cs.namespace(|| "mixing invocation 1"), &mut v, 0, 4, 8, 12, &m[s[ 0]], &m[s[ 1]])?;
mixing_g(cs.namespace(|| "mixing invocation 2"), &mut v, 1, 5, 9, 13, &m[s[ 2]], &m[s[ 3]])?;
mixing_g(cs.namespace(|| "mixing invocation 3"), &mut v, 2, 6, 10, 14, &m[s[ 4]], &m[s[ 5]])?;
mixing_g(cs.namespace(|| "mixing invocation 4"), &mut v, 3, 7, 11, 15, &m[s[ 6]], &m[s[ 7]])?;
mixing_g(cs.namespace(|| "mixing invocation 5"), &mut v, 0, 5, 10, 15, &m[s[ 8]], &m[s[ 9]])?;
mixing_g(cs.namespace(|| "mixing invocation 6"), &mut v, 1, 6, 11, 12, &m[s[10]], &m[s[11]])?;
mixing_g(cs.namespace(|| "mixing invocation 7"), &mut v, 2, 7, 8, 13, &m[s[12]], &m[s[13]])?;
mixing_g(cs.namespace(|| "mixing invocation 8"), &mut v, 3, 4, 9, 14, &m[s[14]], &m[s[15]])?;
}
}
for i in 0..8 {
let mut cs = cs.namespace(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i=i));
h[i] = h[i].xor(cs.namespace(|| "first xor"), &v[i])?;
h[i] = h[i].xor(cs.namespace(|| "second xor"), &v[i + 8])?;
}
Ok(())
}
/*
FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
|
| h[0..7] := IV[0..7] // Initialization Vector.
|
| // Parameter block p[0]
| h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
|
| // Process padded key and data blocks
| IF dd > 1 THEN
| | FOR i = 0 TO dd - 2 DO
| | | h := F( h, d[i], (i + 1) * bb, FALSE )
| | END FOR.
| END IF.
|
| // Final block.
| IF kk = 0 THEN
| | h := F( h, d[dd - 1], ll, TRUE )
| ELSE
| | h := F( h, d[dd - 1], ll + bb, TRUE )
| END IF.
|
| RETURN first "nn" bytes from little-endian word array h[].
|
END FUNCTION.
*/
pub fn blake2s<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS,
input: &[Boolean],
personalization: &[u8]
) -> Result<Vec<Boolean>, SynthesisError>
{
use byteorder::{ByteOrder, LittleEndian};
assert_eq!(personalization.len(), 8);
assert!(input.len() % 8 == 0);
let mut h = Vec::with_capacity(8);
h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32));
h.push(UInt32::constant(0xBB67AE85));
h.push(UInt32::constant(0x3C6EF372));
h.push(UInt32::constant(0xA54FF53A));
h.push(UInt32::constant(0x510E527F));
h.push(UInt32::constant(0x9B05688C));
// Personalization is stored here
h.push(UInt32::constant(0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4])));
h.push(UInt32::constant(0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8])));
let mut blocks: Vec<Vec<UInt32>> = vec![];
for block in input.chunks(512) {
let mut this_block = Vec::with_capacity(16);
for word in block.chunks(32) {
let mut tmp = word.to_vec();
while tmp.len() < 32 {
tmp.push(Boolean::constant(false));
}
this_block.push(UInt32::from_bits(&tmp));
}
while this_block.len() < 16 {
this_block.push(UInt32::constant(0));
}
blocks.push(this_block);
}
if blocks.len() == 0 {
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
}
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
let cs = cs.namespace(|| format!("block {}", i));
blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?;
}
{
let cs = cs.namespace(|| "final block");
blake2s_compression(cs, &mut h, &blocks[blocks.len() - 1], (input.len() / 8) as u64, true)?;
}
Ok(h.iter().flat_map(|b| b.into_bits()).collect())
}
#[cfg(test)]
mod test {
use rand::{XorShiftRng, SeedableRng, Rng};
use pairing::bls12_381::{Bls12};
use ::circuit::boolean::{Boolean, AllocatedBit};
use ::circuit::test::TestConstraintSystem;
use super::blake2s;
use bellman::{ConstraintSystem};
use blake2_rfc::blake2s::Blake2s;
#[test]
fn test_blank_hash() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits = vec![];
let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 0);
// >>> import blake2s from hashlib
// >>> h = blake2s(digest_size=32, person=b'12345678')
// >>> h.hexdigest()
let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8");
let mut out = out.into_iter();
for b in expected.into_iter() {
for i in 0..8 {
let c = out.next().unwrap().get_value().unwrap();
assert_eq!(c, (b >> i) & 1u8 == 1u8);
}
}
}
#[test]
fn test_blake2s_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bits: Vec<_> = (0..512).map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into()).collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518);
}
#[test]
fn test_blake2s_precomp_constraints() {
// Test that 512 fixed leading bits (constants)
// doesn't result in more constraints.
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let input_bits: Vec<_> = (0..512)
.map(|_| Boolean::constant(rng.gen()))
.chain((0..512)
.map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into()))
.collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 21518);
}
#[test]
fn test_blake2s_constant_constraints() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.gen())).collect();
blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert_eq!(cs.num_constraints(), 0);
}
#[test]
fn test_blake2s() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0))
{
let mut h = Blake2s::with_params(32, &[], &[], b"12345678");
let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
h.update(&data);
let hash_result = h.finalize();
let mut cs = TestConstraintSystem::<Bls12>::new();
let mut input_bits = vec![];
for (byte_i, input_byte) in data.into_iter().enumerate() {
for bit_i in 0..8 {
let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i));
input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into());
}
}
let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap();
assert!(cs.is_satisfied());
let mut s = hash_result.as_ref().iter()
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
for b in r {
match b {
Boolean::Is(b) => {
assert!(s.next().unwrap() == b.get_value().unwrap());
},
Boolean::Not(b) => {
assert!(s.next().unwrap() != b.get_value().unwrap());
},
Boolean::Constant(b) => {
assert!(input_len == 0);
assert!(s.next().unwrap() == b);
}
}
}
}
}
}

View File

@@ -1,114 +0,0 @@
use ff::{Field, PrimeField};
use pairing::Engine;
use bellman::{ConstraintSystem, SynthesisError};
use super::boolean::{Boolean};
use super::num::Num;
use super::Assignment;
/// Takes a sequence of booleans and exposes them as compact
/// public inputs
pub fn pack_into_inputs<E, CS>(
mut cs: CS,
bits: &[Boolean]
) -> Result<(), SynthesisError>
where E: Engine, CS: ConstraintSystem<E>
{
for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate()
{
let mut num = Num::<E>::zero();
let mut coeff = E::Fr::one();
for bit in bits {
num = num.add_bool_with_coeff(CS::one(), bit, coeff);
coeff.double();
}
let input = cs.alloc_input(|| format!("input {}", i), || {
Ok(*num.get_value().get()?)
})?;
// num * 1 = input
cs.enforce(
|| format!("packing constraint {}", i),
|_| num.lc(E::Fr::one()),
|lc| lc + CS::one(),
|lc| lc + input
);
}
Ok(())
}
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool>
{
bytes.iter()
.flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec<bool>
{
bytes.iter()
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
.collect()
}
pub fn compute_multipacking<E: Engine>(
bits: &[bool]
) -> Vec<E::Fr>
{
let mut result = vec![];
for bits in bits.chunks(E::Fr::CAPACITY as usize)
{
let mut cur = E::Fr::zero();
let mut coeff = E::Fr::one();
for bit in bits {
if *bit {
cur.add_assign(&coeff);
}
coeff.double();
}
result.push(cur);
}
result
}
#[test]
fn test_multipacking() {
use rand::{SeedableRng, Rng, XorShiftRng};
use bellman::{ConstraintSystem};
use pairing::bls12_381::{Bls12};
use ::circuit::test::*;
use super::boolean::{AllocatedBit, Boolean};
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for num_bits in 0..1500 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let bits: Vec<bool> = (0..num_bits).map(|_| rng.gen()).collect();
let circuit_bits = bits.iter().enumerate()
.map(|(i, &b)| {
Boolean::from(
AllocatedBit::alloc(
cs.namespace(|| format!("bit {}", i)),
Some(b)
).unwrap()
)
})
.collect::<Vec<_>>();
let expected_inputs = compute_multipacking::<Bls12>(&bits);
pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap();
assert!(cs.is_satisfied());
assert!(cs.verify(&expected_inputs));
}
}

View File

@@ -1,194 +0,0 @@
use super::*;
use super::ecc::{
MontgomeryPoint,
EdwardsPoint
};
use super::boolean::Boolean;
use ::jubjub::*;
use bellman::{
ConstraintSystem
};
use super::lookup::*;
pub use pedersen_hash::Personalization;
impl Personalization {
fn get_constant_bools(&self) -> Vec<Boolean> {
self.get_bits()
.into_iter()
.map(|e| Boolean::constant(e))
.collect()
}
}
pub fn pedersen_hash<E: JubjubEngine, CS>(
mut cs: CS,
personalization: Personalization,
bits: &[Boolean],
params: &E::Params
) -> Result<EdwardsPoint<E>, SynthesisError>
where CS: ConstraintSystem<E>
{
let personalization = personalization.get_constant_bools();
assert_eq!(personalization.len(), 6);
let mut edwards_result = None;
let mut bits = personalization.iter().chain(bits.iter());
let mut segment_generators = params.pedersen_circuit_generators().iter();
let boolean_false = Boolean::constant(false);
let mut segment_i = 0;
loop {
let mut segment_result = None;
let mut segment_windows = &segment_generators.next()
.expect("enough segments")[..];
let mut window_i = 0;
while let Some(a) = bits.next() {
let b = bits.next().unwrap_or(&boolean_false);
let c = bits.next().unwrap_or(&boolean_false);
let tmp = lookup3_xy_with_conditional_negation(
cs.namespace(|| format!("segment {}, window {}", segment_i, window_i)),
&[a.clone(), b.clone(), c.clone()],
&segment_windows[0]
)?;
let tmp = MontgomeryPoint::interpret_unchecked(tmp.0, tmp.1);
match segment_result {
None => {
segment_result = Some(tmp);
},
Some(ref mut segment_result) => {
*segment_result = tmp.add(
cs.namespace(|| format!("addition of segment {}, window {}", segment_i, window_i)),
segment_result,
params
)?;
}
}
segment_windows = &segment_windows[1..];
if segment_windows.len() == 0 {
break;
}
window_i += 1;
}
match segment_result {
Some(segment_result) => {
// Convert this segment into twisted Edwards form.
let segment_result = segment_result.into_edwards(
cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)),
params
)?;
match edwards_result {
Some(ref mut edwards_result) => {
*edwards_result = segment_result.add(
cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)),
edwards_result,
params
)?;
},
None => {
edwards_result = Some(segment_result);
}
}
},
None => {
// We didn't process any new bits.
break;
}
}
segment_i += 1;
}
Ok(edwards_result.unwrap())
}
#[cfg(test)]
mod test {
use rand::{SeedableRng, Rng, XorShiftRng};
use super::*;
use ::circuit::test::*;
use ::circuit::boolean::{Boolean, AllocatedBit};
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr};
#[test]
fn test_pedersen_hash_constraints() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let params = &JubjubBls12::new();
let mut cs = TestConstraintSystem::<Bls12>::new();
let input: Vec<bool> = (0..(Fr::NUM_BITS * 2)).map(|_| rng.gen()).collect();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| {
Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap()
)
}).collect();
pedersen_hash(
cs.namespace(|| "pedersen hash"),
Personalization::NoteCommitment,
&input_bools,
params
).unwrap();
assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1377);
}
#[test]
fn test_pedersen_hash() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let params = &JubjubBls12::new();
for length in 0..751 {
for _ in 0..5 {
let mut input: Vec<bool> = (0..length).map(|_| rng.gen()).collect();
let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| {
Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap()
)
}).collect();
let res = pedersen_hash(
cs.namespace(|| "pedersen hash"),
Personalization::MerkleTree(1),
&input_bools,
params
).unwrap();
assert!(cs.is_satisfied());
let expected = ::pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(1),
input.clone().into_iter(),
params
).into_xy();
assert_eq!(res.get_x().get_value().unwrap(), expected.0);
assert_eq!(res.get_y().get_value().unwrap(), expected.1);
// Test against the output of a different personalization
let unexpected = ::pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(0),
input.into_iter(),
params
).into_xy();
assert!(res.get_x().get_value().unwrap() != unexpected.0);
assert!(res.get_y().get_value().unwrap() != unexpected.1);
}
}
}
}

View File

@@ -1,46 +0,0 @@
use jubjub::{
JubjubEngine,
PrimeOrder,
edwards
};
use ff::{
PrimeField
};
use blake2_rfc::blake2s::Blake2s;
use constants;
/// Produces a random point in the Jubjub curve.
/// The point is guaranteed to be prime order
/// and not the identity.
pub fn group_hash<E: JubjubEngine>(
tag: &[u8],
personalization: &[u8],
params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>>
{
assert_eq!(personalization.len(), 8);
// Check to see that scalar field is 255 bits
assert!(E::Fr::NUM_BITS == 255);
let mut h = Blake2s::with_params(32, &[], &[], personalization);
h.update(constants::GH_FIRST_BLOCK);
h.update(tag);
let h = h.finalize().as_ref().to_vec();
assert!(h.len() == 32);
match edwards::Point::<E, _>::read(&h[..], params) {
Ok(p) => {
let p = p.mul_by_cofactor(params);
if p != edwards::Point::zero() {
Some(p)
} else {
None
}
},
Err(_) => None
}
}

View File

@@ -1,23 +0,0 @@
extern crate pairing;
extern crate bellman;
extern crate blake2_rfc;
extern crate digest;
extern crate ff;
extern crate rand;
extern crate byteorder;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[cfg(test)]
extern crate crypto;
pub mod jubjub;
pub mod group_hash;
pub mod circuit;
pub mod pedersen_hash;
pub mod primitives;
pub mod constants;
pub mod redjubjub;
pub mod util;

View File

@@ -1,271 +0,0 @@
use ff::{Field, PrimeField, PrimeFieldRepr};
use constants;
use group_hash::group_hash;
use pedersen_hash::{
pedersen_hash,
Personalization
};
use byteorder::{
LittleEndian,
WriteBytesExt
};
use jubjub::{
JubjubEngine,
JubjubParams,
edwards,
PrimeOrder,
FixedGenerators
};
use blake2_rfc::blake2s::Blake2s;
#[derive(Clone)]
pub struct ValueCommitment<E: JubjubEngine> {
pub value: u64,
pub randomness: E::Fs
}
impl<E: JubjubEngine> ValueCommitment<E> {
pub fn cm(
&self,
params: &E::Params
) -> edwards::Point<E, PrimeOrder>
{
params.generator(FixedGenerators::ValueCommitmentValue)
.mul(self.value, params)
.add(
&params.generator(FixedGenerators::ValueCommitmentRandomness)
.mul(self.randomness, params),
params
)
}
}
#[derive(Clone)]
pub struct ProofGenerationKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>,
pub nsk: E::Fs
}
impl<E: JubjubEngine> ProofGenerationKey<E> {
pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey<E> {
ViewingKey {
ak: self.ak.clone(),
nk: params.generator(FixedGenerators::ProofGenerationKey)
.mul(self.nsk, params)
}
}
}
#[derive(Debug)]
pub struct ViewingKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>,
pub nk: edwards::Point<E, PrimeOrder>
}
impl<E: JubjubEngine> ViewingKey<E> {
pub fn rk(
&self,
ar: E::Fs,
params: &E::Params
) -> edwards::Point<E, PrimeOrder> {
self.ak.add(
&params.generator(FixedGenerators::SpendingKeyGenerator)
.mul(ar, params),
params
)
}
pub fn ivk(&self) -> E::Fs {
let mut preimage = [0; 64];
self.ak.write(&mut preimage[0..32]).unwrap();
self.nk.write(&mut preimage[32..64]).unwrap();
let mut h = Blake2s::with_params(32, &[], &[], constants::CRH_IVK_PERSONALIZATION);
h.update(&preimage);
let mut h = h.finalize().as_ref().to_vec();
// Drop the most significant five bits, so it can be interpreted as a scalar.
h[31] &= 0b0000_0111;
let mut e = <E::Fs as PrimeField>::Repr::default();
e.read_le(&h[..]).unwrap();
E::Fs::from_repr(e).expect("should be a valid scalar")
}
pub fn into_payment_address(
&self,
diversifier: Diversifier,
params: &E::Params
) -> Option<PaymentAddress<E>>
{
diversifier.g_d(params).map(|g_d| {
let pk_d = g_d.mul(self.ivk(), params);
PaymentAddress {
pk_d: pk_d,
diversifier: diversifier
}
})
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Diversifier(pub [u8; 11]);
impl Diversifier {
pub fn g_d<E: JubjubEngine>(
&self,
params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>>
{
group_hash::<E>(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION, params)
}
}
#[derive(Clone, Debug)]
pub struct PaymentAddress<E: JubjubEngine> {
pub pk_d: edwards::Point<E, PrimeOrder>,
pub diversifier: Diversifier
}
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
fn eq(&self, other: &Self) -> bool {
self.pk_d == other.pk_d && self.diversifier == other.diversifier
}
}
impl<E: JubjubEngine> PaymentAddress<E> {
pub fn g_d(
&self,
params: &E::Params
) -> Option<edwards::Point<E, PrimeOrder>>
{
self.diversifier.g_d(params)
}
pub fn create_note(
&self,
value: u64,
randomness: E::Fs,
params: &E::Params
) -> Option<Note<E>>
{
self.g_d(params).map(|g_d| {
Note {
value: value,
r: randomness,
g_d: g_d,
pk_d: self.pk_d.clone()
}
})
}
}
#[derive(Clone, Debug)]
pub struct Note<E: JubjubEngine> {
/// The value of the note
pub value: u64,
/// The diversified base of the address, GH(d)
pub g_d: edwards::Point<E, PrimeOrder>,
/// The public key of the address, g_d^ivk
pub pk_d: edwards::Point<E, PrimeOrder>,
/// The commitment randomness
pub r: E::Fs
}
impl<E: JubjubEngine> PartialEq for Note<E> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
&& self.g_d == other.g_d
&& self.pk_d == other.pk_d
&& self.r == other.r
}
}
impl<E: JubjubEngine> Note<E> {
pub fn uncommitted() -> E::Fr {
// The smallest u-coordinate that is not on the curve
// is one.
// TODO: This should be relocated to JubjubEngine as
// it's specific to the curve we're using, not all
// twisted edwards curves.
E::Fr::one()
}
/// Computes the note commitment, returning the full point.
fn cm_full_point(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder>
{
// Calculate the note contents, as bytes
let mut note_contents = vec![];
// Writing the value in little endian
(&mut note_contents).write_u64::<LittleEndian>(self.value).unwrap();
// Write g_d
self.g_d.write(&mut note_contents).unwrap();
// Write pk_d
self.pk_d.write(&mut note_contents).unwrap();
assert_eq!(note_contents.len(), 32 + 32 + 8);
// Compute the Pedersen hash of the note contents
let hash_of_contents = pedersen_hash(
Personalization::NoteCommitment,
note_contents.into_iter()
.flat_map(|byte| {
(0..8).map(move |i| ((byte >> i) & 1) == 1)
}),
params
);
// Compute final commitment
params.generator(FixedGenerators::NoteCommitmentRandomness)
.mul(self.r, params)
.add(&hash_of_contents, params)
}
/// Computes the nullifier given the viewing key and
/// note position
pub fn nf(
&self,
viewing_key: &ViewingKey<E>,
position: u64,
params: &E::Params
) -> Vec<u8>
{
// Compute rho = cm + position.G
let rho = self
.cm_full_point(params)
.add(
&params.generator(FixedGenerators::NullifierPosition)
.mul(position, params),
params
);
// Compute nf = BLAKE2s(nk | rho)
let mut nf_preimage = [0u8; 64];
viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap();
rho.write(&mut nf_preimage[32..64]).unwrap();
let mut h = Blake2s::with_params(32, &[], &[], constants::PRF_NF_PERSONALIZATION);
h.update(&nf_preimage);
h.finalize().as_ref().to_vec()
}
/// Computes the note commitment
pub fn cm(&self, params: &E::Params) -> E::Fr
{
// The commitment is in the prime order subgroup, so mapping the
// commitment to the x-coordinate is an injective encoding.
self.cm_full_point(params).into_xy().0
}
}

2
zcash_client_backend/.gitignore vendored Normal file
View File

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

View File

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

View File

@@ -199,3 +199,4 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017-2019 Electric Coin Company
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.

View File

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

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

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

View File

@@ -0,0 +1,30 @@
//! Constants for the Zcash main network.
/// The mainnet coin type for ZEC, as defined by [SLIP 44].
///
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
pub const COIN_TYPE: u32 = 133;
/// The HRP for a Bech32-encoded mainnet [`ExtendedSpendingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-main";
/// The HRP for a Bech32-encoded mainnet [`ExtendedFullViewingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews";
/// The HRP for a Bech32-encoded mainnet [`PaymentAddress`].
///
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
///
/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs";

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

@@ -0,0 +1,30 @@
//! Constants for the Zcash test network.
/// The testnet coin type for ZEC, as defined by [SLIP 44].
///
/// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
pub const COIN_TYPE: u32 = 1;
/// The HRP for a Bech32-encoded testnet [`ExtendedSpendingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedSpendingKey`]: zcash_primitives::zip32::ExtendedSpendingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_SPENDING_KEY: &str = "secret-extended-key-test";
/// The HRP for a Bech32-encoded testnet [`ExtendedFullViewingKey`].
///
/// Defined in [ZIP 32].
///
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
/// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling";
/// The HRP for a Bech32-encoded testnet [`PaymentAddress`].
///
/// Defined in section 5.6.4 of the [Zcash Protocol Specification].
///
/// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress
/// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf
pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling";

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

@@ -0,0 +1,347 @@
//! Encoding and decoding functions for Zcash key and address structs.
//!
//! Human-Readable Prefixes (HRPs) for Bech32 encodings are located in the [`constants`]
//! module.
//!
//! [`constants`]: crate::constants
use bech32::{self, Error, FromBase32, ToBase32};
use pairing::bls12_381::Bls12;
use std::io::{self, Write};
use zcash_primitives::{
primitives::PaymentAddress,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
fn bech32_encode<F>(hrp: &str, write: F) -> String
where
F: Fn(&mut dyn Write) -> io::Result<()>,
{
let mut data: Vec<u8> = vec![];
write(&mut data).expect("Should be able to write to a Vec");
bech32::encode(hrp, data.to_base32()).expect("hrp is invalid")
}
fn bech32_decode<T, F>(hrp: &str, s: &str, read: F) -> Result<Option<T>, Error>
where
F: Fn(Vec<u8>) -> Option<T>,
{
let (decoded_hrp, data) = bech32::decode(s)?;
if decoded_hrp == hrp {
Vec::<u8>::from_base32(&data).map(|data| read(data))
} else {
Ok(None)
}
}
/// Writes an [`ExtendedSpendingKey`] as a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{
/// constants::testnet::{COIN_TYPE, HRP_SAPLING_EXTENDED_SPENDING_KEY},
/// encoding::encode_extended_spending_key,
/// keys::spending_key,
/// };
///
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
/// let encoded = encode_extended_spending_key(HRP_SAPLING_EXTENDED_SPENDING_KEY, &extsk);
/// ```
pub fn encode_extended_spending_key(hrp: &str, extsk: &ExtendedSpendingKey) -> String {
bech32_encode(hrp, |w| extsk.write(w))
}
/// Decodes an [`ExtendedSpendingKey`] from a Bech32-encoded string.
pub fn decode_extended_spending_key(
hrp: &str,
s: &str,
) -> Result<Option<ExtendedSpendingKey>, Error> {
bech32_decode(hrp, s, |data| ExtendedSpendingKey::read(&data[..]).ok())
}
/// Writes an [`ExtendedFullViewingKey`] as a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use zcash_client_backend::{
/// constants::testnet::{COIN_TYPE, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY},
/// encoding::encode_extended_full_viewing_key,
/// keys::spending_key,
/// };
/// use zcash_primitives::zip32::ExtendedFullViewingKey;
///
/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, 0);
/// let extfvk = ExtendedFullViewingKey::from(&extsk);
/// let encoded = encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk);
/// ```
pub fn encode_extended_full_viewing_key(hrp: &str, extfvk: &ExtendedFullViewingKey) -> String {
bech32_encode(hrp, |w| extfvk.write(w))
}
/// Decodes an [`ExtendedFullViewingKey`] from a Bech32-encoded string.
pub fn decode_extended_full_viewing_key(
hrp: &str,
s: &str,
) -> Result<Option<ExtendedFullViewingKey>, Error> {
bech32_decode(hrp, s, |data| ExtendedFullViewingKey::read(&data[..]).ok())
}
/// Writes a [`PaymentAddress`] as a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use pairing::bls12_381::Bls12;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
/// encoding::encode_payment_address,
/// };
/// use zcash_primitives::{
/// jubjub::edwards,
/// primitives::{Diversifier, PaymentAddress},
/// JUBJUB,
/// };
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
/// 0xbc, 0xe5,
/// ]);
///
/// let pa = PaymentAddress::from_parts(
/// Diversifier([0u8; 11]),
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// )
/// .unwrap();
///
/// assert_eq!(
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe",
/// );
/// ```
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
bech32_encode(hrp, |w| w.write_all(&addr.to_bytes()))
}
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
///
/// # Examples
///
/// ```
/// use pairing::bls12_381::Bls12;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
/// encoding::decode_payment_address,
/// };
/// use zcash_primitives::{
/// jubjub::edwards,
/// primitives::{Diversifier, PaymentAddress},
/// JUBJUB,
/// };
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
/// 0xbc, 0xe5,
/// ]);
///
/// let pa = PaymentAddress::from_parts(
/// Diversifier([0u8; 11]),
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// )
/// .unwrap();
///
/// assert_eq!(
/// decode_payment_address(
/// HRP_SAPLING_PAYMENT_ADDRESS,
/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe",
/// ),
/// Ok(Some(pa)),
/// );
/// ```
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
bech32_decode(hrp, s, |data| {
if data.len() != 43 {
return None;
}
let mut bytes = [0; 43];
bytes.copy_from_slice(&data);
PaymentAddress::<Bls12>::from_bytes(&bytes, &JUBJUB)
})
}
#[cfg(test)]
mod tests {
use pairing::bls12_381::Bls12;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use zcash_primitives::JUBJUB;
use zcash_primitives::{
jubjub::edwards,
primitives::{Diversifier, PaymentAddress},
zip32::ExtendedSpendingKey,
};
use super::{
decode_extended_full_viewing_key, decode_extended_spending_key, decode_payment_address,
encode_extended_full_viewing_key, encode_extended_spending_key, encode_payment_address,
};
use crate::constants;
#[test]
fn extended_spending_key() {
let extsk = ExtendedSpendingKey::master(&[0; 32][..]);
let encoded_main = "secret-extended-key-main1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs87qvlj";
let encoded_test = "secret-extended-key-test1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qysqws3xh6qmha7gna72fs2n4clnc9zgyd22s658f65pex4exe56qjk5pqj9vfdq7dfdhjc2rs9jdwq0zl99uwycyrxzp86705rk687spn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsvzyw8j";
assert_eq!(
encode_extended_spending_key(
constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
&extsk
),
encoded_main
);
assert_eq!(
decode_extended_spending_key(
constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
encoded_main
)
.unwrap(),
Some(extsk.clone())
);
assert_eq!(
encode_extended_spending_key(
constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
&extsk
),
encoded_test
);
assert_eq!(
decode_extended_spending_key(
constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
encoded_test
)
.unwrap(),
Some(extsk)
);
}
#[test]
fn extended_full_viewing_key() {
let extfvk = (&ExtendedSpendingKey::master(&[0; 32][..])).into();
let encoded_main = "zxviews1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsxmansf";
let encoded_test = "zxviewtestsapling1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs8evfkz";
assert_eq!(
encode_extended_full_viewing_key(
constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
&extfvk
),
encoded_main
);
assert_eq!(
decode_extended_full_viewing_key(
constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
encoded_main
)
.unwrap(),
Some(extfvk.clone())
);
assert_eq!(
encode_extended_full_viewing_key(
constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
&extfvk
),
encoded_test
);
assert_eq!(
decode_extended_full_viewing_key(
constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
encoded_test
)
.unwrap(),
Some(extfvk)
);
}
#[test]
fn payment_address() {
let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let addr = PaymentAddress::from_parts(
Diversifier([0u8; 11]),
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
)
.unwrap();
let encoded_main =
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
let encoded_test =
"ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe";
assert_eq!(
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
encoded_main
);
assert_eq!(
decode_payment_address(
constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
encoded_main
)
.unwrap(),
Some(addr.clone())
);
assert_eq!(
encode_payment_address(constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
encoded_test
);
assert_eq!(
decode_payment_address(
constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
encoded_test
)
.unwrap(),
Some(addr)
);
}
#[test]
fn invalid_diversifier() {
let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let addr = PaymentAddress::from_parts(
Diversifier([1u8; 11]),
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
)
.unwrap();
let encoded_main =
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr);
assert_eq!(
decode_payment_address(
constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
&encoded_main
)
.unwrap(),
None
);
}
}

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