655 Commits

Author SHA1 Message Date
Duke Leto
9e4f80d453 Downgrade arrayvec to original version used in Cargo.lock 2022-03-07 10:17:37 -05:00
Duke Leto
dbbc38b9b8 update to latest blake2-rfc 2022-03-07 10:00:34 -05:00
Duke Leto
3329f46511 update aes, fpe and blake2-rfc deps 2022-03-03 19:34:21 -05:00
Duke Leto
9ef137d7e9 Update aes, aesni and authors 2022-03-03 18:59:02 -05:00
Duke Leto
c625034163 Add authors file 2020-11-19 21:59:46 -05:00
Duke Leto
85bfece0be Update license files 2020-11-19 21:58:39 -05:00
Duke Leto
51768798d3 Update readme and re-license to GPLv3 2020-11-19 21:58:01 -05:00
Duke Leto
9f7d341f9a Merge pull request #4 from yusufsahinhamza/improve-travis-ci
Improve Travis CI
2020-05-01 09:55:44 -04:00
Yusuf Şahin HAMZA
e5fe8d2591 Improve Travis CI 2020-05-01 16:26:15 +03:00
str4d
3b6f5e3d5e Merge pull request #69 from str4d/sapling-note-encryption
Sapling note encryption
2019-06-06 20:50:17 +01:00
Jack Grigg
b65aae9bc1 Test both invalid and incorrect diversifiers 2019-06-06 13:56:06 +01:00
Jack Grigg
fdb6e208db Check note plaintext version byte when decrypting 2019-06-06 13:38:34 +01:00
Jack Grigg
060977fe54 Return edwards::Point from sapling_ka_agree
This matches how sapling_ka_agree and kdf_sapling are defined in the
protocol spec. kdf_sapling also now takes ownership of dhsecret to
discourage use of the sapling_ka_agree output elsewhere.
2019-06-06 13:38:33 +01:00
Jack Grigg
9d80be62f9 Simplify Memo::to_utf8 implementation 2019-06-06 13:38:32 +01:00
Jack Grigg
6846ac5f9e Require that ak in FullViewingKey is prime order 2019-06-06 13:38:31 +01:00
Jack Grigg
75bede4bc8 Use fixed-length arrays instead of Vec 2019-06-06 13:38:30 +01:00
Jack Grigg
d4fce58019 Tweaks to debug output and function names 2019-06-05 14:19:50 +01:00
Jack Grigg
23aa869bf4 Add comments with specification references 2019-06-05 14:19:33 +01:00
Jack Grigg
6d03b5c1db Replace AeadCipher::seal with AeadCipher::seal_to 2019-06-05 13:55:17 +01:00
str4d
0ee1e81f5d Merge pull request #61 from rex4539/fix-typos
Fix typos
2019-05-29 17:38:30 +01:00
str4d
2d97ccb7b8 Merge pull request #81 from bitcartel/52_wrapped_shr
Closes #52. Fix test error "attempt to shift right with overflow".
2019-05-28 18:55:25 +01:00
str4d
c5642f9f5e Merge pull request #70 from rex4539/electric-coin-company
Electric Coin Company
2019-05-23 17:33:38 +01:00
str4d
c2d0a7d048 Merge pull request #74 from str4d/bellman-multicore
Place bellman multicore operations behind a (default) feature flag
2019-05-22 21:25:29 +01:00
Simon
437b66d4ee Closes #52. Fix test error "attempt to shift right with overflow".
Use wrapping function to directly disable integer overflow protection.
2019-05-16 20:56:36 -07:00
Dimitris Apostolou
34f762cea2 Electric Coin Company 2019-05-09 20:32:39 +03:00
Jack Grigg
edf7bc144d Document note_encryption module 2019-04-11 16:08:32 -07:00
Jack Grigg
34658c4bd3 Raise minimum Rust version to 1.32
The crypto_api_chachapoly uses two new features introduced in 1.32:

- Self struct constructors
- u64::to_le_bytes()
2019-04-11 06:41:22 -07:00
Jack Grigg
6dcb4040af Switch to crypto_api_chachapoly crate
This crate exposes both the ChaCha20Poly1305 IETF construction, and the
underlying ChaCha20 IETF primitive, removing the need for depending on
our own fork of the previous chacha20-poly1305-aead crate.
2019-04-11 06:33:41 -07:00
Sean Bowe
8c5cd4e4f6 Place bellman multicore operations behind a (default) feature flag
Co-authored-by: Jack Grigg <jack@z.cash>
2019-04-10 06:01:24 -07:00
Jack Grigg
e17e4b1346 Test invalid decryption edge cases 2019-04-05 21:05:05 +01:00
Jack Grigg
9086dd9afb Enforce consistent plaintext and ciphertext lengths 2019-04-05 21:05:05 +01:00
Jack Grigg
899d852c39 Inline empty nonces 2019-04-05 21:05:05 +01:00
Jack Grigg
566db65a91 Use a slice instead of a vector in prf_expand() 2019-04-05 21:05:05 +01:00
Jack Grigg
2b1583d75f Deduplicate Sapling key agreement logic 2019-04-05 21:05:05 +01:00
Jack Grigg
247f3fb038 Impl traits and functions for Memo 2019-04-05 21:05:05 +01:00
Jack Grigg
484330ebd9 Trial Sapling compact note decryption
Part of ZIP 307.
2019-04-05 21:05:05 +01:00
Jack Grigg
8e098d4d72 Trial Sapling output recovery 2019-04-05 21:05:05 +01:00
Jack Grigg
6996853168 Trial Sapling note decryption 2019-04-05 21:05:05 +01:00
Jack Grigg
757316d355 Test prf_ock 2019-04-05 21:05:05 +01:00
Jack Grigg
70caa7d4b0 Sapling note encryption test vectors 2019-04-05 21:05:05 +01:00
George Tankersley
65bbe7daed Implement Sapling note encryption 2019-04-05 21:05:05 +01:00
Jack Grigg
9b455a12cc Move Sapling key structs from zip32 to zcash_primitives 2019-04-05 21:05:03 +01:00
str4d
d7ba310294 Merge pull request #66 from str4d/block-header
Block header parsing and transaction ID computation
2019-04-05 20:54:11 +01:00
Jack Grigg
6c99d71d4f cargo fmt 2019-04-02 01:30:00 +01:00
Jack Grigg
3501365950 Test Transaction::txid() 2019-04-02 01:29:48 +01:00
Jack Grigg
663f9d619d Use named fields in Transaction struct 2019-04-02 01:29:22 +01:00
Jack Grigg
b856d23069 Reverse a clone of [u8; 32] instead of allocating 2019-03-07 23:43:58 +00:00
Jack Grigg
4289843852 Compute TxId for Transaction 2019-03-07 23:42:00 +00:00
Jack Grigg
670bb277e9 Block header serialisation 2019-03-07 23:41:59 +00:00
Jack Grigg
a1664c6bbc impl Display for BlockHash and TxId 2019-03-07 23:41:46 +00:00
Jack Grigg
20d5cdc571 TxId struct 2019-03-07 23:26:03 +00:00
Jack Grigg
e21be37042 Block header representation 2019-03-07 23:26:00 +00:00
str4d
e4187f07ff Merge pull request #65 from str4d/general-refactor
Initial primitives refactor
2019-03-08 09:01:47 +13:00
Jack Grigg
3d39706aee Make SpendDescription.spend_auth_sig optional 2019-02-26 13:44:28 -07:00
Jack Grigg
012d43bc8c derive Debug for various structs 2019-02-26 13:44:28 -07:00
Jack Grigg
34ca75cceb impl Clone for ExtendedFullViewingKey 2019-02-26 13:44:28 -07:00
Jack Grigg
80db0ae2f9 ExpandedSpendingKey::proof_generation_key(), visibility tweaks 2019-02-26 13:44:28 -07:00
Jack Grigg
9ae5a9d624 Make loading of Sprout key optional in zcash_proofs API 2019-02-26 13:44:28 -07:00
Jack Grigg
2d43e3be7c Move parameter-loading into zcash_proofs 2019-02-26 13:44:28 -07:00
Jack Grigg
09a20aacfe Make zcash_primitives::JUBJUB the canonical instantiation of JubjubBls12 2019-02-26 13:44:28 -07:00
Jack Grigg
6b43b2d0d6 Refactor Sapling spendAuthSig creation into zcash_primitives::sapling 2019-02-26 13:44:28 -07:00
str4d
c57a31e82e Merge pull request #46 from str4d/ff-traits
Migrate to ff and group crates
2019-02-26 20:41:45 +00:00
Dimitris Apostolou
63be3c9470 Fix typos 2019-02-18 13:17:49 +02:00
Jack Grigg
482bef87f0 Add ff and group crates to Cargo workspace 2019-01-06 09:50:07 +00:00
Jack Grigg
b1ce3905d6 Add 'group/' from commit 'ef56fabf7ba3ed990a7886836c855298c9c5eefa'
git-subtree-dir: group
git-subtree-mainline: af9f9c17ee
git-subtree-split: ef56fabf7b
2019-01-06 09:38:21 +00:00
Jack Grigg
af9f9c17ee Add 'ff/' from commit '661558e0c8a5e02e08dac6530d39b2e38919aa04'
git-subtree-dir: ff
git-subtree-mainline: 07955092f3
git-subtree-split: 661558e0c8
2019-01-06 09:37:22 +00:00
Jack Grigg
07955092f3 Update librustzcash crate to use ff crate 2019-01-06 09:36:32 +00:00
Jack Grigg
00983c48cd Update zcash_proofs crate to use ff crate 2019-01-06 09:32:50 +00:00
Jack Grigg
538de482f3 Update zcash_primitives crate to use ff crate 2019-01-06 09:31:20 +00:00
Jack Grigg
2e408957db Update zip32 crate to use ff crate 2019-01-06 09:22:10 +00:00
Jack Grigg
22ccd1bceb Update sapling-crypto crate to use ff crate 2019-01-06 09:22:03 +00:00
Jack Grigg
76cd0d92bb Merge commit '4272cfa5b0dceac471bef115955e1534be84a018' into ff-traits
git-subtree-dir: bellman
git-subtree-split: 4272cfa5b0
2019-01-06 09:21:44 +00:00
Jack Grigg
88746e76fa Cargo.lock changes after pairing update 2019-01-06 09:20:00 +00:00
Jack Grigg
794cf79db2 Merge commit '3d41ee5abaa4888ff3607689aba007be8856816d' into ff-traits
git-subtree-dir: pairing
git-subtree-split: 3d41ee5aba
2019-01-06 09:19:29 +00:00
Jack Grigg
9f7e5fa3ce Dependency updates after pairing update 2019-01-06 09:02:30 +00:00
Jack Grigg
fe16e4a412 Merge commit '183a64b08e9dc7067f78624ec161371f1829623e' into ff-traits
git-subtree-dir: pairing
git-subtree-split: 183a64b08e
2019-01-06 09:01:44 +00:00
ebfull
f65c37ea01 Merge pull request #30 from str4d/sapling-api-cleanup
Sapling proving and verifying API
2019-01-05 17:14:16 -07:00
Jack Grigg
1a1c77536d cargo fmt 1.31.1 2019-01-03 21:52:06 +00:00
ebfull
c03871779a Merge pull request #54 from ebfull/travis
Add travis testing support.
2019-01-03 12:14:15 -07:00
Sean Bowe
4113df4780 Fix version string, cache cargo symbols. 2019-01-03 11:55:16 -07:00
Sean Bowe
813cf16ff2 Add travis testing support. 2019-01-03 11:51:08 -07:00
Jack Grigg
e378229bdd Move Sapling proving and binding signature into zcash_proofs crate 2018-12-01 00:10:51 +00:00
Jack Grigg
922ffe6002 Refactor Sapling proving and binding signature 2018-12-01 00:06:50 +00:00
Jack Grigg
e1841806c5 Move Sapling verification checks into zcash_proofs crate 2018-11-30 23:53:10 +00:00
Jack Grigg
eb2db2e667 Refactor Sapling verification checks 2018-11-30 23:50:50 +00:00
str4d
8be50c9531 Merge pull request #39 from str4d/zcash-transaction-primitives
Transaction primitives
2018-11-30 23:38:38 +00:00
Jack Grigg
c9b23dfdef Extract single-TxOut hashing from signature_hash_data() for clarity 2018-11-30 23:13:39 +00:00
Jack Grigg
e25b614573 Match error message in Amount::Read_i64() to allow_negative value 2018-11-30 23:04:04 +00:00
Jack Grigg
7ff32b04d6 Document enforcement of consensus rules on transaction components 2018-11-30 22:59:48 +00:00
Jack Grigg
d707ebd321 Use Option<[u8; N]> for JoinSplit pubkey and signature in a transaction 2018-11-30 01:08:45 +00:00
Jack Grigg
9b06205ed6 Reject unexpected binding sig during transaction write 2018-11-30 00:54:30 +00:00
Jack Grigg
61ce4dd3d6 Enforce range checks when reading Amounts 2018-11-30 00:30:37 +00:00
Jack Grigg
9282c7da29 Replace tx_read_write() test vector with one from current testnet chain 2018-11-16 09:50:27 +00:00
Jack Grigg
2d2e4aad86 Pass &[E] into Vector::write() instead of &Vec<E> 2018-11-16 08:55:06 +00:00
Jack Grigg
cc183efda4 Define MAX_SIZE constant for CompactSize serialization 2018-11-16 08:52:53 +00:00
Jack Grigg
1f11c404dc Convert Transaction into a wrapping struct with impl Deref
Users who have a Transaction can now only obtain an immutable reference
to its underlying data.
2018-11-09 08:34:49 +13:00
Jack Grigg
0c81695731 ZIP 243 2018-11-09 08:34:49 +13:00
Jack Grigg
2d8b1fe504 ZIP 143 2018-11-09 08:34:38 +13:00
ebfull
06da3b9ac8 Merge pull request #45 from str4d/44-params-path-encoding
Use slices of native strings to pass parameter paths into Rust
2018-10-27 00:09:44 -06:00
Jack Grigg
bbec1b841d Use slices of native strings to pass parameter paths into Rust
On Windows, the slices are [u16] representing UTF-16. On all other
platforms, the slices are [u8] in the native filesystem encoding.

Closes #44.
2018-10-27 16:27:49 +13:00
Jack Grigg
041671f642 Merge branch 'zcash-2.0.1' 2018-10-11 20:57:50 +01:00
Jack Grigg
e490b79907 Transaction serialization 2018-10-11 18:05:50 +01:00
Jack Grigg
91ff2c71cf Helper for serializing CompactSize-prefixed vectors 2018-10-11 17:48:12 +01:00
ebfull
0b6bbfb444 Merge pull request #36 from zcash/ci_integration
CI integration for librustzcash
2018-10-10 17:26:04 -06:00
mdr0id
c0b953b8f7 Remove duplicate test stage for rust lastest 2018-10-09 09:34:10 -07:00
mdr0id
17aa1f55e4 CI integration for librustzcash
Adding rustc --version and removing explicit cd to librustzcash

Sanity check stage pipeline flow

Cleaning up stages
2018-10-09 07:09:41 -07:00
ebfull
e1c6232dd7 Merge pull request #32 from str4d/cargo-workspace
Convert the repo into a Cargo workspace
2018-08-29 12:58:43 -06:00
Jack Grigg
6e0364cd42 Update workspace after pulling in zip32 crate 2018-08-29 09:57:52 +01:00
Jack Grigg
6526b9fb53 Add 'zip32/' from commit '176470ef41583b5bd0bd749bd1b61d417aa8ec79'
git-subtree-dir: zip32
git-subtree-mainline: a9059c9372
git-subtree-split: 176470ef41
2018-08-29 09:55:40 +01:00
Jack Grigg
a9059c9372 Update workspace after pulling in external crates 2018-08-28 23:06:15 +01:00
Jack Grigg
051027cb6d Add 'sapling-crypto/' from commit '21084bde2019c04bd34208e63c3560fe2c02fb0e'
git-subtree-dir: sapling-crypto
git-subtree-mainline: 9f748554d0
git-subtree-split: 21084bde20
2018-08-28 23:04:13 +01:00
Jack Grigg
9f748554d0 Add 'bellman/' from commit '10c5010fd9c2ca69442dc9775ea271e286e776d8'
git-subtree-dir: bellman
git-subtree-mainline: e924247e73
git-subtree-split: 10c5010fd9
2018-08-28 23:03:52 +01:00
Jack Grigg
e924247e73 Add 'pairing/' from commit '09b6e6f9212020f385218e5cf5287e381ccd312b'
git-subtree-dir: pairing
git-subtree-mainline: ad16ba6a35
git-subtree-split: 09b6e6f921
2018-08-28 23:03:42 +01:00
Jack Grigg
ad16ba6a35 Empty crates as a base for code refactoring 2018-08-28 22:24:14 +01:00
str4d
176470ef41 Merge pull request #1 from str4d/zip32
Implement Sapling components of ZIP 32
2018-08-27 15:06:02 +01:00
Jack Grigg
0ec85d362d Set up a Cargo workspace 2018-08-23 21:10:04 +01:00
Jack Grigg
d7257ce010 Move librustzcash crate into subdir 2018-08-23 21:04:02 +01:00
Jack Grigg
6671234ad1 cargo fmt 2018-08-22 23:59:40 +01:00
Jack Grigg
770c4bef25 Simplify creation of FVKFingerprint and FVKTag 2018-08-22 23:59:20 +01:00
Jack Grigg
e27fc674f5 Tidy up DiversifierIndex::increment() 2018-08-22 23:45:56 +01:00
Jack Grigg
ac8b27a5ba Expose contents of DiversifierIndex 2018-08-03 13:22:50 +01:00
Jack Grigg
a4e86be665 Check ivk in test vectors 2018-08-03 13:22:35 +01:00
Jack Grigg
80130f5656 Expose underlying ExpandedSpendingKey and FullViewingKey for writing 2018-08-01 10:41:50 +01:00
Jack Grigg
599079fafe Rename xsk: ExpandedSpendingKey -> expsk to disambiguate 2018-08-01 10:38:07 +01:00
Jack Grigg
39f978bb4f ExtendedSpendingKey::read() and ExtendedFullViewingKey::read() 2018-08-01 10:33:37 +01:00
Jack Grigg
9c9607e47a Add missing wrapping_add() caught by test vectors 2018-08-01 03:00:19 +01:00
Jack Grigg
342d97ac15 Test vectors from Python implementation 2018-08-01 02:52:41 +01:00
Jack Grigg
e47cafd691 ExtendedSpendingKey::write() and ExtendedFullViewingKey::write() 2018-08-01 02:51:18 +01:00
Jack Grigg
3b31854de7 Payment address derivation 2018-07-31 17:24:02 +01:00
Jack Grigg
fea07c52de Diversifier derivation 2018-07-31 17:24:02 +01:00
Jack Grigg
f5d6091c93 Path derivation 2018-07-31 17:24:02 +01:00
Jack Grigg
e87fb329f9 Child key derivation 2018-07-31 17:24:02 +01:00
Jack Grigg
0e1d440287 Serialization and fingerprint for ExpandedSpendingKey and FullViewingKey 2018-07-31 17:24:02 +01:00
Jack Grigg
2fdfa4b671 ExtendedSpendingKey::master() 2018-07-31 17:24:02 +01:00
Jack Grigg
8db848139b Basic conversions 2018-07-31 17:24:02 +01:00
Jack Grigg
1b8da6b12d ZIP 32 structures 2018-07-31 17:23:58 +01:00
ebfull
21084bde20 Merge pull request #81 from ebfull/must_use
Add must_use to operations over Edwards points
2018-07-30 18:40:16 -06:00
Sean Bowe
b4d41b689c Add must_use to operations over Edwards/Montgomery points. 2018-07-30 18:37:01 -06:00
ebfull
b70d6e66fc Merge pull request #79 from ebfull/pedersen-hash-performance
Improve Pedersen hash performance
2018-07-30 18:29:14 -06:00
ebfull
99c03c5708 Merge pull request #80 from ebfull/cofactor-signature-checking
Cofactor signature checking
2018-07-30 11:50:26 -06:00
Sean Bowe
95b498af33 Implement specialized doubling formula for extended twisted edwards coordinates. 2018-07-30 07:30:40 -06:00
Sean Bowe
821810cd82 Change pedersen hash outside the circuit to use window table lookups. 2018-07-30 07:30:40 -06:00
Sean Bowe
9cb8accf09 Fix nits 2018-07-30 07:29:23 -06:00
Sean Bowe
a57d2773ab Add demonstration implementation of batch verification. 2018-07-27 10:54:21 -06:00
Sean Bowe
cae9715a8e Change signature verification equation to permit batch verification. 2018-07-27 10:37:26 -06:00
Sean Bowe
3e43cae526 Add test that torsion doesn't affect signature verification. 2018-07-27 10:28:08 -06:00
ebfull
c2862a4382 Merge pull request #78 from gtank/make-privatekey-public
redjubjub: make PrivateKey internal scalar public
2018-07-12 14:01:11 -06:00
George Tankersley
df7bfce0be redjubjub: make PrivateKey internal scalar public 2018-07-12 19:58:32 +00:00
Jack Grigg
1e3f7739a3 Empty crate 2018-07-10 23:23:02 +01:00
Jack Grigg
4272cfa5b0 Make pairing and groth16 optional 🎉 2018-07-06 21:51:22 +01:00
Jack Grigg
276e09f1fb Use ff:ScalarEngine instead of pairing::Engine in bellman core 2018-07-06 21:37:28 +01:00
Jack Grigg
3e8f2f8202 Use group crate for curve traits and wNAF 2018-07-06 21:24:03 +01:00
Jack Grigg
718b25c949 Use ff crate for Field traits 2018-07-06 21:10:44 +01:00
Jack Grigg
3d41ee5aba Remove now-unused imports from root 2018-07-05 19:37:01 +01:00
Jack Grigg
7dfc50e763 Use group crate for curve traits 2018-07-05 19:36:55 +01:00
Sean Bowe
ef56fabf7b Update version 2018-07-05 12:18:56 -06:00
ebfull
2ffa94f714 Merge pull request #1 from str4d/from-pairing
Migrate curve traits and tests, and WNAF, from pairing
2018-07-05 12:18:34 -06:00
bmerge
183a64b08e Auto merge of #90 - str4d:ff, r=ebfull
Use ff crate for traits and impls

Depends on https://github.com/ebfull/ff/pull/1 and https://github.com/ebfull/ff/pull/5
2018-07-05 17:20:21 +00:00
Jack Grigg
fa8103764a cargo fmt 2018-07-04 22:34:42 +01:00
Jack Grigg
c5b883f91e Migrate to ff 0.4 2018-07-04 22:34:38 +01:00
Jack Grigg
4752a91781 Remove clippy from dependencies. 2018-07-04 22:34:21 +01:00
Sean Bowe
cc5b835102 Start using cargo-clippy for CI. 2018-07-04 22:33:59 +01:00
Jack Grigg
94cacc6152 Migrate curve traits and tests, and WNAF, from pairing 2018-07-03 09:59:28 +01:00
ebfull
661558e0c8 Merge pull request #5 from str4d/more-updates
More updates
2018-07-02 19:53:52 -06:00
Jack Grigg
526676ecfc Bump version to 0.4.0 2018-07-02 23:08:13 +01:00
Jack Grigg
2067360930 Add ScalarEngine trait
This is extracted from pairing's Engine trait.
2018-07-02 23:08:12 +01:00
Jack Grigg
0eb9f5040b Remove u128-support feature and arithmetic
Closes #2.
2018-07-02 23:08:11 +01:00
Jack Grigg
69ce66ae6c Place ff_derive re-exports behind a feature
Part of #3.
2018-07-02 23:08:08 +01:00
Jack Grigg
002173e187 Update README.md
Closes #4.
2018-07-02 23:08:03 +01:00
Jack Grigg
06a152734c Add missing SqrtField import to benches 2018-07-02 18:41:55 +01:00
Jack Grigg
c49590bab7 Change all remaining uses of *Field to reference ff crate 2018-07-02 16:04:52 +01:00
Jack Grigg
bb22a167af Update authors 2018-07-02 15:51:32 +01:00
Jack Grigg
defdf8df52 Connect ff u128-support to pairing u128-support 2018-07-02 15:49:47 +01:00
Jack Grigg
a9d8079c2a Replace implementations of Fq and Fr with derives 2018-07-02 15:48:12 +01:00
Jack Grigg
d9d711ebb7 Use explicit imports instead of re-exporting the ff crate 2018-07-02 15:48:12 +01:00
Jack Grigg
1db099f1cc Use ff crate for Field traits 2018-07-02 15:47:52 +01:00
Sean Bowe
44b601b0a9 Adjust versions 2018-07-02 08:34:26 -06:00
ebfull
729138a31e Merge pull request #1 from str4d/updates
Update dependencies and traits
2018-07-02 08:28:36 -06:00
Sean Bowe
f6d9ec8faf Initial commit 2018-07-02 07:59:33 -06:00
Jack Grigg
91a8dc8284 Update rand crate 2018-07-02 09:18:34 +01:00
Jack Grigg
4c984595e5 Add full paths for some manually-implemented derives
This further reduces the difference between pairing's manual and derived
implementations.
2018-07-02 09:16:42 +01:00
Jack Grigg
623dbd0d74 [MOVEONLY] Move generated code around slightly in ff_derive
This reduces the differences in the generated code between the current
implementations of Fq and Fr in pairing, and their derived versions.
2018-07-02 09:16:42 +01:00
Jack Grigg
b0b754ba04 Return constants and sqrt impls separately for individual rendering 2018-07-02 09:16:42 +01:00
Jack Grigg
5a48059a14 Integrate changes to trait impls from pairing into ff_derive 2018-07-02 09:16:42 +01:00
Jack Grigg
29a9161981 Implement changes to traits in ff_derive 2018-07-02 09:16:42 +01:00
Jack Grigg
58cb06ee92 Pull in trait changes from pairing 2018-07-02 09:16:22 +01:00
Jack Grigg
8201a3933f Pull in arith changes from pairing 2018-06-28 14:31:14 -04:00
Jack Grigg
1a3a2bec74 Update other ff_derive dependencies 2018-06-28 10:18:55 -04:00
Jack Grigg
428f463555 Migrate ff_derive to syn 0.14 and quote 0.6 2018-06-28 10:17:57 -04:00
Jack Grigg
755fc7aba8 cargo fmt 2018-06-27 07:31:53 -04:00
ebfull
6abfcca25a Merge pull request #74 from ebfull/ivk-pub
Make ivk pub
2018-05-18 12:45:38 -06:00
Sean Bowe
89f47ef5c2 Make ivk pub 2018-05-18 12:42:26 -06:00
ebfull
eb409fa3cf Merge pull request #72 from ebfull/little-endian
Switch to little endian
2018-05-17 14:03:43 -06:00
Sean Bowe
2ff318eecb Use little endian for everything in Sapling. 2018-05-17 13:01:31 -06:00
Sean Bowe
f491e02b56 Correctly interpret BLAKE2s inputs and outputs as little endian. 2018-05-17 13:01:31 -06:00
Sean Bowe
87c62e2248 Update to the latest pairing crate version. 2018-05-17 13:01:29 -06:00
bmerge
09b6e6f921 Auto merge of #82 - str4d:read-write-le, r=ebfull
Add read_le and write_le to PrimeFieldRepr
2018-05-17 17:55:18 +00:00
Sean Bowe
da5f1d3e37 Update to latest rustfmt 2018-05-17 11:59:20 -06:00
Sean Bowe
e4143a4bbc Apply rustfmt to benchmarks 2018-05-17 10:50:56 -06:00
Sean Bowe
97bdd1655f Update clippy 2018-05-17 10:44:28 -06:00
str4d
2a28b61468 Bump version 2018-05-17 16:52:19 +12:00
str4d
98bab6877a Add read_le and write_le to PrimeFieldRepr 2018-05-17 16:50:40 +12:00
ebfull
7beeb52730 Merge pull request #69 from ebfull/make-util-pub
Make util module public
2018-05-07 17:47:31 -06:00
Sean Bowe
36cd38e239 Make util module public. 2018-05-07 17:47:04 -06:00
ebfull
3b8246f3eb Merge pull request #68 from ebfull/redjubjub-final-work
Make PublicKey inner Point public so that we can use it during zk-SNARK verification
2018-05-07 14:21:49 -06:00
Sean Bowe
5687acfaf8 Make PublicKey inner Point public so that we can use it during zk-SNARK verification. 2018-05-01 15:23:34 -06:00
ebfull
347666705c Merge pull request #67 from str4d/redjubjub-serialisation
RedJubjub serialisation
2018-04-30 08:50:03 -06:00
Jack Grigg
b27dc2914b Remove redundant signature check
E::Fs guarantees its representation is in the field, implicitly enforcing
that S < order(G).
2018-04-30 13:55:41 +01:00
ebfull
6e08a5a22b Merge pull request #66 from str4d/fix-spend-nf-comment
Fix comment
2018-04-29 13:08:10 -06:00
Jack Grigg
e94dbf2523 Parameterize the generator in RedJubjub
Per the specification, the generator is different between BindingSig and
SpendAuthSig.
2018-04-26 22:42:54 +01:00
Jack Grigg
4eab1fc68a Implement RedJubjub serialization
Also alters the Signature struct to store Rbar and Sbar instead of R and S,
to more closely match the specification.
2018-04-26 22:42:20 +01:00
Jack Grigg
f54feda94f Fix comment
h/t omershlo from Zcash Community Chat for spotting it!
2018-04-26 12:30:08 +01:00
ebfull
e4175d81e9 Merge pull request #64 from str4d/redjubjub
RedJubjub
2018-04-19 12:44:39 -06:00
Jack Grigg
840c23bd23 Specify pre-conditions for signature validation 2018-04-19 16:17:40 +01:00
Jack Grigg
45e4ca38e4 Match additive notation with variable name 2018-04-18 23:59:22 +01:00
Jack Grigg
916dbce2df Implement RedJubjub
Co-authored-by: Sean Bowe <ewillbefull@gmail.com>
2018-04-17 00:40:11 +01:00
Jack Grigg
0f230a70b9 Implement uniform sampling of Jubjub scalars
Co-authored-by: Sean Bowe <ewillbefull@gmail.com>
2018-04-15 15:52:45 -06:00
Sean Bowe
e554b473dd Update to bellman 0.1 2018-04-06 13:29:58 -06:00
ebfull
10c5010fd9 Merge pull request #22 from ebfull/final-changes
Final changes
2018-04-03 18:56:06 -06:00
Sean Bowe
96b2d3e41a Prevent trivial subversion attack against prover. 2018-04-02 22:21:42 -06:00
Sean Bowe
d14f2af8fa Make fields of Parameters/VerifyingKey/Proof public. 2018-04-02 18:15:39 -06:00
ebfull
45c73dbd8f Merge pull request #54 from ebfull/sprout-circuit-minimal
Sprout circuit implementation
2018-04-02 16:55:09 -06:00
Sean Bowe
6e57ef0c4a Bump version 2018-03-31 23:23:37 -06:00
Sean Bowe
6ec7272586 Serialize lengths as 32-bit integers, not 64-bit. 2018-03-31 23:23:08 -06:00
Sean Bowe
c4fd8856ae Make multicore module public for now, because Worker is part of the EvaluationDomain API. 2018-03-31 23:16:55 -06:00
bmerge
dbe897d365 Auto merge of #79 - ebfull:nomorenightly, r=ebfull
Remove feature(i128_type)

The `i128_type` feature was recently stabilized in Rust, so this is unnecessary now for nightly users. In fact, in a few days there should be a new Rust beta (`1.26`) containing stable support for `i128_type`, meaning that (for Zcash) we can switch to the beta compiler for all future Rust development, and even switch to the stable compiler in six weeks when that's released.
2018-03-30 23:55:32 +00:00
Sean Bowe
2d12b9a858 Apply newer rustfmt rules. 2018-03-29 11:18:15 -06:00
Sean Bowe
fcaddaa356 Update Clippy to support latest nightly, and fix some lints. 2018-03-29 10:13:00 -06:00
Sean Bowe
bcc8379a7f Version bump. 2018-03-29 09:23:20 -06:00
Sean Bowe
4cf5a534ec i128_type feature has been stabilized in Rust. 2018-03-29 09:18:26 -06:00
ebfull
1026e92194 Merge pull request #63 from ebfull/gh
First block of GH invocation using random beacon output
2018-03-28 16:05:20 -06:00
Sean Bowe
9f24b68b3a Update constraint system hashes. 2018-03-27 21:41:11 -06:00
Sean Bowe
3704b2422e Fix benchmark due to anchor change in witness. 2018-03-27 21:31:32 -06:00
Sean Bowe
9418001d0e First block of GH invocation using random beacon output. 2018-03-27 14:30:08 -06:00
ebfull
470c9572eb Merge pull request #60 from ebfull/optional-enforce
Allow the authentication path to be not enforced
2018-03-27 14:27:53 -06:00
ebfull
7ee29503ec Merge pull request #61 from str4d/bench-final
Update benchmark to use final Sapling circuit implementation
2018-03-27 13:13:23 -06:00
Jack Grigg
97bead9977 Update benchmark to use final Sapling circuit implementation 2018-03-27 16:03:02 +02:00
Sean Bowe
cfd378685f Add more test vectors to Sprout circuit implementation. 2018-03-26 21:47:40 -06:00
Sean Bowe
def5de3f16 Allow the authentication path to be not enforced for zero-value notes, for indistinguishability. 2018-03-26 13:30:14 -06:00
Jack Grigg
d715e812bd Un-disable benchmark 2018-03-24 00:40:16 +01:00
Sean Bowe
4de908b1e5 Fix bug in circuit uncovered by test vector. 2018-03-21 19:21:16 -06:00
Sean Bowe
162a3877e5 JoinSplit circuit implementation for Sprout. 2018-03-21 19:21:16 -06:00
Sean Bowe
ac13cb05bc Implementation of SHA256. 2018-03-21 19:21:16 -06:00
Sean Bowe
75c5269d3b Implementation of SHA256 choice/majority for UInt32. 2018-03-21 19:21:16 -06:00
Sean Bowe
36a6b5fd90 Implementation of SHA256 majority operation for Boolean. 2018-03-21 19:21:16 -06:00
Sean Bowe
abca61401e Implementation of SHA256 choice operation for Boolean. 2018-03-21 19:21:16 -06:00
Sean Bowe
4f0a553fbb Implement UInt32 encoding/decoding with big-endian representation. 2018-03-21 19:21:16 -06:00
Sean Bowe
51bb5f0f70 Implement UInt32::shr() for SHA256. 2018-03-21 19:21:16 -06:00
ebfull
e6397507ca Merge pull request #57 from ebfull/final-circuit-changes
Final circuit changes (part 1)
2018-03-21 19:11:08 -06:00
Sean Bowe
601e8e38f8 Little-endian byte order interpretation of the output of CRH^ivk. 2018-03-19 18:06:44 -06:00
Sean Bowe
f9e58c01ce Swap bit-endianness of value in note commitment. 2018-03-19 17:54:44 -06:00
Sean Bowe
b14c9f8d68 Change personalizations to actually match spec this time. 2018-03-19 17:26:28 -06:00
Sean Bowe
219d03cc11 Perform multiple checks of circuit in test. 2018-03-19 17:05:51 -06:00
Sean Bowe
8b6f113052 Change personalization to more closely align with the spec. 2018-03-17 10:24:55 -06:00
Sean Bowe
d09c4d6ce8 Simplify value commitment gadget. 2018-03-17 09:10:13 -06:00
Sean Bowe
6d01e78711 Fix variable names in output circuit 2018-03-17 09:02:29 -06:00
Sean Bowe
13b03a0a1a Relocate circuit components for intuitive code paths. 2018-03-16 22:21:29 -06:00
Sean Bowe
00ee962429 Fix some names of variables. 2018-03-16 15:46:19 -06:00
Sean Bowe
8b2f231e2f Change personalization to match specification. 2018-03-16 15:46:19 -06:00
Sean Bowe
7e05feb90b Changes to names and circuit design to match spec. 2018-03-16 15:46:15 -06:00
ebfull
c09292672a Merge pull request #56 from rex4539/fix-typos
Fix typos
2018-03-16 15:44:02 -06:00
Dimitris Apostolou
a7d704cd10 Fix typos 2018-03-16 23:25:36 +02:00
ebfull
39f1152e16 Merge pull request #53 from ebfull/circuit-improvements
Minor circuit improvements
2018-03-15 12:47:26 -06:00
Sean Bowe
21625d69e0 Place Sapling circuit into submodule. 2018-03-15 12:44:19 -06:00
Sean Bowe
ca202ef304 Introduce input multipacking abstraction for nullifiers. 2018-03-15 12:36:05 -06:00
Sean Bowe
52eb59766b Optimize UInt32::addmany/BLAKE2s to combine equality constraints. (Closes #5) 2018-03-15 12:31:10 -06:00
Sean Bowe
827e85547e Public input verification utility for the test framework 2018-03-15 11:42:22 -06:00
ebfull
3c07a2d9b3 Merge pull request #30 from plutomonkey/boolean_enforce_equal
Optimise Boolean::enforce_equal.
2018-03-14 07:28:33 -06:00
Jason Davies
661f318eb7 Optimise Boolean::enforce_equal.
Use a single constraint instead of two.

Fixes #6.
2018-03-09 23:53:28 +00:00
Sean Bowe
2c69abe268 Add Uncommitted^Sapling. 2018-03-09 14:17:45 -07:00
Sean Bowe
c1784f0fdf Check g_d is not small order in Spend circuit. 2018-03-09 14:08:01 -07:00
ebfull
390f2c129b Merge pull request #37 from ebfull/circuit-tests
Circuit tests
2018-03-08 19:18:40 -07:00
Sean Bowe
b6e1b52a44 Fix comment about Montgomery curve selection 2018-03-08 13:03:07 -07:00
Sean Bowe
db28ff7ba1 Simplify the Output witness. 2018-03-08 01:49:27 -07:00
Sean Bowe
9998400117 Relocate structs for cleanliness. 2018-03-08 01:37:55 -07:00
Sean Bowe
512a394b30 Simplify witness for Spend statement. 2018-03-08 01:36:16 -07:00
Sean Bowe
3fbbd933cf Simplify value commitment abstraction. 2018-03-08 01:16:21 -07:00
Sean Bowe
d6d86737c8 Remove some code duplication for value commitment witnessing. 2018-03-08 01:01:00 -07:00
Sean Bowe
f61cc88a71 More comment improvements. 2018-03-08 00:51:53 -07:00
Sean Bowe
b6ef12b077 General code quality improvements. 2018-03-08 00:41:47 -07:00
Sean Bowe
896b144a7d Move first block of group hash to constants submodule. 2018-03-08 00:09:34 -07:00
Sean Bowe
c7c8d3c039 Move personalization constants to submodule. 2018-03-08 00:06:53 -07:00
Sean Bowe
25a8050df8 Remaining tests for input circuit 2018-03-07 23:59:04 -07:00
Sean Bowe
b998190f9e Disable this, for now. 2018-03-07 23:45:08 -07:00
Sean Bowe
0242ed35ab Add some circuit tests for inputs. 2018-03-07 12:19:56 -07:00
Sean Bowe
2d4be07560 Fix group hash comment. 2018-03-06 22:26:03 -07:00
Sean Bowe
ef85173df5 Fix comments in jubjub code. 2018-03-06 22:25:15 -07:00
Sean Bowe
b0b3514fa7 Add bench_50 example. 2018-03-06 10:21:30 -07:00
Sean Bowe
1f65d4b353 Change tree depth to 32 in test. 2018-03-06 10:04:02 -07:00
Sean Bowe
1f2bb62657 Rename proving key to proof generation key to disambiguate from the SNARK. 2018-03-06 09:56:29 -07:00
Sean Bowe
c31092ce7f Remove unnecessary mut binding. 2018-03-06 09:39:56 -07:00
ebfull
c253e51e3d Merge pull request #35 from ebfull/primitives
WIP bundle
2018-03-06 09:10:23 -07:00
Sean Bowe
63c6830429 Cache fewer pedersen hash generators. 2018-03-06 09:03:29 -07:00
Sean Bowe
7a9879eb54 Make EdwardsPoint (x, y) not public. 2018-03-06 08:38:34 -07:00
Sean Bowe
f155c01cf5 Personalize GH for each generator independently. 2018-03-06 08:30:28 -07:00
Sean Bowe
b831942501 Adopt BLAKE2s personalization throughout protocol. 2018-03-05 19:32:27 -07:00
Sean Bowe
d21ff08176 Group hash should use a first block containing random data as per spec. 2018-03-05 18:08:49 -07:00
Sean Bowe
058801bdfc Switch to using the blake2-rfc crate instead. 2018-03-05 17:58:34 -07:00
Sean Bowe
2e846844e7 Perform ak group order check in Spend circuit. 2018-03-05 17:46:12 -07:00
Sean Bowe
7bb630a4b1 Use correct personalization for merkle tree. 2018-03-05 17:27:55 -07:00
Sean Bowe
c6cf0c718c Value placed in note contents should be in big endian bit order. 2018-03-05 16:10:55 -07:00
Sean Bowe
b45a37febb Add comments and reorder some generators. 2018-03-05 16:00:04 -07:00
Sean Bowe
69010d1502 Relocate Pedersen hash personalization enum. 2018-03-05 15:25:40 -07:00
Sean Bowe
8cbcd7739c Rename into_bits and into_bits_strict to signify endianness. 2018-03-05 15:12:51 -07:00
Sean Bowe
3971ecd375 Abstract away the boolean conversion of field witnessing. 2018-03-05 10:52:56 -07:00
Sean Bowe
543f5cd49c Change bit-endianness of into_bits/into_bits_strict. 2018-03-05 10:27:14 -07:00
Sean Bowe
3e15751fd1 Allocate the note value directly in little-endian bit order. 2018-03-05 09:37:13 -07:00
Sean Bowe
e52befb58e Introduce inputize abstractions to simplify circuit code. 2018-03-04 23:33:05 -07:00
Sean Bowe
51c35a9bcf Adopt new versions of pairing and bellman. 2018-03-04 22:25:04 -07:00
ebfull
33feb3763f Merge pull request #17 from plutomonkey/update-rand-dependency
Update "rand" dependency to 0.4.
2018-03-04 20:45:28 -07:00
bmerge
ef7ade8c2c Auto merge of #78 - ebfull:release-0.14, r=ebfull
Release of pairing 0.14.0.

This release makes some changes in response to an audit of this library, as well as some obversations about the API that I made. Some names were changed (`divn` -> `shr`, `muln` -> `shl`) and `add_nocarry`/`sub_noborrow` no longer return anything (as their names would suggest.) Some potentially misleading comments are fixed as well.

The release also applies `rustfmt` and new lints from `clippy` to the codebase, enforced by the CI on a more recent version of the Nightly compiler.

Thanks go to @jasondavies for his contributions to this release.
2018-03-05 03:31:26 +00:00
Sean Bowe
92d2c13285 Release of pairing 0.14.0. 2018-03-04 20:01:23 -07:00
Sean Bowe
c9cacc7467 Bump version and integrate pairing 0.14. 2018-03-04 19:51:03 -07:00
bmerge
6fea7e382d Auto merge of #77 - ebfull:rustfmt-application-2, r=ebfull
Apply rustfmt

Supersedes #76

I've made a change to the CI which checks `rustfmt` against the code. Let's see if it worked.
2018-03-05 02:49:40 +00:00
Sean Bowe
f5370057bd Test deserialization of proofs in MiMC test example. 2018-03-04 19:41:59 -07:00
Sean Bowe
5f8747a0f4 Move MiMC example to tests. 2018-03-04 19:33:59 -07:00
Sean Bowe
86f652fa9f Some minor documentation/changes to domain. 2018-03-04 19:27:33 -07:00
Sean Bowe
f7815f6e49 Remove unused tests and fix documentation. 2018-03-04 18:49:07 -07:00
Sean Bowe
ab1487025f Serialization of parameters and proofs. 2018-03-04 18:49:05 -07:00
Sean Bowe
57687cf70f Creation of the Note primitive. 2018-02-24 22:53:00 -07:00
ebfull
4f4a2d63db Merge pull request #34 from ebfull/output-circuit
WIP circuit development
2018-02-24 17:22:42 -07:00
Sean Bowe
27674bf8ff Derive Clone for EdwardsPoint. 2018-02-24 17:11:17 -07:00
Sean Bowe
3346fba915 Implementation of Jubjub point representation. 2018-02-24 14:11:01 -07:00
Sean Bowe
4441a0da41 Hash the constraint systems to check integrity. 2018-02-24 09:09:20 -07:00
Sean Bowe
23d17b9042 Minor adjustments. 2018-02-23 18:09:26 -07:00
Sean Bowe
7a7774d8af Remove unused imports in Boolean. 2018-02-23 11:28:17 -07:00
Sean Bowe
8c37212645 Remove kary_and from Boolean. 2018-02-23 11:25:59 -07:00
Sean Bowe
a45986ff97 Remove enforce_nand. 2018-02-23 11:24:42 -07:00
Sean Bowe
0d56c7a902 Remove enforce_in_field. 2018-02-23 11:18:39 -07:00
Sean Bowe
69abd0391f Optimize into_bits_strict. 2018-02-23 11:15:14 -07:00
Sean Bowe
97585a30ad Remove from_bits_strict from AllocatedNum. 2018-02-23 09:26:12 -07:00
Sean Bowe
c812805b31 Slight refactor of representations 2018-02-22 18:43:07 -07:00
Sean Bowe
ba7298de3f Crappy mock-up of the circuit. 2018-02-22 11:36:44 -07:00
Sean Bowe
4b6623cf44 Add some personalization and a generator. 2018-02-22 07:29:55 -07:00
Sean Bowe
53083f4290 Apply rustfmt to the codebase. 2018-02-21 11:08:58 -07:00
bmerge
d51a5b0122 Auto merge of #73 - plutomonkey:update-rand-dependency, r=ebfull
Update "rand" dependency to 0.4.
2018-02-21 16:33:33 +00:00
bmerge
da717f4472 Auto merge of #75 - ebfull:primerepr-fixes, r=ebfull
PrimeRepr improvements

These are API-breaking changes that make `PrimeRepr` a little nicer.
2018-02-21 15:56:41 +00:00
ebfull
1a89b3a486 Merge pull request #33 from ebfull/general-improvements
General improvements
2018-02-20 18:39:02 -07:00
Sean Bowe
e8480a2b2c Utility for witnessing points on the curve. 2018-02-20 18:36:53 -07:00
Sean Bowe
c221bc9126 Rename mont to ecc in circuit code. 2018-02-20 18:18:29 -07:00
Sean Bowe
a1c749e6a0 Change fixed-base exponentiation API to handle scalars better. 2018-02-20 18:12:27 -07:00
Sean Bowe
88bdff6ce9 Pedersen hashes with full optimization 2018-02-20 17:22:00 -07:00
Sean Bowe
4fa73efc1e Remove conditional negation implementation from AllocatedNum. 2018-02-20 16:41:33 -07:00
Sean Bowe
c89d47bb07 Remove Montgomery point doubling implementation in the circuit. 2018-02-20 16:38:44 -07:00
Sean Bowe
1610bcfbcf Perform the y-coordinate conditional negation and lookup simultaneously. 2018-02-20 16:31:27 -07:00
Sean Bowe
6f66fd3f9d Express x and y coordinates from lookup as linear combinations. 2018-02-20 16:11:48 -07:00
Sean Bowe
b37d9b11cb More efficient implementation of Edwards doubling in the circuit. 2018-02-20 15:38:28 -07:00
Sean Bowe
6e80c12365 Add TODO 2018-02-20 15:11:29 -07:00
Sean Bowe
d779f31ccd Force personalization of Pedersen hashes. 2018-02-20 15:11:29 -07:00
Sean Bowe
39175a0c2a Remove unneeded imports 2018-02-20 15:11:29 -07:00
Sean Bowe
5118fd5008 Add new fixed generators for the output circuit 2018-02-20 15:11:29 -07:00
Sean Bowe
ec7323159c Booleanize u64 objects. 2018-02-20 15:11:29 -07:00
Sean Bowe
1df7fbeeff Refactor "booleanization" of objects. 2018-02-20 15:11:29 -07:00
ebfull
8d633db82b Merge pull request #29 from ebfull/pedersen-chunks
63 chunks per pedersen hash segment
2018-02-20 15:09:59 -07:00
ebfull
c091e274ee Merge pull request #32 from ebfull/bellman-update
Bellman update
2018-02-16 09:09:27 -07:00
Sean Bowe
2f95a9094a Refactor to handle changes to ConstraintSystem API (no more Variable associated type, one is now static method). 2018-02-16 08:45:18 -07:00
Sean Bowe
814cfef769 Use more descriptive synthesis errors now available in bellman. 2018-02-16 08:45:17 -07:00
Sean Bowe
683aa93b44 Account for change in bellman's API for enforcement to use closures. 2018-02-16 08:44:27 -07:00
Sean Bowe
749d6ceed4 Bump version 2018-02-15 22:22:50 -07:00
Sean Bowe
cb53708767 New implementation of library API. 2018-02-14 12:31:43 -07:00
Sean Bowe
541fda7580 Fix misleading comments on write_be and read_be. 2018-02-13 17:14:17 -07:00
Sean Bowe
b971bdedda Rename divn/muln to shr/shl. 2018-02-13 17:07:10 -07:00
bmerge
7b6e13bcb9 Auto merge of #74 - plutomonkey:clippy, r=ebfull
Update clippy and fix code indentation.

(The code indentation issue was not caught by clippy -- I noticed it by chance!)
2018-02-14 00:03:26 +00:00
Sean Bowe
a0fcf717c8 add_nocarry and sub_noborrow should no longer return anything. 2018-02-13 16:41:10 -07:00
Jason Davies
bce9f5d639 Update clippy and fix code indentation.
(The code indentation issue was not caught by clippy -- I noticed it by
chance!)
2018-02-13 16:02:30 +00:00
Jason Davies
2338174244 Update "rand" dependency to 0.4. 2018-02-13 15:51:16 +00:00
Jason Davies
137b410196 Update "rand" dependency to 0.4. 2018-02-13 15:43:35 +00:00
Sean Bowe
821d22261e Raise the number of chunks in Pedersen hashes to 63. 2018-02-11 12:59:57 -07:00
Sean Bowe
52829af6d8 Fix test for pedersen hash chunks per generator. 2018-02-11 12:59:49 -07:00
ebfull
c8cc190781 Merge pull request #21 from ebfull/gh-revisions
Edwards scalar multiplication inside the circuit
2018-02-10 11:24:11 -07:00
Sean Bowe
73e73d7c8c Remove TODOs by creating tickets. 2018-02-07 13:55:34 -07:00
Sean Bowe
edc4adc32c Implementation of fixed-base Edwards scalar multiplication in the circuit. 2018-02-07 13:33:09 -07:00
Sean Bowe
69833e5162 Start initializing generators for various components in the protocol. 2018-02-06 11:08:17 -07:00
Sean Bowe
55b8f7a575 Dynamic base twisted Edwards scalar multiplication in the circuit. 2018-02-02 14:24:18 -07:00
Sean Bowe
f2c74a4b98 Implement conditional reversal of two allocated numbers. 2018-02-02 11:57:49 -07:00
Sean Bowe
d49c0b0b0d Implement Edwards doubling in the circuit. 2018-02-02 10:01:33 -07:00
Sean Bowe
67f2cea200 Move window table lookup logic into its own module. 2018-02-02 09:50:59 -07:00
Sean Bowe
ba3ef9c84c Implementation of 3-bit window table lookups. 2018-02-02 09:41:44 -07:00
Sean Bowe
9d49a60f48 Replace Montgomery point interpretation with twisted Edwards. 2018-01-29 10:38:18 -07:00
Sean Bowe
f00e8a8292 Change group_hash to output points in the twisted Edwards form. 2018-01-29 08:56:58 -07:00
Sean Bowe
55598e4d4f Define get_for_y for twisted Edwards points. 2018-01-29 08:32:06 -07:00
Sean Bowe
bfda59f80b Remove group hash implementation from inside the circuit, as it is no longer necessary. 2018-01-29 07:24:59 -07:00
ebfull
7d590491bd Merge pull request #15 from ebfull/pedersen-hashes
Pedersen hashes inside and outside the circuit
2018-01-29 06:06:52 -07:00
Sean Bowe
42514e7c47 Remove TODO's in favor of github issues. 2018-01-29 06:06:10 -07:00
Sean Bowe
7211d98c1e Implement linear combination addition with coefficient. 2018-01-07 11:31:35 -07:00
Sean Bowe
6841763ae7 Add TODO for improving lookup performance. 2017-12-28 16:10:19 -07:00
Sean Bowe
849f330441 Add test to monitor the number of constraints consumed by the pedersen hash (in the context of a merkle tree). 2017-12-28 12:00:22 -07:00
Sean Bowe
e9c9618ef4 Implement pedersen hashes inside and outside of the circuit. 2017-12-28 11:06:05 -07:00
Sean Bowe
07f2e553a7 Implement twisted Edwards point conversion and addition in the circuit. 2017-12-22 11:52:13 -07:00
Sean Bowe
8e3bef80a4 Bump version of bellman. 2017-12-22 11:52:13 -07:00
Sean Bowe
e1ab3be3cc Implementation of conditional negation for AllocatedNum. 2017-12-22 11:52:13 -07:00
Sean Bowe
87548f3d1d Implementation of Montgomery point addition in the circuit. 2017-12-22 11:52:08 -07:00
ebfull
041060e5ca Merge pull request #13 from ebfull/tidy-up
Tidy up the codebase (TODOs into github issues)
2017-12-18 22:11:05 -07:00
Sean Bowe
031ae638ce Tidy up the codebase (TODOs into github issues). 2017-12-18 22:08:57 -07:00
ebfull
49cc4e05d9 Merge pull request #4 from ebfull/refactor-jubjub
Group Hash implementation in the circuit
2017-12-18 21:38:30 -07:00
Sean Bowe
1e56289f19 Implementation of group hash in the circuit. 2017-12-18 11:34:15 -07:00
Sean Bowe
d143d3230a Relocate grouphash implementation. 2017-12-18 11:15:02 -07:00
Jay Graber
635bd8104e Add grouphash 2017-12-18 10:58:59 -07:00
Sean Bowe
7d6a57661b Add Montgomery point interpretation. 2017-12-17 10:07:00 -07:00
Sean Bowe
eb8803f9eb Implementation of into_bits_strict for Num. 2017-12-17 09:31:33 -07:00
Sean Bowe
068fbbc2be Implementation of into_bits for Num. 2017-12-17 09:12:04 -07:00
Sean Bowe
6c6a193551 Implementation of from_bits_strict for Num. 2017-12-16 17:03:40 -07:00
Sean Bowe
d04c5acdb8 Implement enforce_in_field and enforce_nand for Boolean. 2017-12-16 16:01:13 -07:00
Sean Bowe
dfd82439de Implementation of k-ary AND. 2017-12-15 14:12:38 -07:00
Sean Bowe
dc981e6abc Allow linear combinations to be added together. 2017-12-14 16:11:24 -07:00
Sean Bowe
46cbfb4831 Implementation of Montgomery point doubling in the circuit. 2017-12-14 15:41:37 -07:00
Sean Bowe
8c4433ee82 Add 2A precomputation to jubjub parameters. 2017-12-14 11:34:57 -07:00
Sean Bowe
6b43a4ed10 Make UInt32::addmany produce constant results when fed constant inputs, to allow for blake2s block precomputation for group hash. 2017-12-12 14:53:16 -07:00
Sean Bowe
3a6e8d448f Add get_for_x to Montgomery implementation. 2017-12-12 10:31:13 -07:00
Sean Bowe
e9d3923829 Refactor jubjub implementation to be abstract over field, parameters. 2017-12-11 23:06:05 -07:00
ebfull
bba5797e72 Merge pull request #2 from ebfull/mont-to-projective
Do not perform inversions when converting from montgomery to edwards
2017-12-11 15:44:11 -07:00
Sean Bowe
c0f5645ab4 Do not perform inversions when converting from Montgomery to projective extended twisted Edwards. 2017-12-06 17:22:35 -07:00
Sean Bowe
7c48792511 Some test cleanups. 2017-12-06 10:21:56 -07:00
Sean Bowe
bcb4925c6d Rename "sapling" to "sapling-crypto" in COPYRIGHT. 2017-12-06 10:15:12 -07:00
Sean Bowe
96bcc84447 Change library name and add README. 2017-12-06 10:10:56 -07:00
Sean Bowe
86619c7334 Implementation of fundamental circuitry and primitive Jubjub curve arithmetic. 2017-12-06 10:06:34 -07:00
bmerge
a8583dd818 Auto merge of #71 - ebfull:expose-arith, r=ebfull
Expose arithmetic

This exposes `adc`/`sbb`/`mac_with_carry` from this library for downstream use, as long as a `expose-arith` feature is enabled. We need this downstream to avoid code duplication.

This also bumps to `0.13.2`.
2017-12-05 04:43:24 +00:00
Sean Bowe
fb679470db Bump version. 2017-12-04 21:48:22 -07:00
Sean Bowe
04a32fb443 Introduce expose-arith unstable feature for exposing arithmetic functions downstream. 2017-12-04 21:47:45 -07:00
Sean Bowe
70a588ceee Version bump 2017-12-02 23:31:37 -07:00
ebfull
3163e801b9 Merge pull request #16 from ebfull/recode
Part one of recoding bellman
2017-12-02 23:30:57 -07:00
Sean Bowe
5a88b4f818 Guarantee inheritance of variable type for public constraint system contexts. 2017-12-02 23:30:23 -07:00
bmerge
cc8be5b780 Auto merge of #70 - ebfull:comment-fixes, r=ebfull
Fix some comments

The `mod q` stuff was spotted by @Pratyush and/or @howardwu.

I just noticed the `// 2` typo.
2017-11-26 10:19:09 +00:00
Sean Bowe
b8394bf14d Fix some comments. 2017-11-26 03:09:06 -07:00
bmerge
ed5a7f31e2 Auto merge of #69 - ebfull:0.13.1-release, r=ebfull
Release of 0.13.1

This release features the following changes:

* The `byteorder` version is now set to `1` as conventional.
* The `CurveAffine`/`CurveProjective` traits now enforce that the scalar field of the `Engine` is the same scalar field for the curves.
* `Engine` is now `'static` and `Clone` to avoid problems with lifetime quirks and auto-derives.
* The scalar field of `Engine` is now guaranteed to be `SqrtField` which helps downstream abstractions.

In this PR I will be updating the `clippy` version and fixing any linting problems that show up. The builder should be using the newest nightly/stable versions of Rust also.
2017-11-26 04:27:58 +00:00
Sean Bowe
bb1ced0bd7 Update clippy version. 2017-11-25 21:32:14 -07:00
Sean Bowe
566f100480 Release of 0.13.1. 2017-11-25 21:01:55 -07:00
bmerge
df9d2aaa37 Auto merge of #68 - ebfull:scalar-sqrt, r=ebfull
Scalar field should be guaranteed to be SqrtField
2017-11-26 03:50:21 +00:00
Sean Bowe
aa0cc06e5e Scalar field should be guaranteed to be SqrtField. 2017-11-25 20:57:10 -07:00
Sean Bowe
1fc640e88f Guarantee that we inherit the correct variable type. 2017-11-24 10:14:11 -07:00
Sean Bowe
92029393f6 Guarantee that variables are Copy+Clone. 2017-11-22 22:55:32 -07:00
Sean Bowe
1d394e00f6 Allow linear combination terms to be inspected for downstream evaluation. 2017-11-22 21:11:41 -07:00
Sean Bowe
e54c4bc1b3 Rename to namespace_public. 2017-11-21 14:00:00 -07:00
Sean Bowe
af91dcda33 Infrastructure for public input namespacing. 2017-11-21 13:44:56 -07:00
Sean Bowe
b7f2f9e409 Part one of Bellman being recoded. 2017-11-21 01:59:49 -07:00
bmerge
affead11a5 Auto merge of #67 - ebfull:static-engine, r=ebfull
Engine should always be 'static, for flexibility in downstream code.
2017-11-21 06:47:28 +00:00
Sean Bowe
4a1ac94799 Implement Clone for Bls12. 2017-11-20 23:53:58 -07:00
Sean Bowe
4b366a143d Ensure Engine's are always Clone. 2017-11-20 23:20:11 -07:00
Sean Bowe
6d55a4052a Move everything into oldsrc. 2017-11-20 23:01:15 -07:00
Sean Bowe
945d86fe7d Make the namespacing not introduce too much indirection. 2017-11-20 15:50:41 -07:00
bmerge
36ad8b672c Auto merge of #66 - ebfull:scalar-assoc, r=ebfull
Enforce that Fr of Engine is the scalar for curve points

In bellman, I want to write:

```rust
pub struct Point<C: CurveProjective>(pub C);

impl<C: CurveProjective> Copy for Point<C> { }

impl<C: CurveProjective> Clone for Point<C> {
    fn clone(&self) -> Point<C> {
        *self
    }
}

impl<C: CurveProjective> Group<C::Engine> for Point<C> {
    fn group_zero() -> Self {
        Point(C::zero())
    }
    fn group_mul_assign(&mut self, by: &C::Scalar) {
        self.0.mul_assign(by.into_repr());
    }
    fn group_add_assign(&mut self, other: &Self) {
        self.0.add_assign(&other.0);
    }
    fn group_sub_assign(&mut self, other: &Self) {
        self.0.sub_assign(&other.0);
    }
}
```

However, this doesn't typecheck because the compiler cannot know that the `Engine`'s `Fr` type (as dictated by the `Group` trait) is the same as the `Scalar` type of the `CurveProjective` point. This can be solved with a where bound over the trait (for now), but it is generally a good idea for this to be constrained in `pairing`.
2017-11-20 19:26:16 +00:00
Sean Bowe
f32cb40929 Engine should always be 'static, for flexibility in downstream code. 2017-11-20 12:22:51 -07:00
Sean Bowe
a1e1aa30b5 More infrastructure for test development. 2017-11-18 20:10:47 -07:00
Sean Bowe
35314c8771 Initial commit 2017-11-13 01:54:13 -07:00
Sean Bowe
3ee1a576f4 Bump version 2017-11-13 01:36:54 -07:00
Sean Bowe
471db6ab27 Enforce that Fr of Engine is the scalar for curve points, for simpler downstream abstractions. 2017-11-12 16:19:08 -07:00
Sean Bowe
7df272b349 Make license files/copyright identical to pairing crate. 2017-11-12 15:56:03 -07:00
Sean Bowe
13a5cbf9d5 Modify the public api surrounding multiexp. 2017-11-12 15:52:10 -07:00
Sean Bowe
e4c62180d7 Use a basic heuristic to choose c for multiexp. 2017-11-12 15:50:34 -07:00
Sean Bowe
b15f5f6f43 Fix some warnings and TODO's. 2017-11-12 13:45:13 -07:00
Sean Bowe
d616362884 Add TestConstraintSystem 2017-11-12 13:26:03 -07:00
Sean Bowe
bc19c76326 Change API to support constraint/variable annotation. 2017-11-11 04:50:30 -07:00
Sean Bowe
3bb6596507 Don't make u128-support a default feature anymore. 2017-11-05 19:35:28 -07:00
Sean Bowe
326996139e Update to use the latest version of pairing library. 2017-10-22 05:45:26 -06:00
bmerge
5d9f77f5bf Auto merge of #65 - ebfull:byteorder-version, r=ebfull
Use 1 for the byteorder crate's version.
2017-10-12 17:48:25 +00:00
Sean Bowe
342b94c76b Use "1" for the byteorder crate's version. 2017-10-12 11:20:57 -06:00
bmerge
48044a853f Auto merge of #64 - ebfull:bump-13, r=ebfull
Bump version to 0.13.0

After #58 and #63 are merged.
2017-10-12 17:20:04 +00:00
bmerge
364796d88c Auto merge of #58 - ebfull:derive-clone, r=ebfull
Derive `Clone` for G1/G2 encodings.

AFAIK this will finally work on stable when the next version of Rust is released.

Closes #55
2017-10-12 16:45:21 +00:00
bmerge
30ae5d6313 Auto merge of #63 - ebfull:rand-unknown-exponent, r=ebfull
G1/G2 rand() should produce elements of unknown exponent.

Closes #60.
2017-10-12 16:16:52 +00:00
Sean Bowe
39c25cd506 Bump version to 0.13.0 2017-10-10 01:54:53 -06:00
Sean Bowe
12b9606b9c G1/G2 rand() should produce elements of unknown exponent. 2017-10-10 01:13:35 -06:00
bmerge
044256c2f7 Auto merge of #62 - mmaker:feature/scale_by_cofactor, r=ebfull
Add "scale_by_cofactor".

Add a function for Affine types that multiplies point by the cofactor of the
group. If the points are in the group, they'll thus end up in the
correct subgroup.

note: this patch is already being tested with previous unittests.
note: this patch raises warning for unused functions and consts.
2017-10-09 23:15:59 +00:00
Sean Bowe
65e50a9e5a Allow scale_by_cofactor to be dead code temporarily. 2017-10-09 17:14:36 -06:00
Michele Orrù
e5607bb528 Add "scale_by_cofactor".
Add a function for Affine types that multiplies point by the cofactor of the
group.
2017-10-09 10:04:22 +02:00
bmerge
dfb836170b Auto merge of #61 - ebfull:minor-changes, r=ebfull
Minor changes
2017-10-05 18:36:27 +00:00
Sean Bowe
dbac57c27b Further refactoring of get_point_from_x() 2017-10-05 12:35:04 -06:00
Sean Bowe
683f21a4d5 Remove spurious newline. 2017-10-04 14:53:42 -06:00
Sean Bowe
85b95750e2 Fix comment about u128-support. 2017-10-04 14:09:40 -06:00
Sean Bowe
931257599d Refactor code for finding affine points from x-coordinates. 2017-10-04 11:43:42 -06:00
bmerge
f9d4c8b6aa Auto merge of #57 - ebfull:relic-interop, r=ebfull
RELIC interop

Closes #2

RELIC has changed the G2 generator to match ours: 25aafecbec

RELIC will not have the same serialization as us.

One last difference is that RELIC conjugates the pairing result to account for the negative curve parameter. It doesn't matter for BLS curves but it's strictly more correct.
2017-10-04 15:51:28 +00:00
Sean Bowe
60887521b6 Derive Clone for {G1|G2}{Uncompressed|Compressed}. 2017-10-03 14:35:00 -06:00
Sean Bowe
18c75f11ab Account for negative curve parameter to be compatible with RELIC. 2017-10-03 14:27:01 -06:00
bmerge
a8bf8362da Auto merge of #54 - ebfull:version-bump-0.12.0, r=ebfull
Bump version to 0.12.0.
2017-09-28 23:39:17 +00:00
Sean Bowe
67f5fbc94c More modifications to satisfy clippy. 2017-09-28 17:37:54 -06:00
Sean Bowe
05339414cc Update clippy and compensate for new lints. 2017-09-28 17:10:06 -06:00
Sean Bowe
927febe4e8 Bump version to 0.12.0. 2017-09-28 16:52:12 -06:00
bmerge
3e1562445a Auto merge of #53 - ebfull:assoc-consts, r=ebfull
Use associated constants for simple constants like these.

Closes #39.
2017-09-28 22:10:55 +00:00
Sean Bowe
4fe3e1d6e3 Use associated constants for simple constants like these. (Closes #39.) 2017-09-28 16:08:56 -06:00
ebfull
abd48980ee Merge pull request #52 from ebfull/fix-copyright
Fix copyright/license information
2017-09-28 15:53:53 -06:00
Sean Bowe
8dbee41977 Add COPYRIGHT file to inform of conditions for contributing and license information. 2017-09-28 15:39:29 -06:00
Sean Bowe
35bf7f63ea Modify the LICENSE-APACHE and LICENSE-MIT files.
This modifies the files so that they are exactly the same as the
Rust project's license files. This does not change the wording of
the license.
2017-09-28 15:29:58 -06:00
Sean Bowe
ce875c902b LICENSE-MIT: Remove inaccurate (misattributed) copyright notice
LICENSE-MIT contains the line "Copyright (c) 2017 Sean Bowe", which
implies that an entity called "Sean Bowe" holds copyrights in the
pairing Rust library. Pairing library contributors retain their
copyrights, and do not assign them to anyone by contributing.
Remove the inaccurate notice.
2017-09-28 15:24:31 -06:00
bmerge
5bb8e3a841 Auto merge of #51 - ebfull:several-fixups, r=ebfull
Several fixups

Closes #50
Closes #48
Closes #46

Also, CI changes this PR will test:

Closes #43
Closes #44
2017-09-28 20:12:59 +00:00
Sean Bowe
4aa51bd3d4 Add security warnings and some instructions to README.md. 2017-09-28 13:32:34 -06:00
Sean Bowe
93e2a132b5 Mask rather than divn, closes #50. 2017-09-28 13:15:29 -06:00
Sean Bowe
291fa71914 This coercion doesn't take place on stable yet. 2017-09-28 13:03:01 -06:00
Sean Bowe
636a037bb1 Make u128-support feature opt-in rather than default. 2017-09-28 12:52:14 -06:00
bmerge
2540ab30fb Auto merge of #47 - ebfull:stable-wnaf, r=ebfull
Introduce a more typesafe wNAF API, and remove the unstable-wnaf feature

Closes #27.
2017-09-28 17:58:18 +00:00
Sean Bowe
6708878f4c Elide these lifetimes. 2017-09-28 11:39:53 -06:00
bmerge
68b57b2ce4 Auto merge of #49 - ebfull:simplify-bench, r=ebfull
Simplify `pairing_benches`.
2017-09-28 17:33:23 +00:00
Sean Bowe
c38cb324f6 Simplify pairing_benches. 2017-09-28 11:31:46 -06:00
Sean Bowe
bda22db9d5 Always recommend a window table size. 2017-09-28 08:38:13 -06:00
Sean Bowe
894b44d034 These structures are no longer exported outside the crate, and these assertions are unnecessary now that the external API can enforce them. 2017-09-28 08:12:37 -06:00
Sean Bowe
06f6334679 Change to docs for satisfying clippy. 2017-09-27 20:06:51 -06:00
Sean Bowe
d230603190 Introduce a more typesafe wNAF API, and remove the unstable-wnaf feature. 2017-09-27 19:09:59 -06:00
bmerge
1398a32b3a Auto merge of #42 - str4d:36-tests-on-stable, r=ebfull
Move benchmarks under benches/ so tests compile on stable

Closes #36.
2017-09-27 17:04:05 +00:00
str4d
3ef34b750c Move benchmarks under benches/ so tests compile on stable
Closes #36.
2017-09-27 17:45:24 +01:00
bmerge
9b6fb37533 Auto merge of #41 - str4d:23-debug-trait, r=ebfull
Force public structures to implement Debug

Closes #23.
2017-09-27 06:04:17 +00:00
str4d
39920186b3 Force public structures to implement Debug
Closes #23.
2017-09-26 15:59:50 +01:00
bmerge
57de78f4f1 Auto merge of #38 - mmaker:feature/legendre-symbol, r=ebfull
Add Legendre symbol.
2017-08-24 19:24:15 +00:00
Michele Orrù
9846ad2d17 Some (easy) cleanups as suggested from @ebfull.
Thanks!
2017-08-24 20:13:07 +02:00
Michele Orrù
2ac2d1213d Some (easy) cleanups as suggested from @daira.
Thanks!
2017-08-23 19:17:20 +02:00
Michele Orrù
a86d0b7270 s/Q/Quadratic/ in LegendreSymbol enum.
Proposed by @ebfull.
2017-08-23 19:10:34 +02:00
Michele Orrù
6feb0f802f Merge traits SqrtField and LegendreField into SqrtField. 2017-08-23 19:08:00 +02:00
Michele Orrù
2d3f498e75 Polish sqrt in fr.rs: use pattern matching with Legendre enums. 2017-08-23 19:07:44 +02:00
Michele Orrù
57b4e73627 Create enum for LegendreSymbol, fix test. 2017-08-23 19:06:43 +02:00
Michele Orrù
1b6cf85251 Add Legendre symbol for Fq and Fq2. 2017-08-23 19:04:56 +02:00
Sean Bowe
2042876fcf Disable default features for pairing. 2017-08-22 20:45:45 -06:00
Sean Bowe
3148662234 Reimplementation of groth16 using pairing library. 2017-08-22 16:03:04 -06:00
bmerge
c4c6e25bc0 Auto merge of #37 - ebfull:0.11-release, r=ebfull
Release 0.11.
2017-08-22 21:37:06 +00:00
Sean Bowe
1239b7648d Release 0.11. 2017-08-22 15:35:02 -06:00
bmerge
dcbd0f3d0c Auto merge of #35 - ebfull:stable-arith, r=ebfull
Introduce stable arithmetic to avoid nightly/unstable features for some platforms

This PR allows the user to opt-out of the "u128-support" feature so that they can use the stable compiler and avoid the unstable `i128_type` feature in the Rust compiler. (Pass `--no-default-features` to opt out.)

This roughly triples the cost of everything in my experience, but it's necessary on some platforms (`arm-linux-androideabi`) which Rust/LLVM doesn't compile 128-bit integers for correctly yet.

**Before this can be merged the CI needs to test against this being enabled or disabled.**
2017-08-22 17:53:46 +00:00
bmerge
68747b4174 Auto merge of #34 - mmaker:fix/is_in_correct_subgroup, r=ebfull
s/is_in_correct_subgroup/is_in_correct_subgroup_assuming_on_curve/

I added a new method`is_in_correct_subgroup_assuming_on_curve` and added the method `is_in_correct_subgroup` for the `CurveAffine` trait.
2017-08-16 16:26:16 +00:00
Michele Orrù
ec49fcc638 s/is_in_correct_subgroup/is_in_correct_subgroup_assuming_on_curve/
Literelly run:
$ sed -i s/is_in_correct_subgroup/is_in_correct_subgroup_assuming_on_curve/g bls12_381/ec.rs
2017-08-16 12:08:52 +02:00
Sean Bowe
51b16c52b3 Only enable i128_type when necessary. 2017-08-15 13:54:51 -06:00
bmerge
3f55cda9d1 Auto merge of #33 - plutomonkey:fix-use, r=ebfull
Remove redundant `use`.
2017-08-15 19:06:15 +00:00
Sean Bowe
3908552fb9 Introduce stable arithmetic to avoid nightly/unstable features for some platforms. 2017-08-15 12:53:22 -06:00
Jason Davies
b893aa17ac Remove redundant use. 2017-08-14 22:15:24 +01:00
bmerge
2eb25d2298 Auto merge of #32 - plutomonkey:fix-const, r=ebfull
Replace inline value with const.
2017-08-14 17:57:37 +00:00
Jason Davies
b1d981e916 Replace inline value with const. 2017-08-14 17:16:21 +01:00
bmerge
990401395c Auto merge of #31 - plutomonkey:fix-doc, r=ebfull
Fix some minor typos in docs.
2017-08-14 15:43:05 +00:00
Jason Davies
122d2b161d Update clippy to 0.0.151. 2017-08-14 16:41:55 +01:00
Jason Davies
d2f47cfa2d Fix minor typos in comments/docs. 2017-08-14 09:47:23 +01:00
Jason Davies
21b0384f28 Fix for superscript period appearing in docs. 2017-08-14 00:22:54 +01:00
Sean Bowe
bf03be0b9d Starting fresh... 2017-08-07 07:43:39 -06:00
bmerge
d72929f64e Auto merge of #26 - ebfull:even-more-adjustments, r=ebfull
Even more adjustments

Closes #22
Closes #24
Closes #25
2017-08-01 17:38:38 +00:00
Sean Bowe
766c902d8c Allow unreadable_literal lint. 2017-08-01 11:37:51 -06:00
Sean Bowe
ca5efdcebe Update clippy again. 2017-08-01 11:23:42 -06:00
Sean Bowe
6410bdf998 Adopt idiomatic code suggestions. 2017-07-31 09:39:57 -06:00
Sean Bowe
34aa52b0f7 Another clippy update. 2017-07-31 08:41:51 -06:00
Sean Bowe
ba2c9c7bf0 Clippy update. 2017-07-30 01:12:35 -06:00
Sean Bowe
964fad5a67 Version bump 2017-07-30 00:56:24 -06:00
Sean Bowe
05183c5348 Make Fq6 public. 2017-07-30 00:55:18 -06:00
Sean Bowe
053698eefb Add Engine associated type to CurveProject/CurveAffine. 2017-07-30 00:54:23 -06:00
Sean Bowe
40ec989184 Add (not particularly efficient) from_str to PrimeField. 2017-07-30 00:47:24 -06:00
Sean Bowe
dcca363d1b Add muln() to PrimeFieldRepr along with tests for muln/divn. 2017-07-29 23:20:59 -06:00
Sean Bowe
9af0c7dd30 PrimeFieldRepr::divn() should accept u32. 2017-07-29 22:50:48 -06:00
Sean Bowe
1a481bc741 PrimeField::s() should return u32. 2017-07-29 22:47:06 -06:00
bmerge
aa5d634c04 Auto merge of #21 - ebfull:bump-again, r=ebfull
Version bump to 0.10.1
2017-07-24 15:52:14 +00:00
Sean Bowe
7960f69ba9 Use newer version of clippy. 2017-07-24 09:51:20 -06:00
Sean Bowe
f111008d66 Version bump to 0.10.1. 2017-07-23 20:21:08 -06:00
bmerge
c643f0b1fa Auto merge of #20 - ebfull:pairing_with, r=ebfull
Add pairing_with API.

I needed this for something I'm doing so I just wrote it.

Closes #8
2017-07-23 22:56:39 +00:00
Sean Bowe
41834bc4b2 Add pairing_with API. 2017-07-20 20:52:36 -06:00
bmerge
f51a383ef1 Auto merge of #19 - ebfull:ohtenrelease, r=ebfull
Version bump.
2017-07-18 19:38:37 +00:00
Sean Bowe
59765c5830 Version bump. 2017-07-18 13:36:18 -06:00
bmerge
8fc026bb61 Auto merge of #18 - ebfull:primefieldreprtests, r=ebfull
Random encoding/decoding tests for `PrimeFieldRepr`
2017-07-18 17:20:57 +00:00
Sean Bowe
176c77d602 Random encoding/decoding tests for PrimeFieldRepr. Closes #3. 2017-07-18 11:18:42 -06:00
bmerge
a6528a7876 Auto merge of #17 - ebfull:various-improvements, r=ebfull
Group encoding negative test vectors

Closes #10

Also simplifies the encoding code, which has the side-effect of being useful for testing. Also adds more descriptive error reporting throughout the API. Also ensures use of Debug/Display are consistent with standard expectations.
2017-07-18 16:21:39 +00:00
Sean Bowe
9e5f70f126 (clippy) More idomatic reference matching. 2017-07-18 10:16:49 -06:00
Sean Bowe
19efad5ba6 Small fixups to reduce diff. 2017-07-18 09:17:38 -06:00
Sean Bowe
0e877810f7 Add tests for rejected encoded G1/G2 points. 2017-07-18 08:58:58 -06:00
Sean Bowe
2bfce59d8e Offer read_be and write_be utilities to simplify code, and for testing. 2017-07-18 08:58:58 -06:00
Sean Bowe
d67109d5d3 EncodedPoint should be Copy/Clone. 2017-07-18 08:58:58 -06:00
Sean Bowe
7b1cd7f211 Improve documentation for errors a bit. 2017-07-18 08:58:58 -06:00
Sean Bowe
1d4710a39e Consistently use Debug and Display. 2017-07-18 08:58:58 -06:00
Sean Bowe
1027dda432 Carry the interpreted value of the encoding through the error. 2017-07-18 08:58:58 -06:00
Sean Bowe
09531d0810 Add error logic to decoding methods. 2017-07-18 08:58:58 -06:00
bmerge
7c35f2b8b0 Auto merge of #16 - ebfull:clippy-impl, r=ebfull
Enable clippy linting

Closes #5
2017-07-17 03:51:15 +00:00
Sean Bowe
451b2c30ad Enable clippy linting. 2017-07-16 21:50:03 -06:00
bmerge
6df2c003c7 Auto merge of #15 - ebfull:serialization-impl, r=ebfull
Implements and documents serialization

Closes #11.
2017-07-17 03:28:05 +00:00
Sean Bowe
c618240c91 Implements and documents serialization, closes #11. 2017-07-16 20:52:00 -06:00
Sean Bowe
e72660056e Ordering cleanup for Fq/Fq2, with documentation. Closes #9. 2017-07-15 15:32:37 -06:00
Sean Bowe
2090e76794 Mark wnaf module unstable via feature unstable-wnaf. Closes #13. 2017-07-14 11:56:55 -06:00
Sean Bowe
806d34b39a Rename unitary_inverse to conjugate. Closes #12. 2017-07-14 11:45:07 -06:00
Sean Bowe
d438f16628 Use divn when rightshifting during rand(). 2017-07-14 11:41:40 -06:00
Sean Bowe
bafb273a6c Simplify doubling code during squaring routine. 2017-07-11 23:49:33 -06:00
Sean Bowe
647f83b53b Rename many .to_* methods to .into_*. 2017-07-11 23:37:29 -06:00
Sean Bowe
ae69eb01b3 Small adjustments to satisfy clippy. 2017-07-11 17:10:32 -06:00
Sean Bowe
3faf8c526a Encoding of G1/G2 elements in compressed or uncompressed form. 2017-07-11 15:01:31 -06:00
Sean Bowe
021077b56b Added wNAF scalar multiplication. 2017-07-10 00:39:38 -06:00
Sean Bowe
b965c58ac1 For performance, don't double/square until we've seen a bit. 2017-07-09 21:11:29 -06:00
Sean Bowe
f2b1b0632d Correct README description of E'. 2017-07-08 23:26:38 -06:00
Sean Bowe
a06216f24b Initial commit 2017-07-08 10:55:43 -06:00
Sean Bowe
c7252a43bf Update README/Cargo.toml with correct documentation links. 2017-06-27 10:35:14 -06:00
Sean Bowe
134b53e812 Version bump. 2017-06-27 09:41:33 -06:00
Sean Bowe
bbc7b44f88 Comments and slight refactoring. 2017-06-27 09:40:58 -06:00
Sean Bowe
02f503c74d Modify README. 2017-06-27 09:27:29 -06:00
Sean Bowe
b1f392ac99 Add multiplicative generator and other useful constants. 2017-06-27 09:14:24 -06:00
Sean Bowe
9aceb63e7e Add Tonelli-Shanks sqrt for 1 mod 16 primes. 2017-06-26 23:22:41 -06:00
Sean Bowe
bbbd397b80 Add more efficient squaring implementation. 2017-06-26 14:42:47 -06:00
Sean Bowe
e6ad371dd7 Remove dead code. 2017-06-26 13:53:55 -06:00
Sean Bowe
03952704b7 Absolute path to rand. 2017-06-26 12:07:23 -06:00
Sean Bowe
13a822f994 Add auto-derivation of prime fields, and modify the traits a little bit. 2017-06-26 11:47:35 -06:00
Sean Bowe
e97f0df3df Fix another comment. 2017-06-25 23:18:32 -06:00
Sean Bowe
6402b74741 Fix comment. 2017-06-25 23:16:08 -06:00
Sean Bowe
cb0c8f1e07 Fix description of ff. 2017-06-25 23:13:56 -06:00
Sean Bowe
cf7d7e823b Add more stuff to the Cargo.toml files. 2017-06-25 23:12:40 -06:00
Sean Bowe
b11ea2db25 Initial commit. 2017-06-25 23:09:43 -06:00
bmerge
e282bc095a Auto merge of #13 - ebfull:fix-comment, r=ebfull
Fix comment about sqrt.

Still just testing CI stuff.
2017-06-19 22:40:09 +00:00
Sean Bowe
9c94216a87 Fix comment about sqrt. 2017-06-19 13:58:30 -06:00
bmerge
be1961124c Auto merge of #12 - ebfull:version-bump, r=ebfull
Version bump
2017-06-19 18:38:01 +00:00
Sean Bowe
5ca9341aeb Version bump 2017-06-19 12:17:10 -06:00
bmerge
8ff6ce741a Auto merge of #10 - ebfull:remove-travis, r=ebfull
Remove travis CI.
2017-06-19 17:40:12 +00:00
Sean Bowe
c9677d8bd2 Remove travis CI. 2017-06-19 11:31:32 -06:00
bmerge
732af319fe Auto merge of #9 - ebfull:various-improvements, r=ebfull
Various improvements to project structure and implementation

This makes wNAF/multiexp more modularized and abstract (for use later in remodeling groth) and starts moving other things around.

Also, good chance to start working on buildbot.
2017-06-19 05:55:32 +00:00
Sean Bowe
e9dee9de16 Move domain to curves. 2017-06-17 20:53:26 -06:00
Sean Bowe
fd3774118a Move BitIterator into root of crate. 2017-06-17 19:46:40 -06:00
Sean Bowe
5cf6acd21a Clean up prime field representation. 2017-06-17 18:57:56 -06:00
Sean Bowe
d4903f19fe Refactoring of multiexp/wnaf. 2017-06-17 17:04:14 -06:00
Sean Bowe
8f6a5737c0 Avoid the use of dynamic dispatch during verification. 2017-06-15 17:40:06 -06:00
Sean Bowe
97d58c7954 Add batchexp utility function to Engine. 2017-05-16 16:41:31 -06:00
Sean Bowe
33dd0de661 Parallelism improvements. 2017-05-16 12:06:59 -06:00
Sean Bowe
bb73258ce9 Parallel FFTs. 2017-05-14 19:37:02 -06:00
Sean Bowe
669853de99 Make domain evaluations public. 2017-05-07 18:27:48 -06:00
Sean Bowe
750131f0a1 Allow evaluation domain to act over all groups. 2017-05-07 18:25:49 -06:00
Sean Bowe
1434ad7b28 Introduce Group for generic group operations. 2017-05-07 18:15:33 -06:00
Sean Bowe
56c75c0c8a Rename Group to Curve. 2017-05-07 18:07:35 -06:00
Sean Bowe
d95a9b0b29 Update to serde 1.0/bincode 0.8. 2017-05-07 10:24:54 -06:00
Sean Bowe
a98e84e09a Move Cow/Convert out of curves module. 2017-05-07 09:39:01 -06:00
Sean Bowe
72a386ec34 Version bump. 2017-04-04 14:45:10 -06:00
Sean Bowe
f1903133ed Rename "groth" to "groth16". 2017-04-04 14:45:10 -06:00
Sean Bowe
d228257da6 Rename to "Input"/"InputMap" 2017-04-04 14:45:10 -06:00
Sean Bowe
c9fbf490dc Implementation of Jens Groth's generic group zk-SNARK proving system. 2017-04-03 21:42:22 -06:00
Sean Bowe
9a3743c7c4 Various improvements to BLS implementation:
* Switch from rayon to crossbeam
* Allow windows to be reused per batch exp
* Allow batchexp to take vector by value
* Allow access to thread-local engine context
* Allow cloning of Engine
* Clean up wNAF abstractions to reduce heap allocation
2017-04-03 21:41:38 -06:00
Sean Bowe
f835556ffb The current year is 2017! 2017-03-13 20:30:09 -06:00
Sean Bowe
25375ee819 Update licenses. 2017-03-13 20:28:56 -06:00
Sean Bowe
9c3f6fb92f Change travis to nightly. 2017-03-10 08:52:40 -07:00
Sean Bowe
2321ead995 In with the new. 2017-02-27 13:39:30 -07:00
Sean Bowe
c506c48c91 Out with the old... 2017-01-07 20:30:09 -07:00
Sean Bowe
e24fcfdc5c Added primitive circuit abstraction, tests for sha3. 2016-01-28 20:37:54 -07:00
Sean Bowe
67003a471b Fixed type error 2016-01-26 14:45:04 -07:00
Sean Bowe
da373758e5 Minor improvements 2016-01-26 14:18:01 -07:00
Sean Bowe
ececc2b60b Implement raw constraints for Bit 2016-01-24 01:42:38 -07:00
Sean Bowe
506ef75e3c Make macro compatible with more recent version of Rust 2016-01-23 02:17:43 -07:00
Sean Bowe
5d49abcf62 Fix tinysnark test 2016-01-22 21:21:40 -07:00
Sean Bowe
d0b589749a Force FieldT alignment 2016-01-22 21:18:52 -07:00
Sean Bowe
710059b779 Relicensed as Apache/MIT dual license. Closes #1. 2016-01-12 14:14:06 -07:00
Sean Bowe
b4d4331926 Refactor bit implementation (small improvements in number of constraints) 2016-01-10 03:26:54 -07:00
Sean Bowe
b82a2f60f7 Reorganize and remove (temporary) unsafe witnessing 2016-01-03 03:45:20 -07:00
Sean Bowe
7415d5ff3c Added unconstrained zk-SNARK implementation for SHA3 2016-01-03 03:15:03 -07:00
Sean Bowe
bc77a837df More cleanup 2015-12-28 04:08:01 -07:00
Sean Bowe
8445bb4bb7 Cleanup 2015-12-28 04:02:22 -07:00
Sean Bowe
75533e5e97 Abstract away the binary mapping from xor/notand. 2015-12-28 02:21:52 -07:00
Sean Bowe
774c4375ee Remove old chunks interface 2015-12-28 02:17:11 -07:00
Sean Bowe
f84e4449a5 Add test vector that tests endianness. 2015-12-28 02:15:15 -07:00
Sean Bowe
5b40589bc0 Made this more idiomatic 2015-12-28 02:13:31 -07:00
Sean Bowe
316b65514c Massive refactor of keccakf 2015-12-28 02:11:52 -07:00
Sean Bowe
4eac6417c3 Completely functional implementation with tests 2015-12-28 00:45:11 -07:00
Sean Bowe
a0d56f3451 getting there 2015-12-28 00:15:16 -07:00
Sean Bowe
0a3d848cbb broken test 2015-12-27 23:24:39 -07:00
Sean Bowe
007b55a843 Clean up warnings 2015-12-27 20:24:45 -07:00
Sean Bowe
ca289581a8 Clean up implementation to use vectors. We'll use arrays when Rust doesn't suck at them. 2015-12-27 20:19:03 -07:00
Sean Bowe
5fa9d9f438 Moved this to src/ 2015-12-27 17:40:11 -07:00
Sean Bowe
38767db686 WIP implementation of keccak with bellman 2015-12-27 17:38:16 -07:00
Sean Bowe
86146ddad7 Rearranged tests, added XOR example. 2015-12-26 10:18:44 -07:00
Sean Bowe
ab33ff017b Remove cs from proof api 2015-12-25 11:21:11 -07:00
Sean Bowe
fc1bdf2148 Added r1cs ppzksnark proving scheme and some tests. 2015-12-25 05:52:14 -07:00
Sean Bowe
a66c21fe11 Update travis configuration 2015-12-25 00:40:53 -07:00
Sean Bowe
1e5d4b40e7 Don't include benchmarking stuff for now. 2015-12-25 00:39:44 -07:00
Sean Bowe
02eb93d5d2 Test the right things 2015-12-25 00:37:52 -07:00
Sean Bowe
0b8b52cb41 Add basic field arithmetic. 2015-12-25 00:30:10 -07:00
Sean Bowe
8e0af61ba4 Add dockerfile 2015-12-24 03:22:56 -07:00
Sean Bowe
8e350f6b6a Update readme with travis build image 2015-12-24 03:19:30 -07:00
Sean Bowe
b554e93f72 Add travis-ci configuration 2015-12-24 03:16:11 -07:00
Sean Bowe
45111d6576 Initial commit 2015-12-24 02:58:38 -07:00
159 changed files with 43585 additions and 792 deletions

66
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,66 @@
# /************************************************************************
# File: .gitlab-ci.yml
# Author: mdr0id
# Date: 9/10/2018
# Description: Used to setup runners/jobs for librustzcash
# Usage: Commit source and the pipeline will trigger the according jobs.
# For now the build and test are done in the same jobs.
#
# Known bugs/missing features:
#
# ************************************************************************/
stages:
- build
- test
- deploy
rust-latest:
stage: build
image: rust:latest
script:
- cargo --verbose --version
- time cargo build --verbose
rust-nightly:
stage: build
image: rustlang/rust:nightly
script:
- cargo --verbose --version
- cargo build --verbose
allow_failure: true
librustzcash-test-latest:
stage: test
image: rust:latest
script:
- cargo --verbose --version
- time cargo test --release --verbose
librustzcash-test-rust-nightly:
stage: test
image: rustlang/rust:nightly
script:
- cargo --verbose --version
- cargo test --release --verbose
allow_failure: true
#used to manually deploy a given release
librustzcash-rust-rc:
stage: deploy
image: rust:latest
script:
- cargo --verbose --version
- time cargo build --release --verbose
when: manual
#used to manually deploy a given release
librustzcash-rust-nightly-rc:
stage: deploy
image: rustlang/rust:nightly
script:
- cargo --verbose --version
- cargo build --release --verbose
allow_failure: true
when: manual

18
.travis.yml Normal file
View File

@@ -0,0 +1,18 @@
language: rust
rust:
- 1.32.0
env:
global:
# See https://stackoverflow.com/a/43339593
- RUST_BACKTRACE=1
before_cache:
- rm -rf "$TRAVIS_HOME/.cargo/registry/src"
cache: cargo
script:
- cargo build --verbose --release --all
- cargo test --verbose --release --all

3
AUTHORS Normal file
View File

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

631
Cargo.lock generated
View File

@@ -1,338 +1,669 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aes"
version = "0.2.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
dependencies = [
"aes-soft 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"aesni 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"aes-soft",
"aesni",
"cipher",
]
[[package]]
name = "aes-soft"
version = "0.2.0"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
dependencies = [
"block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cipher",
"opaque-debug 0.3.0",
]
[[package]]
name = "aesni"
version = "0.4.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
dependencies = [
"block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"stream-cipher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cipher",
"opaque-debug 0.3.0",
]
[[package]]
name = "arrayvec"
version = "0.4.7"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
dependencies = [
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"nodrop",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bellman"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bit-vec",
"byteorder",
"crossbeam",
"ff",
"futures",
"futures-cpupool",
"group",
"num_cpus",
"pairing",
"rand 0.4.6",
]
[[package]]
name = "bit-vec"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
[[package]]
name = "blake2-rfc"
version = "0.2.18"
source = "git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9#7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
source = "git+https://git.hush.is/hush/blake2-rfc?rev=a3adc1d6859b887457c6ae4821ddad1802dad784#a3adc1d6859b887457c6ae4821ddad1802dad784"
dependencies = [
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "block-cipher-trait"
version = "0.5.3"
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"block-padding 0.1.5",
"byte-tools",
"byteorder",
"generic-array 0.12.4",
]
[[package]]
name = "block-modes"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0"
dependencies = [
"block-padding 0.2.1",
"cipher",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
name = "block-padding"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "byte-tools"
version = "0.2.0"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.2.2"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cipher"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
dependencies = [
"generic-array 0.14.5",
]
[[package]]
name = "constant_time_eq"
version = "0.1.3"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "crossbeam"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
[[package]]
name = "crypto_api"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102"
[[package]]
name = "crypto_api_chachapoly"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f9ee35dbace0831b5fe7cb9b43eb029aa14a10f594a115025d4628a2baa63ab"
dependencies = [
"crypto_api",
]
[[package]]
name = "digest"
version = "0.7.2"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
dependencies = [
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.9.1",
]
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array 0.12.4",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "ff"
version = "0.4.0"
dependencies = [
"byteorder",
"ff_derive",
"rand 0.4.6",
]
[[package]]
name = "ff_derive"
version = "0.3.0"
dependencies = [
"num-bigint 0.2.6",
"num-integer",
"num-traits",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "fpe"
version = "0.1.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a25080721bbcd2cd4d765b7d607ea350425fa087ce53cd3e31afcacdab850352"
dependencies = [
"aes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"aes",
"block-modes",
"num-bigint 0.3.3",
"num-integer",
"num-traits",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "futures"
version = "0.1.21"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
[[package]]
name = "futures-cpupool"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
dependencies = [
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures",
"num_cpus",
]
[[package]]
name = "gcc"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
[[package]]
name = "generic-array"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d00328cedcac5e81c683e5620ca6a30756fc23027ebf9bff405c0e8da1fbb7e"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.9.0"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"typenum",
]
[[package]]
name = "generic-array"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "group"
version = "0.1.0"
dependencies = [
"ff",
"rand 0.4.6",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
[[package]]
name = "hex-literal"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639"
dependencies = [
"hex-literal-impl",
"proc-macro-hack",
]
[[package]]
name = "hex-literal-impl"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda"
dependencies = [
"proc-macro-hack",
]
[[package]]
name = "lazy_static"
version = "1.0.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.40"
version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "librustzcash"
version = "0.1.0"
dependencies = [
"bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e)",
"zip32 0.0.0 (git+https://github.com/zcash-hackworks/zip32?rev=176470ef41583b5bd0bd749bd1b61d417aa8ec79)",
"bellman",
"blake2-rfc",
"byteorder",
"ff",
"lazy_static",
"libc",
"pairing",
"rand 0.4.6",
"sapling-crypto",
"zcash_primitives",
"zcash_proofs",
"zip32",
]
[[package]]
name = "nodrop"
version = "0.1.12"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "num-bigint"
version = "0.2.0"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.39"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.5"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.8.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi",
"libc",
]
[[package]]
name = "opaque-debug"
version = "0.1.1"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "pairing"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder",
"ff",
"group",
"rand 0.4.6",
]
[[package]]
name = "proc-macro-hack"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7f95648580798cc44ff8efb9bb0d7ee5205ea32e087b31b0732f3e8c2648ee2"
dependencies = [
"proc-macro-hack-impl",
]
[[package]]
name = "proc-macro-hack-impl"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be55bf0ae1635f4d7c7ddd6efc05c631e98a82104a73d35550bbc52db960027"
[[package]]
name = "proc-macro2"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
dependencies = [
"libc",
"rand 0.4.6",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1",
]
[[package]]
name = "rust-crypto"
version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
dependencies = [
"gcc",
"libc",
"rand 0.3.23",
"rustc-serialize",
"time",
]
[[package]]
name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
[[package]]
name = "sapling-crypto"
version = "0.0.1"
source = "git+https://github.com/zcash-hackworks/sapling-crypto?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e#21084bde2019c04bd34208e63c3560fe2c02fb0e"
dependencies = [
"bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bellman",
"blake2-rfc",
"byteorder",
"digest 0.7.6",
"ff",
"hex-literal",
"pairing",
"rand 0.4.6",
"rust-crypto",
]
[[package]]
name = "stream-cipher"
version = "0.1.0"
name = "sha2"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
dependencies = [
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"block-buffer",
"digest 0.8.1",
"fake-simd",
"opaque-debug 0.2.3",
]
[[package]]
name = "syn"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi",
"winapi",
]
[[package]]
name = "typenum"
version = "1.10.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "winapi"
version = "0.3.4"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "zcash_primitives"
version = "0.0.0"
dependencies = [
"blake2-rfc",
"byteorder",
"crypto_api_chachapoly",
"ff",
"hex",
"lazy_static",
"pairing",
"rand 0.4.6",
"sapling-crypto",
"sha2",
]
[[package]]
name = "zcash_proofs"
version = "0.0.0"
dependencies = [
"bellman",
"blake2-rfc",
"byteorder",
"ff",
"pairing",
"rand 0.4.6",
"sapling-crypto",
]
[[package]]
name = "zcash_wallet"
version = "0.0.0"
[[package]]
name = "zip32"
version = "0.0.0"
source = "git+https://github.com/zcash-hackworks/zip32?rev=176470ef41583b5bd0bd749bd1b61d417aa8ec79#176470ef41583b5bd0bd749bd1b61d417aa8ec79"
dependencies = [
"aes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fpe 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
"sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e)",
"aes",
"blake2-rfc",
"byteorder",
"ff",
"fpe",
"pairing",
"sapling-crypto",
"zcash_primitives",
]
[metadata]
"checksum aes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6fb1737cdc8da3db76e90ca817a194249a38fcb500c2e6ecec39b29448aa873"
"checksum aes-soft 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67cc03b0a090a05cb01e96998a01905d7ceedce1bc23b756c0bb7faa0682ccb1"
"checksum aesni 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f2838c142db62c0c6aea0a24054c46d35488532fdaea0f51dbeba430f0985df5"
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
"checksum bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eae372472c7ea8f7c8fc6a62f7d5535db8302de7f1aafda2e13a97c4830d3bcf"
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)" = "<none>"
"checksum block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "370424437b9459f3dfd68428ed9376ddfe03d8b70ede29cc533b3557df186ab4"
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87"
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
"checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603"
"checksum fpe 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce3371c82bfbd984f624cab093f55e7336f5a6e589f8518e1258f54f011b89ad"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c"
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b"
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
"checksum num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3eceac7784c5dc97c2d6edf30259b4e153e6e2b42b3c85e9a6e9f45d06caef6e"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d620c9c26834b34f039489ac0dfdb12c7ac15ccaf818350a64c9b5334a452ad7"
"checksum pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ceda21136251c6d5a422d3d798d8ac22515a6e8d3521bb60c59a8349d36d0d57"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e)" = "<none>"
"checksum stream-cipher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac49bc6cb2847200d18bfb738ce89448570f4aa1c34ac0348db6205ee69a0777"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum zip32 0.0.0 (git+https://github.com/zcash-hackworks/zip32?rev=176470ef41583b5bd0bd749bd1b61d417aa8ec79)" = "<none>"

View File

@@ -1,38 +1,17 @@
[package]
name = "librustzcash"
version = "0.1.0"
authors = [
"Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>",
"Jay Graber <jay@z.cash>",
"Simon Liu <simon@z.cash>"
[workspace]
members = [
"bellman",
"ff",
"group",
"librustzcash",
"pairing",
"sapling-crypto",
"zcash_primitives",
"zcash_proofs",
"zcash_wallet",
"zip32",
]
[lib]
name = "rustzcash"
path = "src/rustzcash.rs"
crate-type = ["staticlib"]
[dependencies]
libc = "0.2"
pairing = "0.14.2"
lazy_static = "1"
bellman = "0.1"
byteorder = "1"
rand = "0.4"
[dependencies.blake2-rfc]
git = "https://github.com/gtank/blake2-rfc"
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
[dependencies.sapling-crypto]
git = "https://github.com/zcash-hackworks/sapling-crypto"
rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e"
[dependencies.zip32]
git = "https://github.com/zcash-hackworks/zip32"
rev = "176470ef41583b5bd0bd749bd1b61d417aa8ec79"
[profile.release]
lto = true
panic = 'abort'

619
LICENSE Normal file
View File

@@ -0,0 +1,619 @@
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,20 +1,13 @@
# librustzcash
# Hush Rust crates
This repository contains librustzcash, a static library for Zcash code assets written in Rust.
This repository contains a set of Rust crates for
working with low-level Hush stuff.
## Security Warnings
These libraries are currently under development and have not been fully-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.
GNU Public License 3

2
bellman/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
target
Cargo.lock

14
bellman/COPYRIGHT Normal file
View File

@@ -0,0 +1,14 @@
Copyrights in the "bellman" library are retained by their contributors. No
copyright assignment is required to contribute to the "bellman" library.
The "bellman" 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.

31
bellman/Cargo.toml Normal file
View File

@@ -0,0 +1,31 @@
[package]
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
description = "zk-SNARK library"
documentation = "https://github.com/ebfull/bellman"
homepage = "https://github.com/ebfull/bellman"
license = "MIT/Apache-2.0"
name = "bellman"
repository = "https://github.com/ebfull/bellman"
version = "0.1.0"
[dependencies]
rand = "0.4"
bit-vec = "0.4.4"
ff = { path = "../ff" }
futures = "0.1"
futures-cpupool = { version = "0.1", optional = true }
group = { path = "../group" }
num_cpus = { version = "1", optional = true }
crossbeam = { version = "0.3", optional = true }
pairing = { path = "../pairing", optional = true }
byteorder = "1"
[features]
groth16 = ["pairing"]
multicore = ["futures-cpupool", "crossbeam", "num_cpus"]
default = ["groth16", "multicore"]
[[test]]
name = "mimc"
path = "tests/mimc.rs"
required-features = ["groth16"]

201
bellman/LICENSE-APACHE Normal file
View File

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

23
bellman/LICENSE-MIT Normal file
View File

@@ -0,0 +1,23 @@
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.

19
bellman/README.md Normal file
View File

@@ -0,0 +1,19 @@
# 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/).
## 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.

493
bellman/src/domain.rs Normal file
View File

@@ -0,0 +1,493 @@
//! This module contains an `EvaluationDomain` abstraction for
//! performing various kinds of polynomial arithmetic on top of
//! the scalar field.
//!
//! In pairing-based SNARKs like Groth16, we need to calculate
//! a quotient polynomial over a target polynomial with roots
//! at distinct points associated with each constraint of the
//! constraint system. In order to be efficient, we choose these
//! roots to be the powers of a 2^n root of unity in the field.
//! This allows us to perform polynomial operations in O(n)
//! by performing an O(n log n) FFT over such a domain.
use ff::{Field, PrimeField, ScalarEngine};
use group::CurveProjective;
use super::{
SynthesisError
};
use super::multicore::Worker;
pub struct EvaluationDomain<E: ScalarEngine, G: Group<E>> {
coeffs: Vec<G>,
exp: u32,
omega: E::Fr,
omegainv: E::Fr,
geninv: E::Fr,
minv: E::Fr
}
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> {
self.coeffs
}
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, SynthesisError>
{
// Compute the size of our evaluation domain
let mut m = 1;
let mut exp = 0;
while m < coeffs.len() {
m *= 2;
exp += 1;
// The pairing-friendly curve may not be able to support
// large enough (radix2) evaluation domains.
if exp >= E::Fr::S {
return Err(SynthesisError::PolynomialDegreeTooLarge)
}
}
// Compute omega, the 2^exp primitive root of unity
let mut omega = E::Fr::root_of_unity();
for _ in exp..E::Fr::S {
omega.square();
}
// Extend the coeffs vector with zeroes if necessary
coeffs.resize(m, G::group_zero());
Ok(EvaluationDomain {
coeffs: coeffs,
exp: exp,
omega: omega,
omegainv: omega.inverse().unwrap(),
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap()
})
}
pub fn fft(&mut self, worker: &Worker)
{
best_fft(&mut self.coeffs, worker, &self.omega, self.exp);
}
pub fn ifft(&mut self, worker: &Worker)
{
best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp);
worker.scope(self.coeffs.len(), |scope, chunk| {
let minv = self.minv;
for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || {
for v in v {
v.group_mul_assign(&minv);
}
});
}
});
}
pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr)
{
worker.scope(self.coeffs.len(), |scope, chunk| {
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
scope.spawn(move || {
let mut u = g.pow(&[(i * chunk) as u64]);
for v in v.iter_mut() {
v.group_mul_assign(&u);
u.mul_assign(&g);
}
});
}
});
}
pub fn coset_fft(&mut self, worker: &Worker)
{
self.distribute_powers(worker, E::Fr::multiplicative_generator());
self.fft(worker);
}
pub fn icoset_fft(&mut self, worker: &Worker)
{
let geninv = self.geninv;
self.ifft(worker);
self.distribute_powers(worker, geninv);
}
/// This evaluates t(tau) for this domain, which is
/// tau^m - 1 for these radix-2 domains.
pub fn z(&self, tau: &E::Fr) -> E::Fr {
let mut tmp = tau.pow(&[self.coeffs.len() as u64]);
tmp.sub_assign(&E::Fr::one());
tmp
}
/// The target polynomial is the zero polynomial in our
/// evaluation domain, so we must perform division over
/// a coset.
pub fn divide_by_z_on_coset(&mut self, worker: &Worker)
{
let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap();
worker.scope(self.coeffs.len(), |scope, chunk| {
for v in self.coeffs.chunks_mut(chunk) {
scope.spawn(move || {
for v in v {
v.group_mul_assign(&i);
}
});
}
});
}
/// Perform O(n) multiplication of two polynomials in the domain.
pub fn mul_assign(&mut self, worker: &Worker, other: &EvaluationDomain<E, Scalar<E>>) {
assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
scope.spawn(move || {
for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_mul_assign(&b.0);
}
});
}
});
}
/// Perform O(n) subtraction of one polynomial from another in the domain.
pub fn sub_assign(&mut self, worker: &Worker, other: &EvaluationDomain<E, G>) {
assert_eq!(self.coeffs.len(), other.coeffs.len());
worker.scope(self.coeffs.len(), |scope, chunk| {
for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) {
scope.spawn(move || {
for (a, b) in a.iter_mut().zip(b.iter()) {
a.group_sub_assign(&b);
}
});
}
});
}
}
pub trait Group<E: ScalarEngine>: Sized + Copy + Clone + Send + Sync {
fn group_zero() -> Self;
fn group_mul_assign(&mut self, by: &E::Fr);
fn group_add_assign(&mut self, other: &Self);
fn group_sub_assign(&mut self, other: &Self);
}
pub struct Point<G: CurveProjective>(pub G);
impl<G: CurveProjective> PartialEq for Point<G> {
fn eq(&self, other: &Point<G>) -> bool {
self.0 == other.0
}
}
impl<G: CurveProjective> Copy for Point<G> { }
impl<G: CurveProjective> Clone for Point<G> {
fn clone(&self) -> Point<G> {
*self
}
}
impl<G: CurveProjective> Group<G::Engine> for Point<G> {
fn group_zero() -> Self {
Point(G::zero())
}
fn group_mul_assign(&mut self, by: &G::Scalar) {
self.0.mul_assign(by.into_repr());
}
fn group_add_assign(&mut self, other: &Self) {
self.0.add_assign(&other.0);
}
fn group_sub_assign(&mut self, other: &Self) {
self.0.sub_assign(&other.0);
}
}
pub struct Scalar<E: ScalarEngine>(pub E::Fr);
impl<E: ScalarEngine> PartialEq for Scalar<E> {
fn eq(&self, other: &Scalar<E>) -> bool {
self.0 == other.0
}
}
impl<E: ScalarEngine> Copy for Scalar<E> { }
impl<E: ScalarEngine> Clone for Scalar<E> {
fn clone(&self) -> Scalar<E> {
*self
}
}
impl<E: ScalarEngine> Group<E> for Scalar<E> {
fn group_zero() -> Self {
Scalar(E::Fr::zero())
}
fn group_mul_assign(&mut self, by: &E::Fr) {
self.0.mul_assign(by);
}
fn group_add_assign(&mut self, other: &Self) {
self.0.add_assign(&other.0);
}
fn group_sub_assign(&mut self, other: &Self) {
self.0.sub_assign(&other.0);
}
}
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();
if log_n <= log_cpus {
serial_fft(a, omega, log_n);
} else {
parallel_fft(a, worker, omega, log_n, log_cpus);
}
}
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 {
let mut r = 0;
for _ in 0..l {
r = (r << 1) | (n & 1);
n >>= 1;
}
r
}
let n = a.len() as u32;
assert_eq!(n, 1 << log_n);
for k in 0..n {
let rk = bitreverse(k, log_n);
if k < rk {
a.swap(rk as usize, k as usize);
}
}
let mut m = 1;
for _ in 0..log_n {
let w_m = omega.pow(&[(n / (2*m)) as u64]);
let mut k = 0;
while k < n {
let mut w = E::Fr::one();
for j in 0..m {
let mut t = a[(k+j+m) as usize];
t.group_mul_assign(&w);
let mut tmp = a[(k+j) as usize];
tmp.group_sub_assign(&t);
a[(k+j+m) as usize] = tmp;
a[(k+j) as usize].group_add_assign(&t);
w.mul_assign(&w_m);
}
k += 2*m;
}
m *= 2;
}
}
fn parallel_fft<E: ScalarEngine, T: Group<E>>(
a: &mut [T],
worker: &Worker,
omega: &E::Fr,
log_n: u32,
log_cpus: u32
)
{
assert!(log_n >= log_cpus);
let num_cpus = 1 << log_cpus;
let log_new_n = log_n - log_cpus;
let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus];
let new_omega = omega.pow(&[num_cpus as u64]);
worker.scope(0, |scope, _| {
let a = &*a;
for (j, tmp) in tmp.iter_mut().enumerate() {
scope.spawn(move || {
// Shuffle into a sub-FFT
let omega_j = omega.pow(&[j as u64]);
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
let mut elt = E::Fr::one();
for i in 0..(1 << log_new_n) {
for s in 0..num_cpus {
let idx = (i + (s << log_new_n)) % (1 << log_n);
let mut t = a[idx];
t.group_mul_assign(&elt);
tmp[i].group_add_assign(&t);
elt.mul_assign(&omega_step);
}
elt.mul_assign(&omega_j);
}
// Perform sub-FFT
serial_fft(tmp, &new_omega, log_new_n);
});
}
});
// TODO: does this hurt or help?
worker.scope(a.len(), |scope, chunk| {
let tmp = &tmp;
for (idx, a) in a.chunks_mut(chunk).enumerate() {
scope.spawn(move || {
let mut idx = idx * chunk;
let mask = (1 << log_cpus) - 1;
for a in a {
*a = tmp[idx & mask][idx >> log_cpus];
idx += 1;
}
});
}
});
}
// Test multiplying various (low degree) polynomials together and
// comparing with naive evaluations.
#[cfg(feature = "pairing")]
#[test]
fn polynomial_arith() {
use pairing::bls12_381::Bls12;
use rand::{self, Rand};
fn test_mul<E: ScalarEngine, R: rand::Rng>(rng: &mut R)
{
let worker = Worker::new();
for coeffs_a 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 b: Vec<_> = (0..coeffs_b).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect();
// naive evaluation
let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b];
for (i1, a) in a.iter().enumerate() {
for (i2, b) in b.iter().enumerate() {
let mut prod = *a;
prod.group_mul_assign(&b.0);
naive[i1 + i2].group_add_assign(&prod);
}
}
a.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero()));
b.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero()));
let mut a = EvaluationDomain::from_coeffs(a).unwrap();
let mut b = EvaluationDomain::from_coeffs(b).unwrap();
a.fft(&worker);
b.fft(&worker);
a.mul_assign(&worker, &b);
a.ifft(&worker);
for (naive, fft) in naive.iter().zip(a.coeffs.iter()) {
assert!(naive == fft);
}
}
}
}
let rng = &mut rand::thread_rng();
test_mul::<Bls12, _>(rng);
}
#[cfg(feature = "pairing")]
#[test]
fn fft_composition() {
use pairing::bls12_381::Bls12;
use rand;
fn test_comp<E: ScalarEngine, R: rand::Rng>(rng: &mut R)
{
let worker = Worker::new();
for coeffs in 0..10 {
let coeffs = 1 << coeffs;
let mut v = vec![];
for _ in 0..coeffs {
v.push(Scalar::<E>(rng.gen()));
}
let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap();
domain.ifft(&worker);
domain.fft(&worker);
assert!(v == domain.coeffs);
domain.fft(&worker);
domain.ifft(&worker);
assert!(v == domain.coeffs);
domain.icoset_fft(&worker);
domain.coset_fft(&worker);
assert!(v == domain.coeffs);
domain.coset_fft(&worker);
domain.icoset_fft(&worker);
assert!(v == domain.coeffs);
}
}
let rng = &mut rand::thread_rng();
test_comp::<Bls12, _>(rng);
}
#[cfg(feature = "pairing")]
#[test]
fn parallel_fft_consistency() {
use pairing::bls12_381::Bls12;
use rand::{self, Rand};
use std::cmp::min;
fn test_consistency<E: ScalarEngine, R: rand::Rng>(rng: &mut R)
{
let worker = Worker::new();
for _ in 0..5 {
for log_d in 0..10 {
let d = 1 << log_d;
let v1 = (0..d).map(|_| Scalar::<E>(E::Fr::rand(rng))).collect::<Vec<_>>();
let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap();
let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap();
for log_cpus in log_d..min(log_d+1, 3) {
parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus);
serial_fft(&mut v2.coeffs, &v2.omega, log_d);
assert!(v1.coeffs == v2.coeffs);
}
}
}
}
let rng = &mut rand::thread_rng();
test_consistency::<Bls12, _>(rng);
}

View File

@@ -0,0 +1,477 @@
use rand::Rng;
use std::sync::Arc;
use ff::{Field, PrimeField};
use group::{CurveAffine, CurveProjective, Wnaf};
use pairing::Engine;
use super::{
Parameters,
VerifyingKey
};
use ::{
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use ::domain::{
EvaluationDomain,
Scalar
};
use ::multicore::{
Worker
};
/// Generates a random common reference string for
/// a circuit.
pub fn generate_random_parameters<E, C, R>(
circuit: C,
rng: &mut R
) -> Result<Parameters<E>, SynthesisError>
where E: Engine, C: Circuit<E>, R: Rng
{
let g1 = rng.gen();
let g2 = rng.gen();
let alpha = rng.gen();
let beta = rng.gen();
let gamma = rng.gen();
let delta = rng.gen();
let tau = rng.gen();
generate_parameters::<E, C>(
circuit,
g1,
g2,
alpha,
beta,
gamma,
delta,
tau
)
}
/// This is our assembly structure that we'll use to synthesize the
/// circuit into a QAP.
struct KeypairAssembly<E: Engine> {
num_inputs: usize,
num_aux: usize,
num_constraints: usize,
at_inputs: Vec<Vec<(E::Fr, usize)>>,
bt_inputs: Vec<Vec<(E::Fr, usize)>>,
ct_inputs: Vec<Vec<(E::Fr, usize)>>,
at_aux: Vec<Vec<(E::Fr, usize)>>,
bt_aux: Vec<Vec<(E::Fr, usize)>>,
ct_aux: Vec<Vec<(E::Fr, usize)>>
}
impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
type Root = Self;
fn alloc<F, A, AR>(
&mut self,
_: A,
_: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
// There is no assignment, so we don't even invoke the
// function for obtaining one.
let index = self.num_aux;
self.num_aux += 1;
self.at_aux.push(vec![]);
self.bt_aux.push(vec![]);
self.ct_aux.push(vec![]);
Ok(Variable(Index::Aux(index)))
}
fn alloc_input<F, A, AR>(
&mut self,
_: A,
_: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
// There is no assignment, so we don't even invoke the
// function for obtaining one.
let index = self.num_inputs;
self.num_inputs += 1;
self.at_inputs.push(vec![]);
self.bt_inputs.push(vec![]);
self.ct_inputs.push(vec![]);
Ok(Variable(Index::Input(index)))
}
fn enforce<A, AR, LA, LB, LC>(
&mut self,
_: A,
a: LA,
b: LB,
c: LC
)
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>(
l: LinearCombination<E>,
inputs: &mut [Vec<(E::Fr, usize)>],
aux: &mut [Vec<(E::Fr, usize)>],
this_constraint: usize
)
{
for (index, coeff) in l.0 {
match index {
Variable(Index::Input(id)) => inputs[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(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;
}
fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR
{
// Do nothing; we don't care about namespaces in this context.
}
fn pop_namespace(&mut self)
{
// Do nothing; we don't care about namespaces in this context.
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
/// Create parameters for a circuit, given some toxic waste.
pub fn generate_parameters<E, C>(
circuit: C,
g1: E::G1,
g2: E::G2,
alpha: E::Fr,
beta: E::Fr,
gamma: E::Fr,
delta: E::Fr,
tau: E::Fr
) -> Result<Parameters<E>, SynthesisError>
where E: Engine, C: Circuit<E>
{
let mut assembly = KeypairAssembly {
num_inputs: 0,
num_aux: 0,
num_constraints: 0,
at_inputs: vec![],
bt_inputs: vec![],
ct_inputs: vec![],
at_aux: vec![],
bt_aux: vec![],
ct_aux: vec![]
};
// Allocate the "one" input variable
assembly.alloc_input(|| "", || Ok(E::Fr::one()))?;
// Synthesize the circuit.
circuit.synthesize(&mut assembly)?;
// Input constraints to ensure full density of IC query
// x * 0 = 0
for i in 0..assembly.num_inputs {
assembly.enforce(|| "",
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
}
// Create bases for blind evaluation of polynomials at tau
let powers_of_tau = vec![Scalar::<E>(E::Fr::zero()); assembly.num_constraints];
let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?;
// Compute G1 window table
let mut g1_wnaf = Wnaf::new();
let g1_wnaf = g1_wnaf.base(g1, {
// H query
(powers_of_tau.as_ref().len() - 1)
// IC/L queries
+ assembly.num_inputs + assembly.num_aux
// A query
+ assembly.num_inputs + assembly.num_aux
// B query
+ assembly.num_inputs + assembly.num_aux
});
// Compute G2 window table
let mut g2_wnaf = Wnaf::new();
let g2_wnaf = g2_wnaf.base(g2, {
// B query
assembly.num_inputs + assembly.num_aux
});
let gamma_inverse = gamma.inverse().ok_or(SynthesisError::UnexpectedIdentity)?;
let delta_inverse = delta.inverse().ok_or(SynthesisError::UnexpectedIdentity)?;
let worker = Worker::new();
let mut h = vec![E::G1::zero(); powers_of_tau.as_ref().len() - 1];
{
// Compute powers of tau
{
let powers_of_tau = powers_of_tau.as_mut();
worker.scope(powers_of_tau.len(), |scope, chunk| {
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate()
{
scope.spawn(move || {
let mut current_tau_power = tau.pow(&[(i*chunk) as u64]);
for p in powers_of_tau {
p.0 = current_tau_power;
current_tau_power.mul_assign(&tau);
}
});
}
});
}
// coeff = t(x) / delta
let mut coeff = powers_of_tau.z(&tau);
coeff.mul_assign(&delta_inverse);
// Compute the H query with multiple threads
worker.scope(h.len(), |scope, chunk| {
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
{
let mut g1_wnaf = g1_wnaf.shared();
scope.spawn(move || {
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
for (h, p) in h.iter_mut().zip(p.iter())
{
// Compute final exponent
let mut exp = p.0;
exp.mul_assign(&coeff);
// Exponentiate
*h = g1_wnaf.scalar(exp.into_repr());
}
// Batch normalize
E::G1::batch_normalization(h);
});
}
});
}
// Use inverse FFT to convert powers of tau to Lagrange coefficients
powers_of_tau.ifft(&worker);
let powers_of_tau = powers_of_tau.into_coeffs();
let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
let mut b_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux];
let mut b_g2 = vec![E::G2::zero(); assembly.num_inputs + assembly.num_aux];
let mut ic = vec![E::G1::zero(); assembly.num_inputs];
let mut l = vec![E::G1::zero(); assembly.num_aux];
fn eval<E: Engine>(
// wNAF window tables
g1_wnaf: &Wnaf<usize, &[E::G1], &mut Vec<i64>>,
g2_wnaf: &Wnaf<usize, &[E::G2], &mut Vec<i64>>,
// Lagrange coefficients for tau
powers_of_tau: &[Scalar<E>],
// QAP polynomials
at: &[Vec<(E::Fr, usize)>],
bt: &[Vec<(E::Fr, usize)>],
ct: &[Vec<(E::Fr, usize)>],
// Resulting evaluated QAP polynomials
a: &mut [E::G1],
b_g1: &mut [E::G1],
b_g2: &mut [E::G2],
ext: &mut [E::G1],
// Inverse coefficient for ext elements
inv: &E::Fr,
// Trapdoors
alpha: &E::Fr,
beta: &E::Fr,
// Worker
worker: &Worker
)
{
// Sanity check
assert_eq!(a.len(), at.len());
assert_eq!(a.len(), bt.len());
assert_eq!(a.len(), ct.len());
assert_eq!(a.len(), b_g1.len());
assert_eq!(a.len(), b_g2.len());
assert_eq!(a.len(), ext.len());
// Evaluate polynomials in multiple threads
worker.scope(a.len(), |scope, chunk| {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk)
.zip(b_g1.chunks_mut(chunk))
.zip(b_g2.chunks_mut(chunk))
.zip(ext.chunks_mut(chunk))
.zip(at.chunks(chunk))
.zip(bt.chunks(chunk))
.zip(ct.chunks(chunk))
{
let mut g1_wnaf = g1_wnaf.shared();
let mut g2_wnaf = g2_wnaf.shared();
scope.spawn(move || {
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut()
.zip(b_g1.iter_mut())
.zip(b_g2.iter_mut())
.zip(ext.iter_mut())
.zip(at.iter())
.zip(bt.iter())
.zip(ct.iter())
{
fn eval_at_tau<E: Engine>(
powers_of_tau: &[Scalar<E>],
p: &[(E::Fr, usize)]
) -> E::Fr
{
let mut acc = E::Fr::zero();
for &(ref coeff, index) in p {
let mut n = powers_of_tau[index].0;
n.mul_assign(coeff);
acc.add_assign(&n);
}
acc
}
// Evaluate QAP polynomials at tau
let mut at = eval_at_tau(powers_of_tau, at);
let mut bt = eval_at_tau(powers_of_tau, bt);
let ct = eval_at_tau(powers_of_tau, ct);
// Compute A query (in G1)
if !at.is_zero() {
*a = g1_wnaf.scalar(at.into_repr());
}
// Compute B query (in G1/G2)
if !bt.is_zero() {
let bt_repr = bt.into_repr();
*b_g1 = g1_wnaf.scalar(bt_repr);
*b_g2 = g2_wnaf.scalar(bt_repr);
}
at.mul_assign(&beta);
bt.mul_assign(&alpha);
let mut e = at;
e.add_assign(&bt);
e.add_assign(&ct);
e.mul_assign(inv);
*ext = g1_wnaf.scalar(e.into_repr());
}
// Batch normalize
E::G1::batch_normalization(a);
E::G1::batch_normalization(b_g1);
E::G2::batch_normalization(b_g2);
E::G1::batch_normalization(ext);
});
}
});
}
// Evaluate for inputs.
eval(
&g1_wnaf,
&g2_wnaf,
&powers_of_tau,
&assembly.at_inputs,
&assembly.bt_inputs,
&assembly.ct_inputs,
&mut a[0..assembly.num_inputs],
&mut b_g1[0..assembly.num_inputs],
&mut b_g2[0..assembly.num_inputs],
&mut ic,
&gamma_inverse,
&alpha,
&beta,
&worker
);
// Evaluate for auxiliary variables.
eval(
&g1_wnaf,
&g2_wnaf,
&powers_of_tau,
&assembly.at_aux,
&assembly.bt_aux,
&assembly.ct_aux,
&mut a[assembly.num_inputs..],
&mut b_g1[assembly.num_inputs..],
&mut b_g2[assembly.num_inputs..],
&mut l,
&delta_inverse,
&alpha,
&beta,
&worker
);
// Don't allow any elements be unconstrained, so that
// the L query is always fully dense.
for e in l.iter() {
if e.is_zero() {
return Err(SynthesisError::UnconstrainedVariable);
}
}
let g1 = g1.into_affine();
let g2 = g2.into_affine();
let vk = VerifyingKey::<E> {
alpha_g1: g1.mul(alpha).into_affine(),
beta_g1: g1.mul(beta).into_affine(),
beta_g2: g2.mul(beta).into_affine(),
gamma_g2: g2.mul(gamma).into_affine(),
delta_g1: g1.mul(delta).into_affine(),
delta_g2: g2.mul(delta).into_affine(),
ic: ic.into_iter().map(|e| e.into_affine()).collect()
};
Ok(Parameters {
vk: vk,
h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()),
l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()),
// 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()),
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())
})
}

576
bellman/src/groth16/mod.rs Normal file
View File

@@ -0,0 +1,576 @@
use group::{CurveAffine, EncodedPoint};
use pairing::{
Engine,
PairingCurveAffine,
};
use ::{
SynthesisError
};
use multiexp::SourceBuilder;
use std::io::{self, Read, Write};
use std::sync::Arc;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
#[cfg(test)]
mod tests;
mod generator;
mod prover;
mod verifier;
pub use self::generator::*;
pub use self::prover::*;
pub use self::verifier::*;
#[derive(Clone)]
pub struct Proof<E: Engine> {
pub a: E::G1Affine,
pub b: E::G2Affine,
pub c: E::G1Affine
}
impl<E: Engine> PartialEq for Proof<E> {
fn eq(&self, other: &Self) -> bool {
self.a == other.a &&
self.b == other.b &&
self.c == other.c
}
}
impl<E: Engine> Proof<E> {
pub fn write<W: Write>(
&self,
mut writer: W
) -> io::Result<()>
{
writer.write_all(self.a.into_compressed().as_ref())?;
writer.write_all(self.b.into_compressed().as_ref())?;
writer.write_all(self.c.into_compressed().as_ref())?;
Ok(())
}
pub fn read<R: Read>(
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Compressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Compressed::empty();
reader.read_exact(g1_repr.as_mut())?;
let a = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
reader.read_exact(g2_repr.as_mut())?;
let b = g2_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
reader.read_exact(g1_repr.as_mut())?;
let c = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
Ok(Proof {
a: a,
b: b,
c: c
})
}
}
#[derive(Clone)]
pub struct VerifyingKey<E: Engine> {
// alpha in g1 for verifying and for creating A/C elements of
// proof. Never the point at infinity.
pub alpha_g1: E::G1Affine,
// beta in g1 and g2 for verifying and for creating B/C elements
// of proof. Never the point at infinity.
pub beta_g1: E::G1Affine,
pub beta_g2: E::G2Affine,
// gamma in g2 for verifying. Never the point at infinity.
pub gamma_g2: E::G2Affine,
// delta in g1/g2 for verifying and proving, essentially the magic
// trapdoor that forces the prover to evaluate the C element of the
// proof with only components from the CRS. Never the point at
// infinity.
pub delta_g1: E::G1Affine,
pub delta_g2: E::G2Affine,
// Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / gamma
// 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
// at infinity.
pub ic: Vec<E::G1Affine>
}
impl<E: Engine> PartialEq for VerifyingKey<E> {
fn eq(&self, other: &Self) -> bool {
self.alpha_g1 == other.alpha_g1 &&
self.beta_g1 == other.beta_g1 &&
self.beta_g2 == other.beta_g2 &&
self.gamma_g2 == other.gamma_g2 &&
self.delta_g1 == other.delta_g1 &&
self.delta_g2 == other.delta_g2 &&
self.ic == other.ic
}
}
impl<E: Engine> VerifyingKey<E> {
pub fn write<W: Write>(
&self,
mut writer: W
) -> io::Result<()>
{
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_g2.into_uncompressed().as_ref())?;
writer.write_all(self.gamma_g2.into_uncompressed().as_ref())?;
writer.write_all(self.delta_g1.into_uncompressed().as_ref())?;
writer.write_all(self.delta_g2.into_uncompressed().as_ref())?;
writer.write_u32::<BigEndian>(self.ic.len() as u32)?;
for ic in &self.ic {
writer.write_all(ic.into_uncompressed().as_ref())?;
}
Ok(())
}
pub fn read<R: Read>(
mut reader: R
) -> io::Result<Self>
{
let mut g1_repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
let mut g2_repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(g1_repr.as_mut())?;
let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?;
let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g2_repr.as_mut())?;
let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
reader.read_exact(g1_repr.as_mut())?;
let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
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 ic_len = reader.read_u32::<BigEndian>()? as usize;
let mut ic = vec![];
for _ in 0..ic_len {
reader.read_exact(g1_repr.as_mut())?;
let g1 = g1_repr
.into_affine()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})?;
ic.push(g1);
}
Ok(VerifyingKey {
alpha_g1: alpha_g1,
beta_g1: beta_g1,
beta_g2: beta_g2,
gamma_g2: gamma_g2,
delta_g1: delta_g1,
delta_g2: delta_g2,
ic: ic
})
}
}
#[derive(Clone)]
pub struct Parameters<E: Engine> {
pub vk: VerifyingKey<E>,
// Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and
// m-2 inclusive. Never contains points at infinity.
pub h: Arc<Vec<E::G1Affine>>,
// Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta
// for all auxiliary inputs. Variables can never be unconstrained, so this
// never contains points at infinity.
pub l: Arc<Vec<E::G1Affine>>,
// QAP "A" polynomials evaluated at tau in the Lagrange basis. Never contains
// points at infinity: polynomials that evaluate to zero are omitted from
// the CRS and the prover can deterministically skip their evaluation.
pub a: Arc<Vec<E::G1Affine>>,
// QAP "B" polynomials evaluated at tau in the Lagrange basis. Needed in
// G1 and G2 for C/B queries, respectively. Never contains points at
// infinity for the same reason as the "A" polynomials.
pub b_g1: Arc<Vec<E::G1Affine>>,
pub b_g2: Arc<Vec<E::G2Affine>>
}
impl<E: Engine> PartialEq for Parameters<E> {
fn eq(&self, other: &Self) -> bool {
self.vk == other.vk &&
self.h == other.h &&
self.l == other.l &&
self.a == other.a &&
self.b_g1 == other.b_g1 &&
self.b_g2 == other.b_g2
}
}
impl<E: Engine> Parameters<E> {
pub fn write<W: Write>(
&self,
mut writer: W
) -> io::Result<()>
{
self.vk.write(&mut writer)?;
writer.write_u32::<BigEndian>(self.h.len() as u32)?;
for g in &self.h[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
writer.write_u32::<BigEndian>(self.l.len() as u32)?;
for g in &self.l[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
writer.write_u32::<BigEndian>(self.a.len() as u32)?;
for g in &self.a[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
writer.write_u32::<BigEndian>(self.b_g1.len() as u32)?;
for g in &self.b_g1[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
writer.write_u32::<BigEndian>(self.b_g2.len() as u32)?;
for g in &self.b_g2[..] {
writer.write_all(g.into_uncompressed().as_ref())?;
}
Ok(())
}
pub fn read<R: Read>(
mut reader: R,
checked: bool
) -> io::Result<Self>
{
let read_g1 = |reader: &mut R| -> io::Result<E::G1Affine> {
let mut repr = <E::G1Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(repr.as_mut())?;
if checked {
repr
.into_affine()
} else {
repr
.into_affine_unchecked()
}
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})
};
let read_g2 = |reader: &mut R| -> io::Result<E::G2Affine> {
let mut repr = <E::G2Affine as CurveAffine>::Uncompressed::empty();
reader.read_exact(repr.as_mut())?;
if checked {
repr
.into_affine()
} else {
repr
.into_affine_unchecked()
}
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
.and_then(|e| if e.is_zero() {
Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity"))
} else {
Ok(e)
})
};
let vk = VerifyingKey::<E>::read(&mut reader)?;
let mut h = vec![];
let mut l = vec![];
let mut a = vec![];
let mut b_g1 = vec![];
let mut b_g2 = vec![];
{
let len = reader.read_u32::<BigEndian>()? as usize;
for _ in 0..len {
h.push(read_g1(&mut reader)?);
}
}
{
let len = reader.read_u32::<BigEndian>()? as usize;
for _ in 0..len {
l.push(read_g1(&mut reader)?);
}
}
{
let len = reader.read_u32::<BigEndian>()? as usize;
for _ in 0..len {
a.push(read_g1(&mut reader)?);
}
}
{
let len = reader.read_u32::<BigEndian>()? as usize;
for _ in 0..len {
b_g1.push(read_g1(&mut reader)?);
}
}
{
let len = reader.read_u32::<BigEndian>()? as usize;
for _ in 0..len {
b_g2.push(read_g2(&mut reader)?);
}
}
Ok(Parameters {
vk: vk,
h: Arc::new(h),
l: Arc::new(l),
a: Arc::new(a),
b_g1: Arc::new(b_g1),
b_g2: Arc::new(b_g2)
})
}
}
pub struct PreparedVerifyingKey<E: Engine> {
/// Pairing result of alpha*beta
alpha_g1_beta_g2: E::Fqk,
/// -gamma in G2
neg_gamma_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
/// -delta in G2
neg_delta_g2: <E::G2Affine as PairingCurveAffine>::Prepared,
/// Copy of IC from `VerifiyingKey`.
ic: Vec<E::G1Affine>
}
pub trait ParameterSource<E: Engine> {
type G1Builder: SourceBuilder<E::G1Affine>;
type G2Builder: SourceBuilder<E::G2Affine>;
fn get_vk(
&mut self,
num_ic: usize
) -> 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(
&mut self,
num_inputs: usize,
num_aux: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g1(
&mut self,
num_inputs: usize,
num_aux: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>;
fn get_b_g2(
&mut self,
num_inputs: usize,
num_aux: usize
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>;
}
impl<'a, E: Engine> ParameterSource<E> for &'a Parameters<E> {
type G1Builder = (Arc<Vec<E::G1Affine>>, usize);
type G2Builder = (Arc<Vec<E::G2Affine>>, usize);
fn get_vk(
&mut self,
_: usize
) -> Result<VerifyingKey<E>, SynthesisError>
{
Ok(self.vk.clone())
}
fn get_h(
&mut self,
_: usize
) -> Result<Self::G1Builder, SynthesisError>
{
Ok((self.h.clone(), 0))
}
fn get_l(
&mut self,
_: usize
) -> Result<Self::G1Builder, SynthesisError>
{
Ok((self.l.clone(), 0))
}
fn get_a(
&mut self,
num_inputs: usize,
_: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
{
Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs)))
}
fn get_b_g1(
&mut self,
num_inputs: usize,
_: usize
) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>
{
Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs)))
}
fn get_b_g2(
&mut self,
num_inputs: usize,
_: usize
) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>
{
Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs)))
}
}
#[cfg(test)]
mod test_with_bls12_381 {
use super::*;
use {Circuit, SynthesisError, ConstraintSystem};
use ff::Field;
use rand::{Rand, thread_rng};
use pairing::bls12_381::{Bls12, Fr};
#[test]
fn serialization() {
struct MySillyCircuit<E: Engine> {
a: Option<E::Fr>,
b: Option<E::Fr>
}
impl<E: Engine> Circuit<E> for MySillyCircuit<E> {
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?;
let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?;
let c = cs.alloc_input(|| "c", || {
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
a.mul_assign(&b);
Ok(a)
})?;
cs.enforce(
|| "a*b=c",
|lc| lc + a,
|lc| lc + b,
|lc| lc + c
);
Ok(())
}
}
let rng = &mut thread_rng();
let params = generate_random_parameters::<Bls12, _, _>(
MySillyCircuit { a: None, b: None },
rng
).unwrap();
{
let mut v = vec![];
params.write(&mut v).unwrap();
assert_eq!(v.len(), 2136);
let de_params = Parameters::read(&v[..], true).unwrap();
assert!(params == de_params);
let de_params = Parameters::read(&v[..], false).unwrap();
assert!(params == de_params);
}
let pvk = prepare_verifying_key::<Bls12>(&params.vk);
for _ in 0..100 {
let a = Fr::rand(rng);
let b = Fr::rand(rng);
let mut c = a;
c.mul_assign(&b);
let proof = create_random_proof(
MySillyCircuit {
a: Some(a),
b: Some(b)
},
&params,
rng
).unwrap();
let mut v = vec![];
proof.write(&mut v).unwrap();
assert_eq!(v.len(), 192);
let de_proof = Proof::read(&v[..]).unwrap();
assert!(proof == de_proof);
assert!(verify_proof(&pvk, &proof, &[c]).unwrap());
assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
}
}
}

View File

@@ -0,0 +1,330 @@
use rand::Rng;
use std::sync::Arc;
use futures::Future;
use ff::{Field, PrimeField};
use group::{CurveAffine, CurveProjective};
use pairing::Engine;
use super::{
ParameterSource,
Proof
};
use ::{
SynthesisError,
Circuit,
ConstraintSystem,
LinearCombination,
Variable,
Index
};
use ::domain::{
EvaluationDomain,
Scalar
};
use ::multiexp::{
DensityTracker,
FullDensity,
multiexp
};
use ::multicore::{
Worker
};
fn eval<E: Engine>(
lc: &LinearCombination<E>,
mut input_density: Option<&mut DensityTracker>,
mut aux_density: Option<&mut DensityTracker>,
input_assignment: &[E::Fr],
aux_assignment: &[E::Fr]
) -> E::Fr
{
let mut acc = E::Fr::zero();
for &(index, coeff) in lc.0.iter() {
let mut tmp;
match index {
Variable(Index::Input(i)) => {
tmp = input_assignment[i];
if let Some(ref mut v) = input_density {
v.inc(i);
}
},
Variable(Index::Aux(i)) => {
tmp = aux_assignment[i];
if let Some(ref mut v) = aux_density {
v.inc(i);
}
}
}
if coeff == E::Fr::one() {
acc.add_assign(&tmp);
} else {
tmp.mul_assign(&coeff);
acc.add_assign(&tmp);
}
}
acc
}
struct ProvingAssignment<E: Engine> {
// Density of queries
a_aux_density: DensityTracker,
b_input_density: DensityTracker,
b_aux_density: DensityTracker,
// Evaluations of A, B, C polynomials
a: Vec<Scalar<E>>,
b: Vec<Scalar<E>>,
c: Vec<Scalar<E>>,
// Assignments of variables
input_assignment: Vec<E::Fr>,
aux_assignment: Vec<E::Fr>
}
impl<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
type Root = Self;
fn alloc<F, A, AR>(
&mut self,
_: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
self.aux_assignment.push(f()?);
self.a_aux_density.add_element();
self.b_aux_density.add_element();
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
}
fn alloc_input<F, A, AR>(
&mut self,
_: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
self.input_assignment.push(f()?);
self.b_input_density.add_element();
Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
}
fn enforce<A, AR, LA, LB, LC>(
&mut self,
_: A,
a: LA,
b: LB,
c: LC
)
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 b = b(LinearCombination::zero());
let c = c(LinearCombination::zero());
self.a.push(Scalar(eval(
&a,
// Inputs have full density in the A query
// because there are constraints of the
// form x * 0 = 0 for each input.
None,
Some(&mut self.a_aux_density),
&self.input_assignment,
&self.aux_assignment
)));
self.b.push(Scalar(eval(
&b,
Some(&mut self.b_input_density),
Some(&mut self.b_aux_density),
&self.input_assignment,
&self.aux_assignment
)));
self.c.push(Scalar(eval(
&c,
// There is no C polynomial query,
// though there is an (beta)A + (alpha)B + C
// query for all aux variables.
// However, that query has full density.
None,
None,
&self.input_assignment,
&self.aux_assignment
)));
}
fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR
{
// Do nothing; we don't care about namespaces in this context.
}
fn pop_namespace(&mut self)
{
// Do nothing; we don't care about namespaces in this context.
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
circuit: C,
params: P,
rng: &mut R
) -> Result<Proof<E>, SynthesisError>
where E: Engine, C: Circuit<E>, R: Rng
{
let r = rng.gen();
let s = rng.gen();
create_proof::<E, C, P>(circuit, params, r, s)
}
pub fn create_proof<E, C, P: ParameterSource<E>>(
circuit: C,
mut params: P,
r: E::Fr,
s: E::Fr
) -> Result<Proof<E>, SynthesisError>
where E: Engine, C: Circuit<E>
{
let mut prover = ProvingAssignment {
a_aux_density: DensityTracker::new(),
b_input_density: DensityTracker::new(),
b_aux_density: DensityTracker::new(),
a: vec![],
b: vec![],
c: vec![],
input_assignment: vec![],
aux_assignment: vec![]
};
prover.alloc_input(|| "", || Ok(E::Fr::one()))?;
circuit.synthesize(&mut prover)?;
for i in 0..prover.input_assignment.len() {
prover.enforce(|| "",
|lc| lc + Variable(Index::Input(i)),
|lc| lc,
|lc| lc,
);
}
let worker = Worker::new();
let vk = params.get_vk(prover.input_assignment.len())?;
let h = {
let mut a = EvaluationDomain::from_coeffs(prover.a)?;
let mut b = EvaluationDomain::from_coeffs(prover.b)?;
let mut c = EvaluationDomain::from_coeffs(prover.c)?;
a.ifft(&worker);
a.coset_fft(&worker);
b.ifft(&worker);
b.coset_fft(&worker);
c.ifft(&worker);
c.coset_fft(&worker);
a.mul_assign(&worker, &b);
drop(b);
a.sub_assign(&worker, &c);
drop(c);
a.divide_by_z_on_coset(&worker);
a.icoset_fft(&worker);
let mut a = a.into_coeffs();
let a_len = a.len() - 1;
a.truncate(a_len);
// TODO: parallelize if it's even helpful
let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::<Vec<_>>());
multiexp(&worker, params.get_h(a.len())?, FullDensity, a)
};
// 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 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 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 = multiexp(&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_total = b_input_density.get_total_density();
let b_aux_density = Arc::new(prover.b_aux_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 = multiexp(&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 = 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);
if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() {
// If this element is zero, someone is trying to perform a
// subversion-CRS attack.
return Err(SynthesisError::UnexpectedIdentity);
}
let mut g_a = vk.delta_g1.mul(r);
g_a.add_assign_mixed(&vk.alpha_g1);
let mut g_b = vk.delta_g2.mul(s);
g_b.add_assign_mixed(&vk.beta_g2);
let mut g_c;
{
let mut rs = r;
rs.mul_assign(&s);
g_c = vk.delta_g1.mul(rs);
g_c.add_assign(&vk.alpha_g1.mul(s));
g_c.add_assign(&vk.beta_g1.mul(r));
}
let mut a_answer = a_inputs.wait()?;
a_answer.add_assign(&a_aux.wait()?);
g_a.add_assign(&a_answer);
a_answer.mul_assign(s);
g_c.add_assign(&a_answer);
let mut b1_answer = b_g1_inputs.wait()?;
b1_answer.add_assign(&b_g1_aux.wait()?);
let mut b2_answer = b_g2_inputs.wait()?;
b2_answer.add_assign(&b_g2_aux.wait()?);
g_b.add_assign(&b2_answer);
b1_answer.mul_assign(r);
g_c.add_assign(&b1_answer);
g_c.add_assign(&h.wait()?);
g_c.add_assign(&l.wait()?);
Ok(Proof {
a: g_a.into_affine(),
b: g_b.into_affine(),
c: g_c.into_affine()
})
}

View File

@@ -0,0 +1,449 @@
use ff::{
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError,
PrimeFieldRepr, ScalarEngine, SqrtField};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use pairing::{Engine, PairingCurveAffine};
use std::cmp::Ordering;
use std::fmt;
use rand::{Rand, Rng};
use std::num::Wrapping;
const MODULUS_R: Wrapping<u32> = Wrapping(64513);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Fr(Wrapping<u32>);
impl fmt::Display for Fr {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
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 {
fn zero() -> Self {
Fr(Wrapping(0))
}
fn one() -> Self {
Fr(Wrapping(1))
}
fn is_zero(&self) -> bool {
(self.0).0 == 0
}
fn square(&mut self) {
self.0 = (self.0 * self.0) % MODULUS_R;
}
fn double(&mut self) {
self.0 = (self.0 << 1) % MODULUS_R;
}
fn negate(&mut self) {
if !<Fr as Field>::is_zero(self) {
self.0 = MODULUS_R - self.0;
}
}
fn add_assign(&mut self, other: &Self) {
self.0 = (self.0 + other.0) % MODULUS_R;
}
fn sub_assign(&mut self, other: &Self) {
self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R;
}
fn mul_assign(&mut self, other: &Self) {
self.0 = (self.0 * other.0) % MODULUS_R;
}
fn inverse(&self) -> Option<Self> {
if <Fr as Field>::is_zero(self) {
None
} else {
Some(self.pow(&[(MODULUS_R.0 as u64) - 2]))
}
}
fn frobenius_map(&mut self, _: usize) {
// identity
}
}
impl SqrtField for Fr {
fn legendre(&self) -> LegendreSymbol {
// s = self^((r - 1) // 2)
let s = self.pow([32256]);
if s == <Fr as Field>::zero() { LegendreSymbol::Zero }
else if s == <Fr as Field>::one() { LegendreSymbol::QuadraticResidue }
else { LegendreSymbol::QuadraticNonResidue }
}
fn sqrt(&self) -> Option<Self> {
// Tonelli-Shank's algorithm for q mod 16 = 1
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
match self.legendre() {
LegendreSymbol::Zero => Some(*self),
LegendreSymbol::QuadraticNonResidue => None,
LegendreSymbol::QuadraticResidue => {
let mut c = Fr::root_of_unity();
// r = self^((t + 1) // 2)
let mut r = self.pow([32]);
// t = self^t
let mut t = self.pow([63]);
let mut m = Fr::S;
while t != <Fr as Field>::one() {
let mut i = 1;
{
let mut t2i = t;
t2i.square();
loop {
if t2i == <Fr as Field>::one() {
break;
}
t2i.square();
i += 1;
}
}
for _ in 0..(m - i - 1) {
c.square();
}
<Fr as Field>::mul_assign(&mut r, &c);
c.square();
<Fr as Field>::mul_assign(&mut t, &c);
m = i;
}
Some(r)
}
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FrRepr([u64; 1]);
impl Ord for FrRepr {
fn cmp(&self, other: &FrRepr) -> Ordering {
(self.0)[0].cmp(&(other.0)[0])
}
}
impl PartialOrd for FrRepr {
fn partial_cmp(&self, other: &FrRepr) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Rand for FrRepr {
fn rand<R: Rng>(rng: &mut R) -> Self {
FrRepr([rng.gen()])
}
}
impl fmt::Display for FrRepr {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", (self.0)[0])
}
}
impl From<u64> for FrRepr {
fn from(v: u64) -> FrRepr {
FrRepr([v])
}
}
impl From<Fr> for FrRepr {
fn from(v: Fr) -> FrRepr {
FrRepr([(v.0).0 as u64])
}
}
impl AsMut<[u64]> for FrRepr {
fn as_mut(&mut self) -> &mut [u64] {
&mut self.0[..]
}
}
impl AsRef<[u64]> for FrRepr {
fn as_ref(&self) -> &[u64] {
&self.0[..]
}
}
impl Default for FrRepr {
fn default() -> FrRepr {
FrRepr::from(0u64)
}
}
impl PrimeFieldRepr for FrRepr {
fn sub_noborrow(&mut self, other: &Self) {
self.0[0] = self.0[0].wrapping_sub(other.0[0]);
}
fn add_nocarry(&mut self, other: &Self) {
self.0[0] = self.0[0].wrapping_add(other.0[0]);
}
fn num_bits(&self) -> u32 {
64 - self.0[0].leading_zeros()
}
fn is_zero(&self) -> bool {
self.0[0] == 0
}
fn is_odd(&self) -> bool {
!self.is_even()
}
fn is_even(&self) -> bool {
self.0[0] % 2 == 0
}
fn div2(&mut self) {
self.shr(1)
}
fn shr(&mut self, amt: u32) {
self.0[0] >>= amt;
}
fn mul2(&mut self) {
self.shl(1)
}
fn shl(&mut self, amt: u32) {
self.0[0] <<= amt;
}
}
impl PrimeField for Fr {
type Repr = FrRepr;
const NUM_BITS: u32 = 16;
const CAPACITY: u32 = 15;
const S: u32 = 10;
fn from_repr(repr: FrRepr) -> Result<Self, PrimeFieldDecodingError> {
if repr.0[0] >= (MODULUS_R.0 as u64) {
Err(PrimeFieldDecodingError::NotInField(format!("{}", repr)))
} else {
Ok(Fr(Wrapping(repr.0[0] as u32)))
}
}
fn into_repr(&self) -> FrRepr {
FrRepr::from(*self)
}
fn char() -> FrRepr {
Fr(MODULUS_R).into()
}
fn multiplicative_generator() -> Fr {
Fr(Wrapping(5))
}
fn root_of_unity() -> Fr {
Fr(Wrapping(57751))
}
}
#[derive(Clone)]
pub struct DummyEngine;
impl ScalarEngine for DummyEngine {
type Fr = Fr;
}
impl Engine for DummyEngine {
type G1 = Fr;
type G1Affine = Fr;
type G2 = Fr;
type G2Affine = Fr;
type Fq = Fr;
type Fqe = Fr;
// TODO: This should be F_645131 or something. Doesn't matter for now.
type Fqk = Fr;
fn miller_loop<'a, I>(i: I) -> Self::Fqk
where I: IntoIterator<Item=&'a (
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared
)>
{
let mut acc = <Fr as Field>::zero();
for &(a, b) in i {
let mut tmp = *a;
<Fr as Field>::mul_assign(&mut tmp, b);
<Fr as Field>::add_assign(&mut acc, &tmp);
}
acc
}
/// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk>
{
Some(*this)
}
}
impl CurveProjective for Fr {
type Affine = Fr;
type Base = Fr;
type Scalar = Fr;
type Engine = DummyEngine;
fn zero() -> Self {
<Fr as Field>::zero()
}
fn one() -> Self {
<Fr as Field>::one()
}
fn is_zero(&self) -> bool {
<Fr as Field>::is_zero(self)
}
fn batch_normalization(_: &mut [Self]) {
}
fn is_normalized(&self) -> bool {
true
}
fn double(&mut self) {
<Fr as Field>::double(self);
}
fn add_assign(&mut self, other: &Self) {
<Fr as Field>::add_assign(self, other);
}
fn add_assign_mixed(&mut self, other: &Self) {
<Fr as Field>::add_assign(self, other);
}
fn negate(&mut self) {
<Fr as Field>::negate(self);
}
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S)
{
let tmp = Fr::from_repr(other.into()).unwrap();
<Fr as Field>::mul_assign(self, &tmp);
}
fn into_affine(&self) -> Fr {
*self
}
fn recommended_wnaf_for_scalar(_: <Self::Scalar as PrimeField>::Repr) -> usize {
3
}
fn recommended_wnaf_for_num_scalars(_: usize) -> usize {
3
}
}
#[derive(Copy, Clone)]
pub struct FakePoint;
impl AsMut<[u8]> for FakePoint {
fn as_mut(&mut self) -> &mut [u8] {
unimplemented!()
}
}
impl AsRef<[u8]> for FakePoint {
fn as_ref(&self) -> &[u8] {
unimplemented!()
}
}
impl EncodedPoint for FakePoint {
type Affine = Fr;
fn empty() -> Self {
unimplemented!()
}
fn size() -> usize {
unimplemented!()
}
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError> {
unimplemented!()
}
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError> {
unimplemented!()
}
fn from_affine(_: Self::Affine) -> Self {
unimplemented!()
}
}
impl CurveAffine for Fr {
type Compressed = FakePoint;
type Uncompressed = FakePoint;
type Projective = Fr;
type Base = Fr;
type Scalar = Fr;
type Engine = DummyEngine;
fn zero() -> Self {
<Fr as Field>::zero()
}
fn one() -> Self {
<Fr as Field>::one()
}
fn is_zero(&self) -> bool {
<Fr as Field>::is_zero(self)
}
fn negate(&mut self) {
<Fr as Field>::negate(self);
}
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective
{
let mut res = *self;
let tmp = Fr::from_repr(other.into()).unwrap();
<Fr as Field>::mul_assign(&mut res, &tmp);
res
}
fn into_projective(&self) -> Self::Projective {
*self
}
}
impl PairingCurveAffine for Fr {
type Prepared = Fr;
type Pair = Fr;
type PairingResult = Fr;
fn prepare(&self) -> Self::Prepared {
*self
}
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
self.mul(*other)
}
}

View File

@@ -0,0 +1,397 @@
use ff::{Field, PrimeField};
use pairing::Engine;
mod dummy_engine;
use self::dummy_engine::*;
use std::marker::PhantomData;
use ::{
Circuit,
ConstraintSystem,
SynthesisError
};
use super::{
generate_parameters,
prepare_verifying_key,
create_proof,
verify_proof
};
struct XORDemo<E: Engine> {
a: Option<bool>,
b: Option<bool>,
_marker: PhantomData<E>
}
impl<E: Engine> Circuit<E> for XORDemo<E> {
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
let a_var = cs.alloc(|| "a", || {
if self.a.is_some() {
if self.a.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
})?;
cs.enforce(
|| "a_boolean_constraint",
|lc| lc + CS::one() - a_var,
|lc| lc + a_var,
|lc| lc
);
let b_var = cs.alloc(|| "b", || {
if self.b.is_some() {
if self.b.unwrap() {
Ok(E::Fr::one())
} else {
Ok(E::Fr::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
})?;
cs.enforce(
|| "b_boolean_constraint",
|lc| lc + CS::one() - b_var,
|lc| lc + b_var,
|lc| lc
);
let c_var = cs.alloc_input(|| "c", || {
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 {
Err(SynthesisError::AssignmentMissing)
}
})?;
cs.enforce(
|| "c_xor_constraint",
|lc| lc + a_var + a_var,
|lc| lc + b_var,
|lc| lc + a_var + b_var - c_var
);
Ok(())
}
}
#[test]
fn test_xordemo() {
let g1 = Fr::one();
let g2 = Fr::one();
let alpha = Fr::from_str("48577").unwrap();
let beta = Fr::from_str("22580").unwrap();
let gamma = Fr::from_str("53332").unwrap();
let delta = Fr::from_str("5481").unwrap();
let tau = Fr::from_str("3673").unwrap();
let params = {
let c = XORDemo::<DummyEngine> {
a: None,
b: None,
_marker: PhantomData
};
generate_parameters(
c,
g1,
g2,
alpha,
beta,
gamma,
delta,
tau
).unwrap()
};
// This will synthesize the constraint system:
//
// public inputs: a_0 = 1, a_1 = c
// aux inputs: a_2 = a, a_3 = b
// constraints:
// (a_0 - a_2) * (a_2) = 0
// (a_0 - a_3) * (a_3) = 0
// (a_2 + a_2) * (a_3) = (a_2 + a_3 - a_1)
// (a_0) * 0 = 0
// (a_1) * 0 = 0
// The evaluation domain is 8. The H query should
// have 7 elements (it's a quotient polynomial)
assert_eq!(7, params.h.len());
let mut root_of_unity = Fr::root_of_unity();
// We expect this to be a 2^10 root of unity
assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 10]));
// Let's turn it into a 2^3 root of unity.
root_of_unity = root_of_unity.pow(&[1 << 7]);
assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 3]));
assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity);
// Let's compute all the points in our evaluation domain.
let mut points = Vec::with_capacity(8);
for i in 0..8 {
points.push(root_of_unity.pow(&[i]));
}
// Let's compute t(tau) = (tau - p_0)(tau - p_1)...
// = tau^8 - 1
let mut t_at_tau = tau.pow(&[8]);
t_at_tau.sub_assign(&Fr::one());
{
let mut tmp = Fr::one();
for p in &points {
let mut term = tau;
term.sub_assign(p);
tmp.mul_assign(&term);
}
assert_eq!(tmp, t_at_tau);
}
// We expect our H query to be 7 elements of the form...
// {tau^i t(tau) / delta}
let delta_inverse = delta.inverse().unwrap();
let gamma_inverse = gamma.inverse().unwrap();
{
let mut coeff = delta_inverse;
coeff.mul_assign(&t_at_tau);
let mut cur = Fr::one();
for h in params.h.iter() {
let mut tmp = cur;
tmp.mul_assign(&coeff);
assert_eq!(*h, tmp);
cur.mul_assign(&tau);
}
}
// The density of the IC query is 2 (2 inputs)
assert_eq!(2, params.vk.ic.len());
// The density of the L query is 2 (2 aux variables)
assert_eq!(2, params.l.len());
// The density of the A query is 4 (each variable is in at least one A term)
assert_eq!(4, params.a.len());
// The density of the B query is 2 (two variables are in at least one B term)
assert_eq!(2, params.b_g1.len());
assert_eq!(2, params.b_g2.len());
/*
Lagrange interpolation polynomials in our evaluation domain:
,-------------------------------. ,-------------------------------. ,-------------------------------.
| A TERM | | B TERM | | C TERM |
`-------------------------------. `-------------------------------' `-------------------------------'
| a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 |
| 1 | 0 | 64512 | 0 | | 0 | 0 | 1 | 0 | | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 64512 | | 0 | 0 | 0 | 1 | | 0 | 0 | 0 | 0 |
| 0 | 0 | 2 | 0 | | 0 | 0 | 0 | 1 | | 0 | 64512 | 1 | 1 |
| 1 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 |
`-------'-------'-------'-------' `-------'-------'-------'-------' `-------'-------'-------'-------'
Example for u_0:
sage: r = 64513
sage: Fr = GF(r)
sage: omega = (Fr(5)^63)^(2^7)
sage: tau = Fr(3673)
sage: R.<x> = PolynomialRing(Fr, 'x')
sage: def eval(tau, c0, c1, c2, c3, c4):
....: p = R.lagrange_polynomial([(omega^0, c0), (omega^1, c1), (omega^2, c2), (omega^3, c3), (omega^4, c4), (omega^5, 0), (omega^6, 0), (omega^7, 0)])
....: return p.substitute(tau)
sage: eval(tau, 1, 1, 0, 1, 0)
59158
*/
let u_i = [59158, 48317, 21767, 10402].iter().map(|e| {
Fr::from_str(&format!("{}", e)).unwrap()
}).collect::<Vec<Fr>>();
let v_i = [0, 0, 60619, 30791].iter().map(|e| {
Fr::from_str(&format!("{}", e)).unwrap()
}).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()
.zip(&params.a[..])
{
assert_eq!(u, a);
}
for (v, b) in v_i.iter()
.filter(|&&e| e != Fr::zero())
.zip(&params.b_g1[..])
{
assert_eq!(v, b);
}
for (v, b) in v_i.iter()
.filter(|&&e| e != Fr::zero())
.zip(&params.b_g2[..])
{
assert_eq!(v, b);
}
for i in 0..4 {
let mut tmp1 = beta;
tmp1.mul_assign(&u_i[i]);
let mut tmp2 = alpha;
tmp2.mul_assign(&v_i[i]);
tmp1.add_assign(&tmp2);
tmp1.add_assign(&w_i[i]);
if i < 2 {
// Check the correctness of the IC query elements
tmp1.mul_assign(&gamma_inverse);
assert_eq!(tmp1, params.vk.ic[i]);
} else {
// Check the correctness of the L query elements
tmp1.mul_assign(&delta_inverse);
assert_eq!(tmp1, params.l[i - 2]);
}
}
// Check consistency of the other elements
assert_eq!(alpha, params.vk.alpha_g1);
assert_eq!(beta, params.vk.beta_g1);
assert_eq!(beta, params.vk.beta_g2);
assert_eq!(gamma, params.vk.gamma_g2);
assert_eq!(delta, params.vk.delta_g1);
assert_eq!(delta, params.vk.delta_g2);
let pvk = prepare_verifying_key(&params.vk);
let r = Fr::from_str("27134").unwrap();
let s = Fr::from_str("17146").unwrap();
let proof = {
let c = XORDemo {
a: Some(true),
b: Some(false),
_marker: PhantomData
};
create_proof(
c,
&params,
r,
s
).unwrap()
};
// A(x) =
// a_0 * (44865*x^7 + 56449*x^6 + 44865*x^5 + 8064*x^4 + 3520*x^3 + 56449*x^2 + 3520*x + 40321) +
// a_1 * (8064*x^7 + 56449*x^6 + 8064*x^5 + 56449*x^4 + 8064*x^3 + 56449*x^2 + 8064*x + 56449) +
// a_2 * (16983*x^7 + 24192*x^6 + 63658*x^5 + 56449*x^4 + 16983*x^3 + 24192*x^2 + 63658*x + 56449) +
// a_3 * (5539*x^7 + 27797*x^6 + 6045*x^5 + 56449*x^4 + 58974*x^3 + 36716*x^2 + 58468*x + 8064) +
{
// proof A = alpha + A(tau) + delta * r
let mut expected_a = delta;
expected_a.mul_assign(&r);
expected_a.add_assign(&alpha);
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[2]); // a_2 = 1
// a_3 = 0
assert_eq!(proof.a, expected_a);
}
// B(x) =
// a_0 * (0) +
// a_1 * (0) +
// a_2 * (56449*x^7 + 56449*x^6 + 56449*x^5 + 56449*x^4 + 56449*x^3 + 56449*x^2 + 56449*x + 56449) +
// a_3 * (31177*x^7 + 44780*x^6 + 21752*x^5 + 42255*x^3 + 35861*x^2 + 33842*x + 48385)
{
// proof B = beta + B(tau) + delta * s
let mut expected_b = delta;
expected_b.mul_assign(&s);
expected_b.add_assign(&beta);
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[2]); // a_2 = 1
// a_3 = 0
assert_eq!(proof.b, expected_b);
}
// C(x) =
// a_0 * (0) +
// a_1 * (27797*x^7 + 56449*x^6 + 36716*x^5 + 8064*x^4 + 27797*x^3 + 56449*x^2 + 36716*x + 8064) +
// a_2 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) +
// a_3 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449)
//
// If A * B = C at each point in the domain, then the following polynomial...
// P(x) = A(x) * B(x) - C(x)
// = 49752*x^14 + 13914*x^13 + 29243*x^12 + 27227*x^11 + 62362*x^10 + 35703*x^9 + 4032*x^8 + 14761*x^6 + 50599*x^5 + 35270*x^4 + 37286*x^3 + 2151*x^2 + 28810*x + 60481
//
// ... should be divisible by t(x), producing the quotient polynomial:
// h(x) = P(x) / t(x)
// = 49752*x^6 + 13914*x^5 + 29243*x^4 + 27227*x^3 + 62362*x^2 + 35703*x + 4032
{
let mut expected_c = Fr::zero();
// A * s
let mut tmp = proof.a;
tmp.mul_assign(&s);
expected_c.add_assign(&tmp);
// B * r
let mut tmp = proof.b;
tmp.mul_assign(&r);
expected_c.add_assign(&tmp);
// delta * r * s
let mut tmp = delta;
tmp.mul_assign(&r);
tmp.mul_assign(&s);
expected_c.sub_assign(&tmp);
// L query answer
// a_2 = 1, a_3 = 0
expected_c.add_assign(&params.l[0]);
// H query answer
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() {
let coeff = Fr::from_str(&format!("{}", coeff)).unwrap();
let mut tmp = params.h[i];
tmp.mul_assign(&coeff);
expected_c.add_assign(&tmp);
}
assert_eq!(expected_c, proof.c);
}
assert!(verify_proof(
&pvk,
&proof,
&[Fr::one()]
).unwrap());
}

View File

@@ -0,0 +1,63 @@
use ff::PrimeField;
use group::{CurveAffine, CurveProjective};
use pairing::{Engine, PairingCurveAffine};
use super::{
Proof,
VerifyingKey,
PreparedVerifyingKey
};
use ::{
SynthesisError
};
pub fn prepare_verifying_key<E: Engine>(
vk: &VerifyingKey<E>
) -> PreparedVerifyingKey<E>
{
let mut gamma = vk.gamma_g2;
gamma.negate();
let mut delta = vk.delta_g2;
delta.negate();
PreparedVerifyingKey {
alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2),
neg_gamma_g2: gamma.prepare(),
neg_delta_g2: delta.prepare(),
ic: vk.ic.clone()
}
}
pub fn verify_proof<'a, E: Engine>(
pvk: &'a PreparedVerifyingKey<E>,
proof: &Proof<E>,
public_inputs: &[E::Fr]
) -> Result<bool, SynthesisError>
{
if (public_inputs.len() + 1) != pvk.ic.len() {
return Err(SynthesisError::MalformedVerifyingKey);
}
let mut acc = pvk.ic[0].into_projective();
for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) {
acc.add_assign(&b.mul(i.into_repr()));
}
// The original verification equation is:
// A * B = alpha * beta + inputs * gamma + C * delta
// ... however, we rearrange it so that it is:
// A * B - inputs * gamma - C * delta = alpha * beta
// or equivalently:
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
// which allows us to do a single final exponentiation.
Ok(E::final_exponentiation(
&E::miller_loop([
(&proof.a.prepare(), &proof.b.prepare()),
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
(&proof.c.prepare(), &pvk.neg_delta_g2)
].into_iter())
).unwrap() == pvk.alpha_g1_beta_g2)
}

433
bellman/src/lib.rs Normal file
View File

@@ -0,0 +1,433 @@
extern crate ff;
extern crate group;
#[cfg(feature = "pairing")]
extern crate pairing;
extern crate rand;
extern crate futures;
extern crate bit_vec;
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;
#[cfg(feature = "groth16")]
pub mod groth16;
use ff::{Field, ScalarEngine};
use std::ops::{Add, Sub};
use std::fmt;
use std::error::Error;
use std::io;
use std::marker::PhantomData;
/// Computations are expressed in terms of arithmetic circuits, in particular
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a
/// circuit that can be synthesized. The `synthesize` method is called during
/// CRS generation and during proving.
pub trait Circuit<E: ScalarEngine> {
/// Synthesize the circuit into a rank-1 quadratic constraint system
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>;
}
/// Represents a variable in our constraint system.
#[derive(Copy, Clone, Debug)]
pub struct Variable(Index);
impl Variable {
/// This constructs a variable with an arbitrary index.
/// Circuit implementations are not recommended to use this.
pub fn new_unchecked(idx: Index) -> Variable {
Variable(idx)
}
/// This returns the index underlying the variable.
/// Circuit implementations are not recommended to use this.
pub fn get_unchecked(&self) -> Index {
self.0
}
}
/// Represents the index of either an input variable or
/// auxiliary variable.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Index {
Input(usize),
Aux(usize)
}
/// This represents a linear combination of some variables, with coefficients
/// in the scalar field of a pairing-friendly elliptic curve group.
#[derive(Clone)]
pub struct LinearCombination<E: ScalarEngine>(Vec<(Variable, E::Fr)>);
impl<E: ScalarEngine> AsRef<[(Variable, E::Fr)]> for LinearCombination<E> {
fn as_ref(&self) -> &[(Variable, E::Fr)] {
&self.0
}
}
impl<E: ScalarEngine> LinearCombination<E> {
pub fn zero() -> LinearCombination<E> {
LinearCombination(vec![])
}
}
impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
self.0.push((var, coeff));
self
}
}
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
coeff.negate();
self + (coeff, var)
}
}
impl<E: ScalarEngine> Add<Variable> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(self, other: Variable) -> LinearCombination<E> {
self + (E::Fr::one(), other)
}
}
impl<E: ScalarEngine> Sub<Variable> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(self, other: Variable) -> LinearCombination<E> {
self - (E::Fr::one(), other)
}
}
impl<'a, E: ScalarEngine> Add<&'a LinearCombination<E>> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
for s in &other.0 {
self = self + (s.1, s.0);
}
self
}
}
impl<'a, E: ScalarEngine> Sub<&'a LinearCombination<E>> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
for s in &other.0 {
self = self - (s.1, s.0);
}
self
}
}
impl<'a, E: ScalarEngine> Add<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
for s in &other.0 {
let mut tmp = s.1;
tmp.mul_assign(&coeff);
self = self + (tmp, s.0);
}
self
}
}
impl<'a, E: ScalarEngine> Sub<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
for s in &other.0 {
let mut tmp = s.1;
tmp.mul_assign(&coeff);
self = self - (tmp, s.0);
}
self
}
}
/// This is an error that could occur during circuit synthesis contexts,
/// such as CRS generation, proving or verification.
#[derive(Debug)]
pub enum SynthesisError {
/// During synthesis, we lacked knowledge of a variable assignment.
AssignmentMissing,
/// During synthesis, we divided by zero.
DivisionByZero,
/// During synthesis, we constructed an unsatisfiable constraint system.
Unsatisfiable,
/// During synthesis, our polynomials ended up being too high of degree
PolynomialDegreeTooLarge,
/// During proof generation, we encountered an identity in the CRS
UnexpectedIdentity,
/// During proof generation, we encountered an I/O error with the CRS
IoError(io::Error),
/// During verification, our verifying key was malformed.
MalformedVerifyingKey,
/// During CRS generation, we observed an unconstrained auxiliary variable
UnconstrainedVariable
}
impl From<io::Error> for SynthesisError {
fn from(e: io::Error) -> SynthesisError {
SynthesisError::IoError(e)
}
}
impl Error for SynthesisError {
fn description(&self) -> &str {
match *self {
SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed",
SynthesisError::DivisionByZero => "division by zero",
SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
SynthesisError::IoError(_) => "encountered an I/O error",
SynthesisError::MalformedVerifyingKey => "malformed verifying key",
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained"
}
}
}
impl fmt::Display for SynthesisError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
if let &SynthesisError::IoError(ref e) = self {
write!(f, "I/O error: ")?;
e.fmt(f)
} else {
write!(f, "{}", self.description())
}
}
}
/// Represents a constraint system which can have new variables
/// allocated and constrains between them formed.
pub trait ConstraintSystem<E: ScalarEngine>: Sized {
/// Represents the type of the "root" of this constraint system
/// so that nested namespaces can minimize indirection.
type Root: ConstraintSystem<E>;
/// Return the "one" input variable
fn one() -> Variable {
Variable::new_unchecked(Index::Input(0))
}
/// Allocate a private variable in the constraint system. The provided function is used to
/// 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
/// namespace.
fn alloc<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
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
/// determine the assignment of the variable.
fn alloc_input<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
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
/// in order to derive a unique name for the constraint in the current namespace.
fn enforce<A, AR, LA, LB, LC>(
&mut self,
annotation: A,
a: LA,
b: LB,
c: LC
)
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
/// for downstream use; use `namespace` instead.
fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR;
/// Exit out of the existing namespace. Not intended for
/// downstream use; use `namespace` instead.
fn pop_namespace(&mut self);
/// Gets the "root" constraint system, bypassing the namespacing.
/// Not intended for downstream use; use `namespace` instead.
fn get_root(&mut self) -> &mut Self::Root;
/// Begin a namespace for this constraint system.
fn namespace<'a, NR, N>(
&'a mut self,
name_fn: N
) -> Namespace<'a, E, Self::Root>
where NR: Into<String>, N: FnOnce() -> NR
{
self.get_root().push_namespace(name_fn);
Namespace(self.get_root(), PhantomData)
}
}
/// This is a "namespaced" constraint system which borrows a constraint system (pushing
/// a namespace context) and, when dropped, pops out of the namespace context.
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E> + 'a>(&'a mut CS, PhantomData<E>);
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, CS> {
type Root = CS::Root;
fn one() -> Variable {
CS::one()
}
fn alloc<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
self.0.alloc(annotation, f)
}
fn alloc_input<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
self.0.alloc_input(annotation, f)
}
fn enforce<A, AR, LA, LB, LC>(
&mut self,
annotation: A,
a: LA,
b: LB,
c: LC
)
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)
}
// Downstream users who use `namespace` will never interact with these
// functions and they will never be invoked because the namespace is
// never a root constraint system.
fn push_namespace<NR, N>(&mut self, _: N)
where NR: Into<String>, N: FnOnce() -> NR
{
panic!("only the root's push_namespace should be called");
}
fn pop_namespace(&mut self)
{
panic!("only the root's pop_namespace should be called");
}
fn get_root(&mut self) -> &mut Self::Root
{
self.0.get_root()
}
}
impl<'a, E: ScalarEngine, CS: ConstraintSystem<E>> Drop for Namespace<'a, E, CS> {
fn drop(&mut self) {
self.get_root().pop_namespace()
}
}
/// Convenience implementation of ConstraintSystem<E> for mutable references to
/// constraint systems.
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for &'cs mut CS {
type Root = CS::Root;
fn one() -> Variable {
CS::one()
}
fn alloc<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
(**self).alloc(annotation, f)
}
fn alloc_input<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
(**self).alloc_input(annotation, f)
}
fn enforce<A, AR, LA, LB, LC>(
&mut self,
annotation: A,
a: LA,
b: LB,
c: LC
)
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)
}
fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR
{
(**self).push_namespace(name_fn)
}
fn pop_namespace(&mut self)
{
(**self).pop_namespace()
}
fn get_root(&mut self) -> &mut Self::Root
{
(**self).get_root()
}
}

168
bellman/src/multicore.rs Normal file
View File

@@ -0,0 +1,168 @@
//! This is an interface for dealing with the kinds of
//! parallel computations involved in bellman. It's
//! currently just a thin wrapper around CpuPool and
//! crossbeam but may be extended in the future to
//! allow for various parallelism strategies.
#[cfg(feature = "multicore")]
mod implementation {
use num_cpus;
use futures::{Future, IntoFuture, Poll};
use futures_cpupool::{CpuPool, CpuFuture};
use crossbeam::{self, Scope};
#[derive(Clone)]
pub struct Worker {
cpus: usize,
pool: CpuPool
}
impl Worker {
// We don't expose this outside the library so that
// all `Worker` instances have the same number of
// CPUs configured.
pub(crate) fn new_with_cpus(cpus: usize) -> Worker {
Worker {
cpus: cpus,
pool: CpuPool::new(cpus)
}
}
pub fn new() -> Worker {
Self::new_with_cpus(num_cpus::get())
}
pub fn log_num_cpus(&self) -> u32 {
log2_floor(self.cpus)
}
pub fn compute<F, R>(
&self, f: F
) -> WorkerFuture<R::Item, R::Error>
where F: FnOnce() -> R + Send + 'static,
R: IntoFuture + 'static,
R::Future: Send + 'static,
R::Item: Send + 'static,
R::Error: Send + 'static
{
WorkerFuture {
future: self.pool.spawn_fn(f)
}
}
pub fn scope<'a, F, R>(
&self,
elements: usize,
f: F
) -> R
where F: FnOnce(&Scope<'a>, usize) -> R
{
let chunk_size = if elements < self.cpus {
1
} else {
elements / self.cpus
};
crossbeam::scope(|scope| {
f(scope, chunk_size)
})
}
}
pub struct WorkerFuture<T, E> {
future: CpuFuture<T, E>
}
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
type Item = T;
type Error = E;
fn poll(&mut self) -> Poll<Self::Item, Self::Error>
{
self.future.poll()
}
}
fn log2_floor(num: usize) -> u32 {
assert!(num > 0);
let mut pow = 0;
while (1 << (pow+1)) <= num {
pow += 1;
}
pow
}
#[test]
fn test_log2_floor() {
assert_eq!(log2_floor(1), 0);
assert_eq!(log2_floor(2), 1);
assert_eq!(log2_floor(3), 1);
assert_eq!(log2_floor(4), 2);
assert_eq!(log2_floor(5), 2);
assert_eq!(log2_floor(6), 2);
assert_eq!(log2_floor(7), 2);
assert_eq!(log2_floor(8), 3);
}
}
#[cfg(not(feature = "multicore"))]
mod implementation {
use futures::{future, Future, IntoFuture, Poll};
#[derive(Clone)]
pub struct Worker;
impl Worker {
pub fn new() -> Worker {
Worker
}
pub fn log_num_cpus(&self) -> u32 {
0
}
pub fn compute<F, R>(&self, f: F) -> R::Future
where
F: FnOnce() -> R + Send + 'static,
R: IntoFuture + 'static,
R::Future: Send + 'static,
R::Item: Send + 'static,
R::Error: Send + 'static,
{
f().into_future()
}
pub fn scope<F, R>(&self, elements: usize, f: F) -> R
where
F: FnOnce(&DummyScope, usize) -> R,
{
f(&DummyScope, elements)
}
}
pub struct WorkerFuture<T, E> {
future: future::FutureResult<T, E>,
}
impl<T: Send + 'static, E: Send + 'static> Future for WorkerFuture<T, E> {
type Item = T;
type Error = E;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.future.poll()
}
}
pub struct DummyScope;
impl DummyScope {
pub fn spawn<F: FnOnce()>(&self, f: F) {
f();
}
}
}
pub use self::implementation::*;

298
bellman/src/multiexp.rs Normal file
View File

@@ -0,0 +1,298 @@
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::SynthesisError;
/// An object that builds a source of bases.
pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
type Source: Source<G>;
fn new(self) -> Self::Source;
}
/// A source of bases, like an iterator.
pub trait Source<G: CurveAffine> {
/// 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>;
/// Skips `amt` elements from the source, avoiding deserialization.
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
}
impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
type Source = (Arc<Vec<G>>, usize);
fn new(self) -> (Arc<Vec<G>>, usize) {
(self.0.clone(), self.1)
}
}
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
fn add_assign_mixed(&mut self, to: &mut <G as CurveAffine>::Projective) -> Result<(), SynthesisError> {
if self.0.len() <= self.1 {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
}
if self.0[self.1].is_zero() {
return Err(SynthesisError::UnexpectedIdentity)
}
to.add_assign_mixed(&self.0[self.1]);
self.1 += 1;
Ok(())
}
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
if self.0.len() <= self.1 {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into());
}
self.1 += amt;
Ok(())
}
}
pub trait QueryDensity {
/// Returns whether the base exists.
type Iter: Iterator<Item=bool>;
fn iter(self) -> Self::Iter;
fn get_query_size(self) -> Option<usize>;
}
#[derive(Clone)]
pub struct FullDensity;
impl AsRef<FullDensity> for FullDensity {
fn as_ref(&self) -> &FullDensity {
self
}
}
impl<'a> QueryDensity for &'a FullDensity {
type Iter = iter::Repeat<bool>;
fn iter(self) -> Self::Iter {
iter::repeat(true)
}
fn get_query_size(self) -> Option<usize> {
None
}
}
pub struct DensityTracker {
bv: BitVec,
total_density: usize
}
impl<'a> QueryDensity for &'a DensityTracker {
type Iter = bit_vec::Iter<'a>;
fn iter(self) -> Self::Iter {
self.bv.iter()
}
fn get_query_size(self) -> Option<usize> {
Some(self.bv.len())
}
}
impl DensityTracker {
pub fn new() -> DensityTracker {
DensityTracker {
bv: BitVec::new(),
total_density: 0
}
}
pub fn add_element(&mut self) {
self.bv.push(false);
}
pub fn inc(&mut self, idx: usize) {
if !self.bv.get(idx).unwrap() {
self.bv.set(idx, true);
self.total_density += 1;
}
}
pub fn get_total_density(&self) -> usize {
self.total_density
}
}
fn multiexp_inner<Q, D, G, S>(
pool: &Worker,
bases: S,
density_map: D,
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
mut skip: u32,
c: u32,
handle_trivial: bool
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine,
S: SourceBuilder<G>
{
// Perform this region of the multiexp
let this = {
let bases = bases.clone();
let exponents = exponents.clone();
let density_map = density_map.clone();
pool.compute(move || {
// Accumulate the result
let mut acc = G::Projective::zero();
// Build a source for the bases
let mut bases = bases.new();
// Create space for the buckets
let mut buckets = vec![<G as CurveAffine>::Projective::zero(); (1 << c) - 1];
let zero = <G::Engine as ScalarEngine>::Fr::zero().into_repr();
let one = <G::Engine as ScalarEngine>::Fr::one().into_repr();
// Sort the bases into buckets
for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) {
if density {
if exp == zero {
bases.skip(1)?;
} else if exp == one {
if handle_trivial {
bases.add_assign_mixed(&mut acc)?;
} else {
bases.skip(1)?;
}
} else {
let mut exp = exp;
exp.shr(skip);
let exp = exp.as_ref()[0] % (1 << c);
if exp != 0 {
bases.add_assign_mixed(&mut buckets[(exp - 1) as usize])?;
} else {
bases.skip(1)?;
}
}
}
}
// Summation by parts
// e.g. 3a + 2b + 1c = a +
// (a) + b +
// ((a) + b) + c
let mut running_sum = G::Projective::zero();
for exp in buckets.into_iter().rev() {
running_sum.add_assign(&exp);
acc.add_assign(&running_sum);
}
Ok(acc)
})
};
skip += c;
if skip >= <G::Engine as ScalarEngine>::Fr::NUM_BITS {
// There isn't another region.
Box::new(this)
} else {
// There's another region more significant. Calculate and join it with
// this region recursively.
Box::new(
this.join(multiexp_inner(pool, bases, density_map, exponents, skip, c, false))
.map(move |(this, mut higher)| {
for _ in 0..c {
higher.double();
}
higher.add_assign(&this);
higher
})
)
}
}
/// Perform multi-exponentiation. The caller is responsible for ensuring the
/// query size is the same as the number of exponents.
pub fn multiexp<Q, D, G, S>(
pool: &Worker,
bases: S,
density_map: D,
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine,
S: SourceBuilder<G>
{
let c = if exponents.len() < 32 {
3u32
} else {
(f64::from(exponents.len() as u32)).ln().ceil() as u32
};
if let Some(query_size) = density_map.as_ref().get_query_size() {
// If the density map has a known query size, it should not be
// inconsistent with the number of exponents.
assert!(query_size == exponents.len());
}
multiexp_inner(pool, bases, density_map, exponents, 0, c, true)
}
#[cfg(feature = "pairing")]
#[test]
fn test_with_bls12() {
fn naive_multiexp<G: CurveAffine>(
bases: Arc<Vec<G>>,
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>
) -> G::Projective
{
assert_eq!(bases.len(), exponents.len());
let mut acc = G::Projective::zero();
for (base, exp) in bases.iter().zip(exponents.iter()) {
acc.add_assign(&base.mul(*exp));
}
acc
}
use rand::{self, Rand};
use pairing::{bls12_381::Bls12, Engine};
const SAMPLES: usize = 1 << 14;
let rng = &mut rand::thread_rng();
let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as ScalarEngine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>());
let g = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::G1::rand(rng).into_affine()).collect::<Vec<_>>());
let naive = naive_multiexp(g.clone(), v.clone());
let pool = Worker::new();
let fast = multiexp(
&pool,
(g, 0),
FullDensity,
v
).wait().unwrap();
assert_eq!(naive, fast);
}

250
bellman/tests/mimc.rs Normal file
View File

@@ -0,0 +1,250 @@
extern crate bellman;
extern crate ff;
extern crate pairing;
extern crate rand;
// For randomness (during paramgen and proof generation)
use rand::{thread_rng, Rng};
// For benchmarking
use std::time::{Duration, Instant};
// Bring in some tools for using pairing-friendly curves
use ff::Field;
use pairing::Engine;
// We're going to use the BLS12-381 pairing-friendly elliptic curve.
use pairing::bls12_381::{
Bls12
};
// We'll use these interfaces to construct our circuit.
use bellman::{
Circuit,
ConstraintSystem,
SynthesisError
};
// We're going to use the Groth16 proving system.
use bellman::groth16::{
Proof,
generate_random_parameters,
prepare_verifying_key,
create_random_proof,
verify_proof,
};
const MIMC_ROUNDS: usize = 322;
/// This is an implementation of MiMC, specifically a
/// variant named `LongsightF322p3` for BLS12-381.
/// See http://eprint.iacr.org/2016/492 for more
/// information about this construction.
///
/// ```
/// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) {
/// for i from 0 up to 321 {
/// xL, xR := xR + (xL + Ci)^3, xL
/// }
/// return xL
/// }
/// ```
fn mimc<E: Engine>(
mut xl: E::Fr,
mut xr: E::Fr,
constants: &[E::Fr]
) -> E::Fr
{
assert_eq!(constants.len(), MIMC_ROUNDS);
for i in 0..MIMC_ROUNDS {
let mut tmp1 = xl;
tmp1.add_assign(&constants[i]);
let mut tmp2 = tmp1;
tmp2.square();
tmp2.mul_assign(&tmp1);
tmp2.add_assign(&xr);
xr = xl;
xl = tmp2;
}
xl
}
/// This is our demo circuit for proving knowledge of the
/// preimage of a MiMC hash invocation.
struct MiMCDemo<'a, E: Engine> {
xl: Option<E::Fr>,
xr: Option<E::Fr>,
constants: &'a [E::Fr]
}
/// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to
/// synthesize the constraint system.
impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
assert_eq!(self.constants.len(), MIMC_ROUNDS);
// Allocate the first component of the preimage.
let mut xl_value = self.xl;
let mut xl = cs.alloc(|| "preimage xl", || {
xl_value.ok_or(SynthesisError::AssignmentMissing)
})?;
// Allocate the second component of the preimage.
let mut xr_value = self.xr;
let mut xr = cs.alloc(|| "preimage xr", || {
xr_value.ok_or(SynthesisError::AssignmentMissing)
})?;
for i in 0..MIMC_ROUNDS {
// xL, xR := xR + (xL + Ci)^3, xL
let cs = &mut cs.namespace(|| format!("round {}", i));
// tmp = (xL + Ci)^2
let mut tmp_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.square();
e
});
let mut tmp = cs.alloc(|| "tmp", || {
tmp_value.ok_or(SynthesisError::AssignmentMissing)
})?;
cs.enforce(
|| "tmp = (xL + Ci)^2",
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + tmp
);
// new_xL = xR + (xL + Ci)^3
// new_xL = xR + tmp * (xL + Ci)
// new_xL - xR = tmp * (xL + Ci)
let mut new_xl_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.mul_assign(&tmp_value.unwrap());
e.add_assign(&xr_value.unwrap());
e
});
let mut new_xl = if i == (MIMC_ROUNDS-1) {
// This is the last round, xL is our image and so
// we allocate a public input.
cs.alloc_input(|| "image", || {
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
})?
} else {
cs.alloc(|| "new_xl", || {
new_xl_value.ok_or(SynthesisError::AssignmentMissing)
})?
};
cs.enforce(
|| "new_xL = xR + (xL + Ci)^3",
|lc| lc + tmp,
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + new_xl - xr
);
// xR = xL
xr = xl;
xr_value = xl_value;
// xL = new_xL
xl = new_xl;
xl_value = new_xl_value;
}
Ok(())
}
}
#[test]
fn test_mimc() {
// This may not be cryptographically safe, use
// `OsRng` (for example) in production software.
let rng = &mut thread_rng();
// Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
println!("Creating parameters...");
// Create parameters for our circuit
let params = {
let c = MiMCDemo::<Bls12> {
xl: None,
xr: None,
constants: &constants
};
generate_random_parameters(c, rng).unwrap()
};
// Prepare the verification key (for proof verification)
let pvk = prepare_verifying_key(&params.vk);
println!("Creating proofs...");
// Let's benchmark stuff!
const SAMPLES: u32 = 50;
let mut total_proving = Duration::new(0, 0);
let mut total_verifying = Duration::new(0, 0);
// Just a place to put the proof data, so we can
// benchmark deserialization.
let mut proof_vec = vec![];
for _ in 0..SAMPLES {
// Generate a random preimage and compute the image
let xl = rng.gen();
let xr = rng.gen();
let image = mimc::<Bls12>(xl, xr, &constants);
proof_vec.truncate(0);
let start = Instant::now();
{
// Create an instance of our circuit (with the
// witness)
let c = MiMCDemo {
xl: Some(xl),
xr: Some(xr),
constants: &constants
};
// Create a groth16 proof with our parameters.
let proof = create_random_proof(c, &params, rng).unwrap();
proof.write(&mut proof_vec).unwrap();
}
total_proving += start.elapsed();
let start = Instant::now();
let proof = Proof::read(&proof_vec[..]).unwrap();
// Check the proof
assert!(verify_proof(
&pvk,
&proof,
&[image]
).unwrap());
total_verifying += start.elapsed();
}
let proving_avg = total_proving / SAMPLES;
let proving_avg = 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 = 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 verifying time: {:?} seconds", verifying_avg);
}

3
ff/.gitignore vendored Normal file
View File

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

18
ff/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "ff"
version = "0.4.0"
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
description = "Library for building and interfacing with finite fields"
documentation = "https://docs.rs/ff/"
homepage = "https://github.com/ebfull/ff"
license = "MIT/Apache-2.0"
repository = "https://github.com/ebfull/ff"
[dependencies]
byteorder = "1"
rand = "0.4"
ff_derive = { version = "0.3.0", path = "ff_derive", optional = true }
[features]
default = []
derive = ["ff_derive"]

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017 Zcash Company
Copyright (c) 2017 Sean Bowe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

60
ff/README.md Normal file
View File

@@ -0,0 +1,60 @@
# ff
`ff` is a finite field library written in pure Rust, with no `unsafe{}` code.
## Disclaimers
* This library does not provide constant-time guarantees.
## Usage
Add the `ff` crate to your `Cargo.toml`:
```toml
[dependencies]
ff = "0.4"
```
The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. See the **[documentation](https://docs.rs/ff/0.4.0/ff/)** for more.
### #![derive(PrimeField)]
If you need an implementation of a prime field, this library also provides a procedural macro that will expand into an efficient implementation of a prime field when supplied with the modulus. `PrimeFieldGenerator` must be an element of Fp of p-1 order, that is also quadratic nonresidue.
First, enable the `derive` crate feature:
```toml
[dependencies]
ff = { version = "0.4", features = ["derive"] }
```
And then use the macro like so:
```rust
extern crate rand;
#[macro_use]
extern crate ff;
#[derive(PrimeField)]
#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"]
#[PrimeFieldGenerator = "7"]
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.
## 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.

20
ff/ff_derive/Cargo.toml Normal file
View File

@@ -0,0 +1,20 @@
[package]
name = "ff_derive"
version = "0.3.0"
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
description = "Procedural macro library used to build custom prime field implementations"
documentation = "https://docs.rs/ff/"
homepage = "https://github.com/ebfull/ff"
license = "MIT/Apache-2.0"
repository = "https://github.com/ebfull/ff"
[lib]
proc-macro = true
[dependencies]
num-bigint = "0.2"
num-traits = "0.2"
num-integer = "0.1"
proc-macro2 = "0.4"
quote = "0.6"
syn = "0.14"

1065
ff/ff_derive/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff

393
ff/src/lib.rs Normal file
View File

@@ -0,0 +1,393 @@
#![allow(unused_imports)]
extern crate byteorder;
extern crate rand;
#[cfg(feature = "derive")]
#[macro_use]
extern crate ff_derive;
#[cfg(feature = "derive")]
pub use ff_derive::*;
use std::error::Error;
use std::fmt;
use std::io::{self, Read, Write};
/// This trait represents an element of a field.
pub trait Field:
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static + rand::Rand
{
/// Returns the zero element of the field, the additive identity.
fn zero() -> Self;
/// Returns the one element of the field, the multiplicative identity.
fn one() -> Self;
/// Returns true iff this element is zero.
fn is_zero(&self) -> bool;
/// Squares this element.
fn square(&mut self);
/// Doubles this element.
fn double(&mut self);
/// Negates this element.
fn negate(&mut self);
/// Adds another element to this element.
fn add_assign(&mut self, other: &Self);
/// Subtracts another element from this element.
fn sub_assign(&mut self, other: &Self);
/// Multiplies another element by this element.
fn mul_assign(&mut self, other: &Self);
/// Computes the multiplicative inverse of this element, if nonzero.
fn inverse(&self) -> Option<Self>;
/// Exponentiates this element by a power of the base prime modulus via
/// the Frobenius automorphism.
fn frobenius_map(&mut self, power: usize);
/// Exponentiates this element by a number represented with `u64` limbs,
/// least significant digit first.
fn pow<S: AsRef<[u64]>>(&self, exp: S) -> Self {
let mut res = Self::one();
let mut found_one = false;
for i in BitIterator::new(exp) {
if found_one {
res.square();
} else {
found_one = i;
}
if i {
res.mul_assign(self);
}
}
res
}
}
/// This trait represents an element of a field that has a square root operation described for it.
pub trait SqrtField: Field {
/// Returns the Legendre symbol of the field element.
fn legendre(&self) -> LegendreSymbol;
/// Returns the square root of the field element, if it is
/// quadratic residue.
fn sqrt(&self) -> Option<Self>;
}
/// This trait represents a wrapper around a biginteger which can encode any element of a particular
/// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit
/// first.
pub trait PrimeFieldRepr:
Sized
+ Copy
+ Clone
+ Eq
+ Ord
+ Send
+ Sync
+ Default
+ fmt::Debug
+ fmt::Display
+ 'static
+ rand::Rand
+ AsRef<[u64]>
+ AsMut<[u64]>
+ From<u64>
{
/// Subtract another represetation from this one.
fn sub_noborrow(&mut self, other: &Self);
/// Add another representation to this one.
fn add_nocarry(&mut self, other: &Self);
/// Compute the number of bits needed to encode this number. Always a
/// multiple of 64.
fn num_bits(&self) -> u32;
/// Returns true iff this number is zero.
fn is_zero(&self) -> bool;
/// Returns true iff this number is odd.
fn is_odd(&self) -> bool;
/// Returns true iff this number is even.
fn is_even(&self) -> bool;
/// Performs a rightwise bitshift of this number, effectively dividing
/// it by 2.
fn div2(&mut self);
/// Performs a rightwise bitshift of this number by some amount.
fn shr(&mut self, amt: u32);
/// Performs a leftwise bitshift of this number, effectively multiplying
/// it by 2. Overflow is ignored.
fn mul2(&mut self);
/// Performs a leftwise bitshift of this number by some amount.
fn shl(&mut self, amt: u32);
/// Writes this `PrimeFieldRepr` as a big endian integer.
fn write_be<W: Write>(&self, mut writer: W) -> io::Result<()> {
use byteorder::{BigEndian, WriteBytesExt};
for digit in self.as_ref().iter().rev() {
writer.write_u64::<BigEndian>(*digit)?;
}
Ok(())
}
/// Reads a big endian integer into this representation.
fn read_be<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
use byteorder::{BigEndian, ReadBytesExt};
for digit in self.as_mut().iter_mut().rev() {
*digit = reader.read_u64::<BigEndian>()?;
}
Ok(())
}
/// Writes this `PrimeFieldRepr` as a little endian integer.
fn write_le<W: Write>(&self, mut writer: W) -> io::Result<()> {
use byteorder::{LittleEndian, WriteBytesExt};
for digit in self.as_ref().iter() {
writer.write_u64::<LittleEndian>(*digit)?;
}
Ok(())
}
/// Reads a little endian integer into this representation.
fn read_le<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
use byteorder::{LittleEndian, ReadBytesExt};
for digit in self.as_mut().iter_mut() {
*digit = reader.read_u64::<LittleEndian>()?;
}
Ok(())
}
}
#[derive(Debug, PartialEq)]
pub enum LegendreSymbol {
Zero = 0,
QuadraticResidue = 1,
QuadraticNonResidue = -1,
}
/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
/// `PrimeField` element.
#[derive(Debug)]
pub enum PrimeFieldDecodingError {
/// The encoded value is not in the field
NotInField(String),
}
impl Error for PrimeFieldDecodingError {
fn description(&self) -> &str {
match *self {
PrimeFieldDecodingError::NotInField(..) => "not an element of the field",
}
}
}
impl fmt::Display for PrimeFieldDecodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
PrimeFieldDecodingError::NotInField(ref repr) => {
write!(f, "{} is not an element of the field", repr)
}
}
}
}
/// This represents an element of a prime field.
pub trait PrimeField: Field {
/// The prime field can be converted back and forth into this biginteger
/// representation.
type Repr: PrimeFieldRepr + From<Self>;
/// Interpret a string of numbers as a (congruent) prime field element.
/// Does not accept unnecessary leading zeroes or a blank string.
fn from_str(s: &str) -> Option<Self> {
if s.is_empty() {
return None;
}
if s == "0" {
return Some(Self::zero());
}
let mut res = Self::zero();
let ten = Self::from_repr(Self::Repr::from(10)).unwrap();
let mut first_digit = true;
for c in s.chars() {
match c.to_digit(10) {
Some(c) => {
if first_digit {
if c == 0 {
return None;
}
first_digit = false;
}
res.mul_assign(&ten);
res.add_assign(&Self::from_repr(Self::Repr::from(u64::from(c))).unwrap());
}
None => {
return None;
}
}
}
Some(res)
}
/// Convert this prime field element into a biginteger representation.
fn from_repr(Self::Repr) -> Result<Self, PrimeFieldDecodingError>;
/// Convert a biginteger representation into a prime field element, if
/// the number is an element of the field.
fn into_repr(&self) -> Self::Repr;
/// Returns the field characteristic; the modulus.
fn char() -> Self::Repr;
/// How many bits are needed to represent an element of this field.
const NUM_BITS: u32;
/// How many bits of information can be reliably stored in the field element.
const CAPACITY: u32;
/// Returns the multiplicative generator of `char()` - 1 order. This element
/// must also be quadratic nonresidue.
fn multiplicative_generator() -> Self;
/// 2^s * t = `char()` - 1 with t odd.
const S: u32;
/// Returns the 2^s root of unity computed by exponentiating the `multiplicative_generator()`
/// by t.
fn root_of_unity() -> Self;
}
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
/// with well-defined relationships. Specific relationships (for example, a
/// pairing-friendly curve) can be defined in a subtrait.
pub trait ScalarEngine: Sized + 'static + Clone {
/// This is the scalar field of the engine's groups.
type Fr: PrimeField + SqrtField;
}
#[derive(Debug)]
pub struct BitIterator<E> {
t: E,
n: usize,
}
impl<E: AsRef<[u64]>> BitIterator<E> {
pub fn new(t: E) -> Self {
let n = t.as_ref().len() * 64;
BitIterator { t, n }
}
}
impl<E: AsRef<[u64]>> Iterator for BitIterator<E> {
type Item = bool;
fn next(&mut self) -> Option<bool> {
if self.n == 0 {
None
} else {
self.n -= 1;
let part = self.n / 64;
let bit = self.n - (64 * part);
Some(self.t.as_ref()[part] & (1 << bit) > 0)
}
}
}
#[test]
fn test_bit_iterator() {
let mut a = BitIterator::new([0xa953d79b83f6ab59, 0x6dea2059e200bd39]);
let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001";
for e in expected.chars() {
assert!(a.next().unwrap() == (e == '1'));
}
assert!(a.next().is_none());
let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001";
let mut a = BitIterator::new([
0x429d5f3ac3a3b759,
0xb10f4c66768b1c92,
0x92368b6d16ecd3b4,
0xa57ea85ae8775219,
]);
for e in expected.chars() {
assert!(a.next().unwrap() == (e == '1'));
}
assert!(a.next().is_none());
}
pub use self::arith_impl::*;
mod arith_impl {
/// Calculate a - b - borrow, returning the result and modifying
/// the borrow value.
#[inline(always)]
pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 {
let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow);
*borrow = if tmp >> 64 == 0 { 1 } else { 0 };
tmp as u64
}
/// Calculate a + b + carry, returning the sum and modifying the
/// carry value.
#[inline(always)]
pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 {
let tmp = u128::from(a) + u128::from(b) + u128::from(*carry);
*carry = (tmp >> 64) as u64;
tmp as u64
}
/// Calculate a + (b * c) + carry, returning the least significant digit
/// and setting carry to the most significant digit.
#[inline(always)]
pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 {
let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry);
*carry = (tmp >> 64) as u64;
tmp as u64
}
}

3
group/.gitignore vendored Normal file
View File

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

14
group/COPYRIGHT Normal file
View File

@@ -0,0 +1,14 @@
Copyrights in the "group" library are retained by their contributors. No
copyright assignment is required to contribute to the "group" library.
The "group" 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.

18
group/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "group"
version = "0.1.0"
authors = [
"The Hush Developers",
"Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>",
]
license = "MIT/Apache-2.0"
description = "Elliptic curve group traits and utilities"
documentation = "https://docs.rs/group/"
homepage = "https://github.com/ebfull/group"
repository = "https://github.com/ebfull/group"
[dependencies]
ff = { path = "../ff" }
rand = "0.4"

201
group/LICENSE-APACHE Normal file
View File

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

23
group/LICENSE-MIT Normal file
View File

@@ -0,0 +1,23 @@
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.

17
group/README.md Normal file
View File

@@ -0,0 +1,17 @@
# group [![Crates.io](https://img.shields.io/crates/v/group.svg)](https://crates.io/crates/group) #
## 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.

196
group/src/lib.rs Normal file
View File

@@ -0,0 +1,196 @@
extern crate ff;
extern crate rand;
use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
use std::error::Error;
use std::fmt;
pub mod tests;
mod wnaf;
pub use self::wnaf::Wnaf;
/// Projective representation of an elliptic curve point guaranteed to be
/// in the correct prime order subgroup.
pub trait CurveProjective:
PartialEq
+ Eq
+ Sized
+ Copy
+ Clone
+ Send
+ Sync
+ fmt::Debug
+ fmt::Display
+ rand::Rand
+ 'static
{
type Engine: ScalarEngine<Fr = Self::Scalar>;
type Scalar: PrimeField + SqrtField;
type Base: SqrtField;
type Affine: CurveAffine<Projective = Self, Scalar = Self::Scalar>;
/// Returns the additive identity.
fn zero() -> Self;
/// Returns a fixed generator of unknown exponent.
fn one() -> Self;
/// Determines if this point is the point at infinity.
fn is_zero(&self) -> bool;
/// Normalizes a slice of projective elements so that
/// conversion to affine is cheap.
fn batch_normalization(v: &mut [Self]);
/// Checks if the point is already "normalized" so that
/// cheap affine conversion is possible.
fn is_normalized(&self) -> bool;
/// Doubles this element.
fn double(&mut self);
/// Adds another element to this element.
fn add_assign(&mut self, other: &Self);
/// Subtracts another element from this element.
fn sub_assign(&mut self, other: &Self) {
let mut tmp = *other;
tmp.negate();
self.add_assign(&tmp);
}
/// Adds an affine element to this element.
fn add_assign_mixed(&mut self, other: &Self::Affine);
/// Negates this element.
fn negate(&mut self);
/// Performs scalar multiplication of this element.
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S);
/// Converts this element into its affine representation.
fn into_affine(&self) -> Self::Affine;
/// Recommends a wNAF window table size given a scalar. Always returns a number
/// between 2 and 22, inclusive.
fn recommended_wnaf_for_scalar(scalar: <Self::Scalar as PrimeField>::Repr) -> usize;
/// Recommends a wNAF window size given the number of scalars you intend to multiply
/// a base by. Always returns a number between 2 and 22, inclusive.
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize;
}
/// Affine representation of an elliptic curve point guaranteed to be
/// in the correct prime order subgroup.
pub trait CurveAffine:
Copy + Clone + Sized + Send + Sync + fmt::Debug + fmt::Display + PartialEq + Eq + 'static
{
type Engine: ScalarEngine<Fr = Self::Scalar>;
type Scalar: PrimeField + SqrtField;
type Base: SqrtField;
type Projective: CurveProjective<Affine = Self, Scalar = Self::Scalar>;
type Uncompressed: EncodedPoint<Affine = Self>;
type Compressed: EncodedPoint<Affine = Self>;
/// Returns the additive identity.
fn zero() -> Self;
/// Returns a fixed generator of unknown exponent.
fn one() -> Self;
/// Determines if this point represents the point at infinity; the
/// additive identity.
fn is_zero(&self) -> bool;
/// Negates this element.
fn negate(&mut self);
/// Performs scalar multiplication of this element with mixed addition.
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective;
/// Converts this element into its affine representation.
fn into_projective(&self) -> Self::Projective;
/// Converts this element into its compressed encoding, so long as it's not
/// the point at infinity.
fn into_compressed(&self) -> Self::Compressed {
<Self::Compressed as EncodedPoint>::from_affine(*self)
}
/// Converts this element into its uncompressed encoding, so long as it's not
/// the point at infinity.
fn into_uncompressed(&self) -> Self::Uncompressed {
<Self::Uncompressed as EncodedPoint>::from_affine(*self)
}
}
/// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`.
pub trait EncodedPoint:
Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + 'static
{
type Affine: CurveAffine;
/// Creates an empty representation.
fn empty() -> Self;
/// Returns the number of bytes consumed by this representation.
fn size() -> usize;
/// Converts an `EncodedPoint` into a `CurveAffine` element,
/// if the encoding represents a valid element.
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError>;
/// Converts an `EncodedPoint` into a `CurveAffine` element,
/// without guaranteeing that the encoding represents a valid
/// element. This is useful when the caller knows the encoding is
/// valid already.
///
/// If the encoding is invalid, this can break API invariants,
/// so caution is strongly encouraged.
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError>;
/// Creates an `EncodedPoint` from an affine point, as long as the
/// point is not the point at infinity.
fn from_affine(affine: Self::Affine) -> Self;
}
/// An error that may occur when trying to decode an `EncodedPoint`.
#[derive(Debug)]
pub enum GroupDecodingError {
/// The coordinate(s) do not lie on the curve.
NotOnCurve,
/// The element is not part of the r-order subgroup.
NotInSubgroup,
/// One of the coordinates could not be decoded
CoordinateDecodingError(&'static str, PrimeFieldDecodingError),
/// The compression mode of the encoded element was not as expected
UnexpectedCompressionMode,
/// The encoding contained bits that should not have been set
UnexpectedInformation,
}
impl Error for GroupDecodingError {
fn description(&self) -> &str {
match *self {
GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve",
GroupDecodingError::NotInSubgroup => "the element is not part of an r-order subgroup",
GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded",
GroupDecodingError::UnexpectedCompressionMode => {
"encoding has unexpected compression mode"
}
GroupDecodingError::UnexpectedInformation => "encoding has unexpected information",
}
}
}
impl fmt::Display for GroupDecodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
GroupDecodingError::CoordinateDecodingError(description, ref err) => {
write!(f, "{} decoding error: {}", description, err)
}
_ => write!(f, "{}", self.description()),
}
}
}

421
group/src/tests/mod.rs Normal file
View File

@@ -0,0 +1,421 @@
use rand::{Rand, Rng, SeedableRng, XorShiftRng};
use {CurveAffine, CurveProjective, EncodedPoint};
pub fn curve_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
// Negation edge case with zero.
{
let mut z = G::zero();
z.negate();
assert!(z.is_zero());
}
// Doubling edge case with zero.
{
let mut z = G::zero();
z.double();
assert!(z.is_zero());
}
// Addition edge cases with zero
{
let mut r = G::rand(&mut rng);
let rcopy = r;
r.add_assign(&G::zero());
assert_eq!(r, rcopy);
r.add_assign_mixed(&G::Affine::zero());
assert_eq!(r, rcopy);
let mut z = G::zero();
z.add_assign(&G::zero());
assert!(z.is_zero());
z.add_assign_mixed(&G::Affine::zero());
assert!(z.is_zero());
let mut z2 = z;
z2.add_assign(&r);
z.add_assign_mixed(&r.into_affine());
assert_eq!(z, z2);
assert_eq!(z, r);
}
// Transformations
{
let a = G::rand(&mut rng);
let b = a.into_affine().into_projective();
let c = a.into_affine()
.into_projective()
.into_affine()
.into_projective();
assert_eq!(a, b);
assert_eq!(b, c);
}
random_addition_tests::<G>();
random_multiplication_tests::<G>();
random_doubling_tests::<G>();
random_negation_tests::<G>();
random_transformation_tests::<G>();
random_wnaf_tests::<G>();
random_encoding_tests::<G::Affine>();
}
fn random_wnaf_tests<G: CurveProjective>() {
use ff::PrimeField;
use wnaf::*;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
{
let mut table = vec![];
let mut wnaf = vec![];
for w in 2..14 {
for _ in 0..100 {
let g = G::rand(&mut rng);
let s = G::Scalar::rand(&mut rng).into_repr();
let mut g1 = g;
g1.mul_assign(s);
wnaf_table(&mut table, g, w);
wnaf_form(&mut wnaf, s, w);
let g2 = wnaf_exp(&table, &wnaf);
assert_eq!(g1, g2);
}
}
}
{
fn only_compiles_if_send<S: Send>(_: &S) {}
for _ in 0..100 {
let g = G::rand(&mut rng);
let s = G::Scalar::rand(&mut rng).into_repr();
let mut g1 = g;
g1.mul_assign(s);
let g2 = {
let mut wnaf = Wnaf::new();
wnaf.base(g, 1).scalar(s)
};
let g3 = {
let mut wnaf = Wnaf::new();
wnaf.scalar(s).base(g)
};
let g4 = {
let mut wnaf = Wnaf::new();
let mut shared = wnaf.base(g, 1).shared();
only_compiles_if_send(&shared);
shared.scalar(s)
};
let g5 = {
let mut wnaf = Wnaf::new();
let mut shared = wnaf.scalar(s).shared();
only_compiles_if_send(&shared);
shared.base(g)
};
let g6 = {
let mut wnaf = Wnaf::new();
{
// Populate the vectors.
wnaf.base(rng.gen(), 1).scalar(rng.gen());
}
wnaf.base(g, 1).scalar(s)
};
let g7 = {
let mut wnaf = Wnaf::new();
{
// Populate the vectors.
wnaf.base(rng.gen(), 1).scalar(rng.gen());
}
wnaf.scalar(s).base(g)
};
let g8 = {
let mut wnaf = Wnaf::new();
{
// Populate the vectors.
wnaf.base(rng.gen(), 1).scalar(rng.gen());
}
let mut shared = wnaf.base(g, 1).shared();
only_compiles_if_send(&shared);
shared.scalar(s)
};
let g9 = {
let mut wnaf = Wnaf::new();
{
// Populate the vectors.
wnaf.base(rng.gen(), 1).scalar(rng.gen());
}
let mut shared = wnaf.scalar(s).shared();
only_compiles_if_send(&shared);
shared.base(g)
};
assert_eq!(g1, g2);
assert_eq!(g1, g3);
assert_eq!(g1, g4);
assert_eq!(g1, g5);
assert_eq!(g1, g6);
assert_eq!(g1, g7);
assert_eq!(g1, g8);
assert_eq!(g1, g9);
}
}
}
fn random_negation_tests<G: CurveProjective>() {
use ff::Field;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let r = G::rand(&mut rng);
let s = G::Scalar::rand(&mut rng);
let mut sneg = s;
sneg.negate();
let mut t1 = r;
t1.mul_assign(s);
let mut t2 = r;
t2.mul_assign(sneg);
let mut t3 = t1;
t3.add_assign(&t2);
assert!(t3.is_zero());
let mut t4 = t1;
t4.add_assign_mixed(&t2.into_affine());
assert!(t4.is_zero());
t1.negate();
assert_eq!(t1, t2);
}
}
fn random_doubling_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let mut a = G::rand(&mut rng);
let mut b = G::rand(&mut rng);
// 2(a + b)
let mut tmp1 = a;
tmp1.add_assign(&b);
tmp1.double();
// 2a + 2b
a.double();
b.double();
let mut tmp2 = a;
tmp2.add_assign(&b);
let mut tmp3 = a;
tmp3.add_assign_mixed(&b.into_affine());
assert_eq!(tmp1, tmp2);
assert_eq!(tmp1, tmp3);
}
}
fn random_multiplication_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let mut a = G::rand(&mut rng);
let mut b = G::rand(&mut rng);
let a_affine = a.into_affine();
let b_affine = b.into_affine();
let s = G::Scalar::rand(&mut rng);
// s ( a + b )
let mut tmp1 = a;
tmp1.add_assign(&b);
tmp1.mul_assign(s);
// sa + sb
a.mul_assign(s);
b.mul_assign(s);
let mut tmp2 = a;
tmp2.add_assign(&b);
// Affine multiplication
let mut tmp3 = a_affine.mul(s);
tmp3.add_assign(&b_affine.mul(s));
assert_eq!(tmp1, tmp2);
assert_eq!(tmp1, tmp3);
}
}
fn random_addition_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let a = G::rand(&mut rng);
let b = G::rand(&mut rng);
let c = G::rand(&mut rng);
let a_affine = a.into_affine();
let b_affine = b.into_affine();
let c_affine = c.into_affine();
// a + a should equal the doubling
{
let mut aplusa = a;
aplusa.add_assign(&a);
let mut aplusamixed = a;
aplusamixed.add_assign_mixed(&a.into_affine());
let mut adouble = a;
adouble.double();
assert_eq!(aplusa, adouble);
assert_eq!(aplusa, aplusamixed);
}
let mut tmp = vec![G::zero(); 6];
// (a + b) + c
tmp[0] = a;
tmp[0].add_assign(&b);
tmp[0].add_assign(&c);
// a + (b + c)
tmp[1] = b;
tmp[1].add_assign(&c);
tmp[1].add_assign(&a);
// (a + c) + b
tmp[2] = a;
tmp[2].add_assign(&c);
tmp[2].add_assign(&b);
// Mixed addition
// (a + b) + c
tmp[3] = a_affine.into_projective();
tmp[3].add_assign_mixed(&b_affine);
tmp[3].add_assign_mixed(&c_affine);
// a + (b + c)
tmp[4] = b_affine.into_projective();
tmp[4].add_assign_mixed(&c_affine);
tmp[4].add_assign_mixed(&a_affine);
// (a + c) + b
tmp[5] = a_affine.into_projective();
tmp[5].add_assign_mixed(&c_affine);
tmp[5].add_assign_mixed(&b_affine);
// Comparisons
for i in 0..6 {
for j in 0..6 {
assert_eq!(tmp[i], tmp[j]);
assert_eq!(tmp[i].into_affine(), tmp[j].into_affine());
}
assert!(tmp[i] != a);
assert!(tmp[i] != b);
assert!(tmp[i] != c);
assert!(a != tmp[i]);
assert!(b != tmp[i]);
assert!(c != tmp[i]);
}
}
}
fn random_transformation_tests<G: CurveProjective>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let g = G::rand(&mut rng);
let g_affine = g.into_affine();
let g_projective = g_affine.into_projective();
assert_eq!(g, g_projective);
}
// Batch normalization
for _ in 0..10 {
let mut v = (0..1000).map(|_| G::rand(&mut rng)).collect::<Vec<_>>();
for i in &v {
assert!(!i.is_normalized());
}
use rand::distributions::{IndependentSample, Range};
let between = Range::new(0, 1000);
// Sprinkle in some normalized points
for _ in 0..5 {
v[between.ind_sample(&mut rng)] = G::zero();
}
for _ in 0..5 {
let s = between.ind_sample(&mut rng);
v[s] = v[s].into_affine().into_projective();
}
let expected_v = v.iter()
.map(|v| v.into_affine().into_projective())
.collect::<Vec<_>>();
G::batch_normalization(&mut v);
for i in &v {
assert!(i.is_normalized());
}
assert_eq!(v, expected_v);
}
}
fn random_encoding_tests<G: CurveAffine>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
assert_eq!(
G::zero().into_uncompressed().into_affine().unwrap(),
G::zero()
);
assert_eq!(
G::zero().into_compressed().into_affine().unwrap(),
G::zero()
);
for _ in 0..1000 {
let mut r = G::Projective::rand(&mut rng).into_affine();
let uncompressed = r.into_uncompressed();
let de_uncompressed = uncompressed.into_affine().unwrap();
assert_eq!(de_uncompressed, r);
let compressed = r.into_compressed();
let de_compressed = compressed.into_affine().unwrap();
assert_eq!(de_compressed, r);
r.negate();
let compressed = r.into_compressed();
let de_compressed = compressed.into_affine().unwrap();
assert_eq!(de_compressed, r);
}
}

181
group/src/wnaf.rs Normal file
View File

@@ -0,0 +1,181 @@
use ff::{PrimeField, PrimeFieldRepr};
use super::CurveProjective;
/// Replaces the contents of `table` with a w-NAF window table for the given window size.
pub(crate) fn wnaf_table<G: CurveProjective>(table: &mut Vec<G>, mut base: G, window: usize) {
table.truncate(0);
table.reserve(1 << (window - 1));
let mut dbl = base;
dbl.double();
for _ in 0..(1 << (window - 1)) {
table.push(base);
base.add_assign(&dbl);
}
}
/// Replaces the contents of `wnaf` with the w-NAF representation of a scalar.
pub(crate) fn wnaf_form<S: PrimeFieldRepr>(wnaf: &mut Vec<i64>, mut c: S, window: usize) {
wnaf.truncate(0);
while !c.is_zero() {
let mut u;
if c.is_odd() {
u = (c.as_ref()[0] % (1 << (window + 1))) as i64;
if u > (1 << window) {
u -= 1 << (window + 1);
}
if u > 0 {
c.sub_noborrow(&S::from(u as u64));
} else {
c.add_nocarry(&S::from((-u) as u64));
}
} else {
u = 0;
}
wnaf.push(u);
c.div2();
}
}
/// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar.
///
/// This function must be provided a `table` and `wnaf` that were constructed with
/// the same window size; otherwise, it may panic or produce invalid results.
pub(crate) fn wnaf_exp<G: CurveProjective>(table: &[G], wnaf: &[i64]) -> G {
let mut result = G::zero();
let mut found_one = false;
for n in wnaf.iter().rev() {
if found_one {
result.double();
}
if *n != 0 {
found_one = true;
if *n > 0 {
result.add_assign(&table[(n / 2) as usize]);
} else {
result.sub_assign(&table[((-n) / 2) as usize]);
}
}
}
result
}
/// A "w-ary non-adjacent form" exponentiation context.
#[derive(Debug)]
pub struct Wnaf<W, B, S> {
base: B,
scalar: S,
window_size: W,
}
impl<G: CurveProjective> Wnaf<(), Vec<G>, Vec<i64>> {
/// Construct a new wNAF context without allocating.
pub fn new() -> Self {
Wnaf {
base: vec![],
scalar: vec![],
window_size: (),
}
}
/// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that
/// can perform exponentiations with `.scalar(..)`.
pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf<usize, &[G], &mut Vec<i64>> {
// Compute the appropriate window size based on the number of scalars.
let window_size = G::recommended_wnaf_for_num_scalars(num_scalars);
// Compute a wNAF table for the provided base and window size.
wnaf_table(&mut self.base, base, window_size);
// Return a Wnaf object that immutably borrows the computed base storage location,
// but mutably borrows the scalar storage location.
Wnaf {
base: &self.base[..],
scalar: &mut self.scalar,
window_size,
}
}
/// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform
/// exponentiations with `.base(..)`.
pub fn scalar(
&mut self,
scalar: <<G as CurveProjective>::Scalar as PrimeField>::Repr,
) -> Wnaf<usize, &mut Vec<G>, &[i64]> {
// Compute the appropriate window size for the scalar.
let window_size = G::recommended_wnaf_for_scalar(scalar);
// Compute the wNAF form of the scalar.
wnaf_form(&mut self.scalar, scalar, window_size);
// Return a Wnaf object that mutably borrows the base storage location, but
// immutably borrows the computed wNAF form scalar location.
Wnaf {
base: &mut self.base,
scalar: &self.scalar[..],
window_size,
}
}
}
impl<'a, G: CurveProjective> Wnaf<usize, &'a [G], &'a mut Vec<i64>> {
/// Constructs new space for the scalar representation while borrowing
/// the computed window table, for sending the window table across threads.
pub fn shared(&self) -> Wnaf<usize, &'a [G], Vec<i64>> {
Wnaf {
base: self.base,
scalar: vec![],
window_size: self.window_size,
}
}
}
impl<'a, G: CurveProjective> Wnaf<usize, &'a mut Vec<G>, &'a [i64]> {
/// Constructs new space for the window table while borrowing
/// the computed scalar representation, for sending the scalar representation
/// across threads.
pub fn shared(&self) -> Wnaf<usize, Vec<G>, &'a [i64]> {
Wnaf {
base: vec![],
scalar: self.scalar,
window_size: self.window_size,
}
}
}
impl<B, S: AsRef<[i64]>> Wnaf<usize, B, S> {
/// Performs exponentiation given a base.
pub fn base<G: CurveProjective>(&mut self, base: G) -> G
where
B: AsMut<Vec<G>>,
{
wnaf_table(self.base.as_mut(), base, self.window_size);
wnaf_exp(self.base.as_mut(), self.scalar.as_ref())
}
}
impl<B, S: AsMut<Vec<i64>>> Wnaf<usize, B, S> {
/// Performs exponentiation given a scalar.
pub fn scalar<G: CurveProjective>(
&mut self,
scalar: <<G as CurveProjective>::Scalar as PrimeField>::Repr,
) -> G
where
B: AsRef<[G]>,
{
wnaf_form(self.scalar.as_mut(), scalar, self.window_size);
wnaf_exp(self.base.as_ref(), self.scalar.as_mut())
}
}

32
librustzcash/Cargo.toml Normal file
View File

@@ -0,0 +1,32 @@
[package]
name = "librustzcash"
version = "0.1.0"
authors = [
"The Hush Developers",
"Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>",
"Jay Graber <jay@z.cash>",
"Simon Liu <simon@z.cash>"
]
[lib]
name = "rustzcash"
path = "src/rustzcash.rs"
crate-type = ["staticlib"]
[dependencies]
bellman = { path = "../bellman" }
ff = { path = "../ff" }
libc = "0.2"
pairing = { path = "../pairing" }
lazy_static = "1"
byteorder = "1"
rand = "0.4"
sapling-crypto = { path = "../sapling-crypto" }
zcash_primitives = { path = "../zcash_primitives" }
zcash_proofs = { path = "../zcash_proofs" }
zip32 = { path = "../zip32" }
[dependencies.blake2-rfc]
git = "https://git.hush.is/hush/blake2-rfc"
rev = "a3adc1d6859b887457c6ae4821ddad1802dad784"

20
librustzcash/README.md Normal file
View File

@@ -0,0 +1,20 @@
# librustzcash
This repository contains librustzcash, a static library for Zcash code assets written in Rust.
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* 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

@@ -4,6 +4,12 @@
#include <stdint.h>
extern "C" {
#ifdef WIN32
typedef uint16_t codeunit;
#else
typedef uint8_t codeunit;
#endif
void librustzcash_to_scalar(const unsigned char *input, unsigned char *result);
void librustzcash_ask_to_ak(const unsigned char *ask, unsigned char *result);
@@ -19,11 +25,14 @@ extern "C" {
/// Loads the zk-SNARK parameters into memory and saves
/// paths as necessary. Only called once.
void librustzcash_init_zksnark_params(
const char* spend_path,
const codeunit* spend_path,
size_t spend_path_len,
const char* spend_hash,
const char* output_path,
const codeunit* output_path,
size_t output_path_len,
const char* output_hash,
const char* sprout_path,
const codeunit* sprout_path,
size_t sprout_path_len,
const char* sprout_hash
);

View File

@@ -149,8 +149,7 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec<u8> {
vout[j + x] = ((
// Big-endian
acc_value >> (acc_bits + (8 * (out_width - x - 1)))
)
& (
) & (
// Apply bit_len_mask across byte boundaries
(bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF
)) as u8;

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12;
use pairing::{PrimeField, PrimeFieldRepr};
use rand::{OsRng, Rng};
use sapling_crypto::jubjub::{edwards, JubjubBls12};
use sapling_crypto::primitives::{Diversifier, ViewingKey};

View File

@@ -1,4 +1,5 @@
use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr};
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12;
use sapling_crypto::{
jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams},
primitives::{Diversifier, ProofGenerationKey},

View File

@@ -1,4 +1,5 @@
use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr};
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::Bls12;
use sapling_crypto::{
jubjub::{FixedGenerators, JubjubEngine},
redjubjub::{PrivateKey, PublicKey, Signature},

3
pairing/.gitignore vendored Normal file
View File

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

14
pairing/COPYRIGHT Normal file
View File

@@ -0,0 +1,14 @@
Copyrights in the "pairing" library are retained by their contributors. No
copyright assignment is required to contribute to the "pairing" library.
The "pairing" 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.

27
pairing/Cargo.toml Normal file
View File

@@ -0,0 +1,27 @@
[package]
name = "pairing"
# Remember to change version string in README.md.
version = "0.14.2"
authors = [
"The Hush Developers",
"Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>",
]
license = "MIT/Apache-2.0"
description = "Pairing-friendly elliptic curve library"
documentation = "https://docs.rs/pairing/"
homepage = "https://github.com/ebfull/pairing"
repository = "https://github.com/ebfull/pairing"
[dependencies]
rand = "0.4"
byteorder = "1"
ff = { path = "../ff", features = ["derive"] }
group = { path = "../group" }
[features]
unstable-features = ["expose-arith"]
expose-arith = []
default = []

201
pairing/LICENSE-APACHE Normal file
View File

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

23
pairing/LICENSE-MIT Normal file
View File

@@ -0,0 +1,23 @@
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.

27
pairing/README.md Normal file
View File

@@ -0,0 +1,27 @@
# 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.
## [Documentation](https://docs.rs/pairing/)
Bring the `pairing` crate into your project just as you normally would.
## Security Warnings
This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks.
## 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

@@ -0,0 +1,127 @@
mod g1 {
use rand::{Rand, SeedableRng, XorShiftRng};
use group::CurveProjective;
use pairing::bls12_381::*;
#[bench]
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(G1, Fr)> = (0..SAMPLES)
.map(|_| (G1::rand(&mut rng), Fr::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.mul_assign(v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(G1, G1)> = (0..SAMPLES)
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(G1, G1Affine)> = (0..SAMPLES)
.map(|_| (G1::rand(&mut rng), G1::rand(&mut rng).into()))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_assign_mixed(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
}
mod g2 {
use rand::{Rand, SeedableRng, XorShiftRng};
use group::CurveProjective;
use pairing::bls12_381::*;
#[bench]
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(G2, Fr)> = (0..SAMPLES)
.map(|_| (G2::rand(&mut rng), Fr::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.mul_assign(v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(G2, G2)> = (0..SAMPLES)
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(G2, G2Affine)> = (0..SAMPLES)
.map(|_| (G2::rand(&mut rng), G2::rand(&mut rng).into()))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_assign_mixed(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
}

View File

@@ -0,0 +1,268 @@
use rand::{Rand, SeedableRng, XorShiftRng};
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
use pairing::bls12_381::*;
#[bench]
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
.map(|_| {
let mut tmp1 = FqRepr::rand(&mut rng);
let mut tmp2 = FqRepr::rand(&mut rng);
// Shave a few bits off to avoid overflow.
for _ in 0..3 {
tmp1.div2();
tmp2.div2();
}
(tmp1, tmp2)
})
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_nocarry(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES)
.map(|_| {
let tmp1 = FqRepr::rand(&mut rng);
let mut tmp2 = tmp1;
// Ensure tmp2 is smaller than tmp1.
for _ in 0..10 {
tmp2.div2();
}
(tmp1, tmp2)
})
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.sub_noborrow(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let tmp = v[count].num_bits();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.mul2();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<FqRepr> = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.div2();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.sub_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fq, Fq)> = (0..SAMPLES)
.map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.mul_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_square(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.square();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_inverse(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
count = (count + 1) % SAMPLES;
v[count].inverse()
});
}
#[bench]
fn bench_fq_negate(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.negate();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq> = (0..SAMPLES)
.map(|_| {
let mut tmp = Fq::rand(&mut rng);
tmp.square();
tmp
})
.collect();
let mut count = 0;
b.iter(|| {
count = (count + 1) % SAMPLES;
v[count].sqrt()
});
}
#[bench]
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
count = (count + 1) % SAMPLES;
v[count].into_repr()
});
}
#[bench]
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<FqRepr> = (0..SAMPLES)
.map(|_| Fq::rand(&mut rng).into_repr())
.collect();
let mut count = 0;
b.iter(|| {
count = (count + 1) % SAMPLES;
Fq::from_repr(v[count])
});
}

View File

@@ -0,0 +1,94 @@
use rand::{Rand, SeedableRng, XorShiftRng};
use ff::Field;
use pairing::bls12_381::*;
#[bench]
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.sub_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fq12, Fq12)> = (0..SAMPLES)
.map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.mul_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq12_squaring(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.square();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let tmp = v[count].inverse();
count = (count + 1) % SAMPLES;
tmp
});
}

View File

@@ -0,0 +1,110 @@
use rand::{Rand, SeedableRng, XorShiftRng};
use ff::{Field, SqrtField};
use pairing::bls12_381::*;
#[bench]
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.sub_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fq2, Fq2)> = (0..SAMPLES)
.map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.mul_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq2_squaring(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.square();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let tmp = v[count].inverse();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let tmp = v[count].sqrt();
count = (count + 1) % SAMPLES;
tmp
});
}

View File

@@ -0,0 +1,268 @@
use rand::{Rand, SeedableRng, XorShiftRng};
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
use pairing::bls12_381::*;
#[bench]
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
.map(|_| {
let mut tmp1 = FrRepr::rand(&mut rng);
let mut tmp2 = FrRepr::rand(&mut rng);
// Shave a few bits off to avoid overflow.
for _ in 0..3 {
tmp1.div2();
tmp2.div2();
}
(tmp1, tmp2)
})
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_nocarry(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES)
.map(|_| {
let tmp1 = FrRepr::rand(&mut rng);
let mut tmp2 = tmp1;
// Ensure tmp2 is smaller than tmp1.
for _ in 0..10 {
tmp2.div2();
}
(tmp1, tmp2)
})
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.sub_noborrow(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let tmp = v[count].num_bits();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.mul2();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<FrRepr> = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.div2();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.add_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.sub_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(Fr, Fr)> = (0..SAMPLES)
.map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count].0;
tmp.mul_assign(&v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_square(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.square();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_inverse(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
count = (count + 1) % SAMPLES;
v[count].inverse()
});
}
#[bench]
fn bench_fr_negate(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let mut tmp = v[count];
tmp.negate();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fr> = (0..SAMPLES)
.map(|_| {
let mut tmp = Fr::rand(&mut rng);
tmp.square();
tmp
})
.collect();
let mut count = 0;
b.iter(|| {
count = (count + 1) % SAMPLES;
v[count].sqrt()
});
}
#[bench]
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
count = (count + 1) % SAMPLES;
v[count].into_repr()
});
}
#[bench]
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<FrRepr> = (0..SAMPLES)
.map(|_| Fr::rand(&mut rng).into_repr())
.collect();
let mut count = 0;
b.iter(|| {
count = (count + 1) % SAMPLES;
Fr::from_repr(v[count])
});
}

View File

@@ -0,0 +1,107 @@
mod ec;
mod fq;
mod fq12;
mod fq2;
mod fr;
use rand::{Rand, SeedableRng, XorShiftRng};
use pairing::bls12_381::*;
use pairing::{Engine, PairingCurveAffine};
#[bench]
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let tmp = G1Affine::from(v[count]).prepare();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::rand(&mut rng)).collect();
let mut count = 0;
b.iter(|| {
let tmp = G2Affine::from(v[count]).prepare();
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES)
.map(|_| {
(
G1Affine::from(G1::rand(&mut rng)).prepare(),
G2Affine::from(G2::rand(&mut rng)).prepare(),
)
})
.collect();
let mut count = 0;
b.iter(|| {
let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<Fq12> = (0..SAMPLES)
.map(|_| {
(
G1Affine::from(G1::rand(&mut rng)).prepare(),
G2Affine::from(G2::rand(&mut rng)).prepare(),
)
})
.map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)]))
.collect();
let mut count = 0;
b.iter(|| {
let tmp = Bls12::final_exponentiation(&v[count]);
count = (count + 1) % SAMPLES;
tmp
});
}
#[bench]
fn bench_pairing_full(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let v: Vec<(G1, G2)> = (0..SAMPLES)
.map(|_| (G1::rand(&mut rng), G2::rand(&mut rng)))
.collect();
let mut count = 0;
b.iter(|| {
let tmp = Bls12::pairing(v[count].0, v[count].1);
count = (count + 1) % SAMPLES;
tmp
});
}

View File

@@ -0,0 +1,9 @@
#![feature(test)]
extern crate ff;
extern crate group;
extern crate pairing;
extern crate rand;
extern crate test;
mod bls12_381;

View File

@@ -0,0 +1,71 @@
# BLS12-381
This is an implementation of the BLS12-381 pairing-friendly elliptic curve construction.
## BLS12 Parameterization
BLS12 curves are parameterized by a value *x* such that the base field modulus *q* and subgroup *r* can be computed by:
* q = (x - 1)<sup>2</sup> ((x<sup>4</sup> - x<sup>2</sup> + 1) / 3) + x
* r = (x<sup>4</sup> - x<sup>2</sup> + 1)
Given primes *q* and *r* parameterized as above, we can easily construct an elliptic curve over the prime field F<sub>*q*</sub> which contains a subgroup of order *r* such that *r* | (*q*<sup>12</sup> - 1), giving it an embedding degree of 12. Instantiating its sextic twist over an extension field F<sub>q<sup>2</sup></sub> gives rise to an efficient bilinear pairing function between elements of the order *r* subgroups of either curves, into an order *r* multiplicative subgroup of F<sub>q<sup>12</sup></sub>.
In zk-SNARK schemes, we require F<sub>r</sub> with large 2<sup>n</sup> roots of unity for performing efficient fast-fourier transforms. As such, guaranteeing that large 2<sup>n</sup> | (r - 1), or equivalently that *x* has a large 2<sup>n</sup> factor, gives rise to BLS12 curves suitable for zk-SNARKs.
Due to recent research, it is estimated by many that *q* should be approximately 384 bits to target 128-bit security. Conveniently, *r* is approximately 256 bits when *q* is approximately 384 bits, making BLS12 curves ideal for 128-bit security. It also makes them ideal for many zk-SNARK applications, as the scalar field can be used for keying material such as embedded curve constructions.
Many curves match our descriptions, but we require some extra properties for efficiency purposes:
* *q* should be smaller than 2<sup>383</sup>, and *r* should be smaller than 2<sup>255</sup>, so that the most significant bit is unset when using 64-bit or 32-bit limbs. This allows for cheap reductions.
* F<sub>q<sup>12</sup></sub> is typically constructed using towers of extension fields. As a byproduct of [research](https://eprint.iacr.org/2011/465.pdf) for BLS curves of embedding degree 24, we can identify subfamilies of BLS12 curves (for our purposes, where x mod 72 = {16, 64}) that produce efficient extension field towers and twisting isomorphisms.
* We desire *x* of small Hamming weight, to increase the performance of the pairing function.
## BLS12-381 Instantiation
The BLS12-381 construction is instantiated by `x = -0xd201000000010000`, which produces the largest `q` and smallest Hamming weight of `x` that meets the above requirements. This produces:
* q = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` (381 bits)
* r = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` (255 bits)
Our extension field tower is constructed as follows:
1. F<sub>q<sup>2</sup></sub> is constructed as F<sub>q</sub>(u) / (u<sup>2</sup> - β) where β = -1.
2. F<sub>q<sup>6</sup></sub> is constructed as F<sub>q<sup>2</sup></sub>(v) / (v<sup>3</sup> - ξ) where ξ = u + 1
3. F<sub>q<sup>12</sup></sub> is constructed as F<sub>q<sup>6</sup></sub>(w) / (w<sup>2</sup> - γ) where γ = v
Now, we instantiate the elliptic curve E(F<sub>q</sub>) : y<sup>2</sup> = x<sup>3</sup> + 4, and the elliptic curve E'(F<sub>q<sup>2</sup></sub>) : y<sup>2</sup> = x<sup>3</sup> + 4(u + 1).
The group G<sub>1</sub> is the *r* order subgroup of E, which has cofactor (x - 1)<sup>2</sup> / 3. The group G<sub>2</sub> is the *r* order subgroup of E', which has cofactor (x<sup>8</sup> - 4x<sup>7</sup> + 5x<sup>6</sup> - 4x<sup>4</sup> + 6x<sup>3</sup> - 4x<sup>2</sup> - 4x + 13) / 9.
### Generators
The generators of G<sub>1</sub> and G<sub>2</sub> are computed by finding the lexicographically smallest valid `x`-coordinate, and its lexicographically smallest `y`-coordinate and scaling it by the cofactor such that the result is not the point at infinity.
#### G1
```
x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
```
#### G2
```
x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
```
### Serialization
* Fq elements are encoded in big-endian form. They occupy 48 bytes in this form.
* Fq2 elements are encoded in big-endian form, meaning that the Fq element c0 + c1 * u is represented by the Fq element c1 followed by the Fq element c0. This means Fq2 elements occupy 96 bytes in this form.
* The group G1 uses Fq elements for coordinates. The group G2 uses Fq2 elements for coordinates.
* G1 and G2 elements can be encoded in uncompressed form (the x-coordinate followed by the y-coordinate) or in compressed form (just the x-coordinate). G1 elements occupy 96 bytes in uncompressed form, and 48 bytes in compressed form. G2 elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed form.
The most-significant three bits of a G1 or G2 encoding should be masked away before the coordinate(s) are interpreted. These bits are used to unambiguously represent the underlying element:
* The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form.
* The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero.
* The third-most significant bit is set if (and only if) this point is in compressed form _and_ it is not the point at infinity _and_ its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate.

2030
pairing/src/bls12_381/ec.rs Normal file

File diff suppressed because it is too large Load Diff

2246
pairing/src/bls12_381/fq.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,189 @@
use super::fq::FROBENIUS_COEFF_FQ12_C1;
use super::fq2::Fq2;
use super::fq6::Fq6;
use ff::Field;
use rand::{Rand, Rng};
/// An element of Fq12, represented by c0 + c1 * w.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Fq12 {
pub c0: Fq6,
pub c1: Fq6,
}
impl ::std::fmt::Display for Fq12 {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
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 {
pub fn conjugate(&mut self) {
self.c1.negate();
}
pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) {
let mut aa = self.c0;
aa.mul_by_01(c0, c1);
let mut bb = self.c1;
bb.mul_by_1(c4);
let mut o = *c1;
o.add_assign(c4);
self.c1.add_assign(&self.c0);
self.c1.mul_by_01(c0, &o);
self.c1.sub_assign(&aa);
self.c1.sub_assign(&bb);
self.c0 = bb;
self.c0.mul_by_nonresidue();
self.c0.add_assign(&aa);
}
}
impl Field for Fq12 {
fn zero() -> Self {
Fq12 {
c0: Fq6::zero(),
c1: Fq6::zero(),
}
}
fn one() -> Self {
Fq12 {
c0: Fq6::one(),
c1: Fq6::zero(),
}
}
fn is_zero(&self) -> bool {
self.c0.is_zero() && self.c1.is_zero()
}
fn double(&mut self) {
self.c0.double();
self.c1.double();
}
fn negate(&mut self) {
self.c0.negate();
self.c1.negate();
}
fn add_assign(&mut self, other: &Self) {
self.c0.add_assign(&other.c0);
self.c1.add_assign(&other.c1);
}
fn sub_assign(&mut self, other: &Self) {
self.c0.sub_assign(&other.c0);
self.c1.sub_assign(&other.c1);
}
fn frobenius_map(&mut self, power: usize) {
self.c0.frobenius_map(power);
self.c1.frobenius_map(power);
self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
}
fn square(&mut self) {
let mut ab = self.c0;
ab.mul_assign(&self.c1);
let mut c0c1 = self.c0;
c0c1.add_assign(&self.c1);
let mut c0 = self.c1;
c0.mul_by_nonresidue();
c0.add_assign(&self.c0);
c0.mul_assign(&c0c1);
c0.sub_assign(&ab);
self.c1 = ab;
self.c1.add_assign(&ab);
ab.mul_by_nonresidue();
c0.sub_assign(&ab);
self.c0 = c0;
}
fn mul_assign(&mut self, other: &Self) {
let mut aa = self.c0;
aa.mul_assign(&other.c0);
let mut bb = self.c1;
bb.mul_assign(&other.c1);
let mut o = other.c0;
o.add_assign(&other.c1);
self.c1.add_assign(&self.c0);
self.c1.mul_assign(&o);
self.c1.sub_assign(&aa);
self.c1.sub_assign(&bb);
self.c0 = bb;
self.c0.mul_by_nonresidue();
self.c0.add_assign(&aa);
}
fn inverse(&self) -> Option<Self> {
let mut c0s = self.c0;
c0s.square();
let mut c1s = self.c1;
c1s.square();
c1s.mul_by_nonresidue();
c0s.sub_assign(&c1s);
c0s.inverse().map(|t| {
let mut tmp = Fq12 { c0: t, c1: t };
tmp.c0.mul_assign(&self.c0);
tmp.c1.mul_assign(&self.c1);
tmp.c1.negate();
tmp
})
}
}
#[cfg(test)]
use rand::{SeedableRng, XorShiftRng};
#[test]
fn test_fq12_mul_by_014() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let c0 = Fq2::rand(&mut rng);
let c1 = Fq2::rand(&mut rng);
let c5 = Fq2::rand(&mut rng);
let mut a = Fq12::rand(&mut rng);
let mut b = a;
a.mul_by_014(&c0, &c1, &c5);
b.mul_assign(&Fq12 {
c0: Fq6 {
c0: c0,
c1: c1,
c2: Fq2::zero(),
},
c1: Fq6 {
c0: Fq2::zero(),
c1: c5,
c2: Fq2::zero(),
},
});
assert_eq!(a, b);
}
}
#[test]
fn fq12_field_tests() {
use ff::PrimeField;
::tests::field::random_field_tests::<Fq12>();
::tests::field::random_frobenius_tests::<Fq12, _>(super::fq::Fq::char(), 13);
}

View File

@@ -0,0 +1,910 @@
use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE};
use ff::{Field, SqrtField};
use rand::{Rand, Rng};
use std::cmp::Ordering;
/// An element of Fq2, represented by c0 + c1 * u.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Fq2 {
pub c0: Fq,
pub c1: Fq,
}
impl ::std::fmt::Display for Fq2 {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "Fq2({} + {} * u)", self.c0, self.c1)
}
}
/// `Fq2` elements are ordered lexicographically.
impl Ord for Fq2 {
#[inline(always)]
fn cmp(&self, other: &Fq2) -> Ordering {
match self.c1.cmp(&other.c1) {
Ordering::Greater => Ordering::Greater,
Ordering::Less => Ordering::Less,
Ordering::Equal => self.c0.cmp(&other.c0),
}
}
}
impl PartialOrd for Fq2 {
#[inline(always)]
fn partial_cmp(&self, other: &Fq2) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Fq2 {
/// Multiply this element by the cubic and quadratic nonresidue 1 + u.
pub fn mul_by_nonresidue(&mut self) {
let t0 = self.c0;
self.c0.sub_assign(&self.c1);
self.c1.add_assign(&t0);
}
/// Norm of Fq2 as extension field in i over Fq
pub fn norm(&self) -> Fq {
let mut t0 = self.c0;
let mut t1 = self.c1;
t0.square();
t1.square();
t1.add_assign(&t0);
t1
}
}
impl Rand for Fq2 {
fn rand<R: Rng>(rng: &mut R) -> Self {
Fq2 {
c0: rng.gen(),
c1: rng.gen(),
}
}
}
impl Field for Fq2 {
fn zero() -> Self {
Fq2 {
c0: Fq::zero(),
c1: Fq::zero(),
}
}
fn one() -> Self {
Fq2 {
c0: Fq::one(),
c1: Fq::zero(),
}
}
fn is_zero(&self) -> bool {
self.c0.is_zero() && self.c1.is_zero()
}
fn square(&mut self) {
let mut ab = self.c0;
ab.mul_assign(&self.c1);
let mut c0c1 = self.c0;
c0c1.add_assign(&self.c1);
let mut c0 = self.c1;
c0.negate();
c0.add_assign(&self.c0);
c0.mul_assign(&c0c1);
c0.sub_assign(&ab);
self.c1 = ab;
self.c1.add_assign(&ab);
c0.add_assign(&ab);
self.c0 = c0;
}
fn double(&mut self) {
self.c0.double();
self.c1.double();
}
fn negate(&mut self) {
self.c0.negate();
self.c1.negate();
}
fn add_assign(&mut self, other: &Self) {
self.c0.add_assign(&other.c0);
self.c1.add_assign(&other.c1);
}
fn sub_assign(&mut self, other: &Self) {
self.c0.sub_assign(&other.c0);
self.c1.sub_assign(&other.c1);
}
fn mul_assign(&mut self, other: &Self) {
let mut aa = self.c0;
aa.mul_assign(&other.c0);
let mut bb = self.c1;
bb.mul_assign(&other.c1);
let mut o = other.c0;
o.add_assign(&other.c1);
self.c1.add_assign(&self.c0);
self.c1.mul_assign(&o);
self.c1.sub_assign(&aa);
self.c1.sub_assign(&bb);
self.c0 = aa;
self.c0.sub_assign(&bb);
}
fn inverse(&self) -> Option<Self> {
let mut t1 = self.c1;
t1.square();
let mut t0 = self.c0;
t0.square();
t0.add_assign(&t1);
t0.inverse().map(|t| {
let mut tmp = Fq2 {
c0: self.c0,
c1: self.c1,
};
tmp.c0.mul_assign(&t);
tmp.c1.mul_assign(&t);
tmp.c1.negate();
tmp
})
}
fn frobenius_map(&mut self, power: usize) {
self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]);
}
}
impl SqrtField for Fq2 {
fn legendre(&self) -> ::ff::LegendreSymbol {
self.norm().legendre()
}
fn sqrt(&self) -> Option<Self> {
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf
if self.is_zero() {
Some(Self::zero())
} else {
// a1 = self^((q - 3) / 4)
let mut a1 = self.pow([
0xee7fbfffffffeaaa,
0x7aaffffac54ffff,
0xd9cc34a83dac3d89,
0xd91dd2e13ce144af,
0x92c6e9ed90d2eb35,
0x680447a8e5ff9a6,
]);
let mut alpha = a1;
alpha.square();
alpha.mul_assign(self);
let mut a0 = alpha;
a0.frobenius_map(1);
a0.mul_assign(&alpha);
let neg1 = Fq2 {
c0: NEGATIVE_ONE,
c1: Fq::zero(),
};
if a0 == neg1 {
None
} else {
a1.mul_assign(self);
if alpha == neg1 {
a1.mul_assign(&Fq2 {
c0: Fq::zero(),
c1: Fq::one(),
});
} else {
alpha.add_assign(&Fq2::one());
// alpha = alpha^((q - 1) / 2)
alpha = alpha.pow([
0xdcff7fffffffd555,
0xf55ffff58a9ffff,
0xb39869507b587b12,
0xb23ba5c279c2895f,
0x258dd3db21a5d66b,
0xd0088f51cbff34d,
]);
a1.mul_assign(&alpha);
}
Some(a1)
}
}
}
}
#[test]
fn test_fq2_ordering() {
let mut a = Fq2 {
c0: Fq::zero(),
c1: Fq::zero(),
};
let mut b = a.clone();
assert!(a.cmp(&b) == Ordering::Equal);
b.c0.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Less);
a.c0.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Equal);
b.c1.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Less);
a.c0.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Less);
a.c1.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Greater);
b.c0.add_assign(&Fq::one());
assert!(a.cmp(&b) == Ordering::Equal);
}
#[test]
fn test_fq2_basics() {
assert_eq!(
Fq2 {
c0: Fq::zero(),
c1: Fq::zero(),
},
Fq2::zero()
);
assert_eq!(
Fq2 {
c0: Fq::one(),
c1: Fq::zero(),
},
Fq2::one()
);
assert!(Fq2::zero().is_zero());
assert!(!Fq2::one().is_zero());
assert!(
!Fq2 {
c0: Fq::zero(),
c1: Fq::one(),
}.is_zero()
);
}
#[test]
fn test_fq2_squaring() {
use super::fq::FqRepr;
use ff::PrimeField;
let mut a = Fq2 {
c0: Fq::one(),
c1: Fq::one(),
}; // u + 1
a.square();
assert_eq!(
a,
Fq2 {
c0: Fq::zero(),
c1: Fq::from_repr(FqRepr::from(2)).unwrap(),
}
); // 2u
let mut a = Fq2 {
c0: Fq::zero(),
c1: Fq::one(),
}; // u
a.square();
assert_eq!(a, {
let mut neg1 = Fq::one();
neg1.negate();
Fq2 {
c0: neg1,
c1: Fq::zero(),
}
}); // -1
let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([
0x9c2c6309bbf8b598,
0x4eef5c946536f602,
0x90e34aab6fb6a6bd,
0xf7f295a94e58ae7c,
0x41b76dcc1c3fbe5e,
0x7080c5fa1d8e042,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x38f473b3c870a4ab,
0x6ad3291177c8c7e5,
0xdac5a4c911a4353e,
0xbfb99020604137a0,
0xfc58a7b7be815407,
0x10d1615e75250a21,
])).unwrap(),
};
a.square();
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0xf262c28c538bcf68,
0xb9f2a66eae1073ba,
0xdc46ab8fad67ae0,
0xcb674157618da176,
0x4cf17b5893c3d327,
0x7eac81369c43361
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0xc1579cf58e980cf8,
0xa23eb7e12dd54d98,
0xe75138bce4cec7aa,
0x38d0d7275a9689e1,
0x739c983042779a65,
0x1542a61c8a8db994
])).unwrap(),
}
);
}
#[test]
fn test_fq2_mul() {
use super::fq::FqRepr;
use ff::PrimeField;
let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([
0x85c9f989e1461f03,
0xa2e33c333449a1d6,
0x41e461154a7354a3,
0x9ee53e7e84d7532e,
0x1c202d8ed97afb45,
0x51d3f9253e2516f,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf,
0x143c215d8176b319,
0x4cc48081c09b8903,
0x9533e4a9a5158be,
0x7a5e1ecb676d65f9,
0x180c3ee46656b008,
])).unwrap(),
};
a.mul_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([
0xe21f9169805f537e,
0xfc87e62e179c285d,
0x27ece175be07a531,
0xcd460f9f0c23e430,
0x6c9110292bfa409,
0x2c93a72eb8af83e,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x4b1c3f936d8992d4,
0x1d2a72916dba4c8a,
0x8871c508658d1e5f,
0x57a06d3135a752ae,
0x634cd3c6c565096d,
0x19e17334d4e93558,
])).unwrap(),
});
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x95b5127e6360c7e4,
0xde29c31a19a6937e,
0xf61a96dacf5a39bc,
0x5511fe4d84ee5f78,
0x5310a202d92f9963,
0x1751afbe166e5399
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x84af0e1bd630117a,
0x6c63cd4da2c2aa7,
0x5ba6e5430e883d40,
0xc975106579c275ee,
0x33a9ac82ce4c5083,
0x1ef1a36c201589d
])).unwrap(),
}
);
}
#[test]
fn test_fq2_inverse() {
use super::fq::FqRepr;
use ff::PrimeField;
assert!(Fq2::zero().inverse().is_none());
let a = Fq2 {
c0: Fq::from_repr(FqRepr([
0x85c9f989e1461f03,
0xa2e33c333449a1d6,
0x41e461154a7354a3,
0x9ee53e7e84d7532e,
0x1c202d8ed97afb45,
0x51d3f9253e2516f,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0xa7348a8b511aedcf,
0x143c215d8176b319,
0x4cc48081c09b8903,
0x9533e4a9a5158be,
0x7a5e1ecb676d65f9,
0x180c3ee46656b008,
])).unwrap(),
};
let a = a.inverse().unwrap();
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x70300f9bcb9e594,
0xe5ecda5fdafddbb2,
0x64bef617d2915a8f,
0xdfba703293941c30,
0xa6c3d8f9586f2636,
0x1351ef01941b70c4
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x8c39fd76a8312cb4,
0x15d7b6b95defbff0,
0x947143f89faedee9,
0xcbf651a0f367afb2,
0xdf4e54f0d3ef15a6,
0x103bdf241afb0019
])).unwrap(),
}
);
}
#[test]
fn test_fq2_addition() {
use super::fq::FqRepr;
use ff::PrimeField;
let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([
0x2d0078036923ffc7,
0x11e59ea221a3b6d2,
0x8b1a52e0a90f59ed,
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
0xbe203411c66fb3a5,
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
};
a.add_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([
0x619a02d78dc70ef2,
0xb93adfc9119e33e8,
0x4bf0b99a9f0dca12,
0x3b88899a42a6318f,
0x986a4a62fa82a49d,
0x13ce433fa26027f5,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9,
0xa1379b6facf6e596,
0x402aef1fb797e32f,
0x2236f55246d0d44d,
0x4c8c1800eb104566,
0x11d6e20e986c2085,
])).unwrap(),
});
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x8e9a7adaf6eb0eb9,
0xcb207e6b3341eaba,
0xd70b0c7b481d23ff,
0xf4ef57d604b6bca2,
0x65309427b3d5d090,
0x14c715d5553f01d2
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0xfdb032e7d9079a94,
0x35a2809d15468d83,
0xfe4b23317e0796d5,
0xd62fa51334f560fa,
0x9ad265eb46e01984,
0x1303f3465112c8bc
])).unwrap(),
}
);
}
#[test]
fn test_fq2_subtraction() {
use super::fq::FqRepr;
use ff::PrimeField;
let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([
0x2d0078036923ffc7,
0x11e59ea221a3b6d2,
0x8b1a52e0a90f59ed,
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
0xbe203411c66fb3a5,
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
};
a.sub_assign(&Fq2 {
c0: Fq::from_repr(FqRepr([
0x619a02d78dc70ef2,
0xb93adfc9119e33e8,
0x4bf0b99a9f0dca12,
0x3b88899a42a6318f,
0x986a4a62fa82a49d,
0x13ce433fa26027f5,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x66323bf80b58b9b9,
0xa1379b6facf6e596,
0x402aef1fb797e32f,
0x2236f55246d0d44d,
0x4c8c1800eb104566,
0x11d6e20e986c2085,
])).unwrap(),
});
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x8565752bdb5c9b80,
0x7756bed7c15982e9,
0xa65a6be700b285fe,
0xe255902672ef6c43,
0x7f77a718021c342d,
0x72ba14049fe9881
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0xeb4abaf7c255d1cd,
0x11df49bc6cacc256,
0xe52617930588c69a,
0xf63905f39ad8cb1f,
0x4cd5dd9fb40b3b8f,
0x957411359ba6e4c
])).unwrap(),
}
);
}
#[test]
fn test_fq2_negation() {
use super::fq::FqRepr;
use ff::PrimeField;
let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([
0x2d0078036923ffc7,
0x11e59ea221a3b6d2,
0x8b1a52e0a90f59ed,
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
0xbe203411c66fb3a5,
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
};
a.negate();
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x8cfe87fc96dbaae4,
0xcc6615c8fb0492d,
0xdc167fc04da19c37,
0xab107d49317487ab,
0x7e555df189f880e3,
0x19083f5486a10cbd
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x228109103250c9d0,
0x8a411ad149045812,
0xa9109e8f3041427e,
0xb07e9bc405608611,
0xfcd559cbe77bd8b8,
0x18d400b280d93e62
])).unwrap(),
}
);
}
#[test]
fn test_fq2_doubling() {
use super::fq::FqRepr;
use ff::PrimeField;
let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([
0x2d0078036923ffc7,
0x11e59ea221a3b6d2,
0x8b1a52e0a90f59ed,
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
0xbe203411c66fb3a5,
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
};
a.double();
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x5a00f006d247ff8e,
0x23cb3d4443476da4,
0x1634a5c1521eb3da,
0x72cd9c7784211627,
0x998c938972a657e7,
0x1f1a52b65bdb3b9
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x2efbeddf9b5dc1b6,
0x28d5ca5ad09f4fdb,
0x7c4068238cdf674b,
0x67f15f81dc49195b,
0x9c8c9bd4b79fa83d,
0x25a226f714d506e
])).unwrap(),
}
);
}
#[test]
fn test_fq2_frobenius_map() {
use super::fq::FqRepr;
use ff::PrimeField;
let mut a = Fq2 {
c0: Fq::from_repr(FqRepr([
0x2d0078036923ffc7,
0x11e59ea221a3b6d2,
0x8b1a52e0a90f59ed,
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc,
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
0xbe203411c66fb3a5,
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837,
])).unwrap(),
};
a.frobenius_map(0);
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x2d0078036923ffc7,
0x11e59ea221a3b6d2,
0x8b1a52e0a90f59ed,
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
0xbe203411c66fb3a5,
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837
])).unwrap(),
}
);
a.frobenius_map(1);
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x2d0078036923ffc7,
0x11e59ea221a3b6d2,
0x8b1a52e0a90f59ed,
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x228109103250c9d0,
0x8a411ad149045812,
0xa9109e8f3041427e,
0xb07e9bc405608611,
0xfcd559cbe77bd8b8,
0x18d400b280d93e62
])).unwrap(),
}
);
a.frobenius_map(1);
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x2d0078036923ffc7,
0x11e59ea221a3b6d2,
0x8b1a52e0a90f59ed,
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
0xbe203411c66fb3a5,
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837
])).unwrap(),
}
);
a.frobenius_map(2);
assert_eq!(
a,
Fq2 {
c0: Fq::from_repr(FqRepr([
0x2d0078036923ffc7,
0x11e59ea221a3b6d2,
0x8b1a52e0a90f59ed,
0xb966ce3bc2108b13,
0xccc649c4b9532bf3,
0xf8d295b2ded9dc
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0x977df6efcdaee0db,
0x946ae52d684fa7ed,
0xbe203411c66fb3a5,
0xb3f8afc0ee248cad,
0x4e464dea5bcfd41e,
0x12d1137b8a6a837
])).unwrap(),
}
);
}
#[test]
fn test_fq2_sqrt() {
use super::fq::FqRepr;
use ff::PrimeField;
assert_eq!(
Fq2 {
c0: Fq::from_repr(FqRepr([
0x476b4c309720e227,
0x34c2d04faffdab6,
0xa57e6fc1bab51fd9,
0xdb4a116b5bf74aa1,
0x1e58b2159dfe10e2,
0x7ca7da1f13606ac
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0xfa8de88b7516d2c3,
0x371a75ed14f41629,
0x4cec2dca577a3eb6,
0x212611bca4e99121,
0x8ee5394d77afb3d,
0xec92336650e49d5
])).unwrap(),
}.sqrt()
.unwrap(),
Fq2 {
c0: Fq::from_repr(FqRepr([
0x40b299b2704258c5,
0x6ef7de92e8c68b63,
0x6d2ddbe552203e82,
0x8d7f1f723d02c1d3,
0x881b3e01b611c070,
0x10f6963bbad2ebc5
])).unwrap(),
c1: Fq::from_repr(FqRepr([
0xc099534fc209e752,
0x7670594665676447,
0x28a20faed211efe7,
0x6b852aeaf2afcb1b,
0xa4c93b08105d71a9,
0x8d7cfff94216330
])).unwrap(),
}
);
assert_eq!(
Fq2 {
c0: Fq::from_repr(FqRepr([
0xb9f78429d1517a6b,
0x1eabfffeb153ffff,
0x6730d2a0f6b0f624,
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a
])).unwrap(),
c1: Fq::zero(),
}.sqrt()
.unwrap(),
Fq2 {
c0: Fq::zero(),
c1: Fq::from_repr(FqRepr([
0xb9fefffffd4357a3,
0x1eabfffeb153ffff,
0x6730d2a0f6b0f624,
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a
])).unwrap(),
}
);
}
#[test]
fn test_fq2_legendre() {
use ff::LegendreSymbol::*;
assert_eq!(Zero, Fq2::zero().legendre());
// i^2 = -1
let mut m1 = Fq2::one();
m1.negate();
assert_eq!(QuadraticResidue, m1.legendre());
m1.mul_by_nonresidue();
assert_eq!(QuadraticNonResidue, m1.legendre());
}
#[cfg(test)]
use rand::{SeedableRng, XorShiftRng};
#[test]
fn test_fq2_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let nqr = Fq2 {
c0: Fq::one(),
c1: Fq::one(),
};
for _ in 0..1000 {
let mut a = Fq2::rand(&mut rng);
let mut b = a;
a.mul_by_nonresidue();
b.mul_assign(&nqr);
assert_eq!(a, b);
}
}
#[test]
fn fq2_field_tests() {
use ff::PrimeField;
::tests::field::random_field_tests::<Fq2>();
::tests::field::random_sqrt_tests::<Fq2>();
::tests::field::random_frobenius_tests::<Fq2, _>(super::fq::Fq::char(), 13);
}

View File

@@ -0,0 +1,374 @@
use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2};
use super::fq2::Fq2;
use ff::Field;
use rand::{Rand, Rng};
/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2).
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Fq6 {
pub c0: Fq2,
pub c1: Fq2,
pub c2: Fq2,
}
impl ::std::fmt::Display for Fq6 {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
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 {
/// Multiply by quadratic nonresidue v.
pub fn mul_by_nonresidue(&mut self) {
use std::mem::swap;
swap(&mut self.c0, &mut self.c1);
swap(&mut self.c0, &mut self.c2);
self.c0.mul_by_nonresidue();
}
pub fn mul_by_1(&mut self, c1: &Fq2) {
let mut b_b = self.c1;
b_b.mul_assign(c1);
let mut t1 = *c1;
{
let mut tmp = self.c1;
tmp.add_assign(&self.c2);
t1.mul_assign(&tmp);
t1.sub_assign(&b_b);
t1.mul_by_nonresidue();
}
let mut t2 = *c1;
{
let mut tmp = self.c0;
tmp.add_assign(&self.c1);
t2.mul_assign(&tmp);
t2.sub_assign(&b_b);
}
self.c0 = t1;
self.c1 = t2;
self.c2 = b_b;
}
pub fn mul_by_01(&mut self, c0: &Fq2, c1: &Fq2) {
let mut a_a = self.c0;
let mut b_b = self.c1;
a_a.mul_assign(c0);
b_b.mul_assign(c1);
let mut t1 = *c1;
{
let mut tmp = self.c1;
tmp.add_assign(&self.c2);
t1.mul_assign(&tmp);
t1.sub_assign(&b_b);
t1.mul_by_nonresidue();
t1.add_assign(&a_a);
}
let mut t3 = *c0;
{
let mut tmp = self.c0;
tmp.add_assign(&self.c2);
t3.mul_assign(&tmp);
t3.sub_assign(&a_a);
t3.add_assign(&b_b);
}
let mut t2 = *c0;
t2.add_assign(c1);
{
let mut tmp = self.c0;
tmp.add_assign(&self.c1);
t2.mul_assign(&tmp);
t2.sub_assign(&a_a);
t2.sub_assign(&b_b);
}
self.c0 = t1;
self.c1 = t2;
self.c2 = t3;
}
}
impl Field for Fq6 {
fn zero() -> Self {
Fq6 {
c0: Fq2::zero(),
c1: Fq2::zero(),
c2: Fq2::zero(),
}
}
fn one() -> Self {
Fq6 {
c0: Fq2::one(),
c1: Fq2::zero(),
c2: Fq2::zero(),
}
}
fn is_zero(&self) -> bool {
self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero()
}
fn double(&mut self) {
self.c0.double();
self.c1.double();
self.c2.double();
}
fn negate(&mut self) {
self.c0.negate();
self.c1.negate();
self.c2.negate();
}
fn add_assign(&mut self, other: &Self) {
self.c0.add_assign(&other.c0);
self.c1.add_assign(&other.c1);
self.c2.add_assign(&other.c2);
}
fn sub_assign(&mut self, other: &Self) {
self.c0.sub_assign(&other.c0);
self.c1.sub_assign(&other.c1);
self.c2.sub_assign(&other.c2);
}
fn frobenius_map(&mut self, power: usize) {
self.c0.frobenius_map(power);
self.c1.frobenius_map(power);
self.c2.frobenius_map(power);
self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]);
self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]);
}
fn square(&mut self) {
let mut s0 = self.c0;
s0.square();
let mut ab = self.c0;
ab.mul_assign(&self.c1);
let mut s1 = ab;
s1.double();
let mut s2 = self.c0;
s2.sub_assign(&self.c1);
s2.add_assign(&self.c2);
s2.square();
let mut bc = self.c1;
bc.mul_assign(&self.c2);
let mut s3 = bc;
s3.double();
let mut s4 = self.c2;
s4.square();
self.c0 = s3;
self.c0.mul_by_nonresidue();
self.c0.add_assign(&s0);
self.c1 = s4;
self.c1.mul_by_nonresidue();
self.c1.add_assign(&s1);
self.c2 = s1;
self.c2.add_assign(&s2);
self.c2.add_assign(&s3);
self.c2.sub_assign(&s0);
self.c2.sub_assign(&s4);
}
fn mul_assign(&mut self, other: &Self) {
let mut a_a = self.c0;
let mut b_b = self.c1;
let mut c_c = self.c2;
a_a.mul_assign(&other.c0);
b_b.mul_assign(&other.c1);
c_c.mul_assign(&other.c2);
let mut t1 = other.c1;
t1.add_assign(&other.c2);
{
let mut tmp = self.c1;
tmp.add_assign(&self.c2);
t1.mul_assign(&tmp);
t1.sub_assign(&b_b);
t1.sub_assign(&c_c);
t1.mul_by_nonresidue();
t1.add_assign(&a_a);
}
let mut t3 = other.c0;
t3.add_assign(&other.c2);
{
let mut tmp = self.c0;
tmp.add_assign(&self.c2);
t3.mul_assign(&tmp);
t3.sub_assign(&a_a);
t3.add_assign(&b_b);
t3.sub_assign(&c_c);
}
let mut t2 = other.c0;
t2.add_assign(&other.c1);
{
let mut tmp = self.c0;
tmp.add_assign(&self.c1);
t2.mul_assign(&tmp);
t2.sub_assign(&a_a);
t2.sub_assign(&b_b);
c_c.mul_by_nonresidue();
t2.add_assign(&c_c);
}
self.c0 = t1;
self.c1 = t2;
self.c2 = t3;
}
fn inverse(&self) -> Option<Self> {
let mut c0 = self.c2;
c0.mul_by_nonresidue();
c0.mul_assign(&self.c1);
c0.negate();
{
let mut c0s = self.c0;
c0s.square();
c0.add_assign(&c0s);
}
let mut c1 = self.c2;
c1.square();
c1.mul_by_nonresidue();
{
let mut c01 = self.c0;
c01.mul_assign(&self.c1);
c1.sub_assign(&c01);
}
let mut c2 = self.c1;
c2.square();
{
let mut c02 = self.c0;
c02.mul_assign(&self.c2);
c2.sub_assign(&c02);
}
let mut tmp1 = self.c2;
tmp1.mul_assign(&c1);
let mut tmp2 = self.c1;
tmp2.mul_assign(&c2);
tmp1.add_assign(&tmp2);
tmp1.mul_by_nonresidue();
tmp2 = self.c0;
tmp2.mul_assign(&c0);
tmp1.add_assign(&tmp2);
match tmp1.inverse() {
Some(t) => {
let mut tmp = Fq6 {
c0: t,
c1: t,
c2: t,
};
tmp.c0.mul_assign(&c0);
tmp.c1.mul_assign(&c1);
tmp.c2.mul_assign(&c2);
Some(tmp)
}
None => None,
}
}
}
#[cfg(test)]
use rand::{SeedableRng, XorShiftRng};
#[test]
fn test_fq6_mul_nonresidue() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let nqr = Fq6 {
c0: Fq2::zero(),
c1: Fq2::one(),
c2: Fq2::zero(),
};
for _ in 0..1000 {
let mut a = Fq6::rand(&mut rng);
let mut b = a;
a.mul_by_nonresidue();
b.mul_assign(&nqr);
assert_eq!(a, b);
}
}
#[test]
fn test_fq6_mul_by_1() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let c1 = Fq2::rand(&mut rng);
let mut a = Fq6::rand(&mut rng);
let mut b = a;
a.mul_by_1(&c1);
b.mul_assign(&Fq6 {
c0: Fq2::zero(),
c1: c1,
c2: Fq2::zero(),
});
assert_eq!(a, b);
}
}
#[test]
fn test_fq6_mul_by_01() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let c0 = Fq2::rand(&mut rng);
let c1 = Fq2::rand(&mut rng);
let mut a = Fq6::rand(&mut rng);
let mut b = a;
a.mul_by_01(&c0, &c1);
b.mul_assign(&Fq6 {
c0: c0,
c1: c1,
c2: Fq2::zero(),
});
assert_eq!(a, b);
}
}
#[test]
fn fq6_field_tests() {
use ff::PrimeField;
::tests::field::random_field_tests::<Fq6>();
::tests::field::random_frobenius_tests::<Fq6, _>(super::fq::Fq::char(), 13);
}

986
pairing/src/bls12_381/fr.rs Normal file
View File

@@ -0,0 +1,986 @@
use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr};
#[derive(PrimeField)]
#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"]
#[PrimeFieldGenerator = "7"]
pub struct Fr(FrRepr);
#[cfg(test)]
use rand::{Rand, SeedableRng, XorShiftRng};
#[test]
fn test_fr_repr_ordering() {
fn assert_equality(a: FrRepr, b: FrRepr) {
assert_eq!(a, b);
assert!(a.cmp(&b) == ::std::cmp::Ordering::Equal);
}
fn assert_lt(a: FrRepr, b: FrRepr) {
assert!(a < b);
assert!(b > a);
}
assert_equality(
FrRepr([9999, 9999, 9999, 9999]),
FrRepr([9999, 9999, 9999, 9999]),
);
assert_equality(
FrRepr([9999, 9998, 9999, 9999]),
FrRepr([9999, 9998, 9999, 9999]),
);
assert_equality(
FrRepr([9999, 9999, 9999, 9997]),
FrRepr([9999, 9999, 9999, 9997]),
);
assert_lt(
FrRepr([9999, 9997, 9999, 9998]),
FrRepr([9999, 9997, 9999, 9999]),
);
assert_lt(
FrRepr([9999, 9997, 9998, 9999]),
FrRepr([9999, 9997, 9999, 9999]),
);
assert_lt(
FrRepr([9, 9999, 9999, 9997]),
FrRepr([9999, 9999, 9999, 9997]),
);
}
#[test]
fn test_fr_repr_from() {
assert_eq!(FrRepr::from(100), FrRepr([100, 0, 0, 0]));
}
#[test]
fn test_fr_repr_is_odd() {
assert!(!FrRepr::from(0).is_odd());
assert!(FrRepr::from(0).is_even());
assert!(FrRepr::from(1).is_odd());
assert!(!FrRepr::from(1).is_even());
assert!(!FrRepr::from(324834872).is_odd());
assert!(FrRepr::from(324834872).is_even());
assert!(FrRepr::from(324834873).is_odd());
assert!(!FrRepr::from(324834873).is_even());
}
#[test]
fn test_fr_repr_is_zero() {
assert!(FrRepr::from(0).is_zero());
assert!(!FrRepr::from(1).is_zero());
assert!(!FrRepr([0, 0, 1, 0]).is_zero());
}
#[test]
fn test_fr_repr_div2() {
let mut a = FrRepr([
0xbd2920b19c972321,
0x174ed0466a3be37e,
0xd468d5e3b551f0b5,
0xcb67c072733beefc,
]);
a.div2();
assert_eq!(
a,
FrRepr([
0x5e949058ce4b9190,
0x8ba76823351df1bf,
0x6a346af1daa8f85a,
0x65b3e039399df77e
])
);
for _ in 0..10 {
a.div2();
}
assert_eq!(
a,
FrRepr([
0x6fd7a524163392e4,
0x16a2e9da08cd477c,
0xdf9a8d1abc76aa3e,
0x196cf80e4e677d
])
);
for _ in 0..200 {
a.div2();
}
assert_eq!(a, FrRepr([0x196cf80e4e67, 0x0, 0x0, 0x0]));
for _ in 0..40 {
a.div2();
}
assert_eq!(a, FrRepr([0x19, 0x0, 0x0, 0x0]));
for _ in 0..4 {
a.div2();
}
assert_eq!(a, FrRepr([0x1, 0x0, 0x0, 0x0]));
a.div2();
assert!(a.is_zero());
}
#[test]
fn test_fr_repr_shr() {
let mut a = FrRepr([
0xb33fbaec482a283f,
0x997de0d3a88cb3df,
0x9af62d2a9a0e5525,
0x36003ab08de70da1,
]);
a.shr(0);
assert_eq!(
a,
FrRepr([
0xb33fbaec482a283f,
0x997de0d3a88cb3df,
0x9af62d2a9a0e5525,
0x36003ab08de70da1
])
);
a.shr(1);
assert_eq!(
a,
FrRepr([
0xd99fdd762415141f,
0xccbef069d44659ef,
0xcd7b16954d072a92,
0x1b001d5846f386d0
])
);
a.shr(50);
assert_eq!(
a,
FrRepr([
0xbc1a7511967bf667,
0xc5a55341caa4b32f,
0x75611bce1b4335e,
0x6c0
])
);
a.shr(130);
assert_eq!(a, FrRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0]));
a.shr(64);
assert_eq!(a, FrRepr([0x1b0, 0x0, 0x0, 0x0]));
}
#[test]
fn test_fr_repr_mul2() {
let mut a = FrRepr::from(23712937547);
a.mul2();
assert_eq!(a, FrRepr([0xb0acd6c96, 0x0, 0x0, 0x0]));
for _ in 0..60 {
a.mul2();
}
assert_eq!(a, FrRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0]));
for _ in 0..128 {
a.mul2();
}
assert_eq!(a, FrRepr([0x0, 0x0, 0x6000000000000000, 0xb0acd6c9]));
for _ in 0..60 {
a.mul2();
}
assert_eq!(a, FrRepr([0x0, 0x0, 0x0, 0x9600000000000000]));
for _ in 0..7 {
a.mul2();
}
assert!(a.is_zero());
}
#[test]
fn test_fr_repr_num_bits() {
let mut a = FrRepr::from(0);
assert_eq!(0, a.num_bits());
a = FrRepr::from(1);
for i in 1..257 {
assert_eq!(i, a.num_bits());
a.mul2();
}
assert_eq!(0, a.num_bits());
}
#[test]
fn test_fr_repr_sub_noborrow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut t = FrRepr([
0x8e62a7e85264e2c3,
0xb23d34c1941d3ca,
0x5976930b7502dd15,
0x600f3fb517bf5495,
]);
t.sub_noborrow(&FrRepr([
0xd64f669809cbc6a4,
0xfa76cb9d90cf7637,
0xfefb0df9038d43b3,
0x298a30c744b31acf,
]));
assert!(
t == FrRepr([
0xb813415048991c1f,
0x10ad07ae88725d92,
0x5a7b851271759961,
0x36850eedd30c39c5
])
);
for _ in 0..1000 {
let mut a = FrRepr::rand(&mut rng);
a.0[3] >>= 30;
let mut b = a;
for _ in 0..10 {
b.mul2();
}
let mut c = b;
for _ in 0..10 {
c.mul2();
}
assert!(a < b);
assert!(b < c);
let mut csub_ba = c;
csub_ba.sub_noborrow(&b);
csub_ba.sub_noborrow(&a);
let mut csub_ab = c;
csub_ab.sub_noborrow(&a);
csub_ab.sub_noborrow(&b);
assert_eq!(csub_ab, csub_ba);
}
// Subtracting r+1 from r should produce -1 (mod 2**256)
let mut qplusone = FrRepr([
0xffffffff00000001,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48,
]);
qplusone.sub_noborrow(&FrRepr([
0xffffffff00000002,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48,
]));
assert_eq!(
qplusone,
FrRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff
])
);
}
#[test]
fn test_fr_legendre() {
use ff::LegendreSymbol::*;
use ff::SqrtField;
assert_eq!(QuadraticResidue, Fr::one().legendre());
assert_eq!(Zero, Fr::zero().legendre());
let e = FrRepr([
0x0dbc5349cd5664da,
0x8ac5b6296e3ae29d,
0x127cb819feceaa3b,
0x3a6b21fb03867191,
]);
assert_eq!(QuadraticResidue, Fr::from_repr(e).unwrap().legendre());
let e = FrRepr([
0x96341aefd047c045,
0x9b5f4254500a4d65,
0x1ee08223b68ac240,
0x31d9cd545c0ec7c6,
]);
assert_eq!(QuadraticNonResidue, Fr::from_repr(e).unwrap().legendre());
}
#[test]
fn test_fr_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let mut t = FrRepr([
0xd64f669809cbc6a4,
0xfa76cb9d90cf7637,
0xfefb0df9038d43b3,
0x298a30c744b31acf,
]);
t.add_nocarry(&FrRepr([
0x8e62a7e85264e2c3,
0xb23d34c1941d3ca,
0x5976930b7502dd15,
0x600f3fb517bf5495,
]));
assert_eq!(
t,
FrRepr([
0x64b20e805c30a967,
0x59a9ee9aa114a02,
0x5871a104789020c9,
0x8999707c5c726f65
])
);
// Test for the associativity of addition.
for _ in 0..1000 {
let mut a = FrRepr::rand(&mut rng);
let mut b = FrRepr::rand(&mut rng);
let mut c = FrRepr::rand(&mut rng);
// Unset the first few bits, so that overflow won't occur.
a.0[3] >>= 3;
b.0[3] >>= 3;
c.0[3] >>= 3;
let mut abc = a;
abc.add_nocarry(&b);
abc.add_nocarry(&c);
let mut acb = a;
acb.add_nocarry(&c);
acb.add_nocarry(&b);
let mut bac = b;
bac.add_nocarry(&a);
bac.add_nocarry(&c);
let mut bca = b;
bca.add_nocarry(&c);
bca.add_nocarry(&a);
let mut cab = c;
cab.add_nocarry(&a);
cab.add_nocarry(&b);
let mut cba = c;
cba.add_nocarry(&b);
cba.add_nocarry(&a);
assert_eq!(abc, acb);
assert_eq!(abc, bac);
assert_eq!(abc, bca);
assert_eq!(abc, cab);
assert_eq!(abc, cba);
}
// Adding 1 to (2^256 - 1) should produce zero
let mut x = FrRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
]);
x.add_nocarry(&FrRepr::from(1));
assert!(x.is_zero());
}
#[test]
fn test_fr_is_valid() {
let mut a = Fr(MODULUS);
assert!(!a.is_valid());
a.0.sub_noborrow(&FrRepr::from(1));
assert!(a.is_valid());
assert!(Fr(FrRepr::from(0)).is_valid());
assert!(
Fr(FrRepr([
0xffffffff00000000,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48
])).is_valid()
);
assert!(
!Fr(FrRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff
])).is_valid()
);
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let a = Fr::rand(&mut rng);
assert!(a.is_valid());
}
}
#[test]
fn test_fr_add_assign() {
{
// Random number
let mut tmp = Fr(FrRepr([
0x437ce7616d580765,
0xd42d1ccb29d1235b,
0xed8f753821bd1423,
0x4eede1c9c89528ca,
]));
assert!(tmp.is_valid());
// Test that adding zero has no effect.
tmp.add_assign(&Fr(FrRepr::from(0)));
assert_eq!(
tmp,
Fr(FrRepr([
0x437ce7616d580765,
0xd42d1ccb29d1235b,
0xed8f753821bd1423,
0x4eede1c9c89528ca
]))
);
// Add one and test for the result.
tmp.add_assign(&Fr(FrRepr::from(1)));
assert_eq!(
tmp,
Fr(FrRepr([
0x437ce7616d580766,
0xd42d1ccb29d1235b,
0xed8f753821bd1423,
0x4eede1c9c89528ca
]))
);
// Add another random number that exercises the reduction.
tmp.add_assign(&Fr(FrRepr([
0x946f435944f7dc79,
0xb55e7ee6533a9b9b,
0x1e43b84c2f6194ca,
0x58717ab525463496,
])));
assert_eq!(
tmp,
Fr(FrRepr([
0xd7ec2abbb24fe3de,
0x35cdf7ae7d0d62f7,
0xd899557c477cd0e9,
0x3371b52bc43de018
]))
);
// Add one to (r - 1) and test for the result.
tmp = Fr(FrRepr([
0xffffffff00000000,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48,
]));
tmp.add_assign(&Fr(FrRepr::from(1)));
assert!(tmp.0.is_zero());
// Add a random number to another one such that the result is r - 1
tmp = Fr(FrRepr([
0xade5adacdccb6190,
0xaa21ee0f27db3ccd,
0x2550f4704ae39086,
0x591d1902e7c5ba27,
]));
tmp.add_assign(&Fr(FrRepr([
0x521a525223349e70,
0xa99bb5f3d8231f31,
0xde8e397bebe477e,
0x1ad08e5041d7c321,
])));
assert_eq!(
tmp,
Fr(FrRepr([
0xffffffff00000000,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48
]))
);
// Add one to the result and test for it.
tmp.add_assign(&Fr(FrRepr::from(1)));
assert!(tmp.0.is_zero());
}
// Test associativity
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
// Generate a, b, c and ensure (a + b) + c == a + (b + c).
let a = Fr::rand(&mut rng);
let b = Fr::rand(&mut rng);
let c = Fr::rand(&mut rng);
let mut tmp1 = a;
tmp1.add_assign(&b);
tmp1.add_assign(&c);
let mut tmp2 = b;
tmp2.add_assign(&c);
tmp2.add_assign(&a);
assert!(tmp1.is_valid());
assert!(tmp2.is_valid());
assert_eq!(tmp1, tmp2);
}
}
#[test]
fn test_fr_sub_assign() {
{
// Test arbitrary subtraction that tests reduction.
let mut tmp = Fr(FrRepr([
0x6a68c64b6f735a2b,
0xd5f4d143fe0a1972,
0x37c17f3829267c62,
0xa2f37391f30915c,
]));
tmp.sub_assign(&Fr(FrRepr([
0xade5adacdccb6190,
0xaa21ee0f27db3ccd,
0x2550f4704ae39086,
0x591d1902e7c5ba27,
])));
assert_eq!(
tmp,
Fr(FrRepr([
0xbc83189d92a7f89c,
0x7f908737d62d38a3,
0x45aa62cfe7e4c3e1,
0x24ffc5896108547d
]))
);
// Test the opposite subtraction which doesn't test reduction.
tmp = Fr(FrRepr([
0xade5adacdccb6190,
0xaa21ee0f27db3ccd,
0x2550f4704ae39086,
0x591d1902e7c5ba27,
]));
tmp.sub_assign(&Fr(FrRepr([
0x6a68c64b6f735a2b,
0xd5f4d143fe0a1972,
0x37c17f3829267c62,
0xa2f37391f30915c,
])));
assert_eq!(
tmp,
Fr(FrRepr([
0x437ce7616d580765,
0xd42d1ccb29d1235b,
0xed8f753821bd1423,
0x4eede1c9c89528ca
]))
);
// Test for sensible results with zero
tmp = Fr(FrRepr::from(0));
tmp.sub_assign(&Fr(FrRepr::from(0)));
assert!(tmp.is_zero());
tmp = Fr(FrRepr([
0x437ce7616d580765,
0xd42d1ccb29d1235b,
0xed8f753821bd1423,
0x4eede1c9c89528ca,
]));
tmp.sub_assign(&Fr(FrRepr::from(0)));
assert_eq!(
tmp,
Fr(FrRepr([
0x437ce7616d580765,
0xd42d1ccb29d1235b,
0xed8f753821bd1423,
0x4eede1c9c89528ca
]))
);
}
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
// Ensure that (a - b) + (b - a) = 0.
let a = Fr::rand(&mut rng);
let b = Fr::rand(&mut rng);
let mut tmp1 = a;
tmp1.sub_assign(&b);
let mut tmp2 = b;
tmp2.sub_assign(&a);
tmp1.add_assign(&tmp2);
assert!(tmp1.is_zero());
}
}
#[test]
fn test_fr_mul_assign() {
let mut tmp = Fr(FrRepr([
0x6b7e9b8faeefc81a,
0xe30a8463f348ba42,
0xeff3cb67a8279c9c,
0x3d303651bd7c774d,
]));
tmp.mul_assign(&Fr(FrRepr([
0x13ae28e3bc35ebeb,
0xa10f4488075cae2c,
0x8160e95a853c3b5d,
0x5ae3f03b561a841d,
])));
assert!(
tmp == Fr(FrRepr([
0x23717213ce710f71,
0xdbee1fe53a16e1af,
0xf565d3e1c2a48000,
0x4426507ee75df9d7
]))
);
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000000 {
// Ensure that (a * b) * c = a * (b * c)
let a = Fr::rand(&mut rng);
let b = Fr::rand(&mut rng);
let c = Fr::rand(&mut rng);
let mut tmp1 = a;
tmp1.mul_assign(&b);
tmp1.mul_assign(&c);
let mut tmp2 = b;
tmp2.mul_assign(&c);
tmp2.mul_assign(&a);
assert_eq!(tmp1, tmp2);
}
for _ in 0..1000000 {
// Ensure that r * (a + b + c) = r*a + r*b + r*c
let r = Fr::rand(&mut rng);
let mut a = Fr::rand(&mut rng);
let mut b = Fr::rand(&mut rng);
let mut c = Fr::rand(&mut rng);
let mut tmp1 = a;
tmp1.add_assign(&b);
tmp1.add_assign(&c);
tmp1.mul_assign(&r);
a.mul_assign(&r);
b.mul_assign(&r);
c.mul_assign(&r);
a.add_assign(&b);
a.add_assign(&c);
assert_eq!(tmp1, a);
}
}
#[test]
fn test_fr_squaring() {
let mut a = Fr(FrRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0x73eda753299d7d47,
]));
assert!(a.is_valid());
a.square();
assert_eq!(
a,
Fr::from_repr(FrRepr([
0xc0d698e7bde077b8,
0xb79a310579e76ec2,
0xac1da8d0a9af4e5f,
0x13f629c49bf23e97
])).unwrap()
);
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000000 {
// Ensure that (a * a) = a^2
let a = Fr::rand(&mut rng);
let mut tmp = a;
tmp.square();
let mut tmp2 = a;
tmp2.mul_assign(&a);
assert_eq!(tmp, tmp2);
}
}
#[test]
fn test_fr_inverse() {
assert!(Fr::zero().inverse().is_none());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let one = Fr::one();
for _ in 0..1000 {
// Ensure that a * a^-1 = 1
let mut a = Fr::rand(&mut rng);
let ainv = a.inverse().unwrap();
a.mul_assign(&ainv);
assert_eq!(a, one);
}
}
#[test]
fn test_fr_double() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
// Ensure doubling a is equivalent to adding a to itself.
let mut a = Fr::rand(&mut rng);
let mut b = a;
b.add_assign(&a);
a.double();
assert_eq!(a, b);
}
}
#[test]
fn test_fr_negate() {
{
let mut a = Fr::zero();
a.negate();
assert!(a.is_zero());
}
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
// Ensure (a - (-a)) = 0.
let mut a = Fr::rand(&mut rng);
let mut b = a;
b.negate();
a.add_assign(&b);
assert!(a.is_zero());
}
}
#[test]
fn test_fr_pow() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for i in 0..1000 {
// Exponentiate by various small numbers and ensure it consists with repeated
// multiplication.
let a = Fr::rand(&mut rng);
let target = a.pow(&[i]);
let mut c = Fr::one();
for _ in 0..i {
c.mul_assign(&a);
}
assert_eq!(c, target);
}
for _ in 0..1000 {
// Exponentiating by the modulus should have no effect in a prime field.
let a = Fr::rand(&mut rng);
assert_eq!(a, a.pow(Fr::char()));
}
}
#[test]
fn test_fr_sqrt() {
use ff::SqrtField;
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero());
for _ in 0..1000 {
// Ensure sqrt(a^2) = a or -a
let a = Fr::rand(&mut rng);
let mut nega = a;
nega.negate();
let mut b = a;
b.square();
let b = b.sqrt().unwrap();
assert!(a == b || nega == b);
}
for _ in 0..1000 {
// Ensure sqrt(a)^2 = a for random a
let a = Fr::rand(&mut rng);
if let Some(mut tmp) = a.sqrt() {
tmp.square();
assert_eq!(a, tmp);
}
}
}
#[test]
fn test_fr_from_into_repr() {
// r + 1 should not be in the field
assert!(
Fr::from_repr(FrRepr([
0xffffffff00000002,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48
])).is_err()
);
// r should not be in the field
assert!(Fr::from_repr(Fr::char()).is_err());
// Multiply some arbitrary representations to see if the result is as expected.
let a = FrRepr([
0x25ebe3a3ad3c0c6a,
0x6990e39d092e817c,
0x941f900d42f5658e,
0x44f8a103b38a71e0,
]);
let mut a_fr = Fr::from_repr(a).unwrap();
let b = FrRepr([
0x264e9454885e2475,
0x46f7746bb0308370,
0x4683ef5347411f9,
0x58838d7f208d4492,
]);
let b_fr = Fr::from_repr(b).unwrap();
let c = FrRepr([
0x48a09ab93cfc740d,
0x3a6600fbfc7a671,
0x838567017501d767,
0x7161d6da77745512,
]);
a_fr.mul_assign(&b_fr);
assert_eq!(a_fr.into_repr(), c);
// Zero should be in the field.
assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero());
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
// Try to turn Fr elements into representations and back again, and compare.
let a = Fr::rand(&mut rng);
let a_repr = a.into_repr();
let b_repr = FrRepr::from(a);
assert_eq!(a_repr, b_repr);
let a_again = Fr::from_repr(a_repr).unwrap();
assert_eq!(a, a_again);
}
}
#[test]
fn test_fr_repr_display() {
assert_eq!(
format!(
"{}",
FrRepr([
0x2829c242fa826143,
0x1f32cf4dd4330917,
0x932e4e479d168cd9,
0x513c77587f563f64
])
),
"0x513c77587f563f64932e4e479d168cd91f32cf4dd43309172829c242fa826143".to_string()
);
assert_eq!(
format!(
"{}",
FrRepr([
0x25ebe3a3ad3c0c6a,
0x6990e39d092e817c,
0x941f900d42f5658e,
0x44f8a103b38a71e0
])
),
"0x44f8a103b38a71e0941f900d42f5658e6990e39d092e817c25ebe3a3ad3c0c6a".to_string()
);
assert_eq!(
format!(
"{}",
FrRepr([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff
])
),
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string()
);
assert_eq!(
format!("{}", FrRepr([0, 0, 0, 0])),
"0x0000000000000000000000000000000000000000000000000000000000000000".to_string()
);
}
#[test]
fn test_fr_display() {
assert_eq!(
format!(
"{}",
Fr::from_repr(FrRepr([
0xc3cae746a3b5ecc7,
0x185ec8eb3f5b5aee,
0x684499ffe4b9dd99,
0x7c9bba7afb68faa
])).unwrap()
),
"Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string()
);
assert_eq!(
format!(
"{}",
Fr::from_repr(FrRepr([
0x44c71298ff198106,
0xb0ad10817df79b6a,
0xd034a80a2b74132b,
0x41cf9a1336f50719
])).unwrap()
),
"Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string()
);
}
#[test]
fn test_fr_num_bits() {
assert_eq!(Fr::NUM_BITS, 255);
assert_eq!(Fr::CAPACITY, 254);
}
#[test]
fn test_fr_root_of_unity() {
use ff::SqrtField;
assert_eq!(Fr::S, 32);
assert_eq!(
Fr::multiplicative_generator(),
Fr::from_repr(FrRepr::from(7)).unwrap()
);
assert_eq!(
Fr::multiplicative_generator().pow([
0xfffe5bfeffffffff,
0x9a1d80553bda402,
0x299d7d483339d808,
0x73eda753
]),
Fr::root_of_unity()
);
assert_eq!(Fr::root_of_unity().pow([1 << Fr::S]), Fr::one());
assert!(Fr::multiplicative_generator().sqrt().is_none());
}
#[test]
fn fr_field_tests() {
::tests::field::random_field_tests::<Fr>();
::tests::field::random_sqrt_tests::<Fr>();
::tests::field::random_frobenius_tests::<Fr, _>(Fr::char(), 13);
::tests::field::from_str_tests::<Fr>();
}
#[test]
fn fr_repr_tests() {
::tests::repr::random_repr_tests::<FrRepr>();
}

View File

@@ -0,0 +1,370 @@
mod ec;
mod fq;
mod fq12;
mod fq2;
mod fq6;
mod fr;
#[cfg(test)]
mod tests;
pub use self::ec::{
G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2, G2Affine, G2Compressed, G2Prepared,
G2Uncompressed,
};
pub use self::fq::{Fq, FqRepr};
pub use self::fq12::Fq12;
pub use self::fq2::Fq2;
pub use self::fq6::Fq6;
pub use self::fr::{Fr, FrRepr};
use super::{Engine, PairingCurveAffine};
use ff::{BitIterator, Field, ScalarEngine};
use group::CurveAffine;
// The BLS parameter x for BLS12-381 is -0xd201000000010000
const BLS_X: u64 = 0xd201000000010000;
const BLS_X_IS_NEGATIVE: bool = true;
#[derive(Clone, Debug)]
pub struct Bls12;
impl ScalarEngine for Bls12 {
type Fr = Fr;
}
impl Engine for Bls12 {
type G1 = G1;
type G1Affine = G1Affine;
type G2 = G2;
type G2Affine = G2Affine;
type Fq = Fq;
type Fqe = Fq2;
type Fqk = Fq12;
fn miller_loop<'a, I>(i: I) -> Self::Fqk
where
I: IntoIterator<
Item = &'a (
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared,
),
>,
{
let mut pairs = vec![];
for &(p, q) in i {
if !p.is_zero() && !q.is_zero() {
pairs.push((p, q.coeffs.iter()));
}
}
// Twisting isomorphism from E to E'
fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) {
let mut c0 = coeffs.0;
let mut c1 = coeffs.1;
c0.c0.mul_assign(&p.y);
c0.c1.mul_assign(&p.y);
c1.c0.mul_assign(&p.x);
c1.c1.mul_assign(&p.x);
// Sparse multiplication in Fq12
f.mul_by_014(&coeffs.2, &c1, &c0);
}
let mut f = Fq12::one();
let mut found_one = false;
for i in BitIterator::new(&[BLS_X >> 1]) {
if !found_one {
found_one = i;
continue;
}
for &mut (p, ref mut coeffs) in &mut pairs {
ell(&mut f, coeffs.next().unwrap(), &p.0);
}
if i {
for &mut (p, ref mut coeffs) in &mut pairs {
ell(&mut f, coeffs.next().unwrap(), &p.0);
}
}
f.square();
}
for &mut (p, ref mut coeffs) in &mut pairs {
ell(&mut f, coeffs.next().unwrap(), &p.0);
}
if BLS_X_IS_NEGATIVE {
f.conjugate();
}
f
}
fn final_exponentiation(r: &Fq12) -> Option<Fq12> {
let mut f1 = *r;
f1.conjugate();
match r.inverse() {
Some(mut f2) => {
let mut r = f1;
r.mul_assign(&f2);
f2 = r;
r.frobenius_map(2);
r.mul_assign(&f2);
fn exp_by_x(f: &mut Fq12, x: u64) {
*f = f.pow(&[x]);
if BLS_X_IS_NEGATIVE {
f.conjugate();
}
}
let mut x = BLS_X;
let mut y0 = r;
y0.square();
let mut y1 = y0;
exp_by_x(&mut y1, x);
x >>= 1;
let mut y2 = y1;
exp_by_x(&mut y2, x);
x <<= 1;
let mut y3 = r;
y3.conjugate();
y1.mul_assign(&y3);
y1.conjugate();
y1.mul_assign(&y2);
y2 = y1;
exp_by_x(&mut y2, x);
y3 = y2;
exp_by_x(&mut y3, x);
y1.conjugate();
y3.mul_assign(&y1);
y1.conjugate();
y1.frobenius_map(3);
y2.frobenius_map(2);
y1.mul_assign(&y2);
y2 = y3;
exp_by_x(&mut y2, x);
y2.mul_assign(&y0);
y2.mul_assign(&r);
y1.mul_assign(&y2);
y2 = y3;
y2.frobenius_map(1);
y1.mul_assign(&y2);
Some(y1)
}
None => None,
}
}
}
impl G2Prepared {
pub fn is_zero(&self) -> bool {
self.infinity
}
pub fn from_affine(q: G2Affine) -> Self {
if q.is_zero() {
return G2Prepared {
coeffs: vec![],
infinity: true,
};
}
fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) {
// Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
let mut tmp0 = r.x;
tmp0.square();
let mut tmp1 = r.y;
tmp1.square();
let mut tmp2 = tmp1;
tmp2.square();
let mut tmp3 = tmp1;
tmp3.add_assign(&r.x);
tmp3.square();
tmp3.sub_assign(&tmp0);
tmp3.sub_assign(&tmp2);
tmp3.double();
let mut tmp4 = tmp0;
tmp4.double();
tmp4.add_assign(&tmp0);
let mut tmp6 = r.x;
tmp6.add_assign(&tmp4);
let mut tmp5 = tmp4;
tmp5.square();
let mut zsquared = r.z;
zsquared.square();
r.x = tmp5;
r.x.sub_assign(&tmp3);
r.x.sub_assign(&tmp3);
r.z.add_assign(&r.y);
r.z.square();
r.z.sub_assign(&tmp1);
r.z.sub_assign(&zsquared);
r.y = tmp3;
r.y.sub_assign(&r.x);
r.y.mul_assign(&tmp4);
tmp2.double();
tmp2.double();
tmp2.double();
r.y.sub_assign(&tmp2);
tmp3 = tmp4;
tmp3.mul_assign(&zsquared);
tmp3.double();
tmp3.negate();
tmp6.square();
tmp6.sub_assign(&tmp0);
tmp6.sub_assign(&tmp5);
tmp1.double();
tmp1.double();
tmp6.sub_assign(&tmp1);
tmp0 = r.z;
tmp0.mul_assign(&zsquared);
tmp0.double();
(tmp0, tmp3, tmp6)
}
fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) {
// Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
let mut zsquared = r.z;
zsquared.square();
let mut ysquared = q.y;
ysquared.square();
let mut t0 = zsquared;
t0.mul_assign(&q.x);
let mut t1 = q.y;
t1.add_assign(&r.z);
t1.square();
t1.sub_assign(&ysquared);
t1.sub_assign(&zsquared);
t1.mul_assign(&zsquared);
let mut t2 = t0;
t2.sub_assign(&r.x);
let mut t3 = t2;
t3.square();
let mut t4 = t3;
t4.double();
t4.double();
let mut t5 = t4;
t5.mul_assign(&t2);
let mut t6 = t1;
t6.sub_assign(&r.y);
t6.sub_assign(&r.y);
let mut t9 = t6;
t9.mul_assign(&q.x);
let mut t7 = t4;
t7.mul_assign(&r.x);
r.x = t6;
r.x.square();
r.x.sub_assign(&t5);
r.x.sub_assign(&t7);
r.x.sub_assign(&t7);
r.z.add_assign(&t2);
r.z.square();
r.z.sub_assign(&zsquared);
r.z.sub_assign(&t3);
let mut t10 = q.y;
t10.add_assign(&r.z);
let mut t8 = t7;
t8.sub_assign(&r.x);
t8.mul_assign(&t6);
t0 = r.y;
t0.mul_assign(&t5);
t0.double();
r.y = t8;
r.y.sub_assign(&t0);
t10.square();
t10.sub_assign(&ysquared);
let mut ztsquared = r.z;
ztsquared.square();
t10.sub_assign(&ztsquared);
t9.double();
t9.sub_assign(&t10);
t10 = r.z;
t10.double();
t6.negate();
t1 = t6;
t1.double();
(t10, t1, t9)
}
let mut coeffs = vec![];
let mut r: G2 = q.into();
let mut found_one = false;
for i in BitIterator::new([BLS_X >> 1]) {
if !found_one {
found_one = i;
continue;
}
coeffs.push(doubling_step(&mut r));
if i {
coeffs.push(addition_step(&mut r, &q));
}
}
coeffs.push(doubling_step(&mut r));
G2Prepared {
coeffs,
infinity: false,
}
}
}
#[test]
fn bls12_engine_tests() {
::tests::engine::engine_tests::<Bls12>();
}

View File

@@ -0,0 +1,614 @@
use ff::PrimeFieldRepr;
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use super::*;
use *;
#[test]
fn test_pairing_result_against_relic() {
/*
Sent to me from Diego Aranha (author of RELIC library):
1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6
089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F
1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87
193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F
01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5
018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6
19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D
06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A
11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57
03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2
04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF
0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631
*/
assert_eq!(Bls12::pairing(G1::one(), G2::one()), Fq12 {
c0: Fq6 {
c0: Fq2 {
c0: Fq::from_str("2819105605953691245277803056322684086884703000473961065716485506033588504203831029066448642358042597501014294104502").unwrap(),
c1: Fq::from_str("1323968232986996742571315206151405965104242542339680722164220900812303524334628370163366153839984196298685227734799").unwrap()
},
c1: Fq2 {
c0: Fq::from_str("2987335049721312504428602988447616328830341722376962214011674875969052835043875658579425548512925634040144704192135").unwrap(),
c1: Fq::from_str("3879723582452552452538684314479081967502111497413076598816163759028842927668327542875108457755966417881797966271311").unwrap()
},
c2: Fq2 {
c0: Fq::from_str("261508182517997003171385743374653339186059518494239543139839025878870012614975302676296704930880982238308326681253").unwrap(),
c1: Fq::from_str("231488992246460459663813598342448669854473942105054381511346786719005883340876032043606739070883099647773793170614").unwrap()
}
},
c1: Fq6 {
c0: Fq2 {
c0: Fq::from_str("3993582095516422658773669068931361134188738159766715576187490305611759126554796569868053818105850661142222948198557").unwrap(),
c1: Fq::from_str("1074773511698422344502264006159859710502164045911412750831641680783012525555872467108249271286757399121183508900634").unwrap()
},
c1: Fq2 {
c0: Fq::from_str("2727588299083545686739024317998512740561167011046940249988557419323068809019137624943703910267790601287073339193943").unwrap(),
c1: Fq::from_str("493643299814437640914745677854369670041080344349607504656543355799077485536288866009245028091988146107059514546594").unwrap()
},
c2: Fq2 {
c0: Fq::from_str("734401332196641441839439105942623141234148957972407782257355060229193854324927417865401895596108124443575283868655").unwrap(),
c1: Fq::from_str("2348330098288556420918672502923664952620152483128593484301759394583320358354186482723629999370241674973832318248497").unwrap()
}
}
});
}
fn test_vectors<G: CurveProjective, E: EncodedPoint<Affine = G::Affine>>(expected: &[u8]) {
let mut e = G::zero();
let mut v = vec![];
{
let mut expected = expected;
for _ in 0..1000 {
let e_affine = e.into_affine();
let encoded = E::from_affine(e_affine);
v.extend_from_slice(encoded.as_ref());
let mut decoded = E::empty();
decoded.as_mut().copy_from_slice(&expected[0..E::size()]);
expected = &expected[E::size()..];
let decoded = decoded.into_affine().unwrap();
assert_eq!(e_affine, decoded);
e.add_assign(&G::one());
}
}
assert_eq!(&v[..], expected);
}
#[test]
fn test_g1_uncompressed_valid_vectors() {
test_vectors::<G1, G1Uncompressed>(include_bytes!("g1_uncompressed_valid_test_vectors.dat"));
}
#[test]
fn test_g1_compressed_valid_vectors() {
test_vectors::<G1, G1Compressed>(include_bytes!("g1_compressed_valid_test_vectors.dat"));
}
#[test]
fn test_g2_uncompressed_valid_vectors() {
test_vectors::<G2, G2Uncompressed>(include_bytes!("g2_uncompressed_valid_test_vectors.dat"));
}
#[test]
fn test_g2_compressed_valid_vectors() {
test_vectors::<G2, G2Compressed>(include_bytes!("g2_compressed_valid_test_vectors.dat"));
}
#[test]
fn test_g1_uncompressed_invalid_vectors() {
{
let z = G1Affine::zero().into_uncompressed();
{
let mut z = z;
z.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because we expected an uncompressed point");
}
}
{
let mut z = z;
z.as_mut()[0] |= 0b0010_0000;
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
}
}
for i in 0..G1Uncompressed::size() {
let mut z = z;
z.as_mut()[i] |= 0b0000_0001;
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
}
}
}
let o = G1Affine::one().into_uncompressed();
{
let mut o = o;
o.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
// :)
} else {
panic!("should have rejected the point because we expected an uncompressed point");
}
}
let m = Fq::char();
{
let mut o = o;
m.write_be(&mut o.as_mut()[0..]).unwrap();
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
assert_eq!(coordinate, "x coordinate");
} else {
panic!("should have rejected the point")
}
}
{
let mut o = o;
m.write_be(&mut o.as_mut()[48..]).unwrap();
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
assert_eq!(coordinate, "y coordinate");
} else {
panic!("should have rejected the point")
}
}
{
let m = Fq::zero().into_repr();
let mut o = o;
m.write_be(&mut o.as_mut()[0..]).unwrap();
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
// :)
} else {
panic!("should have rejected the point because it isn't on the curve")
}
}
{
let mut o = o;
let mut x = Fq::one();
loop {
let mut x3b = x;
x3b.square();
x3b.mul_assign(&x);
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
if let Some(y) = x3b.sqrt() {
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
break;
} else {
panic!(
"should have rejected the point because it isn't in the correct subgroup"
)
}
} else {
x.add_assign(&Fq::one());
}
}
}
}
#[test]
fn test_g2_uncompressed_invalid_vectors() {
{
let z = G2Affine::zero().into_uncompressed();
{
let mut z = z;
z.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because we expected an uncompressed point");
}
}
{
let mut z = z;
z.as_mut()[0] |= 0b0010_0000;
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
}
}
for i in 0..G2Uncompressed::size() {
let mut z = z;
z.as_mut()[i] |= 0b0000_0001;
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
}
}
}
let o = G2Affine::one().into_uncompressed();
{
let mut o = o;
o.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
// :)
} else {
panic!("should have rejected the point because we expected an uncompressed point");
}
}
let m = Fq::char();
{
let mut o = o;
m.write_be(&mut o.as_mut()[0..]).unwrap();
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
assert_eq!(coordinate, "x coordinate (c1)");
} else {
panic!("should have rejected the point")
}
}
{
let mut o = o;
m.write_be(&mut o.as_mut()[48..]).unwrap();
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
assert_eq!(coordinate, "x coordinate (c0)");
} else {
panic!("should have rejected the point")
}
}
{
let mut o = o;
m.write_be(&mut o.as_mut()[96..]).unwrap();
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
assert_eq!(coordinate, "y coordinate (c1)");
} else {
panic!("should have rejected the point")
}
}
{
let mut o = o;
m.write_be(&mut o.as_mut()[144..]).unwrap();
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
assert_eq!(coordinate, "y coordinate (c0)");
} else {
panic!("should have rejected the point")
}
}
{
let m = Fq::zero().into_repr();
let mut o = o;
m.write_be(&mut o.as_mut()[0..]).unwrap();
m.write_be(&mut o.as_mut()[48..]).unwrap();
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
// :)
} else {
panic!("should have rejected the point because it isn't on the curve")
}
}
{
let mut o = o;
let mut x = Fq2::one();
loop {
let mut x3b = x;
x3b.square();
x3b.mul_assign(&x);
x3b.add_assign(&Fq2 {
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
}); // TODO: perhaps expose coeff_b through API?
if let Some(y) = x3b.sqrt() {
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
y.c1.into_repr().write_be(&mut o.as_mut()[96..]).unwrap();
y.c0.into_repr().write_be(&mut o.as_mut()[144..]).unwrap();
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
break;
} else {
panic!(
"should have rejected the point because it isn't in the correct subgroup"
)
}
} else {
x.add_assign(&Fq2::one());
}
}
}
}
#[test]
fn test_g1_compressed_invalid_vectors() {
{
let z = G1Affine::zero().into_compressed();
{
let mut z = z;
z.as_mut()[0] &= 0b0111_1111;
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because we expected a compressed point");
}
}
{
let mut z = z;
z.as_mut()[0] |= 0b0010_0000;
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
}
}
for i in 0..G1Compressed::size() {
let mut z = z;
z.as_mut()[i] |= 0b0000_0001;
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
}
}
}
let o = G1Affine::one().into_compressed();
{
let mut o = o;
o.as_mut()[0] &= 0b0111_1111;
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
// :)
} else {
panic!("should have rejected the point because we expected a compressed point");
}
}
let m = Fq::char();
{
let mut o = o;
m.write_be(&mut o.as_mut()[0..]).unwrap();
o.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
assert_eq!(coordinate, "x coordinate");
} else {
panic!("should have rejected the point")
}
}
{
let mut o = o;
let mut x = Fq::one();
loop {
let mut x3b = x;
x3b.square();
x3b.mul_assign(&x);
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
if let Some(_) = x3b.sqrt() {
x.add_assign(&Fq::one());
} else {
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
o.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
break;
} else {
panic!("should have rejected the point because it isn't on the curve")
}
}
}
}
{
let mut o = o;
let mut x = Fq::one();
loop {
let mut x3b = x;
x3b.square();
x3b.mul_assign(&x);
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
if let Some(_) = x3b.sqrt() {
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
o.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
break;
} else {
panic!(
"should have rejected the point because it isn't in the correct subgroup"
)
}
} else {
x.add_assign(&Fq::one());
}
}
}
}
#[test]
fn test_g2_compressed_invalid_vectors() {
{
let z = G2Affine::zero().into_compressed();
{
let mut z = z;
z.as_mut()[0] &= 0b0111_1111;
if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because we expected a compressed point");
}
}
{
let mut z = z;
z.as_mut()[0] |= 0b0010_0000;
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because the parity bit should not be set if the point is at infinity");
}
}
for i in 0..G2Compressed::size() {
let mut z = z;
z.as_mut()[i] |= 0b0000_0001;
if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() {
// :)
} else {
panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity");
}
}
}
let o = G2Affine::one().into_compressed();
{
let mut o = o;
o.as_mut()[0] &= 0b0111_1111;
if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() {
// :)
} else {
panic!("should have rejected the point because we expected a compressed point");
}
}
let m = Fq::char();
{
let mut o = o;
m.write_be(&mut o.as_mut()[0..]).unwrap();
o.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
assert_eq!(coordinate, "x coordinate (c1)");
} else {
panic!("should have rejected the point")
}
}
{
let mut o = o;
m.write_be(&mut o.as_mut()[48..]).unwrap();
o.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() {
assert_eq!(coordinate, "x coordinate (c0)");
} else {
panic!("should have rejected the point")
}
}
{
let mut o = o;
let mut x = Fq2 {
c0: Fq::one(),
c1: Fq::one(),
};
loop {
let mut x3b = x;
x3b.square();
x3b.mul_assign(&x);
x3b.add_assign(&Fq2 {
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
}); // TODO: perhaps expose coeff_b through API?
if let Some(_) = x3b.sqrt() {
x.add_assign(&Fq2::one());
} else {
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
o.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() {
break;
} else {
panic!("should have rejected the point because it isn't on the curve")
}
}
}
}
{
let mut o = o;
let mut x = Fq2 {
c0: Fq::one(),
c1: Fq::one(),
};
loop {
let mut x3b = x;
x3b.square();
x3b.mul_assign(&x);
x3b.add_assign(&Fq2 {
c0: Fq::from_repr(FqRepr::from(4)).unwrap(),
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
}); // TODO: perhaps expose coeff_b through API?
if let Some(_) = x3b.sqrt() {
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
o.as_mut()[0] |= 0b1000_0000;
if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() {
break;
} else {
panic!(
"should have rejected the point because it isn't in the correct subgroup"
)
}
} else {
x.add_assign(&Fq2::one());
}
}
}
}

117
pairing/src/lib.rs Normal file
View File

@@ -0,0 +1,117 @@
// `clippy` is a code linting tool for improving code quality by catching
// common mistakes or strange code patterns. If the `cargo-clippy` feature
// is provided, all compiler warnings are prohibited.
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
#![cfg_attr(feature = "cargo-clippy", allow(inline_always))]
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))]
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
#![cfg_attr(feature = "cargo-clippy", allow(write_literal))]
// Force public structures to implement Debug
#![deny(missing_debug_implementations)]
extern crate byteorder;
extern crate ff;
extern crate group;
extern crate rand;
#[cfg(test)]
pub mod tests;
pub mod bls12_381;
use ff::{Field, PrimeField, ScalarEngine, SqrtField};
use group::{CurveAffine, CurveProjective};
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
/// with well-defined relationships. In particular, the G1/G2 curve groups are
/// of prime order `r`, and are equipped with a bilinear pairing function.
pub trait Engine: ScalarEngine {
/// The projective representation of an element in G1.
type G1: CurveProjective<
Engine = Self,
Base = Self::Fq,
Scalar = Self::Fr,
Affine = Self::G1Affine,
>
+ From<Self::G1Affine>;
/// The affine representation of an element in G1.
type G1Affine: PairingCurveAffine<
Engine = Self,
Base = Self::Fq,
Scalar = Self::Fr,
Projective = Self::G1,
Pair = Self::G2Affine,
PairingResult = Self::Fqk,
>
+ From<Self::G1>;
/// The projective representation of an element in G2.
type G2: CurveProjective<
Engine = Self,
Base = Self::Fqe,
Scalar = Self::Fr,
Affine = Self::G2Affine,
>
+ From<Self::G2Affine>;
/// The affine representation of an element in G2.
type G2Affine: PairingCurveAffine<
Engine = Self,
Base = Self::Fqe,
Scalar = Self::Fr,
Projective = Self::G2,
Pair = Self::G1Affine,
PairingResult = Self::Fqk,
>
+ From<Self::G2>;
/// The base field that hosts G1.
type Fq: PrimeField + SqrtField;
/// The extension field that hosts G2.
type Fqe: SqrtField;
/// The extension field that hosts the target group of the pairing.
type Fqk: Field;
/// Perform a miller loop with some number of (G1, G2) pairs.
fn miller_loop<'a, I>(i: I) -> Self::Fqk
where
I: IntoIterator<
Item = &'a (
&'a <Self::G1Affine as PairingCurveAffine>::Prepared,
&'a <Self::G2Affine as PairingCurveAffine>::Prepared,
),
>;
/// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(&Self::Fqk) -> Option<Self::Fqk>;
/// Performs a complete pairing operation `(p, q)`.
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
where
G1: Into<Self::G1Affine>,
G2: Into<Self::G2Affine>,
{
Self::final_exponentiation(&Self::miller_loop(
[(&(p.into().prepare()), &(q.into().prepare()))].into_iter(),
)).unwrap()
}
}
/// Affine representation of an elliptic curve point that can be used
/// to perform pairings.
pub trait PairingCurveAffine: CurveAffine {
type Prepared: Clone + Send + Sync + 'static;
type Pair: PairingCurveAffine<Pair = Self>;
type PairingResult: Field;
/// Prepares this element for pairing purposes.
fn prepare(&self) -> Self::Prepared;
/// Perform a pairing
fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult;
}

127
pairing/src/tests/engine.rs Normal file
View File

@@ -0,0 +1,127 @@
use group::{CurveAffine, CurveProjective};
use rand::{Rand, SeedableRng, XorShiftRng};
use {Engine, Field, PairingCurveAffine, PrimeField};
pub fn engine_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..10 {
let a = E::G1::rand(&mut rng).into_affine();
let b = E::G2::rand(&mut rng).into_affine();
assert!(a.pairing_with(&b) == b.pairing_with(&a));
assert!(a.pairing_with(&b) == E::pairing(a, b));
}
for _ in 0..1000 {
let z1 = E::G1Affine::zero().prepare();
let z2 = E::G2Affine::zero().prepare();
let a = E::G1::rand(&mut rng).into_affine().prepare();
let b = E::G2::rand(&mut rng).into_affine().prepare();
let c = E::G1::rand(&mut rng).into_affine().prepare();
let d = E::G2::rand(&mut rng).into_affine().prepare();
assert_eq!(
E::Fqk::one(),
E::final_exponentiation(&E::miller_loop(&[(&z1, &b)])).unwrap()
);
assert_eq!(
E::Fqk::one(),
E::final_exponentiation(&E::miller_loop(&[(&a, &z2)])).unwrap()
);
assert_eq!(
E::final_exponentiation(&E::miller_loop(&[(&z1, &b), (&c, &d)])).unwrap(),
E::final_exponentiation(&E::miller_loop(&[(&a, &z2), (&c, &d)])).unwrap()
);
assert_eq!(
E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&z1, &d)])).unwrap(),
E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &z2)])).unwrap()
);
}
random_bilinearity_tests::<E>();
random_miller_loop_tests::<E>();
}
fn random_miller_loop_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
// Exercise the miller loop for a reduced pairing
for _ in 0..1000 {
let a = E::G1::rand(&mut rng);
let b = E::G2::rand(&mut rng);
let p2 = E::pairing(a, b);
let a = a.into_affine().prepare();
let b = b.into_affine().prepare();
let p1 = E::final_exponentiation(&E::miller_loop(&[(&a, &b)])).unwrap();
assert_eq!(p1, p2);
}
// Exercise a double miller loop
for _ in 0..1000 {
let a = E::G1::rand(&mut rng);
let b = E::G2::rand(&mut rng);
let c = E::G1::rand(&mut rng);
let d = E::G2::rand(&mut rng);
let ab = E::pairing(a, b);
let cd = E::pairing(c, d);
let mut abcd = ab;
abcd.mul_assign(&cd);
let a = a.into_affine().prepare();
let b = b.into_affine().prepare();
let c = c.into_affine().prepare();
let d = d.into_affine().prepare();
let abcd_with_double_loop =
E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &d)])).unwrap();
assert_eq!(abcd, abcd_with_double_loop);
}
}
fn random_bilinearity_tests<E: Engine>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let a = E::G1::rand(&mut rng);
let b = E::G2::rand(&mut rng);
let c = E::Fr::rand(&mut rng);
let d = E::Fr::rand(&mut rng);
let mut ac = a;
ac.mul_assign(c);
let mut ad = a;
ad.mul_assign(d);
let mut bc = b;
bc.mul_assign(c);
let mut bd = b;
bd.mul_assign(d);
let acbd = E::pairing(ac, bd);
let adbc = E::pairing(ad, bc);
let mut cd = c;
cd.mul_assign(&d);
let abcd = E::pairing(a, b).pow(cd.into_repr());
assert_eq!(acbd, adbc);
assert_eq!(acbd, abcd);
}
}

266
pairing/src/tests/field.rs Normal file
View File

@@ -0,0 +1,266 @@
use ff::{Field, LegendreSymbol, PrimeField, SqrtField};
use rand::{Rng, SeedableRng, XorShiftRng};
pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..100 {
for i in 0..(maxpower + 1) {
let mut a = F::rand(&mut rng);
let mut b = a;
for _ in 0..i {
a = a.pow(&characteristic);
}
b.frobenius_map(i);
assert_eq!(a, b);
}
}
}
pub fn random_sqrt_tests<F: SqrtField>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..10000 {
let a = F::rand(&mut rng);
let mut b = a;
b.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
let b = b.sqrt().unwrap();
let mut negb = b;
negb.negate();
assert!(a == b || a == negb);
}
let mut c = F::one();
for _ in 0..10000 {
let mut b = c;
b.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
b = b.sqrt().unwrap();
if b != c {
b.negate();
}
assert_eq!(b, c);
c.add_assign(&F::one());
}
}
pub fn random_field_tests<F: Field>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
random_multiplication_tests::<F, _>(&mut rng);
random_addition_tests::<F, _>(&mut rng);
random_subtraction_tests::<F, _>(&mut rng);
random_negation_tests::<F, _>(&mut rng);
random_doubling_tests::<F, _>(&mut rng);
random_squaring_tests::<F, _>(&mut rng);
random_inversion_tests::<F, _>(&mut rng);
random_expansion_tests::<F, _>(&mut rng);
assert!(F::zero().is_zero());
{
let mut z = F::zero();
z.negate();
assert!(z.is_zero());
}
assert!(F::zero().inverse().is_none());
// Multiplication by zero
{
let mut a = F::rand(&mut rng);
a.mul_assign(&F::zero());
assert!(a.is_zero());
}
// Addition by zero
{
let mut a = F::rand(&mut rng);
let copy = a;
a.add_assign(&F::zero());
assert_eq!(a, copy);
}
}
pub fn from_str_tests<F: PrimeField>() {
{
let a = "84395729384759238745923745892374598234705297301958723458712394587103249587213984572934750213947582345792304758273458972349582734958273495872304598234";
let b = "38495729084572938457298347502349857029384609283450692834058293405982304598230458230495820394850293845098234059823049582309485203948502938452093482039";
let c = "3248875134290623212325429203829831876024364170316860259933542844758450336418538569901990710701240661702808867062612075657861768196242274635305077449545396068598317421057721935408562373834079015873933065667961469731886739181625866970316226171512545167081793907058686908697431878454091011239990119126";
let mut a = F::from_str(a).unwrap();
let b = F::from_str(b).unwrap();
let c = F::from_str(c).unwrap();
a.mul_assign(&b);
assert_eq!(a, c);
}
{
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let n: u64 = rng.gen();
let a = F::from_str(&format!("{}", n)).unwrap();
let b = F::from_repr(n.into()).unwrap();
assert_eq!(a, b);
}
}
assert!(F::from_str("").is_none());
assert!(F::from_str("0").unwrap().is_zero());
assert!(F::from_str("00").is_none());
assert!(F::from_str("00000000000").is_none());
}
fn random_multiplication_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 {
let a = F::rand(rng);
let b = F::rand(rng);
let c = F::rand(rng);
let mut t0 = a; // (a * b) * c
t0.mul_assign(&b);
t0.mul_assign(&c);
let mut t1 = a; // (a * c) * b
t1.mul_assign(&c);
t1.mul_assign(&b);
let mut t2 = b; // (b * c) * a
t2.mul_assign(&c);
t2.mul_assign(&a);
assert_eq!(t0, t1);
assert_eq!(t1, t2);
}
}
fn random_addition_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 {
let a = F::rand(rng);
let b = F::rand(rng);
let c = F::rand(rng);
let mut t0 = a; // (a + b) + c
t0.add_assign(&b);
t0.add_assign(&c);
let mut t1 = a; // (a + c) + b
t1.add_assign(&c);
t1.add_assign(&b);
let mut t2 = b; // (b + c) + a
t2.add_assign(&c);
t2.add_assign(&a);
assert_eq!(t0, t1);
assert_eq!(t1, t2);
}
}
fn random_subtraction_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 {
let a = F::rand(rng);
let b = F::rand(rng);
let mut t0 = a; // (a - b)
t0.sub_assign(&b);
let mut t1 = b; // (b - a)
t1.sub_assign(&a);
let mut t2 = t0; // (a - b) + (b - a) = 0
t2.add_assign(&t1);
assert!(t2.is_zero());
}
}
fn random_negation_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 {
let a = F::rand(rng);
let mut b = a;
b.negate();
b.add_assign(&a);
assert!(b.is_zero());
}
}
fn random_doubling_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 {
let mut a = F::rand(rng);
let mut b = a;
a.add_assign(&b);
b.double();
assert_eq!(a, b);
}
}
fn random_squaring_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 {
let mut a = F::rand(rng);
let mut b = a;
a.mul_assign(&b);
b.square();
assert_eq!(a, b);
}
}
fn random_inversion_tests<F: Field, R: Rng>(rng: &mut R) {
assert!(F::zero().inverse().is_none());
for _ in 0..10000 {
let mut a = F::rand(rng);
let b = a.inverse().unwrap(); // probablistically nonzero
a.mul_assign(&b);
assert_eq!(a, F::one());
}
}
fn random_expansion_tests<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10000 {
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
let a = F::rand(rng);
let b = F::rand(rng);
let c = F::rand(rng);
let d = F::rand(rng);
let mut t0 = a;
t0.add_assign(&b);
let mut t1 = c;
t1.add_assign(&d);
t0.mul_assign(&t1);
let mut t2 = a;
t2.mul_assign(&c);
let mut t3 = b;
t3.mul_assign(&c);
let mut t4 = a;
t4.mul_assign(&d);
let mut t5 = b;
t5.mul_assign(&d);
t2.add_assign(&t3);
t2.add_assign(&t4);
t2.add_assign(&t5);
assert_eq!(t0, t2);
}
}

3
pairing/src/tests/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod engine;
pub mod field;
pub mod repr;

98
pairing/src/tests/repr.rs Normal file
View File

@@ -0,0 +1,98 @@
use ff::PrimeFieldRepr;
use rand::{SeedableRng, XorShiftRng};
pub fn random_repr_tests<R: PrimeFieldRepr>() {
random_encoding_tests::<R>();
random_shl_tests::<R>();
random_shr_tests::<R>();
}
fn random_encoding_tests<R: PrimeFieldRepr>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let r = R::rand(&mut rng);
// Big endian
{
let mut rdecoded = R::default();
let mut v: Vec<u8> = vec![];
r.write_be(&mut v).unwrap();
rdecoded.read_be(&v[0..]).unwrap();
assert_eq!(r, rdecoded);
}
// Little endian
{
let mut rdecoded = R::default();
let mut v: Vec<u8> = vec![];
r.write_le(&mut v).unwrap();
rdecoded.read_le(&v[0..]).unwrap();
assert_eq!(r, rdecoded);
}
{
let mut rdecoded_le = R::default();
let mut rdecoded_be_flip = R::default();
let mut v: Vec<u8> = vec![];
r.write_le(&mut v).unwrap();
// This reads in little-endian, so we are done.
rdecoded_le.read_le(&v[..]).unwrap();
// This reads in big-endian, so we perform a swap of the
// bytes beforehand.
let v: Vec<u8> = v.into_iter().rev().collect();
rdecoded_be_flip.read_be(&v[..]).unwrap();
assert_eq!(rdecoded_le, rdecoded_be_flip);
}
}
}
fn random_shl_tests<R: PrimeFieldRepr>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..100 {
let r = R::rand(&mut rng);
for shift in 0..(r.num_bits() + 1) {
let mut r1 = r;
let mut r2 = r;
for _ in 0..shift {
r1.mul2();
}
r2.shl(shift);
assert_eq!(r1, r2);
}
}
}
fn random_shr_tests<R: PrimeFieldRepr>() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..100 {
let r = R::rand(&mut rng);
for shift in 0..(r.num_bits() + 1) {
let mut r1 = r;
let mut r2 = r;
for _ in 0..shift {
r1.div2();
}
r2.shr(shift);
assert_eq!(r1, r2);
}
}
}

3
sapling-crypto/.gitignore vendored Normal file
View File

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

14
sapling-crypto/COPYRIGHT Normal file
View File

@@ -0,0 +1,14 @@
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.

28
sapling-crypto/Cargo.toml Normal file
View File

@@ -0,0 +1,28 @@
[package]
authors = ["The Hush Developers", "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 = "GPLv3 or later"
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://git.hush.is/hush/blake2-rfc"
rev = "a3adc1d6859b887457c6ae4821ddad1802dad784"
[dev-dependencies]
hex-literal = "0.1"
rust-crypto = "0.2"

View File

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

View File

@@ -0,0 +1,23 @@
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.

23
sapling-crypto/README.md Normal file
View File

@@ -0,0 +1,23 @@
# 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

@@ -0,0 +1,23 @@
#![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

@@ -0,0 +1,102 @@
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

@@ -0,0 +1,438 @@
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);
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,309 @@
use ff::Field;
use pairing::Engine;
use super::*;
use super::num::{
AllocatedNum,
Num
};
use super::boolean::Boolean;
use bellman::{
ConstraintSystem
};
// Synthesize the constants for each base pattern.
fn synth<'a, E: Engine, I>(
window_size: usize,
constants: I,
assignment: &mut [E::Fr]
)
where I: IntoIterator<Item=&'a E::Fr>
{
assert_eq!(assignment.len(), 1 << window_size);
for (i, constant) in constants.into_iter().enumerate() {
let mut cur = assignment[i];
cur.negate();
cur.add_assign(constant);
assignment[i] = cur;
for (j, eval) in assignment.iter_mut().enumerate().skip(i + 1) {
if j & i == i {
eval.add_assign(&cur);
}
}
}
}
/// Performs a 3-bit window table lookup. `bits` is in
/// little-endian order.
pub fn lookup3_xy<E: Engine, CS>(
mut cs: CS,
bits: &[Boolean],
coords: &[(E::Fr, E::Fr)]
) -> Result<(AllocatedNum<E>, AllocatedNum<E>), SynthesisError>
where CS: ConstraintSystem<E>
{
assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 8);
// Calculate the index into `coords`
let i =
match (bits[0].get_value(), bits[1].get_value(), bits[2].get_value()) {
(Some(a_value), Some(b_value), Some(c_value)) => {
let mut tmp = 0;
if a_value {
tmp += 1;
}
if b_value {
tmp += 2;
}
if c_value {
tmp += 4;
}
Some(tmp)
},
_ => None
};
// Allocate the x-coordinate resulting from the lookup
let res_x = AllocatedNum::alloc(
cs.namespace(|| "x"),
|| {
Ok(coords[*i.get()?].0)
}
)?;
// Allocate the y-coordinate resulting from the lookup
let res_y = AllocatedNum::alloc(
cs.namespace(|| "y"),
|| {
Ok(coords[*i.get()?].1)
}
)?;
// Compute the coefficients for the lookup constraints
let mut x_coeffs = [E::Fr::zero(); 8];
let mut y_coeffs = [E::Fr::zero(); 8];
synth::<E, _>(3, coords.iter().map(|c| &c.0), &mut x_coeffs);
synth::<E, _>(3, coords.iter().map(|c| &c.1), &mut y_coeffs);
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[1], &bits[2])?;
let one = CS::one();
cs.enforce(
|| "x-coordinate lookup",
|lc| lc + (x_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, x_coeffs[0b011])
+ &bits[2].lc::<E>(one, x_coeffs[0b101])
+ &precomp.lc::<E>(one, x_coeffs[0b111]),
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|lc| lc + res_x.get_variable()
- (x_coeffs[0b000], one)
- &bits[1].lc::<E>(one, x_coeffs[0b010])
- &bits[2].lc::<E>(one, x_coeffs[0b100])
- &precomp.lc::<E>(one, x_coeffs[0b110]),
);
cs.enforce(
|| "y-coordinate lookup",
|lc| lc + (y_coeffs[0b001], one)
+ &bits[1].lc::<E>(one, y_coeffs[0b011])
+ &bits[2].lc::<E>(one, y_coeffs[0b101])
+ &precomp.lc::<E>(one, y_coeffs[0b111]),
|lc| lc + &bits[0].lc::<E>(one, E::Fr::one()),
|lc| lc + res_y.get_variable()
- (y_coeffs[0b000], one)
- &bits[1].lc::<E>(one, y_coeffs[0b010])
- &bits[2].lc::<E>(one, y_coeffs[0b100])
- &precomp.lc::<E>(one, y_coeffs[0b110]),
);
Ok((res_x, res_y))
}
/// Performs a 3-bit window table lookup, where
/// one of the bits is a sign bit.
pub fn lookup3_xy_with_conditional_negation<E: Engine, CS>(
mut cs: CS,
bits: &[Boolean],
coords: &[(E::Fr, E::Fr)]
) -> Result<(Num<E>, Num<E>), SynthesisError>
where CS: ConstraintSystem<E>
{
assert_eq!(bits.len(), 3);
assert_eq!(coords.len(), 4);
// Calculate the index into `coords`
let i =
match (bits[0].get_value(), bits[1].get_value()) {
(Some(a_value), Some(b_value)) => {
let mut tmp = 0;
if a_value {
tmp += 1;
}
if b_value {
tmp += 2;
}
Some(tmp)
},
_ => None
};
// Allocate the y-coordinate resulting from the lookup
// and conditional negation
let y = AllocatedNum::alloc(
cs.namespace(|| "y"),
|| {
let mut tmp = coords[*i.get()?].1;
if *bits[2].get_value().get()? {
tmp.negate();
}
Ok(tmp)
}
)?;
let one = CS::one();
// Compute the coefficients for the lookup constraints
let mut x_coeffs = [E::Fr::zero(); 4];
let mut y_coeffs = [E::Fr::zero(); 4];
synth::<E, _>(2, coords.iter().map(|c| &c.0), &mut x_coeffs);
synth::<E, _>(2, coords.iter().map(|c| &c.1), &mut y_coeffs);
let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?;
let x = Num::zero()
.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[1], x_coeffs[0b10])
.add_bool_with_coeff(one, &precomp, x_coeffs[0b11]);
let y_lc = precomp.lc::<E>(one, y_coeffs[0b11]) +
&bits[1].lc::<E>(one, y_coeffs[0b10]) +
&bits[0].lc::<E>(one, y_coeffs[0b01]) +
(y_coeffs[0b00], one);
cs.enforce(
|| "y-coordinate lookup",
|lc| lc + &y_lc + &y_lc,
|lc| lc + &bits[2].lc::<E>(one, E::Fr::one()),
|lc| lc + &y_lc - y.get_variable()
);
Ok((x, y.into()))
}
#[cfg(test)]
mod test {
use rand::{SeedableRng, Rand, Rng, XorShiftRng};
use super::*;
use ::circuit::test::*;
use ::circuit::boolean::{Boolean, AllocatedBit};
use pairing::bls12_381::{Bls12, Fr};
#[test]
fn test_lookup3_xy() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0656]);
for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.gen();
let a = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.gen();
let b = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.gen();
let c = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..8).map(|_| (rng.gen(), rng.gen())).collect();
let res = lookup3_xy(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied());
let mut index = 0;
if a_val { index += 1 }
if b_val { index += 2 }
if c_val { index += 4 }
assert_eq!(res.0.get_value().unwrap(), points[index].0);
assert_eq!(res.1.get_value().unwrap(), points[index].1);
}
}
#[test]
fn test_lookup3_xy_with_conditional_negation() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..100 {
let mut cs = TestConstraintSystem::<Bls12>::new();
let a_val = rng.gen();
let a = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()
);
let b_val = rng.gen();
let b = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()
);
let c_val = rng.gen();
let c = Boolean::from(
AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap()
);
let bits = vec![a, b, c];
let points: Vec<(Fr, Fr)> = (0..4).map(|_| (rng.gen(), rng.gen())).collect();
let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap();
assert!(cs.is_satisfied());
let mut index = 0;
if a_val { index += 1 }
if b_val { index += 2 }
assert_eq!(res.0.get_value().unwrap(), points[index].0);
let mut tmp = points[index].1;
if c_val { tmp.negate() }
assert_eq!(res.1.get_value().unwrap(), tmp);
}
}
#[test]
fn test_synth() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let window_size = 4;
let mut assignment = vec![Fr::zero(); 1 << window_size];
let constants: Vec<_> = (0..(1 << window_size)).map(|_| Fr::rand(&mut rng)).collect();
synth::<Bls12, _>(window_size, &constants, &mut assignment);
for b in 0..(1 << window_size) {
let mut acc = Fr::zero();
for j in 0..(1 << window_size) {
if j & b == j {
acc.add_assign(&assignment[j]);
}
}
assert_eq!(acc, constants[b]);
}
}
}

View File

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

View File

@@ -0,0 +1,134 @@
use ff::{Field, PrimeField};
use pairing::Engine;
use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
pub struct MultiEq<E: Engine, CS: ConstraintSystem<E>>{
cs: CS,
ops: usize,
bits_used: usize,
lhs: LinearCombination<E>,
rhs: LinearCombination<E>,
}
impl<E: Engine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
pub fn new(cs: CS) -> Self {
MultiEq {
cs: cs,
ops: 0,
bits_used: 0,
lhs: LinearCombination::zero(),
rhs: LinearCombination::zero()
}
}
fn accumulate(&mut self)
{
let ops = self.ops;
let lhs = self.lhs.clone();
let rhs = self.rhs.clone();
self.cs.enforce(
|| format!("multieq {}", ops),
|_| lhs,
|lc| lc + CS::one(),
|_| rhs
);
self.lhs = LinearCombination::zero();
self.rhs = LinearCombination::zero();
self.bits_used = 0;
self.ops += 1;
}
pub fn enforce_equal(
&mut self,
num_bits: usize,
lhs: &LinearCombination<E>,
rhs: &LinearCombination<E>
)
{
// Check if we will exceed the capacity
if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) {
self.accumulate();
}
assert!((E::Fr::CAPACITY as usize) > (self.bits_used + num_bits));
let coeff = E::Fr::from_str("2").unwrap().pow(&[self.bits_used as u64]);
self.lhs = self.lhs.clone() + (coeff, lhs);
self.rhs = self.rhs.clone() + (coeff, rhs);
self.bits_used += num_bits;
}
}
impl<E: Engine, CS: ConstraintSystem<E>> Drop for MultiEq<E, CS> {
fn drop(&mut self) {
if self.bits_used > 0 {
self.accumulate();
}
}
}
impl<E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> for MultiEq<E, CS>
{
type Root = Self;
fn one() -> Variable {
CS::one()
}
fn alloc<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
self.cs.alloc(annotation, f)
}
fn alloc_input<F, A, AR>(
&mut self,
annotation: A,
f: F
) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<E::Fr, SynthesisError>, A: FnOnce() -> AR, AR: Into<String>
{
self.cs.alloc_input(annotation, f)
}
fn enforce<A, AR, LA, LB, LC>(
&mut self,
annotation: A,
a: LA,
b: LB,
c: LC
)
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)
}
fn push_namespace<NR, N>(&mut self, name_fn: N)
where NR: Into<String>, N: FnOnce() -> NR
{
self.cs.get_root().push_namespace(name_fn)
}
fn pop_namespace(&mut self)
{
self.cs.get_root().pop_namespace()
}
fn get_root(&mut self) -> &mut Self::Root
{
self
}
}

View File

@@ -0,0 +1,114 @@
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

@@ -0,0 +1,617 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr};
use pairing::Engine;
use bellman::{
SynthesisError,
ConstraintSystem,
LinearCombination,
Variable
};
use super::{
Assignment
};
use super::boolean::{
self,
Boolean,
AllocatedBit
};
pub struct AllocatedNum<E: Engine> {
value: Option<E::Fr>,
variable: Variable
}
impl<E: Engine> Clone for AllocatedNum<E> {
fn clone(&self) -> Self {
AllocatedNum {
value: self.value,
variable: self.variable
}
}
}
impl<E: Engine> AllocatedNum<E> {
pub fn alloc<CS, F>(
mut cs: CS,
value: F,
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>,
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
let mut new_value = None;
let var = cs.alloc(|| "num", || {
let tmp = value()?;
new_value = Some(tmp);
Ok(tmp)
})?;
Ok(AllocatedNum {
value: new_value,
variable: var
})
}
pub fn inputize<CS>(
&self,
mut cs: CS
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{
let input = cs.alloc_input(
|| "input variable",
|| {
Ok(*self.value.get()?)
}
)?;
cs.enforce(
|| "enforce input is correct",
|lc| lc + input,
|lc| lc + CS::one(),
|lc| lc + self.variable
);
Ok(())
}
/// Deconstructs this allocated number into its
/// boolean representation in little-endian bit
/// order, requiring that the representation
/// strictly exists "in the field" (i.e., a
/// congruency is not allowed.)
pub fn into_bits_le_strict<CS>(
&self,
mut cs: CS
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
{
pub fn kary_and<E, CS>(
mut cs: CS,
v: &[AllocatedBit]
) -> Result<AllocatedBit, SynthesisError>
where E: Engine,
CS: ConstraintSystem<E>
{
assert!(v.len() > 0);
// Let's keep this simple for now and just AND them all
// manually
let mut cur = None;
for (i, v) in v.iter().enumerate() {
if cur.is_none() {
cur = Some(v.clone());
} else {
cur = Some(AllocatedBit::and(
cs.namespace(|| format!("and {}", i)),
cur.as_ref().unwrap(),
v
)?);
}
}
Ok(cur.expect("v.len() > 0"))
}
// We want to ensure that the bit representation of a is
// less than or equal to r - 1.
let mut a = self.value.map(|e| BitIterator::new(e.into_repr()));
let mut b = E::Fr::char();
b.sub_noborrow(&1.into());
let mut result = vec![];
// Runs of ones in r
let mut last_run = None;
let mut current_run = vec![];
let mut found_one = false;
let mut i = 0;
for b in BitIterator::new(b) {
let a_bit = a.as_mut().map(|e| e.next().unwrap());
// Skip over unset bits at the beginning
found_one |= b;
if !found_one {
// a_bit should also be false
a_bit.map(|e| assert!(!e));
continue;
}
if b {
// This is part of a run of ones. Let's just
// allocate the boolean with the expected value.
let a_bit = AllocatedBit::alloc(
cs.namespace(|| format!("bit {}", i)),
a_bit
)?;
// ... and add it to the current run of ones.
current_run.push(a_bit.clone());
result.push(a_bit);
} else {
if current_run.len() > 0 {
// This is the start of a run of zeros, but we need
// to k-ary AND against `last_run` first.
if last_run.is_some() {
current_run.push(last_run.clone().unwrap());
}
last_run = Some(kary_and(
cs.namespace(|| format!("run ending at {}", i)),
&current_run
)?);
current_run.truncate(0);
}
// If `last_run` is true, `a` must be false, or it would
// not be in the field.
//
// If `last_run` is false, `a` can be true or false.
let a_bit = AllocatedBit::alloc_conditionally(
cs.namespace(|| format!("bit {}", i)),
a_bit,
&last_run.as_ref().expect("char always starts with a one")
)?;
result.push(a_bit);
}
i += 1;
}
// char is prime, so we'll always end on
// a run of zeros.
assert_eq!(current_run.len(), 0);
// Now, we have `result` in big-endian order.
// However, now we have to unpack self!
let mut lc = LinearCombination::zero();
let mut coeff = E::Fr::one();
for bit in result.iter().rev() {
lc = lc + (coeff, bit.get_variable());
coeff.double();
}
lc = lc - self.variable;
cs.enforce(
|| "unpacking constraint",
|lc| lc,
|lc| lc,
|_| lc
);
// Convert into booleans, and reverse for little-endian bit order
Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect())
}
/// Convert the allocated number into its little-endian representation.
/// Note that this does not strongly enforce that the commitment is
/// "in the field."
pub fn into_bits_le<CS>(
&self,
mut cs: CS
) -> Result<Vec<Boolean>, SynthesisError>
where CS: ConstraintSystem<E>
{
let bits = boolean::field_into_allocated_bits_le(
&mut cs,
self.value
)?;
let mut lc = LinearCombination::zero();
let mut coeff = E::Fr::one();
for bit in bits.iter() {
lc = lc + (coeff, bit.get_variable());
coeff.double();
}
lc = lc - self.variable;
cs.enforce(
|| "unpacking constraint",
|lc| lc,
|lc| lc,
|_| lc
);
Ok(bits.into_iter().map(|b| Boolean::from(b)).collect())
}
pub fn mul<CS>(
&self,
mut cs: CS,
other: &Self
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{
let mut value = None;
let var = cs.alloc(|| "product num", || {
let mut tmp = *self.value.get()?;
tmp.mul_assign(other.value.get()?);
value = Some(tmp);
Ok(tmp)
})?;
// Constrain: a * b = ab
cs.enforce(
|| "multiplication constraint",
|lc| lc + self.variable,
|lc| lc + other.variable,
|lc| lc + var
);
Ok(AllocatedNum {
value: value,
variable: var
})
}
pub fn square<CS>(
&self,
mut cs: CS
) -> Result<Self, SynthesisError>
where CS: ConstraintSystem<E>
{
let mut value = None;
let var = cs.alloc(|| "squared num", || {
let mut tmp = *self.value.get()?;
tmp.square();
value = Some(tmp);
Ok(tmp)
})?;
// Constrain: a * a = aa
cs.enforce(
|| "squaring constraint",
|lc| lc + self.variable,
|lc| lc + self.variable,
|lc| lc + var
);
Ok(AllocatedNum {
value: value,
variable: var
})
}
pub fn assert_nonzero<CS>(
&self,
mut cs: CS
) -> Result<(), SynthesisError>
where CS: ConstraintSystem<E>
{
let inv = cs.alloc(|| "ephemeral inverse", || {
let tmp = *self.value.get()?;
if tmp.is_zero() {
Err(SynthesisError::DivisionByZero)
} else {
Ok(tmp.inverse().unwrap())
}
})?;
// Constrain a * inv = 1, which is only valid
// iff a has a multiplicative inverse, untrue
// for zero.
cs.enforce(
|| "nonzero assertion constraint",
|lc| lc + self.variable,
|lc| lc + inv,
|lc| lc + CS::one()
);
Ok(())
}
/// Takes two allocated numbers (a, b) and returns
/// (b, a) if the condition is true, and (a, b)
/// otherwise.
pub fn conditionally_reverse<CS>(
mut cs: CS,
a: &Self,
b: &Self,
condition: &Boolean
) -> Result<(Self, Self), SynthesisError>
where CS: ConstraintSystem<E>
{
let c = Self::alloc(
cs.namespace(|| "conditional reversal result 1"),
|| {
if *condition.get_value().get()? {
Ok(*b.value.get()?)
} else {
Ok(*a.value.get()?)
}
}
)?;
cs.enforce(
|| "first conditional reversal",
|lc| lc + a.variable - b.variable,
|_| condition.lc(CS::one(), E::Fr::one()),
|lc| lc + a.variable - c.variable
);
let d = Self::alloc(
cs.namespace(|| "conditional reversal result 2"),
|| {
if *condition.get_value().get()? {
Ok(*a.value.get()?)
} else {
Ok(*b.value.get()?)
}
}
)?;
cs.enforce(
|| "second conditional reversal",
|lc| lc + b.variable - a.variable,
|_| condition.lc(CS::one(), E::Fr::one()),
|lc| lc + b.variable - d.variable
);
Ok((c, d))
}
pub fn get_value(&self) -> Option<E::Fr> {
self.value
}
pub fn get_variable(&self) -> Variable {
self.variable
}
}
pub struct Num<E: Engine> {
value: Option<E::Fr>,
lc: LinearCombination<E>
}
impl<E: Engine> From<AllocatedNum<E>> for Num<E> {
fn from(num: AllocatedNum<E>) -> Num<E> {
Num {
value: num.value,
lc: LinearCombination::<E>::zero() + num.variable
}
}
}
impl<E: Engine> Num<E> {
pub fn zero() -> Self {
Num {
value: Some(E::Fr::zero()),
lc: LinearCombination::zero()
}
}
pub fn get_value(&self) -> Option<E::Fr> {
self.value
}
pub fn lc(&self, coeff: E::Fr) -> LinearCombination<E> {
LinearCombination::zero() + (coeff, &self.lc)
}
pub fn add_bool_with_coeff(
self,
one: Variable,
bit: &Boolean,
coeff: E::Fr
) -> Self
{
let newval = match (self.value, bit.get_value()) {
(Some(mut curval), Some(bval)) => {
if bval {
curval.add_assign(&coeff);
}
Some(curval)
},
_ => None
};
Num {
value: newval,
lc: self.lc + &bit.lc(one, coeff)
}
}
}
#[cfg(test)]
mod test {
use rand::{SeedableRng, Rand, Rng, XorShiftRng};
use bellman::{ConstraintSystem};
use ff::{BitIterator, Field, PrimeField};
use pairing::bls12_381::{Bls12, Fr};
use ::circuit::test::*;
use super::{AllocatedNum, Boolean};
#[test]
fn test_allocated_num() {
let mut cs = TestConstraintSystem::<Bls12>::new();
AllocatedNum::alloc(&mut cs, || Ok(Fr::one())).unwrap();
assert!(cs.get("num") == Fr::one());
}
#[test]
fn test_num_squaring() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap();
let n2 = n.square(&mut cs).unwrap();
assert!(cs.is_satisfied());
assert!(cs.get("squared num") == Fr::from_str("9").unwrap());
assert!(n2.value.unwrap() == Fr::from_str("9").unwrap());
cs.set("squared num", Fr::from_str("10").unwrap());
assert!(!cs.is_satisfied());
}
#[test]
fn test_num_multiplication() {
let mut cs = TestConstraintSystem::<Bls12>::new();
let n = 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();
assert!(cs.is_satisfied());
assert!(cs.get("product num") == Fr::from_str("120").unwrap());
assert!(n3.value.unwrap() == Fr::from_str("120").unwrap());
cs.set("product num", Fr::from_str("121").unwrap());
assert!(!cs.is_satisfied());
}
#[test]
fn test_num_conditional_reversal() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
{
let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap();
let condition = Boolean::constant(false);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
assert!(cs.is_satisfied());
assert_eq!(a.value.unwrap(), c.value.unwrap());
assert_eq!(b.value.unwrap(), d.value.unwrap());
}
{
let mut cs = TestConstraintSystem::<Bls12>::new();
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap();
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap();
let condition = Boolean::constant(true);
let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap();
assert!(cs.is_satisfied());
assert_eq!(a.value.unwrap(), d.value.unwrap());
assert_eq!(b.value.unwrap(), c.value.unwrap());
}
}
#[test]
fn test_num_nonzero() {
{
let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap();
n.assert_nonzero(&mut cs).unwrap();
assert!(cs.is_satisfied());
cs.set("ephemeral inverse", Fr::from_str("3").unwrap());
assert!(cs.which_is_unsatisfied() == Some("nonzero assertion constraint"));
}
{
let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::zero())).unwrap();
assert!(n.assert_nonzero(&mut cs).is_err());
}
}
#[test]
fn test_into_bits_strict() {
let mut negone = Fr::one();
negone.negate();
let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap();
n.into_bits_le_strict(&mut cs).unwrap();
assert!(cs.is_satisfied());
// make the bit representation the characteristic
cs.set("bit 254/boolean", Fr::one());
// this makes the conditional boolean constraint fail
assert_eq!(cs.which_is_unsatisfied().unwrap(), "bit 254/boolean constraint");
}
#[test]
fn test_into_bits() {
let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for i in 0..200 {
let r = Fr::rand(&mut rng);
let mut cs = TestConstraintSystem::<Bls12>::new();
let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap();
let bits = if i % 2 == 0 {
n.into_bits_le(&mut cs).unwrap()
} else {
n.into_bits_le_strict(&mut cs).unwrap()
};
assert!(cs.is_satisfied());
for (b, a) in BitIterator::new(r.into_repr()).skip(1).zip(bits.iter().rev()) {
if let &Boolean::Is(ref a) = a {
assert_eq!(b, a.get_value().unwrap());
} else {
unreachable!()
}
}
cs.set("num", Fr::rand(&mut rng));
assert!(!cs.is_satisfied());
cs.set("num", r);
assert!(cs.is_satisfied());
for i in 0..Fr::NUM_BITS {
let name = format!("bit {}/boolean", i);
let cur = cs.get(&name);
let mut tmp = Fr::one();
tmp.sub_assign(&cur);
cs.set(&name, tmp);
assert!(!cs.is_satisfied());
cs.set(&name, cur);
assert!(cs.is_satisfied());
}
}
}
}

View File

@@ -0,0 +1,194 @@
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);
}
}
}
}

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