diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5af4ce3a50..84eae863d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.39.0 + toolchain: 1.40.0 override: true # cargo fmt does not build the code, and running it in a fresh clone of @@ -48,8 +48,23 @@ jobs: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.39.0 + toolchain: 1.40.0 override: true + + - name: Fetch path to Zcash parameters + working-directory: ./zcash_proofs + run: echo "::set-env name=ZCASH_PARAMS::$(cargo run --release --example get-params-path --features directories)" + - name: Cache Zcash parameters + id: cache-params + uses: actions/cache@v2 + with: + path: ${{ env.ZCASH_PARAMS }} + key: ${{ runner.os }}-params + - name: Fetch Zcash parameters + if: steps.cache-params.outputs.cache-hit != 'true' + working-directory: ./zcash_proofs + run: cargo run --release --example download-params --features download-params + - name: cargo fetch uses: actions-rs/cargo@v1 with: @@ -70,6 +85,31 @@ jobs: command: test args: --verbose --release --all -- --ignored + build: + name: Build target ${{ matrix.target }} + runs-on: ubuntu-latest + strategy: + matrix: + target: + - wasm32-unknown-unknown + - wasm32-wasi + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.40.0 + override: true + - name: Add target + run: rustup target add ${{ matrix.target }} + - name: cargo fetch + uses: actions-rs/cargo@v1 + with: + command: fetch + - name: Build zcash_proofs for target + working-directory: ./zcash_proofs + run: cargo build --verbose --no-default-features --target ${{ matrix.target }} + codecov: name: Code coverage runs-on: ubuntu-latest @@ -86,11 +126,26 @@ jobs: with: command: install args: cargo-tarpaulin + + - name: Fetch path to Zcash parameters + working-directory: ./zcash_proofs + run: echo "::set-env name=ZCASH_PARAMS::$(cargo run --release --example get-params-path --features directories)" + - name: Cache Zcash parameters + id: cache-params + uses: actions/cache@v2 + with: + path: ${{ env.ZCASH_PARAMS }} + key: ${{ runner.os }}-params + - name: Fetch Zcash parameters + if: steps.cache-params.outputs.cache-hit != 'true' + working-directory: ./zcash_proofs + run: cargo run --release --example download-params --features download-params + - name: Generate coverage report uses: actions-rs/cargo@v1 with: command: tarpaulin - args: --release --timeout 600 --out Xml --packages "zcash_client_backend,zcash_primitives,zcash_proofs" + args: --release --timeout 600 --out Xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v1.0.3 with: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c72494c781..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: rust -rust: - - 1.39.0 - -cache: cargo - -before_script: - - rustup component add rustfmt - -script: - - cargo build --verbose --release --all - - cargo fmt --all -- --check - - cargo test --verbose --release --all - - cargo test --verbose --release --all -- --ignored - -before_cache: - - rm -rf "$TRAVIS_HOME/.cargo/registry/src" - - cargo install cargo-update || echo "cargo-update already installed" - - cargo install-update -a # update outdated cached binaries diff --git a/Cargo.toml b/Cargo.toml index dd595b1bd3..d321054995 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,19 @@ [workspace] members = [ - "bellman", - "ff", - "group", - "pairing", + "components/equihash", "zcash_client_backend", + "zcash_client_sqlite", + "zcash_extensions", "zcash_history", "zcash_primitives", "zcash_proofs", - "jubjub", - "bls12_381", ] [profile.release] lto = true panic = 'abort' codegen-units = 1 + +[profile.dev] +debug = true +opt-level = 0 diff --git a/bellman/.gitignore b/bellman/.gitignore deleted file mode 100644 index a9d37c560c..0000000000 --- a/bellman/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock diff --git a/bellman/COPYRIGHT b/bellman/COPYRIGHT deleted file mode 100644 index 8b5f8cf37e..0000000000 --- a/bellman/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -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. diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml deleted file mode 100644 index b056b6c2bd..0000000000 --- a/bellman/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -authors = ["Sean Bowe "] -description = "zk-SNARK library" -readme = "README.md" -homepage = "https://github.com/ebfull/bellman" -license = "MIT/Apache-2.0" -name = "bellman" -repository = "https://github.com/ebfull/bellman" -version = "0.6.0" -edition = "2018" - -[dependencies] -bit-vec = "0.4.4" -blake2s_simd = "0.5" -ff = { version = "0.6", path = "../ff" } -futures = "0.1" -futures-cpupool = { version = "0.1", optional = true } -group = { version = "0.6", path = "../group" } -num_cpus = { version = "1", optional = true } -crossbeam = { version = "0.7", optional = true } -pairing = { version = "0.16", path = "../pairing", optional = true } -rand_core = "0.5" -byteorder = "1" -subtle = "2.2.1" - -[dev-dependencies] -hex-literal = "0.2" -rand = "0.7" -rand_xorshift = "0.2" -sha2 = "0.8" - -[features] -groth16 = ["pairing"] -multicore = ["futures-cpupool", "crossbeam", "num_cpus"] -default = ["groth16", "multicore"] - -[[test]] -name = "mimc" -path = "tests/mimc.rs" -required-features = ["groth16"] - -[badges] -maintenance = { status = "actively-developed" } diff --git a/bellman/LICENSE-APACHE b/bellman/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/bellman/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/bellman/LICENSE-MIT b/bellman/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/bellman/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/bellman/README.md b/bellman/README.md deleted file mode 100644 index d64dd9c1e4..0000000000 --- a/bellman/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) # - -`bellman` is a crate for building zk-SNARK circuits. It provides circuit traits -and primitive structures, as well as basic gadget implementations such as -booleans and number abstractions. - -## Roadmap - -`bellman` is being refactored into a generic proving library. Currently it is -pairing-specific, and different types of proving systems need to be implemented -as sub-modules. After the refactor, `bellman` will be generic using the `ff` and -`group` crates, while specific proving systems will be separate crates that pull -in the dependencies they require. - -## 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. diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs deleted file mode 100644 index 0e9192e446..0000000000 --- a/bellman/src/domain.rs +++ /dev/null @@ -1,501 +0,0 @@ -//! 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 2n root of unity in the -//! field. This allows us to perform polynomial operations in O(n) by performing -//! an O(n log n) FFT over such a domain. -//! -//! [`EvaluationDomain`]: crate::domain::EvaluationDomain -//! [Groth16]: https://eprint.iacr.org/2016/260 - -use ff::{Field, PrimeField, ScalarEngine}; -use group::CurveProjective; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -use super::SynthesisError; - -use super::multicore::Worker; - -pub struct EvaluationDomain> { - coeffs: Vec, - exp: u32, - omega: E::Fr, - omegainv: E::Fr, - geninv: E::Fr, - minv: E::Fr, -} - -impl> AsRef<[G]> for EvaluationDomain { - fn as_ref(&self) -> &[G] { - &self.coeffs - } -} - -impl> AsMut<[G]> for EvaluationDomain { - fn as_mut(&mut self) -> &mut [G] { - &mut self.coeffs - } -} - -impl> EvaluationDomain { - pub fn into_coeffs(self) -> Vec { - self.coeffs - } - - pub fn from_coeffs(mut coeffs: Vec) -> Result, 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 = omega.square(); - } - - // Extend the coeffs vector with zeroes if necessary - coeffs.resize(m, G::group_zero()); - - Ok(EvaluationDomain { - coeffs, - exp, - omega, - omegainv: omega.invert().unwrap(), - geninv: E::Fr::multiplicative_generator().invert().unwrap(), - minv: E::Fr::from_str(&format!("{}", m)) - .unwrap() - .invert() - .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 |_scope| { - 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 |_scope| { - let mut u = g.pow_vartime(&[(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_vartime(&[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()).invert().unwrap(); - - worker.scope(self.coeffs.len(), |scope, chunk| { - for v in self.coeffs.chunks_mut(chunk) { - scope.spawn(move |_scope| { - 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>) { - 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 |_scope| { - 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) { - 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 |_scope| { - for (a, b) in a.iter_mut().zip(b.iter()) { - a.group_sub_assign(&b); - } - }); - } - }); - } -} - -pub trait Group: 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(pub G); - -impl PartialEq for Point { - fn eq(&self, other: &Point) -> bool { - self.0 == other.0 - } -} - -impl Copy for Point {} - -impl Clone for Point { - fn clone(&self) -> Point { - *self - } -} - -impl Group for Point { - 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(pub E::Fr); - -impl PartialEq for Scalar { - fn eq(&self, other: &Scalar) -> bool { - self.0 == other.0 - } -} - -impl Copy for Scalar {} - -impl Clone for Scalar { - fn clone(&self) -> Scalar { - *self - } -} - -impl Group for Scalar { - 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>(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>(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_vartime(&[u64::from(n / (2 * m))]); - - 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>( - 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_vartime(&[num_cpus as u64]); - - worker.scope(0, |scope, _| { - let a = &*a; - - for (j, tmp) in tmp.iter_mut().enumerate() { - scope.spawn(move |_scope| { - // Shuffle into a sub-FFT - let omega_j = omega.pow_vartime(&[j as u64]); - let omega_step = omega.pow_vartime(&[(j as u64) << log_new_n]); - - let mut elt = E::Fr::one(); - for (i, tmp) in tmp.iter_mut().enumerate() { - for s in 0..num_cpus { - let idx = (i + (s << log_new_n)) % (1 << log_n); - let mut t = a[idx]; - t.group_mul_assign(&elt); - tmp.group_add_assign(&t); - 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 |_scope| { - 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_core::RngCore; - - fn test_mul(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::Fr::random(rng))) - .collect(); - let mut b: Vec<_> = (0..coeffs_b) - .map(|_| Scalar::(E::Fr::random(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::(rng); -} - -#[cfg(feature = "pairing")] -#[test] -fn fft_composition() { - use pairing::bls12_381::Bls12; - use rand_core::RngCore; - - fn test_comp(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::Fr::random(rng))); - } - - 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::(rng); -} - -#[cfg(feature = "pairing")] -#[test] -fn parallel_fft_consistency() { - use pairing::bls12_381::Bls12; - use rand_core::RngCore; - use std::cmp::min; - - fn test_consistency(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::Fr::random(rng))) - .collect::>(); - 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::(rng); -} diff --git a/bellman/src/gadgets.rs b/bellman/src/gadgets.rs deleted file mode 100644 index b0ce734722..0000000000 --- a/bellman/src/gadgets.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Self-contained sub-circuit implementations for various primitives. - -pub mod test; - -pub mod blake2s; -pub mod boolean; -pub mod lookup; -pub mod multieq; -pub mod multipack; -pub mod num; -pub mod sha256; -pub mod uint32; - -use crate::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`. -pub trait Assignment { - fn get(&self) -> Result<&T, SynthesisError>; -} - -impl Assignment for Option { - fn get(&self) -> Result<&T, SynthesisError> { - match *self { - Some(ref v) => Ok(v), - None => Err(SynthesisError::AssignmentMissing), - } - } -} diff --git a/bellman/src/gadgets/blake2s.rs b/bellman/src/gadgets/blake2s.rs deleted file mode 100644 index 79c42e48b1..0000000000 --- a/bellman/src/gadgets/blake2s.rs +++ /dev/null @@ -1,697 +0,0 @@ -//! The [BLAKE2s] hash function with personalization support. -//! -//! [BLAKE2s]: https://tools.ietf.org/html/rfc7693 - -use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32}; -use crate::{ConstraintSystem, SynthesisError}; -use ff::ScalarEngine; - -/* -2.1. Parameters - The following table summarizes various parameters and their ranges: - | BLAKE2b | BLAKE2s | - --------------+------------------+------------------+ - Bits in word | w = 64 | w = 32 | - Rounds in F | r = 12 | r = 10 | - Block bytes | bb = 128 | bb = 64 | - Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 | - Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 | - Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 | - --------------+------------------+------------------+ - G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) | - constants = | (32, 24, 16, 63) | (16, 12, 8, 7) | - --------------+------------------+------------------+ -*/ - -const R1: usize = 16; -const R2: usize = 12; -const R3: usize = 8; -const R4: usize = 7; - -/* - Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | - ----------+-------------------------------------------------+ - SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | - SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 | - SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 | - SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 | - SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 | - SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 | - SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 | - SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 | - SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 | - SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 | - ----------+-------------------------------------------------+ -*/ - -const SIGMA: [[usize; 16]; 10] = [ - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], - [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], - [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], - [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], - [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], - [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], - [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], - [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], - [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], -]; - -/* -3.1. Mixing Function G - The G primitive function mixes two input words, "x" and "y", into - four words indexed by "a", "b", "c", and "d" in the working vector - v[0..15]. The full modified vector is returned. The rotation - constants (R1, R2, R3, R4) are given in Section 2.1. - FUNCTION G( v[0..15], a, b, c, d, x, y ) - | - | v[a] := (v[a] + v[b] + x) mod 2**w - | v[d] := (v[d] ^ v[a]) >>> R1 - | v[c] := (v[c] + v[d]) mod 2**w - | v[b] := (v[b] ^ v[c]) >>> R2 - | v[a] := (v[a] + v[b] + y) mod 2**w - | v[d] := (v[d] ^ v[a]) >>> R3 - | v[c] := (v[c] + v[d]) mod 2**w - | v[b] := (v[b] ^ v[c]) >>> R4 - | - | RETURN v[0..15] - | - END FUNCTION. -*/ - -fn mixing_g, M>( - mut cs: M, - v: &mut [UInt32], - a: usize, - b: usize, - c: usize, - d: usize, - x: &UInt32, - y: &UInt32, -) -> Result<(), SynthesisError> -where - M: ConstraintSystem>, -{ - 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>( - 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>( - mut cs: CS, - input: &[Boolean], - personalization: &[u8], -) -> Result, 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![]; - - for block in input.chunks(512) { - let mut this_block = Vec::with_capacity(16); - for word in block.chunks(32) { - let mut tmp = word.to_vec(); - while tmp.len() < 32 { - tmp.push(Boolean::constant(false)); - } - this_block.push(UInt32::from_bits(&tmp)); - } - while this_block.len() < 16 { - this_block.push(UInt32::constant(0)); - } - blocks.push(this_block); - } - - if blocks.is_empty() { - blocks.push((0..16).map(|_| UInt32::constant(0)).collect()); - } - - for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() { - let cs = cs.namespace(|| format!("block {}", i)); - - blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?; - } - - { - let cs = cs.namespace(|| "final block"); - - blake2s_compression( - cs, - &mut h, - &blocks[blocks.len() - 1], - (input.len() / 8) as u64, - true, - )?; - } - - Ok(h.into_iter().flat_map(|b| b.into_bits()).collect()) -} - -#[cfg(test)] -mod test { - use blake2s_simd::Params as Blake2sParams; - use hex_literal::hex; - use pairing::bls12_381::Bls12; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - use super::blake2s; - use crate::gadgets::boolean::{AllocatedBit, Boolean}; - use crate::gadgets::test::TestConstraintSystem; - use crate::ConstraintSystem; - - #[test] - fn test_blank_hash() { - let mut cs = TestConstraintSystem::::new(); - let input_bits = vec![]; - let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 0); - - // >>> import blake2s from hashlib - // >>> h = blake2s(digest_size=32, person=b'12345678') - // >>> h.hexdigest() - let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8"); - - let mut out = out.into_iter(); - for b in expected.iter() { - for i in 0..8 { - let c = out.next().unwrap().get_value().unwrap(); - - assert_eq!(c, (b >> i) & 1u8 == 1u8); - } - } - } - - #[test] - fn test_blake2s_constraints() { - let mut cs = TestConstraintSystem::::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::::new(); - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let input_bits: Vec<_> = (0..512) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .chain((0..512).map(|i| { - AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)) - .unwrap() - .into() - })) - .collect(); - blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 21518); - } - - #[test] - fn test_blake2s_constant_constraints() { - let mut cs = TestConstraintSystem::::new(); - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let input_bits: Vec<_> = (0..512) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect(); - blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - assert_eq!(cs.num_constraints(), 0); - } - - #[test] - fn test_blake2s() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) { - let mut h = Blake2sParams::new() - .hash_length(32) - .personal(b"12345678") - .to_state(); - - let data: Vec = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - - h.update(&data); - - let hash_result = h.finalize(); - - let mut cs = TestConstraintSystem::::new(); - - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in 0..8 { - let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); - - input_bits.push( - AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) - .unwrap() - .into(), - ); - } - } - - let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - - assert!(cs.is_satisfied()); - - let mut s = hash_result - .as_ref() - .iter() - .flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)); - - for b in r { - match b { - Boolean::Is(b) => { - assert!(s.next().unwrap() == b.get_value().unwrap()); - } - Boolean::Not(b) => { - assert!(s.next().unwrap() != b.get_value().unwrap()); - } - Boolean::Constant(b) => { - assert!(input_len == 0); - assert!(s.next().unwrap() == b); - } - } - } - } - } - - #[test] - fn test_blake2s_256_vars() { - let data: Vec = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec(); - assert_eq!(data.len(), 256); - - let mut cs = TestConstraintSystem::::new(); - - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in 0..8 { - let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); - - input_bits.push( - AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) - .unwrap() - .into(), - ); - } - } - - let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - - assert!(cs.is_satisfied()); - - let expected = hex!("0af5695115ced92c8a0341e43869209636e9aa6472e4576f0f2b996cf812b30e"); - - let mut out = r.into_iter(); - for b in expected.iter() { - for i in 0..8 { - let c = out.next().unwrap().get_value().unwrap(); - - assert_eq!(c, (b >> i) & 1u8 == 1u8); - } - } - } - - #[test] - fn test_blake2s_700_vars() { - let data: Vec = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec(); - assert_eq!(data.len(), 700); - - let mut cs = TestConstraintSystem::::new(); - - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in 0..8 { - let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); - - input_bits.push( - AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) - .unwrap() - .into(), - ); - } - } - - let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - - assert!(cs.is_satisfied()); - - let expected = hex!("2ab8f0683167ba220eef19dccf4f9b1a8193cc09b35e0235842323950530f18a"); - - let mut out = r.into_iter(); - for b in expected.iter() { - for i in 0..8 { - let c = out.next().unwrap().get_value().unwrap(); - - assert_eq!(c, (b >> i) & 1u8 == 1u8); - } - } - } - - #[test] - fn test_blake2s_test_vectors() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let expecteds = [ - hex!("a1309e334376c8f36a736a4ab0e691ef931ee3ebdb9ea96187127136fea622a1"), - hex!("82fefff60f265cea255252f7c194a7f93965dffee0609ef74eb67f0d76cd41c6"), - ]; - for i in 0..2 { - let mut h = Blake2sParams::new() - .hash_length(32) - .personal(b"12345678") - .to_state(); - let input_len = 1024; - let data: Vec = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - - h.update(&data); - - let hash_result = h.finalize(); - - let mut cs = TestConstraintSystem::::new(); - - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in 0..8 { - let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); - - input_bits.push( - AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)) - .unwrap() - .into(), - ); - } - } - - let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); - - assert!(cs.is_satisfied()); - - let mut s = hash_result - .as_ref() - .iter() - .flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)); - - for b in r { - match b { - Boolean::Is(b) => { - assert!(s.next().unwrap() == b.get_value().unwrap()); - } - Boolean::Not(b) => { - assert!(s.next().unwrap() != b.get_value().unwrap()); - } - Boolean::Constant(b) => { - assert!(input_len == 0); - assert!(s.next().unwrap() == b); - } - } - } - - assert_eq!(expecteds[i], hash_result.as_bytes()); - } - } -} diff --git a/bellman/src/gadgets/boolean.rs b/bellman/src/gadgets/boolean.rs deleted file mode 100644 index d3c882d112..0000000000 --- a/bellman/src/gadgets/boolean.rs +++ /dev/null @@ -1,1812 +0,0 @@ -//! Gadgets for allocating bits in the circuit and performing boolean logic. - -use ff::{BitIterator, Field, PrimeField, ScalarEngine}; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; - -use super::Assignment; - -/// Represents a variable in the constraint system which is guaranteed -/// to be either zero or one. -#[derive(Clone)] -pub struct AllocatedBit { - variable: Variable, - value: Option, -} - -impl AllocatedBit { - pub fn get_value(&self) -> Option { - self.value - } - - pub fn get_variable(&self) -> Variable { - self.variable - } - - /// Allocate a variable in the constraint system which can only be a - /// boolean value. Further, constrain that the boolean is false - /// unless the condition is false. - pub fn alloc_conditionally( - mut cs: CS, - value: Option, - must_be_false: &AllocatedBit, - ) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let var = cs.alloc( - || "boolean", - || { - if *value.get()? { - Ok(E::Fr::one()) - } else { - Ok(E::Fr::zero()) - } - }, - )?; - - // Constrain: (1 - must_be_false - a) * a = 0 - // if must_be_false is true, the equation - // reduces to -a * a = 0, which implies a = 0. - // if must_be_false is false, the equation - // reduces to (1 - a) * a = 0, which is a - // traditional boolean constraint. - cs.enforce( - || "boolean constraint", - |lc| lc + CS::one() - must_be_false.variable - var, - |lc| lc + var, - |lc| lc, - ); - - Ok(AllocatedBit { - variable: var, - value, - }) - } - - /// Allocate a variable in the constraint system which can only be a - /// boolean value. - pub fn alloc(mut cs: CS, value: Option) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let var = cs.alloc( - || "boolean", - || { - if *value.get()? { - Ok(E::Fr::one()) - } else { - Ok(E::Fr::zero()) - } - }, - )?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - cs.enforce( - || "boolean constraint", - |lc| lc + CS::one() - var, - |lc| lc + var, - |lc| lc, - ); - - Ok(AllocatedBit { - variable: var, - value, - }) - } - - /// Performs an XOR operation over the two operands, returning - /// an `AllocatedBit`. - pub fn xor(mut cs: CS, a: &Self, b: &Self) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "xor result", - || { - if *a.value.get()? ^ *b.value.get()? { - result_value = Some(true); - - Ok(E::Fr::one()) - } else { - result_value = Some(false); - - Ok(E::Fr::zero()) - } - }, - )?; - - // Constrain (a + a) * (b) = (a + b - c) - // Given that a and b are boolean constrained, if they - // are equal, the only solution for c is 0, and if they - // are different, the only solution for c is 1. - // - // ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c - // (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c - // (1 - ab) * (1 - (1 - a - b + ab)) = c - // (1 - ab) * (a + b - ab) = c - // a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c - // a + b - ab - ab - ab + ab = c - // a + b - 2ab = c - // -2a * b = c - a - b - // 2a * b = a + b - c - // (a + a) * b = a + b - c - cs.enforce( - || "xor constraint", - |lc| lc + a.variable + a.variable, - |lc| lc + b.variable, - |lc| lc + a.variable + b.variable - result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } - - /// Performs an AND operation over the two operands, returning - /// an `AllocatedBit`. - pub fn and(mut cs: CS, a: &Self, b: &Self) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "and result", - || { - if *a.value.get()? & *b.value.get()? { - result_value = Some(true); - - Ok(E::Fr::one()) - } else { - result_value = Some(false); - - Ok(E::Fr::zero()) - } - }, - )?; - - // Constrain (a) * (b) = (c), ensuring c is 1 iff - // a AND b are both 1. - cs.enforce( - || "and constraint", - |lc| lc + a.variable, - |lc| lc + b.variable, - |lc| lc + result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } - - /// Calculates `a AND (NOT b)`. - pub fn and_not(mut cs: CS, a: &Self, b: &Self) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "and not result", - || { - if *a.value.get()? & !*b.value.get()? { - result_value = Some(true); - - Ok(E::Fr::one()) - } else { - result_value = Some(false); - - Ok(E::Fr::zero()) - } - }, - )?; - - // Constrain (a) * (1 - b) = (c), ensuring c is 1 iff - // a is true and b is false, and otherwise c is 0. - cs.enforce( - || "and not constraint", - |lc| lc + a.variable, - |lc| lc + CS::one() - b.variable, - |lc| lc + result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } - - /// Calculates `(NOT a) AND (NOT b)`. - pub fn nor(mut cs: CS, a: &Self, b: &Self) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "nor result", - || { - if !*a.value.get()? & !*b.value.get()? { - result_value = Some(true); - - Ok(E::Fr::one()) - } else { - result_value = Some(false); - - Ok(E::Fr::zero()) - } - }, - )?; - - // Constrain (1 - a) * (1 - b) = (c), ensuring c is 1 iff - // a and b are both false, and otherwise c is 0. - cs.enforce( - || "nor constraint", - |lc| lc + CS::one() - a.variable, - |lc| lc + CS::one() - b.variable, - |lc| lc + result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } -} - -pub fn u64_into_boolean_vec_le>( - mut cs: CS, - value: Option, -) -> Result, SynthesisError> { - let values = match value { - Some(ref value) => { - let mut tmp = Vec::with_capacity(64); - - for i in 0..64 { - tmp.push(Some(*value >> i & 1 == 1)); - } - - tmp - } - None => vec![None; 64], - }; - - let bits = values - .into_iter() - .enumerate() - .map(|(i, b)| { - Ok(Boolean::from(AllocatedBit::alloc( - cs.namespace(|| format!("bit {}", i)), - b, - )?)) - }) - .collect::, SynthesisError>>()?; - - Ok(bits) -} - -pub fn field_into_boolean_vec_le, F: PrimeField>( - cs: CS, - value: Option, -) -> Result, SynthesisError> { - let v = field_into_allocated_bits_le::(cs, value)?; - - Ok(v.into_iter().map(Boolean::from).collect()) -} - -pub fn field_into_allocated_bits_le, F: PrimeField>( - mut cs: CS, - value: Option, -) -> Result, SynthesisError> { - // Deconstruct in big-endian bit order - let values = match value { - Some(ref value) => { - let mut field_char = BitIterator::new(F::char()); - - let mut tmp = Vec::with_capacity(F::NUM_BITS as usize); - - let mut found_one = false; - for b in BitIterator::new(value.into_repr()) { - // Skip leading bits - found_one |= field_char.next().unwrap(); - if !found_one { - continue; - } - - tmp.push(Some(b)); - } - - assert_eq!(tmp.len(), F::NUM_BITS as usize); - - tmp - } - None => vec![None; F::NUM_BITS as usize], - }; - - // Allocate in little-endian order - let bits = values - .into_iter() - .rev() - .enumerate() - .map(|(i, b)| AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), b)) - .collect::, SynthesisError>>()?; - - Ok(bits) -} - -/// This is a boolean value which may be either a constant or -/// an interpretation of an `AllocatedBit`. -#[derive(Clone)] -pub enum Boolean { - /// Existential view of the boolean variable - Is(AllocatedBit), - /// Negated view of the boolean variable - Not(AllocatedBit), - /// Constant (not an allocated variable) - Constant(bool), -} - -impl Boolean { - pub fn is_constant(&self) -> bool { - match *self { - Boolean::Constant(_) => true, - _ => false, - } - } - - pub fn enforce_equal(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError> - where - E: ScalarEngine, - CS: ConstraintSystem, - { - match (a, b) { - (&Boolean::Constant(a), &Boolean::Constant(b)) => { - if a == b { - Ok(()) - } else { - Err(SynthesisError::Unsatisfiable) - } - } - (&Boolean::Constant(true), a) | (a, &Boolean::Constant(true)) => { - cs.enforce( - || "enforce equal to one", - |lc| lc, - |lc| lc, - |lc| lc + CS::one() - &a.lc(CS::one(), E::Fr::one()), - ); - - Ok(()) - } - (&Boolean::Constant(false), a) | (a, &Boolean::Constant(false)) => { - cs.enforce( - || "enforce equal to zero", - |lc| lc, - |lc| lc, - |_| a.lc(CS::one(), E::Fr::one()), - ); - - Ok(()) - } - (a, b) => { - cs.enforce( - || "enforce equal", - |lc| lc, - |lc| lc, - |_| a.lc(CS::one(), E::Fr::one()) - &b.lc(CS::one(), E::Fr::one()), - ); - - Ok(()) - } - } - } - - pub fn get_value(&self) -> Option { - match *self { - Boolean::Constant(c) => Some(c), - Boolean::Is(ref v) => v.get_value(), - Boolean::Not(ref v) => v.get_value().map(|b| !b), - } - } - - pub fn lc(&self, one: Variable, coeff: E::Fr) -> LinearCombination { - match *self { - Boolean::Constant(c) => { - if c { - LinearCombination::::zero() + (coeff, one) - } else { - LinearCombination::::zero() - } - } - Boolean::Is(ref v) => LinearCombination::::zero() + (coeff, v.get_variable()), - Boolean::Not(ref v) => { - LinearCombination::::zero() + (coeff, one) - (coeff, v.get_variable()) - } - } - } - - /// Construct a boolean from a known constant - pub fn constant(b: bool) -> Self { - Boolean::Constant(b) - } - - /// Return a negated interpretation of this boolean. - pub fn not(&self) -> Self { - match *self { - Boolean::Constant(c) => Boolean::Constant(!c), - Boolean::Is(ref v) => Boolean::Not(v.clone()), - Boolean::Not(ref v) => Boolean::Is(v.clone()), - } - } - - /// Perform XOR over two boolean operands - pub fn xor<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - match (a, b) { - (&Boolean::Constant(false), x) | (x, &Boolean::Constant(false)) => Ok(x.clone()), - (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.not()), - // a XOR (NOT b) = NOT(a XOR b) - (is @ &Boolean::Is(_), not @ &Boolean::Not(_)) - | (not @ &Boolean::Not(_), is @ &Boolean::Is(_)) => { - Ok(Boolean::xor(cs, is, ¬.not())?.not()) - } - // a XOR b = (NOT a) XOR (NOT b) - (&Boolean::Is(ref a), &Boolean::Is(ref b)) - | (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { - Ok(Boolean::Is(AllocatedBit::xor(cs, a, b)?)) - } - } - } - - /// Perform AND over two boolean operands - pub fn and<'a, E, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - match (a, b) { - // false AND x is always false - (&Boolean::Constant(false), _) | (_, &Boolean::Constant(false)) => { - Ok(Boolean::Constant(false)) - } - // true AND x is always x - (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.clone()), - // a AND (NOT b) - (&Boolean::Is(ref is), &Boolean::Not(ref not)) - | (&Boolean::Not(ref not), &Boolean::Is(ref is)) => { - Ok(Boolean::Is(AllocatedBit::and_not(cs, is, not)?)) - } - // (NOT a) AND (NOT b) = a NOR b - (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { - Ok(Boolean::Is(AllocatedBit::nor(cs, a, b)?)) - } - // a AND b - (&Boolean::Is(ref a), &Boolean::Is(ref b)) => { - Ok(Boolean::Is(AllocatedBit::and(cs, a, b)?)) - } - } - } - - /// Computes (a and b) xor ((not a) and c) - pub fn sha256_ch<'a, E, CS>( - mut cs: CS, - a: &'a Self, - b: &'a Self, - c: &'a Self, - ) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let ch_value = match (a.get_value(), b.get_value(), c.get_value()) { - (Some(a), Some(b), Some(c)) => { - // (a and b) xor ((not a) and c) - Some((a & b) ^ ((!a) & c)) - } - _ => None, - }; - - match (a, b, c) { - (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { - // They're all constants, so we can just compute the value. - - return Ok(Boolean::Constant(ch_value.expect("they're all constants"))); - } - (&Boolean::Constant(false), _, c) => { - // If a is false - // (a and b) xor ((not a) and c) - // equals - // (false) xor (c) - // equals - // c - return Ok(c.clone()); - } - (a, &Boolean::Constant(false), c) => { - // If b is false - // (a and b) xor ((not a) and c) - // equals - // ((not a) and c) - return Boolean::and(cs, &a.not(), &c); - } - (a, b, &Boolean::Constant(false)) => { - // If c is false - // (a and b) xor ((not a) and c) - // equals - // (a and b) - return Boolean::and(cs, &a, &b); - } - (a, b, &Boolean::Constant(true)) => { - // If c is true - // (a and b) xor ((not a) and c) - // equals - // (a and b) xor (not a) - // equals - // not (a and (not b)) - return Ok(Boolean::and(cs, &a, &b.not())?.not()); - } - (a, &Boolean::Constant(true), c) => { - // If b is true - // (a and b) xor ((not a) and c) - // equals - // a xor ((not a) and c) - // equals - // not ((not a) and (not c)) - return Ok(Boolean::and(cs, &a.not(), &c.not())?.not()); - } - (&Boolean::Constant(true), _, _) => { - // If a is true - // (a and b) xor ((not a) and c) - // equals - // b xor ((not a) and c) - // So we just continue! - } - (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Not(_)) => {} - } - - let ch = cs.alloc( - || "ch", - || { - ch_value - .get() - .map(|v| if *v { E::Fr::one() } else { E::Fr::zero() }) - }, - )?; - - // a(b - c) = ch - c - cs.enforce( - || "ch computation", - |_| b.lc(CS::one(), E::Fr::one()) - &c.lc(CS::one(), E::Fr::one()), - |_| a.lc(CS::one(), E::Fr::one()), - |lc| lc + ch - &c.lc(CS::one(), E::Fr::one()), - ); - - Ok(AllocatedBit { - value: ch_value, - variable: ch, - } - .into()) - } - - /// Computes (a and b) xor (a and c) xor (b and c) - pub fn sha256_maj<'a, E, CS>( - mut cs: CS, - a: &'a Self, - b: &'a Self, - c: &'a Self, - ) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let maj_value = match (a.get_value(), b.get_value(), c.get_value()) { - (Some(a), Some(b), Some(c)) => { - // (a and b) xor (a and c) xor (b and c) - Some((a & b) ^ (a & c) ^ (b & c)) - } - _ => None, - }; - - match (a, b, c) { - (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { - // They're all constants, so we can just compute the value. - - return Ok(Boolean::Constant(maj_value.expect("they're all constants"))); - } - (&Boolean::Constant(false), b, c) => { - // If a is false, - // (a and b) xor (a and c) xor (b and c) - // equals - // (b and c) - return Boolean::and(cs, b, c); - } - (a, &Boolean::Constant(false), c) => { - // If b is false, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and c) - return Boolean::and(cs, a, c); - } - (a, b, &Boolean::Constant(false)) => { - // If c is false, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and b) - return Boolean::and(cs, a, b); - } - (a, b, &Boolean::Constant(true)) => { - // If c is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and b) xor (a) xor (b) - // equals - // not ((not a) and (not b)) - return Ok(Boolean::and(cs, &a.not(), &b.not())?.not()); - } - (a, &Boolean::Constant(true), c) => { - // If b is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a) xor (a and c) xor (c) - return Ok(Boolean::and(cs, &a.not(), &c.not())?.not()); - } - (&Boolean::Constant(true), b, c) => { - // If a is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (b) xor (c) xor (b and c) - return Ok(Boolean::and(cs, &b.not(), &c.not())?.not()); - } - (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Not(_)) => {} - } - - let maj = cs.alloc( - || "maj", - || { - maj_value - .get() - .map(|v| if *v { E::Fr::one() } else { E::Fr::zero() }) - }, - )?; - - // ¬(¬a ∧ ¬b) ∧ ¬(¬a ∧ ¬c) ∧ ¬(¬b ∧ ¬c) - // (1 - ((1 - a) * (1 - b))) * (1 - ((1 - a) * (1 - c))) * (1 - ((1 - b) * (1 - c))) - // (a + b - ab) * (a + c - ac) * (b + c - bc) - // -2abc + ab + ac + bc - // a (-2bc + b + c) + bc - // - // (b) * (c) = (bc) - // (2bc - b - c) * (a) = bc - maj - - let bc = Self::and(cs.namespace(|| "b and c"), b, c)?; - - cs.enforce( - || "maj computation", - |_| { - bc.lc(CS::one(), E::Fr::one()) + &bc.lc(CS::one(), E::Fr::one()) - - &b.lc(CS::one(), E::Fr::one()) - - &c.lc(CS::one(), E::Fr::one()) - }, - |_| a.lc(CS::one(), E::Fr::one()), - |_| bc.lc(CS::one(), E::Fr::one()) - maj, - ); - - Ok(AllocatedBit { - value: maj_value, - variable: maj, - } - .into()) - } -} - -impl From for Boolean { - fn from(b: AllocatedBit) -> Boolean { - Boolean::Is(b) - } -} - -#[cfg(test)] -mod test { - use super::{field_into_allocated_bits_le, u64_into_boolean_vec_le, AllocatedBit, Boolean}; - use crate::gadgets::test::*; - use crate::ConstraintSystem; - use ff::{Field, PrimeField}; - use pairing::bls12_381::{Bls12, Fr}; - - #[test] - fn test_allocated_bit() { - let mut cs = TestConstraintSystem::::new(); - - AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - assert!(cs.get("boolean") == Fr::one()); - assert!(cs.is_satisfied()); - cs.set("boolean", Fr::zero()); - assert!(cs.is_satisfied()); - cs.set("boolean", Fr::from_str("2").unwrap()); - assert!(!cs.is_satisfied()); - assert!(cs.which_is_unsatisfied() == Some("boolean constraint")); - } - - #[test] - fn test_xor() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::xor(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), *a_val ^ *b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("xor result") - == if *a_val ^ *b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "xor result", - if *a_val ^ *b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_and() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::and(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), *a_val & *b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("and result") - == if *a_val & *b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "and result", - if *a_val & *b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_and_not() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::and_not(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), *a_val & !*b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("and not result") - == if *a_val & !*b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "and not result", - if *a_val & !*b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_nor() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::nor(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), !*a_val & !*b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("nor result") - == if !*a_val & !*b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "nor result", - if !*a_val & !*b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_enforce_equal() { - for a_bool in [false, true].iter().cloned() { - for b_bool in [false, true].iter().cloned() { - for a_neg in [false, true].iter().cloned() { - for b_neg in [false, true].iter().cloned() { - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), - ); - let mut b = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_bool)).unwrap(), - ); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); - - assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); - } - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::Constant(a_bool); - let mut b = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_bool)).unwrap(), - ); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); - - assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); - } - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), - ); - let mut b = Boolean::Constant(b_bool); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); - - assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); - } - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::Constant(a_bool); - let mut b = Boolean::Constant(b_bool); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - let result = Boolean::enforce_equal(&mut cs, &a, &b); - - if (a_bool ^ a_neg) == (b_bool ^ b_neg) { - assert!(result.is_ok()); - assert!(cs.is_satisfied()); - } else { - assert!(result.is_err()); - } - } - } - } - } - } - } - - #[test] - fn test_boolean_negation() { - let mut cs = TestConstraintSystem::::new(); - - let mut b = Boolean::from(AllocatedBit::alloc(&mut cs, Some(true)).unwrap()); - - match b { - Boolean::Is(_) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Not(_) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Is(_) => {} - _ => panic!("unexpected value"), - } - - b = Boolean::constant(true); - - match b { - Boolean::Constant(true) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Constant(false) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Constant(true) => {} - _ => panic!("unexpected value"), - } - } - - #[derive(Copy, Clone, Debug)] - enum OperandType { - True, - False, - AllocatedTrue, - AllocatedFalse, - NegatedAllocatedTrue, - NegatedAllocatedFalse, - } - - impl OperandType { - fn is_constant(&self) -> bool { - match *self { - OperandType::True => true, - OperandType::False => true, - OperandType::AllocatedTrue => false, - OperandType::AllocatedFalse => false, - OperandType::NegatedAllocatedTrue => false, - OperandType::NegatedAllocatedFalse => false, - } - } - - fn val(&self) -> bool { - match *self { - OperandType::True => true, - OperandType::False => false, - OperandType::AllocatedTrue => true, - OperandType::AllocatedFalse => false, - OperandType::NegatedAllocatedTrue => false, - OperandType::NegatedAllocatedFalse => true, - } - } - } - - #[test] - fn test_boolean_xor() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); - - let a; - let b; - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - } - - let c = Boolean::xor(&mut cs, &a, &b).unwrap(); - - assert!(cs.is_satisfied()); - - match (first_operand, second_operand, c) { - (OperandType::True, OperandType::True, Boolean::Constant(false)) => {} - (OperandType::True, OperandType::False, Boolean::Constant(true)) => {} - (OperandType::True, OperandType::AllocatedTrue, Boolean::Not(_)) => {} - (OperandType::True, OperandType::AllocatedFalse, Boolean::Not(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Is(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Is(_)) => {} - - (OperandType::False, OperandType::True, Boolean::Constant(true)) => {} - (OperandType::False, OperandType::False, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::AllocatedTrue, Boolean::Is(_)) => {} - (OperandType::False, OperandType::AllocatedFalse, Boolean::Is(_)) => {} - (OperandType::False, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {} - (OperandType::False, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {} - - (OperandType::AllocatedTrue, OperandType::True, Boolean::Not(_)) => {} - (OperandType::AllocatedTrue, OperandType::False, Boolean::Is(_)) => {} - ( - OperandType::AllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - (OperandType::AllocatedFalse, OperandType::True, Boolean::Not(_)) => {} - (OperandType::AllocatedFalse, OperandType::False, Boolean::Is(_)) => {} - ( - OperandType::AllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Is(_)) => {} - (OperandType::NegatedAllocatedTrue, OperandType::False, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Is(_)) => {} - (OperandType::NegatedAllocatedFalse, OperandType::False, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - _ => panic!("this should never be encountered"), - } - } - } - } - - #[test] - fn test_boolean_and() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); - - let a; - let b; - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - } - - let c = Boolean::and(&mut cs, &a, &b).unwrap(); - - assert!(cs.is_satisfied()); - - match (first_operand, second_operand, c) { - (OperandType::True, OperandType::True, Boolean::Constant(true)) => {} - (OperandType::True, OperandType::False, Boolean::Constant(false)) => {} - (OperandType::True, OperandType::AllocatedTrue, Boolean::Is(_)) => {} - (OperandType::True, OperandType::AllocatedFalse, Boolean::Is(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {} - - (OperandType::False, OperandType::True, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::False, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::AllocatedTrue, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::AllocatedFalse, Boolean::Constant(false)) => { - } - ( - OperandType::False, - OperandType::NegatedAllocatedTrue, - Boolean::Constant(false), - ) => {} - ( - OperandType::False, - OperandType::NegatedAllocatedFalse, - Boolean::Constant(false), - ) => {} - - (OperandType::AllocatedTrue, OperandType::True, Boolean::Is(_)) => {} - (OperandType::AllocatedTrue, OperandType::False, Boolean::Constant(false)) => {} - ( - OperandType::AllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - (OperandType::AllocatedFalse, OperandType::True, Boolean::Is(_)) => {} - (OperandType::AllocatedFalse, OperandType::False, Boolean::Constant(false)) => { - } - ( - OperandType::AllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedTrue, - OperandType::False, - Boolean::Constant(false), - ) => {} - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedFalse, - OperandType::False, - Boolean::Constant(false), - ) => {} - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - _ => { - panic!( - "unexpected behavior at {:?} AND {:?}", - first_operand, second_operand - ); - } - } - } - } - } - - #[test] - fn test_u64_into_boolean_vec_le() { - let mut cs = TestConstraintSystem::::new(); - - let bits = u64_into_boolean_vec_le(&mut cs, Some(17234652694787248421)).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(bits.len(), 64); - - assert_eq!(bits[63 - 0].get_value().unwrap(), true); - assert_eq!(bits[63 - 1].get_value().unwrap(), true); - assert_eq!(bits[63 - 2].get_value().unwrap(), true); - assert_eq!(bits[63 - 3].get_value().unwrap(), false); - assert_eq!(bits[63 - 4].get_value().unwrap(), true); - assert_eq!(bits[63 - 5].get_value().unwrap(), true); - assert_eq!(bits[63 - 20].get_value().unwrap(), true); - assert_eq!(bits[63 - 21].get_value().unwrap(), false); - assert_eq!(bits[63 - 22].get_value().unwrap(), false); - } - - #[test] - fn test_field_into_allocated_bits_le() { - let mut cs = TestConstraintSystem::::new(); - - let r = Fr::from_str( - "9147677615426976802526883532204139322118074541891858454835346926874644257775", - ) - .unwrap(); - - let bits = field_into_allocated_bits_le(&mut cs, Some(r)).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(bits.len(), 255); - - assert_eq!(bits[254 - 0].value.unwrap(), false); - assert_eq!(bits[254 - 1].value.unwrap(), false); - assert_eq!(bits[254 - 2].value.unwrap(), true); - assert_eq!(bits[254 - 3].value.unwrap(), false); - assert_eq!(bits[254 - 4].value.unwrap(), true); - assert_eq!(bits[254 - 5].value.unwrap(), false); - assert_eq!(bits[254 - 20].value.unwrap(), true); - assert_eq!(bits[254 - 23].value.unwrap(), true); - } - - #[test] - fn test_boolean_sha256_ch() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - for third_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); - - let a; - let b; - let c; - - // ch = (a and b) xor ((not a) and c) - let expected = (first_operand.val() & second_operand.val()) - ^ ((!first_operand.val()) & third_operand.val()); - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - .not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - .not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - c = dyn_construct(third_operand, "c"); - } - - let maj = Boolean::sha256_ch(&mut cs, &a, &b, &c).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(maj.get_value().unwrap(), expected); - - if first_operand.is_constant() - || second_operand.is_constant() - || third_operand.is_constant() - { - if first_operand.is_constant() - && second_operand.is_constant() - && third_operand.is_constant() - { - assert_eq!(cs.num_constraints(), 0); - } - } else { - assert_eq!(cs.get("ch"), { - if expected { - Fr::one() - } else { - Fr::zero() - } - }); - cs.set("ch", { - if expected { - Fr::zero() - } else { - Fr::one() - } - }); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "ch computation"); - } - } - } - } - } - - #[test] - fn test_boolean_sha256_maj() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - for third_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); - - let a; - let b; - let c; - - // maj = (a and b) xor (a and c) xor (b and c) - let expected = (first_operand.val() & second_operand.val()) - ^ (first_operand.val() & third_operand.val()) - ^ (second_operand.val() & third_operand.val()); - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - .not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - .not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - c = dyn_construct(third_operand, "c"); - } - - let maj = Boolean::sha256_maj(&mut cs, &a, &b, &c).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(maj.get_value().unwrap(), expected); - - if first_operand.is_constant() - || second_operand.is_constant() - || third_operand.is_constant() - { - if first_operand.is_constant() - && second_operand.is_constant() - && third_operand.is_constant() - { - assert_eq!(cs.num_constraints(), 0); - } - } else { - assert_eq!(cs.get("maj"), { - if expected { - Fr::one() - } else { - Fr::zero() - } - }); - cs.set("maj", { - if expected { - Fr::zero() - } else { - Fr::one() - } - }); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "maj computation"); - } - } - } - } - } - - #[test] - fn test_alloc_conditionally() { - { - let mut cs = TestConstraintSystem::::new(); - let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); - - let value = None; - // if value is none, fail with SynthesisError - let is_err = AllocatedBit::alloc_conditionally( - cs.namespace(|| "alloc_conditionally"), - value, - &b, - ) - .is_err(); - assert!(is_err); - } - - { - // since value is true, b must be false, so it should succeed - let mut cs = TestConstraintSystem::::new(); - - let value = Some(true); - let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); - let allocated_value = AllocatedBit::alloc_conditionally( - cs.namespace(|| "alloc_conditionally"), - value, - &b, - ) - .unwrap(); - - assert_eq!(allocated_value.get_value().unwrap(), true); - assert!(cs.is_satisfied()); - } - - { - // since value is true, b must be false, so it should fail - let mut cs = TestConstraintSystem::::new(); - - let value = Some(true); - let b = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b) - .unwrap(); - - assert!(!cs.is_satisfied()); - } - - { - // since value is false, we don't care about the value of the bit - - let value = Some(false); - //check with false bit - let mut cs = TestConstraintSystem::::new(); - let b1 = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); - AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b1) - .unwrap(); - - assert!(cs.is_satisfied()); - - //check with true bit - let mut cs = TestConstraintSystem::::new(); - let b2 = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b2) - .unwrap(); - - assert!(cs.is_satisfied()); - } - } -} diff --git a/bellman/src/gadgets/lookup.rs b/bellman/src/gadgets/lookup.rs deleted file mode 100644 index bde86e2ba9..0000000000 --- a/bellman/src/gadgets/lookup.rs +++ /dev/null @@ -1,317 +0,0 @@ -//! Window table lookup gadgets. - -use ff::{Field, ScalarEngine}; -use std::ops::{AddAssign, Neg}; - -use super::boolean::Boolean; -use super::num::{AllocatedNum, Num}; -use super::*; -use crate::ConstraintSystem; - -// Synthesize the constants for each base pattern. -fn synth<'a, E: ScalarEngine, I>(window_size: usize, constants: I, assignment: &mut [E::Fr]) -where - I: IntoIterator, -{ - assert_eq!(assignment.len(), 1 << window_size); - - for (i, constant) in constants.into_iter().enumerate() { - let mut cur = assignment[i].neg(); - 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( - mut cs: CS, - bits: &[Boolean], - coords: &[(E::Fr, E::Fr)], -) -> Result<(AllocatedNum, AllocatedNum), SynthesisError> -where - CS: ConstraintSystem, -{ - 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::(3, coords.iter().map(|c| &c.0), &mut x_coeffs); - synth::(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::(one, x_coeffs[0b011]) - + &bits[2].lc::(one, x_coeffs[0b101]) - + &precomp.lc::(one, x_coeffs[0b111]) - }, - |lc| lc + &bits[0].lc::(one, E::Fr::one()), - |lc| { - lc + res_x.get_variable() - - (x_coeffs[0b000], one) - - &bits[1].lc::(one, x_coeffs[0b010]) - - &bits[2].lc::(one, x_coeffs[0b100]) - - &precomp.lc::(one, x_coeffs[0b110]) - }, - ); - - cs.enforce( - || "y-coordinate lookup", - |lc| { - lc + (y_coeffs[0b001], one) - + &bits[1].lc::(one, y_coeffs[0b011]) - + &bits[2].lc::(one, y_coeffs[0b101]) - + &precomp.lc::(one, y_coeffs[0b111]) - }, - |lc| lc + &bits[0].lc::(one, E::Fr::one()), - |lc| { - lc + res_y.get_variable() - - (y_coeffs[0b000], one) - - &bits[1].lc::(one, y_coeffs[0b010]) - - &bits[2].lc::(one, y_coeffs[0b100]) - - &precomp.lc::(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( - mut cs: CS, - bits: &[Boolean], - coords: &[(E::Fr, E::Fr)], -) -> Result<(Num, Num), SynthesisError> -where - CS: ConstraintSystem, -{ - 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 = tmp.neg(); - } - 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::(2, coords.iter().map(|c| &c.0), &mut x_coeffs); - synth::(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::(one, y_coeffs[0b11]) - + &bits[1].lc::(one, y_coeffs[0b10]) - + &bits[0].lc::(one, y_coeffs[0b01]) - + (y_coeffs[0b00], one); - - cs.enforce( - || "y-coordinate lookup", - |lc| lc + &y_lc + &y_lc, - |lc| lc + &bits[2].lc::(one, E::Fr::one()), - |lc| lc + &y_lc - y.get_variable(), - ); - - Ok((x, y.into())) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::gadgets::boolean::{AllocatedBit, Boolean}; - use crate::gadgets::test::*; - use pairing::bls12_381::{Bls12, Fr}; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - #[test] - fn test_lookup3_xy() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); - - let a_val = rng.next_u32() % 2 != 0; - let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); - - let b_val = rng.next_u32() % 2 != 0; - let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()); - - let c_val = rng.next_u32() % 2 != 0; - 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(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) - .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([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); - - let a_val = rng.next_u32() % 2 != 0; - let a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap()); - - let b_val = rng.next_u32() % 2 != 0; - let b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap()); - - let c_val = rng.next_u32() % 2 != 0; - 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(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) - .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 = tmp.neg() - } - assert_eq!(res.1.get_value().unwrap(), tmp); - } - } - - #[test] - fn test_synth() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let window_size = 4; - - let mut assignment = vec![Fr::zero(); 1 << window_size]; - let constants: Vec<_> = (0..(1 << window_size)) - .map(|_| Fr::random(&mut rng)) - .collect(); - - synth::(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]); - } - } -} diff --git a/bellman/src/gadgets/multieq.rs b/bellman/src/gadgets/multieq.rs deleted file mode 100644 index 37b2d9429e..0000000000 --- a/bellman/src/gadgets/multieq.rs +++ /dev/null @@ -1,121 +0,0 @@ -use ff::{Field, PrimeField, ScalarEngine}; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; - -pub struct MultiEq> { - cs: CS, - ops: usize, - bits_used: usize, - lhs: LinearCombination, - rhs: LinearCombination, -} - -impl> MultiEq { - pub fn new(cs: CS) -> Self { - MultiEq { - 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, - rhs: &LinearCombination, - ) { - // 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_vartime(&[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> Drop for MultiEq { - fn drop(&mut self) { - if self.bits_used > 0 { - self.accumulate(); - } - } -} - -impl> ConstraintSystem for MultiEq { - type Root = Self; - - fn one() -> Variable { - CS::one() - } - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.cs.alloc(annotation, f) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.cs.alloc_input(annotation, f) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - self.cs.enforce(annotation, a, b, c) - } - - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - 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 - } -} diff --git a/bellman/src/gadgets/multipack.rs b/bellman/src/gadgets/multipack.rs deleted file mode 100644 index 8ee6a1546d..0000000000 --- a/bellman/src/gadgets/multipack.rs +++ /dev/null @@ -1,112 +0,0 @@ -//! Helpers for packing vectors of bits into scalar field elements. - -use super::boolean::Boolean; -use super::num::Num; -use super::Assignment; -use crate::{ConstraintSystem, SynthesisError}; -use ff::{Field, PrimeField, ScalarEngine}; -use std::ops::AddAssign; - -/// Takes a sequence of booleans and exposes them as compact -/// public inputs -pub fn pack_into_inputs(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError> -where - E: ScalarEngine, - CS: ConstraintSystem, -{ - for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() { - let mut num = Num::::zero(); - let mut coeff = E::Fr::one(); - for bit in bits { - num = num.add_bool_with_coeff(CS::one(), bit, coeff); - - 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 { - 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 { - bytes - .iter() - .flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1)) - .collect() -} - -pub fn compute_multipacking(bits: &[bool]) -> Vec { - 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 = coeff.double(); - } - - result.push(cur); - } - - result -} - -#[test] -fn test_multipacking() { - use crate::ConstraintSystem; - use pairing::bls12_381::Bls12; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - use super::boolean::{AllocatedBit, Boolean}; - use crate::gadgets::test::*; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for num_bits in 0..1500 { - let mut cs = TestConstraintSystem::::new(); - - let bits: Vec = (0..num_bits).map(|_| rng.next_u32() % 2 != 0).collect(); - - let circuit_bits = bits - .iter() - .enumerate() - .map(|(i, &b)| { - Boolean::from( - AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(b)).unwrap(), - ) - }) - .collect::>(); - - let expected_inputs = compute_multipacking::(&bits); - - pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap(); - - assert!(cs.is_satisfied()); - assert!(cs.verify(&expected_inputs)); - } -} diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs deleted file mode 100644 index e460d201c1..0000000000 --- a/bellman/src/gadgets/num.rs +++ /dev/null @@ -1,589 +0,0 @@ -//! Gadgets representing numbers in the scalar field of the underlying curve. - -use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine}; -use std::ops::{AddAssign, MulAssign}; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; - -use super::Assignment; - -use super::boolean::{self, AllocatedBit, Boolean}; - -pub struct AllocatedNum { - value: Option, - variable: Variable, -} - -impl Clone for AllocatedNum { - fn clone(&self) -> Self { - AllocatedNum { - value: self.value, - variable: self.variable, - } - } -} - -impl AllocatedNum { - pub fn alloc(mut cs: CS, value: F) -> Result - where - CS: ConstraintSystem, - F: FnOnce() -> Result, - { - 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(&self, mut cs: CS) -> Result<(), SynthesisError> - where - CS: ConstraintSystem, - { - 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 to_bits_le_strict(&self, mut cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - pub fn kary_and( - mut cs: CS, - v: &[AllocatedBit], - ) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - assert!(!v.is_empty()); - - // 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.is_empty() { - // 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)), - ¤t_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 = 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(Boolean::from).rev().collect()) - } - - /// Convert the allocated number into its little-endian representation. - /// Note that this does not strongly enforce that the commitment is - /// "in the field." - pub fn to_bits_le(&self, mut cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - 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 = coeff.double(); - } - - lc = lc - self.variable; - - cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); - - Ok(bits.into_iter().map(Boolean::from).collect()) - } - - pub fn mul(&self, mut cs: CS, other: &Self) -> Result - where - CS: ConstraintSystem, - { - 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, - variable: var, - }) - } - - pub fn square(&self, mut cs: CS) -> Result - where - CS: ConstraintSystem, - { - let mut value = None; - - let var = cs.alloc( - || "squared num", - || { - let tmp = self.value.get()?.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, - variable: var, - }) - } - - pub fn assert_nonzero(&self, mut cs: CS) -> Result<(), SynthesisError> - where - CS: ConstraintSystem, - { - let inv = cs.alloc( - || "ephemeral inverse", - || { - let tmp = *self.value.get()?; - - if tmp.is_zero() { - Err(SynthesisError::DivisionByZero) - } else { - Ok(tmp.invert().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( - mut cs: CS, - a: &Self, - b: &Self, - condition: &Boolean, - ) -> Result<(Self, Self), SynthesisError> - where - CS: ConstraintSystem, - { - 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 { - self.value - } - - pub fn get_variable(&self) -> Variable { - self.variable - } -} - -pub struct Num { - value: Option, - lc: LinearCombination, -} - -impl From> for Num { - fn from(num: AllocatedNum) -> Num { - Num { - value: num.value, - lc: LinearCombination::::zero() + num.variable, - } - } -} - -impl Num { - pub fn zero() -> Self { - Num { - value: Some(E::Fr::zero()), - lc: LinearCombination::zero(), - } - } - - pub fn get_value(&self) -> Option { - self.value - } - - pub fn lc(&self, coeff: E::Fr) -> LinearCombination { - 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 crate::ConstraintSystem; - use ff::{BitIterator, Field, PrimeField}; - use pairing::bls12_381::{Bls12, Fr}; - use rand_core::SeedableRng; - use rand_xorshift::XorShiftRng; - use std::ops::{Neg, SubAssign}; - - use super::{AllocatedNum, Boolean}; - use crate::gadgets::test::*; - - #[test] - fn test_allocated_num() { - let mut cs = TestConstraintSystem::::new(); - - AllocatedNum::alloc(&mut cs, || Ok(Fr::one())).unwrap(); - - assert!(cs.get("num") == Fr::one()); - } - - #[test] - fn test_num_squaring() { - let mut cs = TestConstraintSystem::::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::::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([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - { - let mut cs = TestConstraintSystem::::new(); - - let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); - let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).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::::new(); - - let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::random(&mut rng))).unwrap(); - let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::random(&mut rng))).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::::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::::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 negone = Fr::one().neg(); - - let mut cs = TestConstraintSystem::::new(); - - let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap(); - n.to_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([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for i in 0..200 { - let r = Fr::random(&mut rng); - let mut cs = TestConstraintSystem::::new(); - - let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); - - let bits = if i % 2 == 0 { - n.to_bits_le(&mut cs).unwrap() - } else { - n.to_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::random(&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()); - } - } - } -} diff --git a/bellman/src/gadgets/sha256.rs b/bellman/src/gadgets/sha256.rs deleted file mode 100644 index 1be898e0d8..0000000000 --- a/bellman/src/gadgets/sha256.rs +++ /dev/null @@ -1,389 +0,0 @@ -//! Circuits for the [SHA-256] hash function and its internal compression -//! function. -//! -//! [SHA-256]: https://tools.ietf.org/html/rfc6234 - -use super::boolean::Boolean; -use super::multieq::MultiEq; -use super::uint32::UInt32; -use crate::{ConstraintSystem, SynthesisError}; -use ff::ScalarEngine; - -#[allow(clippy::unreadable_literal)] -const ROUND_CONSTANTS: [u32; 64] = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, -]; - -#[allow(clippy::unreadable_literal)] -const IV: [u32; 8] = [ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, -]; - -pub fn sha256_block_no_padding( - mut cs: CS, - input: &[Boolean], -) -> Result, SynthesisError> -where - E: ScalarEngine, - CS: ConstraintSystem, -{ - assert_eq!(input.len(), 512); - - Ok( - sha256_compression_function(&mut cs, &input, &get_sha256_iv())? - .into_iter() - .flat_map(|e| e.into_bits_be()) - .collect(), - ) -} - -pub fn sha256(mut cs: CS, input: &[Boolean]) -> Result, SynthesisError> -where - E: ScalarEngine, - CS: ConstraintSystem, -{ - assert!(input.len() % 8 == 0); - - let mut padded = input.to_vec(); - let plen = padded.len() as u64; - // append a single '1' bit - padded.push(Boolean::constant(true)); - // append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512 - while (padded.len() + 64) % 512 != 0 { - padded.push(Boolean::constant(false)); - } - // append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits - for b in (0..64).rev().map(|i| (plen >> i) & 1 == 1) { - padded.push(Boolean::constant(b)); - } - assert!(padded.len() % 512 == 0); - - let mut cur = get_sha256_iv(); - for (i, block) in padded.chunks(512).enumerate() { - cur = sha256_compression_function(cs.namespace(|| format!("block {}", i)), block, &cur)?; - } - - Ok(cur.into_iter().flat_map(|e| e.into_bits_be()).collect()) -} - -fn get_sha256_iv() -> Vec { - IV.iter().map(|&v| UInt32::constant(v)).collect() -} - -fn sha256_compression_function( - cs: CS, - input: &[Boolean], - current_hash_value: &[UInt32], -) -> Result, SynthesisError> -where - E: ScalarEngine, - CS: ConstraintSystem, -{ - assert_eq!(input.len(), 512); - assert_eq!(current_hash_value.len(), 8); - - let mut w = input - .chunks(32) - .map(|e| UInt32::from_bits_be(e)) - .collect::>(); - - // We can save some constraints by combining some of - // the constraints in different u32 additions - let mut cs = MultiEq::new(cs); - - for i in 16..64 { - let cs = &mut cs.namespace(|| format!("w extension {}", i)); - - // s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3) - let mut s0 = w[i - 15].rotr(7); - s0 = s0.xor(cs.namespace(|| "first xor for s0"), &w[i - 15].rotr(18))?; - s0 = s0.xor(cs.namespace(|| "second xor for s0"), &w[i - 15].shr(3))?; - - // s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10) - let mut s1 = w[i - 2].rotr(17); - s1 = s1.xor(cs.namespace(|| "first xor for s1"), &w[i - 2].rotr(19))?; - s1 = s1.xor(cs.namespace(|| "second xor for s1"), &w[i - 2].shr(10))?; - - let tmp = UInt32::addmany( - cs.namespace(|| "computation of w[i]"), - &[w[i - 16].clone(), s0, w[i - 7].clone(), s1], - )?; - - // w[i] := w[i-16] + s0 + w[i-7] + s1 - w.push(tmp); - } - - assert_eq!(w.len(), 64); - - enum Maybe { - Deferred(Vec), - Concrete(UInt32), - } - - impl Maybe { - fn compute(self, cs: M, others: &[UInt32]) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - M: ConstraintSystem>, - { - Ok(match self { - Maybe::Concrete(ref v) => return Ok(v.clone()), - Maybe::Deferred(mut v) => { - v.extend(others.iter().cloned()); - UInt32::addmany(cs, &v)? - } - }) - } - } - - let mut a = Maybe::Concrete(current_hash_value[0].clone()); - let mut b = current_hash_value[1].clone(); - let mut c = current_hash_value[2].clone(); - let mut d = current_hash_value[3].clone(); - let mut e = Maybe::Concrete(current_hash_value[4].clone()); - let mut f = current_hash_value[5].clone(); - let mut g = current_hash_value[6].clone(); - let mut h = current_hash_value[7].clone(); - - for i in 0..64 { - let cs = &mut cs.namespace(|| format!("compression round {}", i)); - - // S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25) - let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?; - let mut s1 = new_e.rotr(6); - s1 = s1.xor(cs.namespace(|| "first xor for s1"), &new_e.rotr(11))?; - s1 = s1.xor(cs.namespace(|| "second xor for s1"), &new_e.rotr(25))?; - - // ch := (e and f) xor ((not e) and g) - let ch = UInt32::sha256_ch(cs.namespace(|| "ch"), &new_e, &f, &g)?; - - // temp1 := h + S1 + ch + k[i] + w[i] - let temp1 = vec![ - h.clone(), - s1, - ch, - UInt32::constant(ROUND_CONSTANTS[i]), - w[i].clone(), - ]; - - // S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22) - let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?; - let mut s0 = new_a.rotr(2); - s0 = s0.xor(cs.namespace(|| "first xor for s0"), &new_a.rotr(13))?; - s0 = s0.xor(cs.namespace(|| "second xor for s0"), &new_a.rotr(22))?; - - // maj := (a and b) xor (a and c) xor (b and c) - let maj = UInt32::sha256_maj(cs.namespace(|| "maj"), &new_a, &b, &c)?; - - // temp2 := S0 + maj - let temp2 = vec![s0, maj]; - - /* - h := g - g := f - f := e - e := d + temp1 - d := c - c := b - b := a - a := temp1 + temp2 - */ - - h = g; - g = f; - f = new_e; - e = Maybe::Deferred(temp1.iter().cloned().chain(Some(d)).collect::>()); - d = c; - c = b; - b = new_a; - a = Maybe::Deferred( - temp1 - .iter() - .cloned() - .chain(temp2.iter().cloned()) - .collect::>(), - ); - } - - /* - Add the compressed chunk to the current hash value: - h0 := h0 + a - h1 := h1 + b - h2 := h2 + c - h3 := h3 + d - h4 := h4 + e - h5 := h5 + f - h6 := h6 + g - h7 := h7 + h - */ - - let h0 = a.compute( - cs.namespace(|| "deferred h0 computation"), - &[current_hash_value[0].clone()], - )?; - - let h1 = UInt32::addmany( - cs.namespace(|| "new h1"), - &[current_hash_value[1].clone(), b], - )?; - - let h2 = UInt32::addmany( - cs.namespace(|| "new h2"), - &[current_hash_value[2].clone(), c], - )?; - - let h3 = UInt32::addmany( - cs.namespace(|| "new h3"), - &[current_hash_value[3].clone(), d], - )?; - - let h4 = e.compute( - cs.namespace(|| "deferred h4 computation"), - &[current_hash_value[4].clone()], - )?; - - let h5 = UInt32::addmany( - cs.namespace(|| "new h5"), - &[current_hash_value[5].clone(), f], - )?; - - let h6 = UInt32::addmany( - cs.namespace(|| "new h6"), - &[current_hash_value[6].clone(), g], - )?; - - let h7 = UInt32::addmany( - cs.namespace(|| "new h7"), - &[current_hash_value[7].clone(), h], - )?; - - Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7]) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::gadgets::boolean::AllocatedBit; - use crate::gadgets::test::TestConstraintSystem; - use hex_literal::hex; - use pairing::bls12_381::Bls12; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - #[test] - fn test_blank_hash() { - let iv = get_sha256_iv(); - - let mut cs = TestConstraintSystem::::new(); - let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); - input_bits[0] = Boolean::Constant(true); - let out = sha256_compression_function(&mut cs, &input_bits, &iv).unwrap(); - let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect(); - - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 0); - - let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - - let mut out = out_bits.into_iter(); - for b in expected.iter() { - for i in (0..8).rev() { - let c = out.next().unwrap().get_value().unwrap(); - - assert_eq!(c, (b >> i) & 1u8 == 1u8); - } - } - } - - #[test] - fn test_full_block() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let iv = get_sha256_iv(); - - let mut cs = TestConstraintSystem::::new(); - let input_bits: Vec<_> = (0..512) - .map(|i| { - Boolean::from( - AllocatedBit::alloc( - cs.namespace(|| format!("input bit {}", i)), - Some(rng.next_u32() % 2 != 0), - ) - .unwrap(), - ) - }) - .collect(); - - sha256_compression_function(cs.namespace(|| "sha256"), &input_bits, &iv).unwrap(); - - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints() - 512, 25840); - } - - #[test] - fn test_against_vectors() { - use sha2::{Digest, Sha256}; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) { - let mut h = Sha256::new(); - let data: Vec = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - h.input(&data); - let hash_result = h.result(); - - let mut cs = TestConstraintSystem::::new(); - let mut input_bits = vec![]; - - for (byte_i, input_byte) in data.into_iter().enumerate() { - for bit_i in (0..8).rev() { - 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 = sha256(&mut cs, &input_bits).unwrap(); - - assert!(cs.is_satisfied()); - - let mut s = hash_result - .as_ref() - .iter() - .flat_map(|&byte| (0..8).rev().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); - } - } - } - } - } -} diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs deleted file mode 100644 index 0a37cd16ff..0000000000 --- a/bellman/src/gadgets/test/mod.rs +++ /dev/null @@ -1,461 +0,0 @@ -//! Helpers for testing circuit implementations. - -use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; - -use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; - -use std::collections::HashMap; -use std::fmt::Write; -use std::ops::{AddAssign, MulAssign, Neg}; - -use byteorder::{BigEndian, ByteOrder}; -use std::cmp::Ordering; -use std::collections::BTreeMap; - -use blake2s_simd::{Params as Blake2sParams, State as Blake2sState}; - -#[derive(Debug)] -enum NamedObject { - Constraint(usize), - Var(Variable), - Namespace, -} - -/// Constraint system for testing purposes. -pub struct TestConstraintSystem { - named_objects: HashMap, - current_namespace: Vec, - constraints: Vec<( - LinearCombination, - LinearCombination, - LinearCombination, - String, - )>, - inputs: Vec<(E::Fr, String)>, - aux: Vec<(E::Fr, String)>, -} - -#[derive(Clone, Copy)] -struct OrderedVariable(Variable); - -impl Eq for OrderedVariable {} -impl PartialEq for OrderedVariable { - fn eq(&self, other: &OrderedVariable) -> bool { - match (self.0.get_unchecked(), other.0.get_unchecked()) { - (Index::Input(ref a), Index::Input(ref b)) => a == b, - (Index::Aux(ref a), Index::Aux(ref b)) => a == b, - _ => false, - } - } -} -impl PartialOrd for OrderedVariable { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for OrderedVariable { - fn cmp(&self, other: &Self) -> Ordering { - match (self.0.get_unchecked(), other.0.get_unchecked()) { - (Index::Input(ref a), Index::Input(ref b)) => a.cmp(b), - (Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b), - (Index::Input(_), Index::Aux(_)) => Ordering::Less, - (Index::Aux(_), Index::Input(_)) => Ordering::Greater, - } - } -} - -fn proc_lc(terms: &[(Variable, E::Fr)]) -> BTreeMap { - let mut map = BTreeMap::new(); - for &(var, coeff) in terms { - map.entry(OrderedVariable(var)) - .or_insert_with(E::Fr::zero) - .add_assign(&coeff); - } - - // Remove terms that have a zero coefficient to normalize - let mut to_remove = vec![]; - for (var, coeff) in map.iter() { - if coeff.is_zero() { - to_remove.push(var.clone()) - } - } - - for var in to_remove { - map.remove(&var); - } - - map -} - -fn hash_lc(terms: &[(Variable, E::Fr)], h: &mut Blake2sState) { - let map = proc_lc::(terms); - - let mut buf = [0u8; 9 + 32]; - BigEndian::write_u64(&mut buf[0..8], map.len() as u64); - h.update(&buf[0..8]); - - for (var, coeff) in map { - match var.0.get_unchecked() { - Index::Input(i) => { - buf[0] = b'I'; - BigEndian::write_u64(&mut buf[1..9], i as u64); - } - Index::Aux(i) => { - buf[0] = b'A'; - BigEndian::write_u64(&mut buf[1..9], i as u64); - } - } - - coeff.into_repr().write_be(&mut buf[9..]).unwrap(); - - h.update(&buf); - } -} - -fn eval_lc( - terms: &[(Variable, E::Fr)], - inputs: &[(E::Fr, String)], - aux: &[(E::Fr, String)], -) -> E::Fr { - let mut acc = E::Fr::zero(); - - for &(var, ref coeff) in terms { - let mut tmp = match var.get_unchecked() { - Index::Input(index) => inputs[index].0, - Index::Aux(index) => aux[index].0, - }; - - tmp.mul_assign(&coeff); - acc.add_assign(&tmp); - } - - acc -} - -impl TestConstraintSystem { - pub fn new() -> TestConstraintSystem { - let mut map = HashMap::new(); - map.insert( - "ONE".into(), - NamedObject::Var(TestConstraintSystem::::one()), - ); - - TestConstraintSystem { - named_objects: map, - current_namespace: vec![], - constraints: vec![], - inputs: vec![(E::Fr::one(), "ONE".into())], - aux: vec![], - } - } - - pub fn pretty_print(&self) -> String { - let mut s = String::new(); - - let negone = E::Fr::one().neg(); - - let powers_of_two = (0..E::Fr::NUM_BITS) - .map(|i| E::Fr::from_str("2").unwrap().pow_vartime(&[u64::from(i)])) - .collect::>(); - - let pp = |s: &mut String, lc: &LinearCombination| { - write!(s, "(").unwrap(); - let mut is_first = true; - for (var, coeff) in proc_lc::(lc.as_ref()) { - if coeff == negone { - write!(s, " - ").unwrap(); - } else if !is_first { - write!(s, " + ").unwrap(); - } - is_first = false; - - if coeff != E::Fr::one() && coeff != negone { - for (i, x) in powers_of_two.iter().enumerate() { - if x == &coeff { - write!(s, "2^{} . ", i).unwrap(); - break; - } - } - - write!(s, "{} . ", coeff).unwrap(); - } - - match var.0.get_unchecked() { - Index::Input(i) => { - write!(s, "`{}`", &self.inputs[i].1).unwrap(); - } - Index::Aux(i) => { - write!(s, "`{}`", &self.aux[i].1).unwrap(); - } - } - } - if is_first { - // Nothing was visited, print 0. - write!(s, "0").unwrap(); - } - write!(s, ")").unwrap(); - }; - - for &(ref a, ref b, ref c, ref name) in &self.constraints { - write!(&mut s, "\n").unwrap(); - - write!(&mut s, "{}: ", name).unwrap(); - pp(&mut s, a); - write!(&mut s, " * ").unwrap(); - pp(&mut s, b); - write!(&mut s, " = ").unwrap(); - pp(&mut s, c); - } - - write!(&mut s, "\n").unwrap(); - - s - } - - pub fn hash(&self) -> String { - let mut h = Blake2sParams::new().hash_length(32).to_state(); - { - let mut buf = [0u8; 24]; - - BigEndian::write_u64(&mut buf[0..8], self.inputs.len() as u64); - BigEndian::write_u64(&mut buf[8..16], self.aux.len() as u64); - BigEndian::write_u64(&mut buf[16..24], self.constraints.len() as u64); - h.update(&buf); - } - - for constraint in &self.constraints { - hash_lc::(constraint.0.as_ref(), &mut h); - hash_lc::(constraint.1.as_ref(), &mut h); - hash_lc::(constraint.2.as_ref(), &mut h); - } - - let mut s = String::new(); - for b in h.finalize().as_ref() { - s += &format!("{:02x}", b); - } - - s - } - - pub fn which_is_unsatisfied(&self) -> Option<&str> { - for &(ref a, ref b, ref c, ref path) in &self.constraints { - let mut a = eval_lc::(a.as_ref(), &self.inputs, &self.aux); - let b = eval_lc::(b.as_ref(), &self.inputs, &self.aux); - let c = eval_lc::(c.as_ref(), &self.inputs, &self.aux); - - a.mul_assign(&b); - - if a != c { - return Some(&*path); - } - } - - None - } - - pub fn is_satisfied(&self) -> bool { - self.which_is_unsatisfied().is_none() - } - - pub fn num_constraints(&self) -> usize { - self.constraints.len() - } - - pub fn set(&mut self, path: &str, to: E::Fr) { - match self.named_objects.get(path) { - Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { - Index::Input(index) => self.inputs[index].0 = to, - Index::Aux(index) => self.aux[index].0 = to, - }, - Some(e) => panic!( - "tried to set path `{}` to value, but `{:?}` already exists there.", - path, e - ), - _ => panic!("no variable exists at path: {}", path), - } - } - - pub fn verify(&self, expected: &[E::Fr]) -> bool { - assert_eq!(expected.len() + 1, self.inputs.len()); - - for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) { - if &a.0 != b { - return false; - } - } - - true - } - - pub fn num_inputs(&self) -> usize { - self.inputs.len() - } - - pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr { - let (assignment, name) = self.inputs[index].clone(); - - assert_eq!(path, name); - - assignment - } - - pub fn get(&mut self, path: &str) -> E::Fr { - match self.named_objects.get(path) { - Some(&NamedObject::Var(ref v)) => match v.get_unchecked() { - Index::Input(index) => self.inputs[index].0, - Index::Aux(index) => self.aux[index].0, - }, - Some(e) => panic!( - "tried to get value of path `{}`, but `{:?}` exists there (not a variable)", - path, e - ), - _ => panic!("no variable exists at path: {}", path), - } - } - - fn set_named_obj(&mut self, path: String, to: NamedObject) { - if self.named_objects.contains_key(&path) { - panic!("tried to create object at existing path: {}", path); - } - - self.named_objects.insert(path, to); - } -} - -fn compute_path(ns: &[String], this: String) -> String { - if this.chars().any(|a| a == '/') { - panic!("'/' is not allowed in names"); - } - - let mut name = String::new(); - - let mut needs_separation = false; - for ns in ns.iter().chain(Some(&this).into_iter()) { - if needs_separation { - name += "/"; - } - - name += ns; - needs_separation = true; - } - - name -} - -impl ConstraintSystem for TestConstraintSystem { - type Root = Self; - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - let index = self.aux.len(); - let path = compute_path(&self.current_namespace, annotation().into()); - self.aux.push((f()?, path.clone())); - let var = Variable::new_unchecked(Index::Aux(index)); - self.set_named_obj(path, NamedObject::Var(var)); - - Ok(var) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - let index = self.inputs.len(); - let path = compute_path(&self.current_namespace, annotation().into()); - self.inputs.push((f()?, path.clone())); - let var = Variable::new_unchecked(Index::Input(index)); - self.set_named_obj(path, NamedObject::Var(var)); - - Ok(var) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - let path = compute_path(&self.current_namespace, annotation().into()); - let index = self.constraints.len(); - self.set_named_obj(path.clone(), NamedObject::Constraint(index)); - - let a = a(LinearCombination::zero()); - let b = b(LinearCombination::zero()); - let c = c(LinearCombination::zero()); - - self.constraints.push((a, b, c, path)); - } - - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - N: FnOnce() -> NR, - { - let name = name_fn().into(); - let path = compute_path(&self.current_namespace, name.clone()); - self.set_named_obj(path.clone(), NamedObject::Namespace); - self.current_namespace.push(name); - } - - fn pop_namespace(&mut self) { - assert!(self.current_namespace.pop().is_some()); - } - - fn get_root(&mut self) -> &mut Self::Root { - self - } -} - -#[test] -fn test_cs() { - use ff::PrimeField; - use pairing::bls12_381::{Bls12, Fr}; - - let mut cs = TestConstraintSystem::::new(); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 0); - let a = cs - .namespace(|| "a") - .alloc(|| "var", || Ok(Fr::from_str("10").unwrap())) - .unwrap(); - let b = cs - .namespace(|| "b") - .alloc(|| "var", || Ok(Fr::from_str("4").unwrap())) - .unwrap(); - let c = cs - .alloc(|| "product", || Ok(Fr::from_str("40").unwrap())) - .unwrap(); - - cs.enforce(|| "mult", |lc| lc + a, |lc| lc + b, |lc| lc + c); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 1); - - cs.set("a/var", Fr::from_str("4").unwrap()); - - let one = TestConstraintSystem::::one(); - cs.enforce(|| "eq", |lc| lc + a, |lc| lc + one, |lc| lc + b); - - assert!(!cs.is_satisfied()); - assert!(cs.which_is_unsatisfied() == Some("mult")); - - assert!(cs.get("product") == Fr::from_str("40").unwrap()); - - cs.set("product", Fr::from_str("16").unwrap()); - assert!(cs.is_satisfied()); - - { - let mut cs = cs.namespace(|| "test1"); - let mut cs = cs.namespace(|| "test2"); - cs.alloc(|| "hehe", || Ok(Fr::one())).unwrap(); - } - - assert!(cs.get("test1/test2/hehe") == Fr::one()); -} diff --git a/bellman/src/gadgets/uint32.rs b/bellman/src/gadgets/uint32.rs deleted file mode 100644 index 16bb651351..0000000000 --- a/bellman/src/gadgets/uint32.rs +++ /dev/null @@ -1,757 +0,0 @@ -//! Circuit representation of a [`u32`], with helpers for the [`sha256`] -//! gadgets. -//! -//! [`sha256`]: crate::gadgets::sha256 - -use ff::{Field, PrimeField, ScalarEngine}; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError}; - -use super::boolean::{AllocatedBit, Boolean}; - -use super::multieq::MultiEq; - -/// Represents an interpretation of 32 `Boolean` objects as an -/// unsigned integer. -#[derive(Clone)] -pub struct UInt32 { - // Least significant bit first - bits: Vec, - value: Option, -} - -impl UInt32 { - /// Construct a constant `UInt32` from a `u32` - pub fn constant(value: u32) -> Self { - let mut bits = Vec::with_capacity(32); - - let mut tmp = value; - for _ in 0..32 { - if tmp & 1 == 1 { - bits.push(Boolean::constant(true)) - } else { - bits.push(Boolean::constant(false)) - } - - tmp >>= 1; - } - - UInt32 { - bits, - value: Some(value), - } - } - - /// Allocate a `UInt32` in the constraint system - pub fn alloc(mut cs: CS, value: Option) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let values = match value { - Some(mut val) => { - let mut v = Vec::with_capacity(32); - - for _ in 0..32 { - v.push(Some(val & 1 == 1)); - val >>= 1; - } - - v - } - None => vec![None; 32], - }; - - let bits = values - .into_iter() - .enumerate() - .map(|(i, v)| { - Ok(Boolean::from(AllocatedBit::alloc( - cs.namespace(|| format!("allocated bit {}", i)), - v, - )?)) - }) - .collect::, SynthesisError>>()?; - - Ok(UInt32 { bits, value }) - } - - pub fn into_bits_be(self) -> Vec { - let mut ret = self.bits; - ret.reverse(); - ret - } - - pub fn from_bits_be(bits: &[Boolean]) -> Self { - assert_eq!(bits.len(), 32); - - let mut value = Some(0u32); - for b in bits { - value.as_mut().map(|v| *v <<= 1); - - match b.get_value() { - Some(true) => { - value.as_mut().map(|v| *v |= 1); - } - Some(false) => {} - None => { - value = None; - } - } - } - - UInt32 { - value, - bits: bits.iter().rev().cloned().collect(), - } - } - - /// Turns this `UInt32` into its little-endian byte order representation. - pub fn into_bits(self) -> Vec { - self.bits - } - - /// Converts a little-endian byte order representation of bits into a - /// `UInt32`. - pub fn from_bits(bits: &[Boolean]) -> Self { - assert_eq!(bits.len(), 32); - - let new_bits = bits.to_vec(); - - let mut value = Some(0u32); - for b in new_bits.iter().rev() { - value.as_mut().map(|v| *v <<= 1); - - match *b { - Boolean::Constant(b) => { - if b { - value.as_mut().map(|v| *v |= 1); - } - } - Boolean::Is(ref b) => match b.get_value() { - Some(true) => { - value.as_mut().map(|v| *v |= 1); - } - Some(false) => {} - None => value = None, - }, - Boolean::Not(ref b) => match b.get_value() { - Some(false) => { - value.as_mut().map(|v| *v |= 1); - } - Some(true) => {} - None => value = None, - }, - } - } - - UInt32 { - value, - bits: new_bits, - } - } - - pub fn rotr(&self, by: usize) -> Self { - let by = by % 32; - - let new_bits = self - .bits - .iter() - .skip(by) - .chain(self.bits.iter()) - .take(32) - .cloned() - .collect(); - - UInt32 { - bits: new_bits, - value: self.value.map(|v| v.rotate_right(by as u32)), - } - } - - pub fn shr(&self, by: usize) -> Self { - let by = by % 32; - - let fill = Boolean::constant(false); - - let new_bits = self - .bits - .iter() // The bits are least significant first - .skip(by) // Skip the bits that will be lost during the shift - .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros - .take(32) // Only 32 bits needed! - .cloned() - .collect(); - - UInt32 { - bits: new_bits, - value: self.value.map(|v| v >> by as u32), - } - } - - fn triop( - mut cs: CS, - a: &Self, - b: &Self, - c: &Self, - tri_fn: F, - circuit_fn: U, - ) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - F: Fn(u32, u32, u32) -> u32, - U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, - { - let new_value = match (a.value, b.value, c.value) { - (Some(a), Some(b), Some(c)) => Some(tri_fn(a, b, c)), - _ => None, - }; - - let bits = a - .bits - .iter() - .zip(b.bits.iter()) - .zip(c.bits.iter()) - .enumerate() - .map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c)) - .collect::>()?; - - Ok(UInt32 { - bits, - value: new_value, - }) - } - - /// Compute the `maj` value (a and b) xor (a and c) xor (b and c) - /// during SHA256. - pub fn sha256_maj(cs: CS, a: &Self, b: &Self, c: &Self) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - Self::triop( - cs, - a, - b, - c, - |a, b, c| (a & b) ^ (a & c) ^ (b & c), - |cs, i, a, b, c| Boolean::sha256_maj(cs.namespace(|| format!("maj {}", i)), a, b, c), - ) - } - - /// Compute the `ch` value `(a and b) xor ((not a) and c)` - /// during SHA256. - pub fn sha256_ch(cs: CS, a: &Self, b: &Self, c: &Self) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - Self::triop( - cs, - a, - b, - c, - |a, b, c| (a & b) ^ ((!a) & c), - |cs, i, a, b, c| Boolean::sha256_ch(cs.namespace(|| format!("ch {}", i)), a, b, c), - ) - } - - /// XOR this `UInt32` with another `UInt32` - pub fn xor(&self, mut cs: CS, other: &Self) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - { - let new_value = match (self.value, other.value) { - (Some(a), Some(b)) => Some(a ^ b), - _ => None, - }; - - let bits = self - .bits - .iter() - .zip(other.bits.iter()) - .enumerate() - .map(|(i, (a, b))| Boolean::xor(cs.namespace(|| format!("xor of bit {}", i)), a, b)) - .collect::>()?; - - Ok(UInt32 { - bits, - value: new_value, - }) - } - - /// Perform modular addition of several `UInt32` objects. - pub fn addmany(mut cs: M, operands: &[Self]) -> Result - where - E: ScalarEngine, - CS: ConstraintSystem, - M: ConstraintSystem>, - { - // Make some arbitrary bounds for ourselves to avoid overflows - // in the scalar field - assert!(E::Fr::NUM_BITS >= 64); - assert!(operands.len() >= 2); // Weird trivial cases that should never happen - assert!(operands.len() <= 10); - - // Compute the maximum value of the sum so we allocate enough bits for - // the result - let mut max_value = (operands.len() as u64) * (u64::from(u32::max_value())); - - // Keep track of the resulting value - let mut result_value = Some(0u64); - - // This is a linear combination that we will enforce to equal the - // output - let mut lc = LinearCombination::zero(); - - let mut all_constants = true; - - // Iterate over the operands - for op in operands { - // Accumulate the value - match op.value { - Some(val) => { - result_value.as_mut().map(|v| *v += u64::from(val)); - } - None => { - // If any of our operands have unknown value, we won't - // know the value of the result - result_value = None; - } - } - - // Iterate over each bit of the operand and add the operand to - // the linear combination - let mut coeff = E::Fr::one(); - for bit in &op.bits { - lc = lc + &bit.lc(CS::one(), coeff); - - all_constants &= bit.is_constant(); - - coeff = coeff.double(); - } - } - - // The value of the actual result is modulo 2^32 - let modular_value = result_value.map(|v| v as u32); - - if all_constants && modular_value.is_some() { - // We can just return a constant, rather than - // unpacking the result into allocated bits. - - return Ok(UInt32::constant(modular_value.unwrap())); - } - - // Storage area for the resulting bits - let mut result_bits = vec![]; - - // Linear combination representing the output, - // for comparison with the sum of the operands - let mut result_lc = LinearCombination::zero(); - - // Allocate each bit of the result - let mut coeff = E::Fr::one(); - let mut i = 0; - while max_value != 0 { - // Allocate the bit - let b = AllocatedBit::alloc( - cs.namespace(|| format!("result bit {}", i)), - result_value.map(|v| (v >> i) & 1 == 1), - )?; - - // Add this bit to the result combination - result_lc = result_lc + (coeff, b.get_variable()); - - result_bits.push(b.into()); - - max_value >>= 1; - i += 1; - coeff = coeff.double(); - } - - // Enforce equality between the sum and result - cs.get_root().enforce_equal(i, &lc, &result_lc); - - // Discard carry bits that we don't care about - result_bits.truncate(32); - - Ok(UInt32 { - bits: result_bits, - value: modular_value, - }) - } -} - -#[cfg(test)] -mod test { - use super::UInt32; - use crate::gadgets::boolean::Boolean; - use crate::gadgets::multieq::MultiEq; - use crate::gadgets::test::*; - use crate::ConstraintSystem; - use ff::Field; - use pairing::bls12_381::Bls12; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - - #[test] - fn test_uint32_from_bits_be() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let v = (0..32) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect::>(); - - let b = UInt32::from_bits_be(&v); - - for (i, bit) in b.bits.iter().enumerate() { - match *bit { - Boolean::Constant(bit) => { - assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); - } - _ => unreachable!(), - } - } - - let expected_to_be_same = b.into_bits_be(); - - for x in v.iter().zip(expected_to_be_same.iter()) { - match x { - (&Boolean::Constant(true), &Boolean::Constant(true)) => {} - (&Boolean::Constant(false), &Boolean::Constant(false)) => {} - _ => unreachable!(), - } - } - } - } - - #[test] - fn test_uint32_from_bits() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let v = (0..32) - .map(|_| Boolean::constant(rng.next_u32() % 2 != 0)) - .collect::>(); - - let b = UInt32::from_bits(&v); - - for (i, bit) in b.bits.iter().enumerate() { - match *bit { - Boolean::Constant(bit) => { - assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); - } - _ => unreachable!(), - } - } - - let expected_to_be_same = b.into_bits(); - - for x in v.iter().zip(expected_to_be_same.iter()) { - match x { - (&Boolean::Constant(true), &Boolean::Constant(true)) => {} - (&Boolean::Constant(false), &Boolean::Constant(false)) => {} - _ => unreachable!(), - } - } - } - } - - #[test] - fn test_uint32_xor() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = a ^ b ^ c; - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap(); - - let r = a_bit.xor(cs.namespace(|| "first xor"), &b_bit).unwrap(); - let r = r.xor(cs.namespace(|| "second xor"), &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(ref b) => { - assert!(b.get_value().unwrap() == (expected & 1 == 1)); - } - Boolean::Not(ref b) => { - assert!(!b.get_value().unwrap() == (expected & 1 == 1)); - } - Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_addmany_constants() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let a_bit = UInt32::constant(a); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::constant(c); - - let mut expected = a.wrapping_add(b).wrapping_add(c); - - let r = { - let mut cs = MultiEq::new(&mut cs); - let r = - UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); - r - }; - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(_) => panic!(), - Boolean::Not(_) => panic!(), - Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_addmany() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - let d = rng.next_u32(); - - let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::constant(c); - let d_bit = UInt32::alloc(cs.namespace(|| "d_bit"), Some(d)).unwrap(); - - let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); - let r = { - let mut cs = MultiEq::new(&mut cs); - UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap() - }; - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match *b { - Boolean::Is(ref b) => { - assert!(b.get_value().unwrap() == (expected & 1 == 1)); - } - Boolean::Not(ref b) => { - assert!(!b.get_value().unwrap() == (expected & 1 == 1)); - } - Boolean::Constant(_) => unreachable!(), - } - - expected >>= 1; - } - - // Flip a bit and see if the addition constraint still works - if cs.get("addition/result bit 0/boolean").is_zero() { - cs.set("addition/result bit 0/boolean", Field::one()); - } else { - cs.set("addition/result bit 0/boolean", Field::zero()); - } - - assert!(!cs.is_satisfied()); - } - } - - #[test] - fn test_uint32_rotr() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let mut num = rng.next_u32(); - - let a = UInt32::constant(num); - - for i in 0..32 { - let b = a.rotr(i); - assert_eq!(a.bits.len(), b.bits.len()); - - assert!(b.value.unwrap() == num); - - let mut tmp = num; - for b in &b.bits { - match *b { - Boolean::Constant(b) => { - assert_eq!(b, tmp & 1 == 1); - } - _ => unreachable!(), - } - - tmp >>= 1; - } - - num = num.rotate_right(1); - } - } - - #[test] - fn test_uint32_shr() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..50 { - for i in 0..60 { - let num = rng.next_u32(); - let a = UInt32::constant(num).shr(i); - let b = UInt32::constant(num.wrapping_shr(i as u32)); - - assert_eq!(a.value.unwrap(), num.wrapping_shr(i as u32)); - - assert_eq!(a.bits.len(), b.bits.len()); - for (a, b) in a.bits.iter().zip(b.bits.iter()) { - assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); - } - } - } - } - - #[test] - fn test_uint32_sha256_maj() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = (a & b) ^ (a & c) ^ (b & c); - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap(); - - let r = UInt32::sha256_maj(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match b { - &Boolean::Is(ref b) => { - assert!(b.get_value().unwrap() == (expected & 1 == 1)); - } - &Boolean::Not(ref b) => { - assert!(!b.get_value().unwrap() == (expected & 1 == 1)); - } - &Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } - - #[test] - fn test_uint32_sha256_ch() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); - - let a = rng.next_u32(); - let b = rng.next_u32(); - let c = rng.next_u32(); - - let mut expected = (a & b) ^ ((!a) & c); - - let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); - let b_bit = UInt32::constant(b); - let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap(); - - let r = UInt32::sha256_ch(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); - - assert!(cs.is_satisfied()); - - assert!(r.value == Some(expected)); - - for b in r.bits.iter() { - match b { - &Boolean::Is(ref b) => { - assert!(b.get_value().unwrap() == (expected & 1 == 1)); - } - &Boolean::Not(ref b) => { - assert!(!b.get_value().unwrap() == (expected & 1 == 1)); - } - &Boolean::Constant(b) => { - assert!(b == (expected & 1 == 1)); - } - } - - expected >>= 1; - } - } - } -} diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs deleted file mode 100644 index d99383555e..0000000000 --- a/bellman/src/groth16/generator.rs +++ /dev/null @@ -1,492 +0,0 @@ -use rand_core::RngCore; -use std::ops::{AddAssign, MulAssign}; -use std::sync::Arc; - -use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective, Wnaf}; -use pairing::Engine; - -use super::{Parameters, VerifyingKey}; - -use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; - -use crate::domain::{EvaluationDomain, Scalar}; - -use crate::multicore::Worker; - -/// Generates a random common reference string for -/// a circuit. -pub fn generate_random_parameters( - circuit: C, - rng: &mut R, -) -> Result, SynthesisError> -where - E: Engine, - C: Circuit, - R: RngCore, -{ - let g1 = E::G1::random(rng); - let g2 = E::G2::random(rng); - let alpha = E::Fr::random(rng); - let beta = E::Fr::random(rng); - let gamma = E::Fr::random(rng); - let delta = E::Fr::random(rng); - let tau = E::Fr::random(rng); - - generate_parameters::(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 { - num_inputs: usize, - num_aux: usize, - num_constraints: usize, - at_inputs: Vec>, - bt_inputs: Vec>, - ct_inputs: Vec>, - at_aux: Vec>, - bt_aux: Vec>, - ct_aux: Vec>, -} - -impl ConstraintSystem for KeypairAssembly { - type Root = Self; - - fn alloc(&mut self, _: A, _: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - // 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(&mut self, _: A, _: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - // 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(&mut self, _: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - fn eval( - l: LinearCombination, - 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(&mut self, _: N) - where - NR: Into, - 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( - circuit: C, - g1: E::G1, - g2: E::G2, - alpha: E::Fr, - beta: E::Fr, - gamma: E::Fr, - delta: E::Fr, - tau: E::Fr, -) -> Result, SynthesisError> -where - E: Engine, - C: Circuit, -{ - 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::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 = { - let inverse = gamma.invert(); - if bool::from(inverse.is_some()) { - Ok(inverse.unwrap()) - } else { - Err(SynthesisError::UnexpectedIdentity) - } - }?; - let delta_inverse = { - let inverse = delta.invert(); - if bool::from(inverse.is_some()) { - Ok(inverse.unwrap()) - } else { - Err(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 |_scope| { - let mut current_tau_power = tau.pow_vartime(&[(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 |_scope| { - // 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( - // wNAF window tables - g1_wnaf: &Wnaf>, - g2_wnaf: &Wnaf>, - - // Lagrange coefficients for tau - powers_of_tau: &[Scalar], - - // 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 |_scope| { - 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( - powers_of_tau: &[Scalar], - 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:: { - 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, - 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(), - ), - }) -} diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs deleted file mode 100644 index 6f5af85fc7..0000000000 --- a/bellman/src/groth16/mod.rs +++ /dev/null @@ -1,559 +0,0 @@ -//! The [Groth16] proving system. -//! -//! [Groth16]: https://eprint.iacr.org/2016/260 - -use group::{CurveAffine, EncodedPoint}; -use pairing::{Engine, PairingCurveAffine}; - -use crate::SynthesisError; - -use crate::multiexp::SourceBuilder; -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use std::io::{self, Read, Write}; -use std::sync::Arc; - -#[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 { - pub a: E::G1Affine, - pub b: E::G2Affine, - pub c: E::G1Affine, -} - -impl PartialEq for Proof { - fn eq(&self, other: &Self) -> bool { - self.a == other.a && self.b == other.b && self.c == other.c - } -} - -impl Proof { - pub fn 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(mut reader: R) -> io::Result { - let mut g1_repr = ::Compressed::empty(); - let mut g2_repr = ::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, b, c }) - } -} - -#[derive(Clone)] -pub struct VerifyingKey { - // 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, -} - -impl PartialEq for VerifyingKey { - 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 VerifyingKey { - pub fn 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::(self.ic.len() as u32)?; - for ic in &self.ic { - writer.write_all(ic.into_uncompressed().as_ref())?; - } - - Ok(()) - } - - pub fn read(mut reader: R) -> io::Result { - let mut g1_repr = ::Uncompressed::empty(); - let mut g2_repr = ::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::()? 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, - beta_g1, - beta_g2, - gamma_g2, - delta_g1, - delta_g2, - ic, - }) - } -} - -#[derive(Clone)] -pub struct Parameters { - pub vk: VerifyingKey, - - // 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>, - - // 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>, - - // 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>, - - // 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>, - pub b_g2: Arc>, -} - -impl PartialEq for Parameters { - 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 Parameters { - pub fn write(&self, mut writer: W) -> io::Result<()> { - self.vk.write(&mut writer)?; - - writer.write_u32::(self.h.len() as u32)?; - for g in &self.h[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - - writer.write_u32::(self.l.len() as u32)?; - for g in &self.l[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - - writer.write_u32::(self.a.len() as u32)?; - for g in &self.a[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - - writer.write_u32::(self.b_g1.len() as u32)?; - for g in &self.b_g1[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - - writer.write_u32::(self.b_g2.len() as u32)?; - for g in &self.b_g2[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - - Ok(()) - } - - pub fn read(mut reader: R, checked: bool) -> io::Result { - let read_g1 = |reader: &mut R| -> io::Result { - let mut repr = ::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 { - let mut repr = ::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::::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::()? as usize; - for _ in 0..len { - h.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - l.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - a.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - b_g1.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - b_g2.push(read_g2(&mut reader)?); - } - } - - Ok(Parameters { - 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 { - /// Pairing result of alpha*beta - alpha_g1_beta_g2: E::Fqk, - /// -gamma in G2 - neg_gamma_g2: ::Prepared, - /// -delta in G2 - neg_delta_g2: ::Prepared, - /// Copy of IC from `VerifiyingKey`. - ic: Vec, -} - -pub trait ParameterSource { - type G1Builder: SourceBuilder; - type G2Builder: SourceBuilder; - - fn get_vk(&mut self, num_ic: usize) -> Result, SynthesisError>; - fn get_h(&mut self, num_h: usize) -> Result; - fn get_l(&mut self, num_l: usize) -> Result; - 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 for &'a Parameters { - type G1Builder = (Arc>, usize); - type G2Builder = (Arc>, usize); - - fn get_vk(&mut self, _: usize) -> Result, SynthesisError> { - Ok(self.vk.clone()) - } - - fn get_h(&mut self, _: usize) -> Result { - Ok((self.h.clone(), 0)) - } - - fn get_l(&mut self, _: usize) -> Result { - 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 crate::{Circuit, ConstraintSystem, SynthesisError}; - - use ff::Field; - use pairing::bls12_381::{Bls12, Fr}; - use rand::thread_rng; - use std::ops::MulAssign; - - #[test] - fn serialization() { - struct MySillyCircuit { - a: Option, - b: Option, - } - - impl Circuit for MySillyCircuit { - fn synthesize>( - 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::(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::(¶ms.vk); - - for _ in 0..100 { - let a = Fr::random(rng); - let b = Fr::random(rng); - let mut c = a; - c.mul_assign(&b); - - let proof = create_random_proof( - MySillyCircuit { - a: Some(a), - b: Some(b), - }, - ¶ms, - 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()); - } - } -} diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs deleted file mode 100644 index c31b4db97a..0000000000 --- a/bellman/src/groth16/prover.rs +++ /dev/null @@ -1,351 +0,0 @@ -use rand_core::RngCore; -use std::ops::{AddAssign, MulAssign}; -use std::sync::Arc; - -use futures::Future; - -use ff::{Field, PrimeField}; -use group::{CurveAffine, CurveProjective}; -use pairing::Engine; - -use super::{ParameterSource, Proof}; - -use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; - -use crate::domain::{EvaluationDomain, Scalar}; - -use crate::multiexp::{multiexp, DensityTracker, FullDensity}; - -use crate::multicore::Worker; - -fn eval( - lc: &LinearCombination, - 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 { - // Density of queries - a_aux_density: DensityTracker, - b_input_density: DensityTracker, - b_aux_density: DensityTracker, - - // Evaluations of A, B, C polynomials - a: Vec>, - b: Vec>, - c: Vec>, - - // Assignments of variables - input_assignment: Vec, - aux_assignment: Vec, -} - -impl ConstraintSystem for ProvingAssignment { - type Root = Self; - - fn alloc(&mut self, _: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - 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(&mut self, _: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.input_assignment.push(f()?); - self.b_input_density.add_element(); - - Ok(Variable(Index::Input(self.input_assignment.len() - 1))) - } - - fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - 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(&mut self, _: N) - where - NR: Into, - 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>( - circuit: C, - params: P, - rng: &mut R, -) -> Result, SynthesisError> -where - E: Engine, - C: Circuit, - R: RngCore, -{ - let r = E::Fr::random(rng); - let s = E::Fr::random(rng); - - create_proof::(circuit, params, r, s) -} - -pub fn create_proof>( - circuit: C, - mut params: P, - r: E::Fr, - s: E::Fr, -) -> Result, SynthesisError> -where - E: Engine, - C: Circuit, -{ - 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::>()); - - 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::>(), - ); - let aux_assignment = Arc::new( - prover - .aux_assignment - .into_iter() - .map(|s| s.into_repr()) - .collect::>(), - ); - - 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); - AddAssign::<&E::G1Affine>::add_assign(&mut g_a, &vk.alpha_g1); - let mut g_b = vk.delta_g2.mul(s); - AddAssign::<&E::G2Affine>::add_assign(&mut g_b, &vk.beta_g2); - let mut g_c; - { - let mut rs = r; - rs.mul_assign(&s); - - g_c = vk.delta_g1.mul(rs); - AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.alpha_g1.mul(s)); - AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.beta_g1.mul(r)); - } - let mut a_answer = a_inputs.wait()?; - AddAssign::<&E::G1>::add_assign(&mut a_answer, &a_aux.wait()?); - AddAssign::<&E::G1>::add_assign(&mut g_a, &a_answer); - a_answer.mul_assign(s); - AddAssign::<&E::G1>::add_assign(&mut g_c, &a_answer); - - let mut b1_answer: E::G1 = b_g1_inputs.wait()?; - AddAssign::<&E::G1>::add_assign(&mut b1_answer, &b_g1_aux.wait()?); - let mut b2_answer = b_g2_inputs.wait()?; - AddAssign::<&E::G2>::add_assign(&mut b2_answer, &b_g2_aux.wait()?); - - AddAssign::<&E::G2>::add_assign(&mut g_b, &b2_answer); - b1_answer.mul_assign(r); - AddAssign::<&E::G1>::add_assign(&mut g_c, &b1_answer); - AddAssign::<&E::G1>::add_assign(&mut g_c, &h.wait()?); - AddAssign::<&E::G1>::add_assign(&mut g_c, &l.wait()?); - - Ok(Proof { - a: g_a.into_affine(), - b: g_b.into_affine(), - c: g_c.into_affine(), - }) -} diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs deleted file mode 100644 index 6c011c3ea0..0000000000 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ /dev/null @@ -1,520 +0,0 @@ -use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, SqrtField}; -use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; -use pairing::{Engine, PairingCurveAffine}; - -use rand_core::RngCore; -use std::cmp::Ordering; -use std::fmt; -use std::num::Wrapping; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -const MODULUS_R: Wrapping = Wrapping(64513); - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Fr(Wrapping); - -impl Default for Fr { - fn default() -> Self { - ::zero() - } -} - -impl ConstantTimeEq for Fr { - fn ct_eq(&self, other: &Fr) -> Choice { - (self.0).0.ct_eq(&(other.0).0) - } -} - -impl fmt::Display for Fr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", (self.0).0) - } -} - -impl ConditionallySelectable for Fr { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fr(Wrapping(u32::conditional_select( - &(a.0).0, - &(b.0).0, - choice, - ))) - } -} - -impl Neg for Fr { - type Output = Self; - - fn neg(mut self) -> Self { - if !::is_zero(&self) { - self.0 = MODULUS_R - self.0; - } - self - } -} - -impl<'r> Add<&'r Fr> for Fr { - type Output = Self; - - fn add(self, other: &Self) -> Self { - let mut ret = self; - AddAssign::add_assign(&mut ret, other); - ret - } -} - -impl Add for Fr { - type Output = Self; - - fn add(self, other: Self) -> Self { - self + &other - } -} - -impl<'r> AddAssign<&'r Fr> for Fr { - fn add_assign(&mut self, other: &Self) { - self.0 = (self.0 + other.0) % MODULUS_R; - } -} - -impl AddAssign for Fr { - fn add_assign(&mut self, other: Self) { - AddAssign::add_assign(self, &other); - } -} - -impl<'r> Sub<&'r Fr> for Fr { - type Output = Self; - - fn sub(self, other: &Self) -> Self { - let mut ret = self; - SubAssign::sub_assign(&mut ret, other); - ret - } -} - -impl Sub for Fr { - type Output = Self; - - fn sub(self, other: Self) -> Self { - self - &other - } -} - -impl<'r> SubAssign<&'r Fr> for Fr { - fn sub_assign(&mut self, other: &Self) { - self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R; - } -} - -impl SubAssign for Fr { - fn sub_assign(&mut self, other: Self) { - SubAssign::sub_assign(self, &other); - } -} - -impl<'r> Mul<&'r Fr> for Fr { - type Output = Self; - - fn mul(self, other: &Self) -> Self { - let mut ret = self; - MulAssign::mul_assign(&mut ret, other); - ret - } -} - -impl Mul for Fr { - type Output = Self; - - fn mul(self, other: Self) -> Self { - self * &other - } -} - -impl<'r> MulAssign<&'r Fr> for Fr { - fn mul_assign(&mut self, other: &Self) { - self.0 = (self.0 * other.0) % MODULUS_R; - } -} - -impl MulAssign for Fr { - fn mul_assign(&mut self, other: Self) { - MulAssign::mul_assign(self, &other); - } -} - -impl Field for Fr { - fn random(rng: &mut R) -> Self { - Fr(Wrapping(rng.next_u32()) % MODULUS_R) - } - - fn zero() -> Self { - Fr(Wrapping(0)) - } - - fn one() -> Self { - Fr(Wrapping(1)) - } - - fn is_zero(&self) -> bool { - (self.0).0 == 0 - } - - fn square(&self) -> Self { - Fr((self.0 * self.0) % MODULUS_R) - } - - fn double(&self) -> Self { - Fr((self.0 << 1) % MODULUS_R) - } - - fn invert(&self) -> CtOption { - if ::is_zero(self) { - CtOption::new(::zero(), Choice::from(0)) - } else { - CtOption::new( - self.pow_vartime(&[(MODULUS_R.0 as u64) - 2]), - Choice::from(1), - ) - } - } - - fn frobenius_map(&mut self, _: usize) { - // identity - } -} - -impl SqrtField for Fr { - fn sqrt(&self) -> CtOption { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - let mut c = Fr::root_of_unity(); - // r = self^((t + 1) // 2) - let mut r = self.pow_vartime([32]); - // t = self^t - let mut t = self.pow_vartime([63]); - let mut m = Fr::S; - - while t != ::one() { - let mut i = 1; - { - let mut t2i = t.square(); - loop { - if t2i == ::one() { - break; - } - t2i = t2i.square(); - i += 1; - } - } - - for _ in 0..(m - i - 1) { - c = c.square(); - } - MulAssign::mul_assign(&mut r, &c); - c = c.square(); - MulAssign::mul_assign(&mut t, &c); - m = i; - } - - CtOption::new(r, (r * r).ct_eq(self)) - } -} - -#[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 { - Some(self.cmp(other)) - } -} - -impl fmt::Display for FrRepr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", (self.0)[0]) - } -} - -impl From for FrRepr { - fn from(v: u64) -> FrRepr { - FrRepr([v]) - } -} - -impl From 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 { - if repr.0[0] >= (MODULUS_R.0 as u64) { - Err(PrimeFieldDecodingError::NotInField) - } 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 ::Prepared, - &'a ::Prepared, - ), - >, - { - let mut acc = ::zero(); - - for &(a, b) in i { - let mut tmp = *a; - MulAssign::mul_assign(&mut tmp, b); - AddAssign::add_assign(&mut acc, &tmp); - } - - acc - } - - /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(this: &Self::Fqk) -> CtOption { - CtOption::new(*this, Choice::from(1)) - } -} - -impl CurveProjective for Fr { - type Affine = Fr; - type Base = Fr; - type Scalar = Fr; - type Engine = DummyEngine; - - fn random(rng: &mut R) -> Self { - ::random(rng) - } - - fn zero() -> Self { - ::zero() - } - - fn one() -> Self { - ::one() - } - - fn is_zero(&self) -> bool { - ::is_zero(self) - } - - fn batch_normalization(_: &mut [Self]) {} - - fn is_normalized(&self) -> bool { - true - } - - fn double(&mut self) { - self.0 = ::double(self).0; - } - - fn mul_assign::Repr>>(&mut self, other: S) { - let tmp = Fr::from_repr(other.into()).unwrap(); - - MulAssign::mul_assign(self, &tmp); - } - - fn into_affine(&self) -> Fr { - *self - } - - fn recommended_wnaf_for_scalar(_: ::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 { - unimplemented!() - } - - fn into_affine_unchecked(&self) -> Result { - 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 { - ::zero() - } - - fn one() -> Self { - ::one() - } - - fn is_zero(&self) -> bool { - ::is_zero(self) - } - - fn mul::Repr>>(&self, other: S) -> Self::Projective { - let mut res = *self; - let tmp = Fr::from_repr(other.into()).unwrap(); - - MulAssign::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) - } -} diff --git a/bellman/src/groth16/tests/mod.rs b/bellman/src/groth16/tests/mod.rs deleted file mode 100644 index 5c2f02dbb2..0000000000 --- a/bellman/src/groth16/tests/mod.rs +++ /dev/null @@ -1,382 +0,0 @@ -use ff::{Field, PrimeField}; -use pairing::Engine; - -mod dummy_engine; -use self::dummy_engine::*; - -use std::marker::PhantomData; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -use crate::{Circuit, ConstraintSystem, SynthesisError}; - -use super::{create_proof, generate_parameters, prepare_verifying_key, verify_proof}; - -struct XORDemo { - a: Option, - b: Option, - _marker: PhantomData, -} - -impl Circuit for XORDemo { - fn synthesize>(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:: { - 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_vartime(&[1 << 10])); - - // Let's turn it into a 2^3 root of unity. - root_of_unity = root_of_unity.pow_vartime(&[1 << 7]); - assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[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_vartime(&[i])); - } - - // Let's compute t(tau) = (tau - p_0)(tau - p_1)... - // = tau^8 - 1 - let mut t_at_tau = tau.pow_vartime(&[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.invert().unwrap(); - let gamma_inverse = gamma.invert().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. = 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::>(); - let v_i = [0, 0, 60619, 30791] - .iter() - .map(|e| Fr::from_str(&format!("{}", e)).unwrap()) - .collect::>(); - let w_i = [0, 23320, 41193, 41193] - .iter() - .map(|e| Fr::from_str(&format!("{}", e)).unwrap()) - .collect::>(); - - for (u, a) in u_i.iter().zip(¶ms.a[..]) { - assert_eq!(u, a); - } - - for (v, b) in v_i - .iter() - .filter(|&&e| e != Fr::zero()) - .zip(¶ms.b_g1[..]) - { - assert_eq!(v, b); - } - - for (v, b) in v_i - .iter() - .filter(|&&e| e != Fr::zero()) - .zip(¶ms.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(¶ms.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, ¶ms, 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(¶ms.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()); -} diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs deleted file mode 100644 index 5983667158..0000000000 --- a/bellman/src/groth16/verifier.rs +++ /dev/null @@ -1,55 +0,0 @@ -use ff::PrimeField; -use group::{CurveAffine, CurveProjective}; -use pairing::{Engine, PairingCurveAffine}; -use std::ops::{AddAssign, Neg}; - -use super::{PreparedVerifyingKey, Proof, VerifyingKey}; - -use crate::SynthesisError; - -pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyingKey { - let gamma = vk.gamma_g2.neg(); - let delta = vk.delta_g2.neg(); - - 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, - proof: &Proof, - public_inputs: &[E::Fr], -) -> Result { - 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)) { - AddAssign::<&E::G1>::add_assign(&mut acc, &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), - ] - .iter(), - )) - .unwrap() - == pvk.alpha_g1_beta_g2) -} diff --git a/bellman/src/lib.rs b/bellman/src/lib.rs deleted file mode 100644 index 1e48b0c790..0000000000 --- a/bellman/src/lib.rs +++ /dev/null @@ -1,535 +0,0 @@ -//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit -//! traits and and primitive structures, as well as basic gadget implementations -//! such as booleans and number abstractions. -//! -//! # Example circuit -//! -//! Say we want to write a circuit that proves we know the preimage to some hash -//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a -//! fixed length known in advance (because the circuit parameters will depend on -//! it), but can otherwise have any value. We take the following strategy: -//! -//! - Witness each bit of the preimage. -//! - Compute `hash = SHA-256d(preimage)` inside the circuit. -//! - Expose `hash` as a public input using multiscalar packing. -//! -//! ``` -//! use bellman::{ -//! gadgets::{ -//! boolean::{AllocatedBit, Boolean}, -//! multipack, -//! sha256::sha256, -//! }, -//! groth16, Circuit, ConstraintSystem, SynthesisError, -//! }; -//! use pairing::{bls12_381::Bls12, Engine}; -//! use rand::rngs::OsRng; -//! use sha2::{Digest, Sha256}; -//! -//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order. -//! fn sha256d>( -//! mut cs: CS, -//! data: &[Boolean], -//! ) -> Result, SynthesisError> { -//! // Flip endianness of each input byte -//! let input: Vec<_> = data -//! .chunks(8) -//! .map(|c| c.iter().rev()) -//! .flatten() -//! .cloned() -//! .collect(); -//! -//! let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?; -//! let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?; -//! -//! // Flip endianness of each output byte -//! Ok(res -//! .chunks(8) -//! .map(|c| c.iter().rev()) -//! .flatten() -//! .cloned() -//! .collect()) -//! } -//! -//! struct MyCircuit { -//! /// The input to SHA-256d we are proving that we know. Set to `None` when we -//! /// are verifying a proof (and do not have the witness data). -//! preimage: Option<[u8; 80]>, -//! } -//! -//! impl Circuit for MyCircuit { -//! fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { -//! // Compute the values for the bits of the preimage. If we are verifying a proof, -//! // we still need to create the same constraints, so we return an equivalent-size -//! // Vec of None (indicating that the value of each bit is unknown). -//! let bit_values = if let Some(preimage) = self.preimage { -//! preimage -//! .into_iter() -//! .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)) -//! .flatten() -//! .map(|b| Some(b)) -//! .collect() -//! } else { -//! vec![None; 80 * 8] -//! }; -//! assert_eq!(bit_values.len(), 80 * 8); -//! -//! // Witness the bits of the preimage. -//! let preimage_bits = bit_values -//! .into_iter() -//! .enumerate() -//! // Allocate each bit. -//! .map(|(i, b)| { -//! AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b) -//! }) -//! // Convert the AllocatedBits into Booleans (required for the sha256 gadget). -//! .map(|b| b.map(Boolean::from)) -//! .collect::, _>>()?; -//! -//! // Compute hash = SHA-256d(preimage). -//! let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?; -//! -//! // Expose the vector of 32 boolean variables as compact public inputs. -//! multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash) -//! } -//! } -//! -//! // Create parameters for our circuit. In a production deployment these would -//! // be generated securely using a multiparty computation. -//! let params = { -//! let c = MyCircuit { preimage: None }; -//! groth16::generate_random_parameters::(c, &mut OsRng).unwrap() -//! }; -//! -//! // Prepare the verification key (for proof verification). -//! let pvk = groth16::prepare_verifying_key(¶ms.vk); -//! -//! // Pick a preimage and compute its hash. -//! let preimage = [42; 80]; -//! let hash = Sha256::digest(&Sha256::digest(&preimage)); -//! -//! // Create an instance of our circuit (with the preimage as a witness). -//! let c = MyCircuit { -//! preimage: Some(preimage), -//! }; -//! -//! // Create a Groth16 proof with our parameters. -//! let proof = groth16::create_random_proof(c, ¶ms, &mut OsRng).unwrap(); -//! -//! // Pack the hash as inputs for proof verification. -//! let hash_bits = multipack::bytes_to_bits_le(&hash); -//! let inputs = multipack::compute_multipacking::(&hash_bits); -//! -//! // Check the proof! -//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap()); -//! ``` -//! -//! # Roadmap -//! -//! `bellman` is being refactored into a generic proving library. Currently it -//! is pairing-specific, and different types of proving systems need to be -//! implemented as sub-modules. After the refactor, `bellman` will be generic -//! using the [`ff`] and [`group`] crates, while specific proving systems will -//! be separate crates that pull in the dependencies they require. - -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] - -pub mod domain; -pub mod gadgets; -#[cfg(feature = "groth16")] -pub mod groth16; -pub mod multicore; -mod multiexp; - -use ff::{Field, ScalarEngine}; - -use std::error::Error; -use std::fmt; -use std::io; -use std::marker::PhantomData; -use std::ops::{Add, MulAssign, Neg, Sub}; - -/// 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 { - /// Synthesize the circuit into a rank-1 quadratic constraint system - fn synthesize>(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(Vec<(Variable, E::Fr)>); - -impl AsRef<[(Variable, E::Fr)]> for LinearCombination { - fn as_ref(&self) -> &[(Variable, E::Fr)] { - &self.0 - } -} - -impl LinearCombination { - pub fn zero() -> LinearCombination { - LinearCombination(vec![]) - } -} - -impl Add<(E::Fr, Variable)> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { - self.0.push((var, coeff)); - - self - } -} - -impl Sub<(E::Fr, Variable)> for LinearCombination { - type Output = LinearCombination; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { - self + (coeff.neg(), var) - } -} - -impl Add for LinearCombination { - type Output = LinearCombination; - - fn add(self, other: Variable) -> LinearCombination { - self + (E::Fr::one(), other) - } -} - -impl Sub for LinearCombination { - type Output = LinearCombination; - - fn sub(self, other: Variable) -> LinearCombination { - self - (E::Fr::one(), other) - } -} - -impl<'a, E: ScalarEngine> Add<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, other: &'a LinearCombination) -> LinearCombination { - for s in &other.0 { - self = self + (s.1, s.0); - } - - self - } -} - -impl<'a, E: ScalarEngine> Sub<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { - for s in &other.0 { - self = self - (s.1, s.0); - } - - self - } -} - -impl<'a, E: ScalarEngine> Add<(E::Fr, &'a LinearCombination)> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { - 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)> for LinearCombination { - type Output = LinearCombination; - - fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { - 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 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: Sized { - /// Represents the type of the "root" of this constraint system - /// so that nested namespaces can minimize indirection. - type Root: ConstraintSystem; - - /// 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(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into; - - /// Allocate a public variable in the constraint system. The provided function is used to - /// determine the assignment of the variable. - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into; - - /// 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(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination; - - /// Create a new (sub)namespace and enter into it. Not intended - /// for downstream use; use `namespace` instead. - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - 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(&mut self, name_fn: N) -> Namespace<'_, E, Self::Root> - where - NR: Into, - 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>(&'a mut CS, PhantomData); - -impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for Namespace<'cs, E, CS> { - type Root = CS::Root; - - fn one() -> Variable { - CS::one() - } - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.0.alloc(annotation, f) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.0.alloc_input(annotation, f) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - 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(&mut self, _: N) - where - NR: Into, - 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> Drop for Namespace<'a, E, CS> { - fn drop(&mut self) { - self.get_root().pop_namespace() - } -} - -/// Convenience implementation of ConstraintSystem for mutable references to -/// constraint systems. -impl<'cs, E: ScalarEngine, CS: ConstraintSystem> ConstraintSystem for &'cs mut CS { - type Root = CS::Root; - - fn one() -> Variable { - CS::one() - } - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - (**self).alloc(annotation, f) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - (**self).alloc_input(annotation, f) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - (**self).enforce(annotation, a, b, c) - } - - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - 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() - } -} diff --git a/bellman/src/multicore.rs b/bellman/src/multicore.rs deleted file mode 100644 index ba69b5f338..0000000000 --- a/bellman/src/multicore.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! An interface for dealing with the kinds of parallel computations involved in -//! `bellman`. It's currently just a thin wrapper around [`CpuPool`] and -//! [`crossbeam`] but may be extended in the future to allow for various -//! parallelism strategies. -//! -//! [`CpuPool`]: futures_cpupool::CpuPool - -#[cfg(feature = "multicore")] -mod implementation { - use crossbeam::{self, thread::Scope}; - use futures::{Future, IntoFuture, Poll}; - use futures_cpupool::{CpuFuture, CpuPool}; - use num_cpus; - - #[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, - 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(&self, f: F) -> WorkerFuture - 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 - }; - - // TODO: Handle case where threads fail - crossbeam::scope(|scope| f(scope, chunk_size)) - .expect("Threads aren't allowed to fail yet") - } - } - - pub struct WorkerFuture { - future: CpuFuture, - } - - impl Future for WorkerFuture { - type Item = T; - type Error = E; - - fn poll(&mut self) -> Poll { - 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(&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(&self, elements: usize, f: F) -> R - where - F: FnOnce(&DummyScope, usize) -> R, - { - f(&DummyScope, elements) - } - } - - pub struct WorkerFuture { - future: future::FutureResult, - } - - impl Future for WorkerFuture { - type Item = T; - type Error = E; - - fn poll(&mut self) -> Poll { - self.future.poll() - } - } - - pub struct DummyScope; - - impl DummyScope { - pub fn spawn(&self, f: F) { - f(self); - } - } -} - -pub use self::implementation::*; diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs deleted file mode 100644 index 0bc61ba148..0000000000 --- a/bellman/src/multiexp.rs +++ /dev/null @@ -1,330 +0,0 @@ -use super::multicore::Worker; -use bit_vec::{self, BitVec}; -use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; -use futures::Future; -use group::{CurveAffine, CurveProjective}; -use std::io; -use std::iter; -use std::ops::AddAssign; -use std::sync::Arc; - -use super::SynthesisError; - -/// An object that builds a source of bases. -pub trait SourceBuilder: Send + Sync + 'static + Clone { - type Source: Source; - - fn new(self) -> Self::Source; -} - -/// A source of bases, like an iterator. -pub trait Source { - fn next(&mut self) -> Result<&G, SynthesisError>; - - /// Skips `amt` elements from the source, avoiding deserialization. - fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; -} - -pub trait AddAssignFromSource: CurveProjective { - /// Parses the element from the source. Fails if the point is at infinity. - fn add_assign_from_source::Affine>>( - &mut self, - source: &mut S, - ) -> Result<(), SynthesisError> { - AddAssign::<&::Affine>::add_assign(self, source.next()?); - Ok(()) - } -} -impl AddAssignFromSource for G where G: CurveProjective {} - -impl SourceBuilder for (Arc>, usize) { - type Source = (Arc>, usize); - - fn new(self) -> (Arc>, usize) { - (self.0.clone(), self.1) - } -} - -impl Source for (Arc>, usize) { - fn next(&mut self) -> Result<&G, 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); - } - - let ret = &self.0[self.1]; - self.1 += 1; - - Ok(ret) - } - - 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; - - fn iter(self) -> Self::Iter; - fn get_query_size(self) -> Option; -} - -#[derive(Clone)] -pub struct FullDensity; - -impl AsRef for FullDensity { - fn as_ref(&self) -> &FullDensity { - self - } -} - -impl<'a> QueryDensity for &'a FullDensity { - type Iter = iter::Repeat; - - fn iter(self) -> Self::Iter { - iter::repeat(true) - } - - fn get_query_size(self) -> Option { - 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 { - 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( - pool: &Worker, - bases: S, - density_map: D, - exponents: Arc::Fr as PrimeField>::Repr>>, - mut skip: u32, - c: u32, - handle_trivial: bool, -) -> Box> -where - for<'a> &'a Q: QueryDensity, - D: Send + Sync + 'static + Clone + AsRef, - G: CurveProjective, - S: SourceBuilder<::Affine>, -{ - // 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::zero(); - - // Build a source for the bases - let mut bases = bases.new(); - - // Create space for the buckets - let mut buckets = vec![G::zero(); (1 << c) - 1]; - - let zero = ::Fr::zero().into_repr(); - let one = ::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 { - acc.add_assign_from_source(&mut bases)?; - } else { - bases.skip(1)?; - } - } else { - let mut exp = exp; - exp.shr(skip); - let exp = exp.as_ref()[0] % (1 << c); - - if exp != 0 { - (&mut buckets[(exp - 1) as usize]) - .add_assign_from_source(&mut bases)?; - } else { - bases.skip(1)?; - } - } - } - } - - // Summation by parts - // e.g. 3a + 2b + 1c = a + - // (a) + b + - // ((a) + b) + c - let mut running_sum = G::zero(); - for exp in buckets.into_iter().rev() { - running_sum.add_assign(&exp); - acc.add_assign(&running_sum); - } - - Ok(acc) - }) - }; - - skip += c; - - if skip >= ::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): (_, G)| { - 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( - pool: &Worker, - bases: S, - density_map: D, - exponents: Arc::Fr as PrimeField>::Repr>>, -) -> Box> -where - for<'a> &'a Q: QueryDensity, - D: Send + Sync + 'static + Clone + AsRef, - G: CurveProjective, - S: SourceBuilder<::Affine>, -{ - 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( - bases: Arc::Affine>>, - exponents: Arc::Repr>>, - ) -> G { - assert_eq!(bases.len(), exponents.len()); - - let mut acc = G::zero(); - - for (base, exp) in bases.iter().zip(exponents.iter()) { - AddAssign::<&G>::add_assign(&mut acc, &base.mul(*exp)); - } - - acc - } - - use pairing::{bls12_381::Bls12, Engine}; - use rand; - - const SAMPLES: usize = 1 << 14; - - let rng = &mut rand::thread_rng(); - let v = Arc::new( - (0..SAMPLES) - .map(|_| ::Fr::random(rng).into_repr()) - .collect::>(), - ); - let g = Arc::new( - (0..SAMPLES) - .map(|_| ::G1::random(rng).into_affine()) - .collect::>(), - ); - - let naive: ::G1 = naive_multiexp(g.clone(), v.clone()); - - let pool = Worker::new(); - - let fast = multiexp(&pool, (g, 0), FullDensity, v).wait().unwrap(); - - assert_eq!(naive, fast); -} diff --git a/bellman/tests/mimc.rs b/bellman/tests/mimc.rs deleted file mode 100644 index a1de0f1087..0000000000 --- a/bellman/tests/mimc.rs +++ /dev/null @@ -1,228 +0,0 @@ -// For randomness (during paramgen and proof generation) -use rand::thread_rng; - -// For benchmarking -use std::time::{Duration, Instant}; - -// Bring in some tools for using pairing-friendly curves -use ff::{Field, ScalarEngine}; -use pairing::Engine; -use std::ops::{AddAssign, MulAssign}; - -// 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::{ - create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof, 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(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.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, - xr: Option, - 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 for MiMCDemo<'a, E> { - fn synthesize>(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 tmp_value = xl_value.map(|mut e| { - e.add_assign(&self.constants[i]); - e.square() - }); - let 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 new_xl_value = xl_value.map(|mut e| { - e.add_assign(&self.constants[i]); - e.mul_assign(&tmp_value.unwrap()); - e.add_assign(&xr_value.unwrap()); - e - }); - - let new_xl = if i == (MIMC_ROUNDS - 1) { - // 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(|_| ::Fr::random(rng)) - .collect::>(); - - println!("Creating parameters..."); - - // Create parameters for our circuit - let params = { - let c = MiMCDemo:: { - xl: None, - xr: None, - constants: &constants, - }; - - generate_random_parameters(c, rng).unwrap() - }; - - // Prepare the verification key (for proof verification) - let pvk = prepare_verifying_key(¶ms.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 = ::Fr::random(rng); - let xr = ::Fr::random(rng); - let image = mimc::(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, ¶ms, 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); -} diff --git a/bls12_381/.github/workflows/ci.yml b/bls12_381/.github/workflows/ci.yml deleted file mode 100644 index 39066dbfd9..0000000000 --- a/bls12_381/.github/workflows/ci.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: CI checks - -on: [push, pull_request] - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - # Ensure all code has been formatted with rustfmt - - run: rustup component add rustfmt - - name: Check formatting - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check --color always - - test: - name: Test on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - name: Build tests - uses: actions-rs/cargo@v1 - with: - command: build - args: --verbose --release --tests - - name: Run tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose --release - - no-std: - name: Check no-std compatibility - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - run: rustup target add thumbv6m-none-eabi - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --verbose --target thumbv6m-none-eabi --no-default-features --features groups,pairings - - doc-links: - name: Nightly lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - # Ensure intra-documentation links all resolve correctly - # Requires #![deny(intra_doc_link_resolution_failure)] in crate. - - name: Check intra-doc links - uses: actions-rs/cargo@v1 - with: - command: doc - args: --document-private-items diff --git a/bls12_381/.gitignore b/bls12_381/.gitignore deleted file mode 100644 index 2f88dbac54..0000000000 --- a/bls12_381/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock \ No newline at end of file diff --git a/bls12_381/COPYRIGHT b/bls12_381/COPYRIGHT deleted file mode 100644 index 7764b866e0..0000000000 --- a/bls12_381/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyrights in the "bls12_381" library are retained by their contributors. No -copyright assignment is required to contribute to the "bls12_381" library. - -The "bls12_381" 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. diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml deleted file mode 100644 index 6e77fb51b3..0000000000 --- a/bls12_381/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -authors = ["Sean Bowe "] -description = "Implementation of the BLS12-381 pairing-friendly elliptic curve construction" -documentation = "https://docs.rs/bls12_381/" -homepage = "https://github.com/zkcrypto/bls12_381" -license = "MIT/Apache-2.0" -name = "bls12_381" -repository = "https://github.com/zkcrypto/bls12_381" -version = "0.1.0" -edition = "2018" - -[package.metadata.docs.rs] -rustdoc-args = [ "--html-in-header", "katex-header.html" ] - -[dev-dependencies] -criterion = "0.3" - -[[bench]] -name = "groups" -harness = false -required-features = ["groups"] - -[dependencies.subtle] -version = "2.2.1" -default-features = false - -[features] -default = ["groups", "pairings", "alloc"] -groups = [] -pairings = ["groups"] -alloc = [] -nightly = ["subtle/nightly"] diff --git a/bls12_381/LICENSE-APACHE b/bls12_381/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/bls12_381/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/bls12_381/LICENSE-MIT b/bls12_381/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/bls12_381/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/bls12_381/README.md b/bls12_381/README.md deleted file mode 100644 index ba61f300ba..0000000000 --- a/bls12_381/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# bls12_381 [![Crates.io](https://img.shields.io/crates/v/bls12_381.svg)](https://crates.io/crates/bls12_381) # - -This crate provides an implementation of the BLS12-381 pairing-friendly elliptic curve construction. - -* **This implementation has not been reviewed or audited. Use at your own risk.** -* This implementation targets Rust `1.36` or later. -* This implementation does not require the Rust standard library. -* All operations are constant time unless explicitly noted. - -## Features - -* `groups` (on by default): Enables APIs for performing group arithmetic with G1, G2, and GT. -* `pairings` (on by default): Enables some APIs for performing pairings. -* `alloc` (on by default): Enables APIs that require an allocator; these include pairing optimizations. -* `nightly`: Enables `subtle/nightly` which tries to prevent compiler optimizations that could jeopardize constant time operations. Requires the nightly Rust compiler. - -## [Documentation](https://docs.rs/bls12_381) - -## Curve Description - -BLS12-381 is a pairing-friendly elliptic curve construction from the [BLS family](https://eprint.iacr.org/2002/088), with embedding degree 12. It is built over a 381-bit prime field `GF(p)` with... - -* z = `-0xd201000000010000` -* p = (z - 1)2(z4 - z2 + 1) / 3 + z - * = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` -* q = z4 - z2 + 1 - * = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` - -... yielding two **source groups** G1 and G2, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** GT. Specifically, G1 is the `q`-order subgroup of E(Fp) : y2 = x3 + 4 and G2 is the `q`-order subgroup of E'(Fp2) : y2 = x3 + 4(u + 1) where the extention field Fp2 is defined as Fp(u) / (u2 + 1). - -BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing performance) and also so that `GF(q)` has a large 232 primitive root of unity for performing radix-2 fast Fourier transforms for efficient multi-point evaluation and interpolation. It is also chosen so that it exists in a particularly efficient and rigid subfamily of BLS12 curves. - -### Curve Security - -Pairing-friendly elliptic curve constructions are (necessarily) less secure than conventional elliptic curves due to their small "embedding degree". Given a small enough embedding degree, the pairing function itself would allow for a break in DLP hardness if it projected into a weak target group, as weaknesses in this target group are immediately translated into weaknesses in the source group. - -In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yeild a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level. - -There are [known optimizations](https://ellipticnews.wordpress.com/2016/05/02/kim-barbulescu-variant-of-the-number-field-sieve-to-compute-discrete-logarithms-in-finite-fields/) of the [Number Field Sieve algorithm](https://en.wikipedia.org/wiki/General_number_field_sieve) which could be used to weaken DLP security in the target group by taking advantage of its structure, as it is a multiplicative subgroup of a low-degree extension field. However, these attacks require an (as of yet unknown) efficient algorithm for scanning a large space of polynomials. Even if the attack were practical it would only reduce security to roughly 117 to 120 bits. (This contrasts with 254-bit BN curves which usually have less than 100 bits of security in the same situation.) - -### Alternative Curves - -Applications may wish to exchange pairing performance and/or G2 performance by using BLS24 or KSS16 curves which conservatively target 128-bit security. In applications that need cycles of elliptic curves for e.g. arbitrary proof composition, MNT6/MNT4 curve cycles are known that target the 128-bit security level. In applications that only need fixed-depth proof composition, curves of this form have been constructed as part of Zexe. - -## Acknowledgements - -Please see `Cargo.toml` for a list of primary authors of this codebase. - -## 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. diff --git a/bls12_381/RELEASES.md b/bls12_381/RELEASES.md deleted file mode 100644 index 69afd5204d..0000000000 --- a/bls12_381/RELEASES.md +++ /dev/null @@ -1,3 +0,0 @@ -# 0.1.0 - -Initial release. diff --git a/bls12_381/benches/groups.rs b/bls12_381/benches/groups.rs deleted file mode 100644 index 87c80d029c..0000000000 --- a/bls12_381/benches/groups.rs +++ /dev/null @@ -1,170 +0,0 @@ -#[macro_use] -extern crate criterion; - -extern crate bls12_381; -use bls12_381::*; - -use criterion::{black_box, Criterion}; - -fn criterion_benchmark(c: &mut Criterion) { - // Pairings - { - let g = G1Affine::generator(); - let h = G2Affine::generator(); - c.bench_function("full pairing", move |b| { - b.iter(|| pairing(black_box(&g), black_box(&h))) - }); - c.bench_function("G2 preparation for pairing", move |b| { - b.iter(|| G2Prepared::from(h)) - }); - let prep = G2Prepared::from(h); - c.bench_function("miller loop for pairing", move |b| { - b.iter(|| multi_miller_loop(&[(&g, &prep)])) - }); - let prep = G2Prepared::from(h); - let r = multi_miller_loop(&[(&g, &prep)]); - c.bench_function("final exponentiation for pairing", move |b| { - b.iter(|| r.final_exponentiation()) - }); - } - // G1Affine - { - let name = "G1Affine"; - let a = G1Affine::generator(); - let s = Scalar::from_raw([1, 2, 3, 4]); - let compressed = [0u8; 48]; - let uncompressed = [0u8; 96]; - c.bench_function(&format!("{} check on curve", name), move |b| { - b.iter(|| black_box(a).is_on_curve()) - }); - c.bench_function(&format!("{} check equality", name), move |b| { - b.iter(|| black_box(a) == black_box(a)) - }); - c.bench_function(&format!("{} scalar multiplication", name), move |b| { - b.iter(|| black_box(a) * black_box(s)) - }); - c.bench_function(&format!("{} subgroup check", name), move |b| { - b.iter(|| black_box(a).is_torsion_free()) - }); - c.bench_function( - &format!("{} deserialize compressed point", name), - move |b| b.iter(|| G1Affine::from_compressed(black_box(&compressed))), - ); - c.bench_function( - &format!("{} deserialize uncompressed point", name), - move |b| b.iter(|| G1Affine::from_uncompressed(black_box(&uncompressed))), - ); - } - - // G1Projective - { - let name = "G1Projective"; - let a = G1Projective::generator(); - let a_affine = G1Affine::generator(); - let s = Scalar::from_raw([1, 2, 3, 4]); - - const N: usize = 10000; - let v = vec![G1Projective::generator(); N]; - let mut q = vec![G1Affine::identity(); N]; - - c.bench_function(&format!("{} check on curve", name), move |b| { - b.iter(|| black_box(a).is_on_curve()) - }); - c.bench_function(&format!("{} check equality", name), move |b| { - b.iter(|| black_box(a) == black_box(a)) - }); - c.bench_function(&format!("{} to affine", name), move |b| { - b.iter(|| G1Affine::from(black_box(a))) - }); - c.bench_function(&format!("{} doubling", name), move |b| { - b.iter(|| black_box(a).double()) - }); - c.bench_function(&format!("{} addition", name), move |b| { - b.iter(|| black_box(a).add(&a)) - }); - c.bench_function(&format!("{} mixed addition", name), move |b| { - b.iter(|| black_box(a).add_mixed(&a_affine)) - }); - c.bench_function(&format!("{} scalar multiplication", name), move |b| { - b.iter(|| black_box(a) * black_box(s)) - }); - c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| { - b.iter(|| { - G1Projective::batch_normalize(black_box(&v), black_box(&mut q)); - black_box(&q)[0] - }) - }); - } - - // G2Affine - { - let name = "G2Affine"; - let a = G2Affine::generator(); - let s = Scalar::from_raw([1, 2, 3, 4]); - let compressed = [0u8; 96]; - let uncompressed = [0u8; 192]; - c.bench_function(&format!("{} check on curve", name), move |b| { - b.iter(|| black_box(a).is_on_curve()) - }); - c.bench_function(&format!("{} check equality", name), move |b| { - b.iter(|| black_box(a) == black_box(a)) - }); - c.bench_function(&format!("{} scalar multiplication", name), move |b| { - b.iter(|| black_box(a) * black_box(s)) - }); - c.bench_function(&format!("{} subgroup check", name), move |b| { - b.iter(|| black_box(a).is_torsion_free()) - }); - c.bench_function( - &format!("{} deserialize compressed point", name), - move |b| b.iter(|| G2Affine::from_compressed(black_box(&compressed))), - ); - c.bench_function( - &format!("{} deserialize uncompressed point", name), - move |b| b.iter(|| G2Affine::from_uncompressed(black_box(&uncompressed))), - ); - } - - // G2Projective - { - let name = "G2Projective"; - let a = G2Projective::generator(); - let a_affine = G2Affine::generator(); - let s = Scalar::from_raw([1, 2, 3, 4]); - - const N: usize = 10000; - let v = vec![G2Projective::generator(); N]; - let mut q = vec![G2Affine::identity(); N]; - - c.bench_function(&format!("{} check on curve", name), move |b| { - b.iter(|| black_box(a).is_on_curve()) - }); - c.bench_function(&format!("{} check equality", name), move |b| { - b.iter(|| black_box(a) == black_box(a)) - }); - c.bench_function(&format!("{} to affine", name), move |b| { - b.iter(|| G2Affine::from(black_box(a))) - }); - c.bench_function(&format!("{} doubling", name), move |b| { - b.iter(|| black_box(a).double()) - }); - c.bench_function(&format!("{} addition", name), move |b| { - b.iter(|| black_box(a).add(&a)) - }); - c.bench_function(&format!("{} mixed addition", name), move |b| { - b.iter(|| black_box(a).add_mixed(&a_affine)) - }); - c.bench_function(&format!("{} scalar multiplication", name), move |b| { - b.iter(|| black_box(a) * black_box(s)) - }); - c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| { - b.iter(|| { - G2Projective::batch_normalize(black_box(&v), black_box(&mut q)); - black_box(&q)[0] - }) - }); - } -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/bls12_381/katex-header.html b/bls12_381/katex-header.html deleted file mode 100644 index 98e85904fa..0000000000 --- a/bls12_381/katex-header.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - \ No newline at end of file diff --git a/bls12_381/rust-toolchain b/bls12_381/rust-toolchain deleted file mode 100644 index d70132e1de..0000000000 --- a/bls12_381/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.36.0 \ No newline at end of file diff --git a/bls12_381/src/fp.rs b/bls12_381/src/fp.rs deleted file mode 100644 index 28aa24bf88..0000000000 --- a/bls12_381/src/fp.rs +++ /dev/null @@ -1,866 +0,0 @@ -//! This module provides an implementation of the BLS12-381 base field `GF(p)` -//! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` - -use core::convert::TryFrom; -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::util::{adc, mac, sbb}; - -// The internal representation of this type is six 64-bit unsigned -// integers in little-endian order. `Fp` values are always in -// Montgomery form; i.e., Scalar(a) = aR mod p, with R = 2^384. -#[derive(Copy, Clone)] -pub struct Fp([u64; 6]); - -impl fmt::Debug for Fp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_bytes(); - write!(f, "0x")?; - for &b in tmp.iter() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } -} - -impl Default for Fp { - fn default() -> Self { - Fp::zero() - } -} - -impl ConstantTimeEq for Fp { - fn ct_eq(&self, other: &Self) -> Choice { - self.0[0].ct_eq(&other.0[0]) - & self.0[1].ct_eq(&other.0[1]) - & self.0[2].ct_eq(&other.0[2]) - & self.0[3].ct_eq(&other.0[3]) - & self.0[4].ct_eq(&other.0[4]) - & self.0[5].ct_eq(&other.0[5]) - } -} - -impl Eq for Fp {} -impl PartialEq for Fp { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 - } -} - -impl ConditionallySelectable for Fp { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fp([ - u64::conditional_select(&a.0[0], &b.0[0], choice), - u64::conditional_select(&a.0[1], &b.0[1], choice), - u64::conditional_select(&a.0[2], &b.0[2], choice), - u64::conditional_select(&a.0[3], &b.0[3], choice), - u64::conditional_select(&a.0[4], &b.0[4], choice), - u64::conditional_select(&a.0[5], &b.0[5], choice), - ]) - } -} - -/// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 -const MODULUS: [u64; 6] = [ - 0xb9fe_ffff_ffff_aaab, - 0x1eab_fffe_b153_ffff, - 0x6730_d2a0_f6b0_f624, - 0x6477_4b84_f385_12bf, - 0x4b1b_a7b6_434b_acd7, - 0x1a01_11ea_397f_e69a, -]; - -/// INV = -(p^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0x89f3_fffc_fffc_fffd; - -/// R = 2^384 mod p -const R: Fp = Fp([ - 0x7609_0000_0002_fffd, - 0xebf4_000b_c40c_0002, - 0x5f48_9857_53c7_58ba, - 0x77ce_5853_7052_5745, - 0x5c07_1a97_a256_ec6d, - 0x15f6_5ec3_fa80_e493, -]); - -/// R2 = 2^(384*2) mod p -const R2: Fp = Fp([ - 0xf4df_1f34_1c34_1746, - 0x0a76_e6a6_09d1_04f1, - 0x8de5_476c_4c95_b6d5, - 0x67eb_88a9_939d_83c0, - 0x9a79_3e85_b519_952d, - 0x1198_8fe5_92ca_e3aa, -]); - -impl<'a> Neg for &'a Fp { - type Output = Fp; - - #[inline] - fn neg(self) -> Fp { - self.neg() - } -} - -impl Neg for Fp { - type Output = Fp; - - #[inline] - fn neg(self) -> Fp { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fp> for &'a Fp { - type Output = Fp; - - #[inline] - fn sub(self, rhs: &'b Fp) -> Fp { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fp> for &'a Fp { - type Output = Fp; - - #[inline] - fn add(self, rhs: &'b Fp) -> Fp { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fp> for &'a Fp { - type Output = Fp; - - #[inline] - fn mul(self, rhs: &'b Fp) -> Fp { - self.mul(rhs) - } -} - -impl_binops_additive!(Fp, Fp); -impl_binops_multiplicative!(Fp, Fp); - -impl Fp { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> Fp { - Fp([0, 0, 0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> Fp { - R - } - - pub fn is_zero(&self) -> Choice { - self.ct_eq(&Fp::zero()) - } - - /// Attempts to convert a little-endian byte representation of - /// a scalar into an `Fp`, failing if the input is not canonical. - pub fn from_bytes(bytes: &[u8; 48]) -> CtOption { - let mut tmp = Fp([0, 0, 0, 0, 0, 0]); - - tmp.0[5] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); - tmp.0[4] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); - tmp.0[3] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); - tmp.0[2] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); - tmp.0[1] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()); - tmp.0[0] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow); - let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow); - let (_, borrow) = sbb(tmp.0[5], MODULUS[5], borrow); - - // If the element is smaller than MODULUS then the - // subtraction will underflow, producing a borrow value - // of 0xffff...ffff. Otherwise, it'll be zero. - let is_some = (borrow as u8) & 1; - - // Convert to Montgomery form by computing - // (a.R^0 * R^2) / R = a.R - tmp *= &R2; - - CtOption::new(tmp, Choice::from(is_some)) - } - - /// Converts an element of `Fp` into a byte representation in - /// big-endian byte order. - pub fn to_bytes(&self) -> [u8; 48] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Fp::montgomery_reduce( - self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, - ); - - let mut res = [0; 48]; - res[0..8].copy_from_slice(&tmp.0[5].to_be_bytes()); - res[8..16].copy_from_slice(&tmp.0[4].to_be_bytes()); - res[16..24].copy_from_slice(&tmp.0[3].to_be_bytes()); - res[24..32].copy_from_slice(&tmp.0[2].to_be_bytes()); - res[32..40].copy_from_slice(&tmp.0[1].to_be_bytes()); - res[40..48].copy_from_slice(&tmp.0[0].to_be_bytes()); - - res - } - - /// Returns whether or not this element is strictly lexicographically - /// larger than its negation. - pub fn lexicographically_largest(&self) -> Choice { - // This can be determined by checking to see if the element is - // larger than (p - 1) // 2. If we subtract by ((p - 1) // 2) + 1 - // and there is no underflow, then the element must be larger than - // (p - 1) // 2. - - // First, because self is in Montgomery form we need to reduce it - let tmp = Fp::montgomery_reduce( - self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0, - ); - - let (_, borrow) = sbb(tmp.0[0], 0xdcff_7fff_ffff_d556, 0); - let (_, borrow) = sbb(tmp.0[1], 0x0f55_ffff_58a9_ffff, borrow); - let (_, borrow) = sbb(tmp.0[2], 0xb398_6950_7b58_7b12, borrow); - let (_, borrow) = sbb(tmp.0[3], 0xb23b_a5c2_79c2_895f, borrow); - let (_, borrow) = sbb(tmp.0[4], 0x258d_d3db_21a5_d66b, borrow); - let (_, borrow) = sbb(tmp.0[5], 0x0d00_88f5_1cbf_f34d, borrow); - - // If the element was smaller, the subtraction will underflow - // producing a borrow value of 0xffff...ffff, otherwise it will - // be zero. We create a Choice representing true if there was - // overflow (and so this element is not lexicographically larger - // than its negation) and then negate it. - - !Choice::from((borrow as u8) & 1) - } - - /// Constructs an element of `Fp` without checking that it is - /// canonical. - pub const fn from_raw_unchecked(v: [u64; 6]) -> Fp { - Fp(v) - } - - /// Although this is labeled "vartime", it is only - /// variable time with respect to the exponent. It - /// is also not exposed in the public API. - pub fn pow_vartime(&self, by: &[u64; 6]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - res *= self; - } - } - } - res - } - - #[inline] - pub fn sqrt(&self) -> CtOption { - // We use Shank's method, as p = 3 (mod 4). This means - // we only need to exponentiate by (p+1)/4. This only - // works for elements that are actually quadratic residue, - // so we check that we got the correct result at the end. - - let sqrt = self.pow_vartime(&[ - 0xee7f_bfff_ffff_eaab, - 0x07aa_ffff_ac54_ffff, - 0xd9cc_34a8_3dac_3d89, - 0xd91d_d2e1_3ce1_44af, - 0x92c6_e9ed_90d2_eb35, - 0x0680_447a_8e5f_f9a6, - ]); - - CtOption::new(sqrt, sqrt.square().ct_eq(self)) - } - - #[inline] - /// Computes the multiplicative inverse of this field - /// element, returning None in the case that this element - /// is zero. - pub fn invert(&self) -> CtOption { - // Exponentiate by p - 2 - let t = self.pow_vartime(&[ - 0xb9fe_ffff_ffff_aaa9, - 0x1eab_fffe_b153_ffff, - 0x6730_d2a0_f6b0_f624, - 0x6477_4b84_f385_12bf, - 0x4b1b_a7b6_434b_acd7, - 0x1a01_11ea_397f_e69a, - ]); - - CtOption::new(t, !self.is_zero()) - } - - #[inline] - const fn subtract_p(&self) -> Fp { - let (r0, borrow) = sbb(self.0[0], MODULUS[0], 0); - let (r1, borrow) = sbb(self.0[1], MODULUS[1], borrow); - let (r2, borrow) = sbb(self.0[2], MODULUS[2], borrow); - let (r3, borrow) = sbb(self.0[3], MODULUS[3], borrow); - let (r4, borrow) = sbb(self.0[4], MODULUS[4], borrow); - let (r5, borrow) = sbb(self.0[5], MODULUS[5], borrow); - - // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise - // borrow = 0x000...000. Thus, we use it as a mask! - let r0 = (self.0[0] & borrow) | (r0 & !borrow); - let r1 = (self.0[1] & borrow) | (r1 & !borrow); - let r2 = (self.0[2] & borrow) | (r2 & !borrow); - let r3 = (self.0[3] & borrow) | (r3 & !borrow); - let r4 = (self.0[4] & borrow) | (r4 & !borrow); - let r5 = (self.0[5] & borrow) | (r5 & !borrow); - - Fp([r0, r1, r2, r3, r4, r5]) - } - - #[inline] - pub const fn add(&self, rhs: &Fp) -> Fp { - let (d0, carry) = adc(self.0[0], rhs.0[0], 0); - let (d1, carry) = adc(self.0[1], rhs.0[1], carry); - let (d2, carry) = adc(self.0[2], rhs.0[2], carry); - let (d3, carry) = adc(self.0[3], rhs.0[3], carry); - let (d4, carry) = adc(self.0[4], rhs.0[4], carry); - let (d5, _) = adc(self.0[5], rhs.0[5], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Fp([d0, d1, d2, d3, d4, d5])).subtract_p() - } - - #[inline] - pub const fn neg(&self) -> Fp { - let (d0, borrow) = sbb(MODULUS[0], self.0[0], 0); - let (d1, borrow) = sbb(MODULUS[1], self.0[1], borrow); - let (d2, borrow) = sbb(MODULUS[2], self.0[2], borrow); - let (d3, borrow) = sbb(MODULUS[3], self.0[3], borrow); - let (d4, borrow) = sbb(MODULUS[4], self.0[4], borrow); - let (d5, _) = sbb(MODULUS[5], self.0[5], borrow); - - // Let's use a mask if `self` was zero, which would mean - // the result of the subtraction is p. - let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4] | self.0[5]) == 0) - as u64) - .wrapping_sub(1); - - Fp([ - d0 & mask, - d1 & mask, - d2 & mask, - d3 & mask, - d4 & mask, - d5 & mask, - ]) - } - - #[inline] - pub const fn sub(&self, rhs: &Fp) -> Fp { - (&rhs.neg()).add(self) - } - - #[inline(always)] - const fn montgomery_reduce( - t0: u64, - t1: u64, - t2: u64, - t3: u64, - t4: u64, - t5: u64, - t6: u64, - t7: u64, - t8: u64, - t9: u64, - t10: u64, - t11: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = t0.wrapping_mul(INV); - let (_, carry) = mac(t0, k, MODULUS[0], 0); - let (r1, carry) = mac(t1, k, MODULUS[1], carry); - let (r2, carry) = mac(t2, k, MODULUS[2], carry); - let (r3, carry) = mac(t3, k, MODULUS[3], carry); - let (r4, carry) = mac(t4, k, MODULUS[4], carry); - let (r5, carry) = mac(t5, k, MODULUS[5], carry); - let (r6, r7) = adc(t6, 0, carry); - - let k = r1.wrapping_mul(INV); - let (_, carry) = mac(r1, k, MODULUS[0], 0); - let (r2, carry) = mac(r2, k, MODULUS[1], carry); - let (r3, carry) = mac(r3, k, MODULUS[2], carry); - let (r4, carry) = mac(r4, k, MODULUS[3], carry); - let (r5, carry) = mac(r5, k, MODULUS[4], carry); - let (r6, carry) = mac(r6, k, MODULUS[5], carry); - let (r7, r8) = adc(t7, r7, carry); - - let k = r2.wrapping_mul(INV); - let (_, carry) = mac(r2, k, MODULUS[0], 0); - let (r3, carry) = mac(r3, k, MODULUS[1], carry); - let (r4, carry) = mac(r4, k, MODULUS[2], carry); - let (r5, carry) = mac(r5, k, MODULUS[3], carry); - let (r6, carry) = mac(r6, k, MODULUS[4], carry); - let (r7, carry) = mac(r7, k, MODULUS[5], carry); - let (r8, r9) = adc(t8, r8, carry); - - let k = r3.wrapping_mul(INV); - let (_, carry) = mac(r3, k, MODULUS[0], 0); - let (r4, carry) = mac(r4, k, MODULUS[1], carry); - let (r5, carry) = mac(r5, k, MODULUS[2], carry); - let (r6, carry) = mac(r6, k, MODULUS[3], carry); - let (r7, carry) = mac(r7, k, MODULUS[4], carry); - let (r8, carry) = mac(r8, k, MODULUS[5], carry); - let (r9, r10) = adc(t9, r9, carry); - - let k = r4.wrapping_mul(INV); - let (_, carry) = mac(r4, k, MODULUS[0], 0); - let (r5, carry) = mac(r5, k, MODULUS[1], carry); - let (r6, carry) = mac(r6, k, MODULUS[2], carry); - let (r7, carry) = mac(r7, k, MODULUS[3], carry); - let (r8, carry) = mac(r8, k, MODULUS[4], carry); - let (r9, carry) = mac(r9, k, MODULUS[5], carry); - let (r10, r11) = adc(t10, r10, carry); - - let k = r5.wrapping_mul(INV); - let (_, carry) = mac(r5, k, MODULUS[0], 0); - let (r6, carry) = mac(r6, k, MODULUS[1], carry); - let (r7, carry) = mac(r7, k, MODULUS[2], carry); - let (r8, carry) = mac(r8, k, MODULUS[3], carry); - let (r9, carry) = mac(r9, k, MODULUS[4], carry); - let (r10, carry) = mac(r10, k, MODULUS[5], carry); - let (r11, _) = adc(t11, r11, carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Fp([r6, r7, r8, r9, r10, r11])).subtract_p() - } - - #[inline] - pub const fn mul(&self, rhs: &Fp) -> Fp { - let (t0, carry) = mac(0, self.0[0], rhs.0[0], 0); - let (t1, carry) = mac(0, self.0[0], rhs.0[1], carry); - let (t2, carry) = mac(0, self.0[0], rhs.0[2], carry); - let (t3, carry) = mac(0, self.0[0], rhs.0[3], carry); - let (t4, carry) = mac(0, self.0[0], rhs.0[4], carry); - let (t5, t6) = mac(0, self.0[0], rhs.0[5], carry); - - let (t1, carry) = mac(t1, self.0[1], rhs.0[0], 0); - let (t2, carry) = mac(t2, self.0[1], rhs.0[1], carry); - let (t3, carry) = mac(t3, self.0[1], rhs.0[2], carry); - let (t4, carry) = mac(t4, self.0[1], rhs.0[3], carry); - let (t5, carry) = mac(t5, self.0[1], rhs.0[4], carry); - let (t6, t7) = mac(t6, self.0[1], rhs.0[5], carry); - - let (t2, carry) = mac(t2, self.0[2], rhs.0[0], 0); - let (t3, carry) = mac(t3, self.0[2], rhs.0[1], carry); - let (t4, carry) = mac(t4, self.0[2], rhs.0[2], carry); - let (t5, carry) = mac(t5, self.0[2], rhs.0[3], carry); - let (t6, carry) = mac(t6, self.0[2], rhs.0[4], carry); - let (t7, t8) = mac(t7, self.0[2], rhs.0[5], carry); - - let (t3, carry) = mac(t3, self.0[3], rhs.0[0], 0); - let (t4, carry) = mac(t4, self.0[3], rhs.0[1], carry); - let (t5, carry) = mac(t5, self.0[3], rhs.0[2], carry); - let (t6, carry) = mac(t6, self.0[3], rhs.0[3], carry); - let (t7, carry) = mac(t7, self.0[3], rhs.0[4], carry); - let (t8, t9) = mac(t8, self.0[3], rhs.0[5], carry); - - let (t4, carry) = mac(t4, self.0[4], rhs.0[0], 0); - let (t5, carry) = mac(t5, self.0[4], rhs.0[1], carry); - let (t6, carry) = mac(t6, self.0[4], rhs.0[2], carry); - let (t7, carry) = mac(t7, self.0[4], rhs.0[3], carry); - let (t8, carry) = mac(t8, self.0[4], rhs.0[4], carry); - let (t9, t10) = mac(t9, self.0[4], rhs.0[5], carry); - - let (t5, carry) = mac(t5, self.0[5], rhs.0[0], 0); - let (t6, carry) = mac(t6, self.0[5], rhs.0[1], carry); - let (t7, carry) = mac(t7, self.0[5], rhs.0[2], carry); - let (t8, carry) = mac(t8, self.0[5], rhs.0[3], carry); - let (t9, carry) = mac(t9, self.0[5], rhs.0[4], carry); - let (t10, t11) = mac(t10, self.0[5], rhs.0[5], carry); - - Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> Self { - let (t1, carry) = mac(0, self.0[0], self.0[1], 0); - let (t2, carry) = mac(0, self.0[0], self.0[2], carry); - let (t3, carry) = mac(0, self.0[0], self.0[3], carry); - let (t4, carry) = mac(0, self.0[0], self.0[4], carry); - let (t5, t6) = mac(0, self.0[0], self.0[5], carry); - - let (t3, carry) = mac(t3, self.0[1], self.0[2], 0); - let (t4, carry) = mac(t4, self.0[1], self.0[3], carry); - let (t5, carry) = mac(t5, self.0[1], self.0[4], carry); - let (t6, t7) = mac(t6, self.0[1], self.0[5], carry); - - let (t5, carry) = mac(t5, self.0[2], self.0[3], 0); - let (t6, carry) = mac(t6, self.0[2], self.0[4], carry); - let (t7, t8) = mac(t7, self.0[2], self.0[5], carry); - - let (t7, carry) = mac(t7, self.0[3], self.0[4], 0); - let (t8, t9) = mac(t8, self.0[3], self.0[5], carry); - - let (t9, t10) = mac(t9, self.0[4], self.0[5], 0); - - let t11 = t10 >> 63; - let t10 = (t10 << 1) | (t9 >> 63); - let t9 = (t9 << 1) | (t8 >> 63); - let t8 = (t8 << 1) | (t7 >> 63); - let t7 = (t7 << 1) | (t6 >> 63); - let t6 = (t6 << 1) | (t5 >> 63); - let t5 = (t5 << 1) | (t4 >> 63); - let t4 = (t4 << 1) | (t3 >> 63); - let t3 = (t3 << 1) | (t2 >> 63); - let t2 = (t2 << 1) | (t1 >> 63); - let t1 = t1 << 1; - - let (t0, carry) = mac(0, self.0[0], self.0[0], 0); - let (t1, carry) = adc(t1, 0, carry); - let (t2, carry) = mac(t2, self.0[1], self.0[1], carry); - let (t3, carry) = adc(t3, 0, carry); - let (t4, carry) = mac(t4, self.0[2], self.0[2], carry); - let (t5, carry) = adc(t5, 0, carry); - let (t6, carry) = mac(t6, self.0[3], self.0[3], carry); - let (t7, carry) = adc(t7, 0, carry); - let (t8, carry) = mac(t8, self.0[4], self.0[4], carry); - let (t9, carry) = adc(t9, 0, carry); - let (t10, carry) = mac(t10, self.0[5], self.0[5], carry); - let (t11, _) = adc(t11, 0, carry); - - Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) - } -} - -#[test] -fn test_conditional_selection() { - let a = Fp([1, 2, 3, 4, 5, 6]); - let b = Fp([7, 8, 9, 10, 11, 12]); - - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_equality() { - fn is_equal(a: &Fp, b: &Fp) -> bool { - let eq = a == b; - let ct_eq = a.ct_eq(&b); - - assert_eq!(eq, ct_eq.unwrap_u8() == 1); - - eq - } - - assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - - assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6]))); - assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6]))); -} - -#[test] -fn test_squaring() { - let a = Fp([ - 0xd215_d276_8e83_191b, - 0x5085_d80f_8fb2_8261, - 0xce9a_032d_df39_3a56, - 0x3e9c_4fff_2ca0_c4bb, - 0x6436_b6f7_f4d9_5dfb, - 0x1060_6628_ad4a_4d90, - ]); - let b = Fp([ - 0x33d9_c42a_3cb3_e235, - 0xdad1_1a09_4c4c_d455, - 0xa2f1_44bd_729a_aeba, - 0xd415_0932_be9f_feac, - 0xe27b_c7c4_7d44_ee50, - 0x14b6_a78d_3ec7_a560, - ]); - - assert_eq!(a.square(), b); -} - -#[test] -fn test_multiplication() { - let a = Fp([ - 0x0397_a383_2017_0cd4, - 0x734c_1b2c_9e76_1d30, - 0x5ed2_55ad_9a48_beb5, - 0x095a_3c6b_22a7_fcfc, - 0x2294_ce75_d4e2_6a27, - 0x1333_8bd8_7001_1ebb, - ]); - let b = Fp([ - 0xb9c3_c7c5_b119_6af7, - 0x2580_e208_6ce3_35c1, - 0xf49a_ed3d_8a57_ef42, - 0x41f2_81e4_9846_e878, - 0xe076_2346_c384_52ce, - 0x0652_e893_26e5_7dc0, - ]); - let c = Fp([ - 0xf96e_f3d7_11ab_5355, - 0xe8d4_59ea_00f1_48dd, - 0x53f7_354a_5f00_fa78, - 0x9e34_a4f3_125c_5f83, - 0x3fbe_0c47_ca74_c19e, - 0x01b0_6a8b_bd4a_dfe4, - ]); - - assert_eq!(a * b, c); -} - -#[test] -fn test_addition() { - let a = Fp([ - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b, - ]); - let b = Fp([ - 0x9fd2_8773_3d23_dda0, - 0xb16b_f2af_738b_3554, - 0x3e57_a75b_d3cc_6d1d, - 0x900b_c0bd_627f_d6d6, - 0xd319_a080_efb2_45fe, - 0x15fd_caa4_e4bb_2091, - ]); - let c = Fp([ - 0x3934_42cc_b58b_b327, - 0x1092_685f_3bd5_47e3, - 0x3382_252c_ab6a_c4c9, - 0xf946_94cb_7688_7f55, - 0x4b21_5e90_93a5_e071, - 0x0d56_e30f_34f5_f853, - ]); - - assert_eq!(a + b, c); -} - -#[test] -fn test_subtraction() { - let a = Fp([ - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b, - ]); - let b = Fp([ - 0x9fd2_8773_3d23_dda0, - 0xb16b_f2af_738b_3554, - 0x3e57_a75b_d3cc_6d1d, - 0x900b_c0bd_627f_d6d6, - 0xd319_a080_efb2_45fe, - 0x15fd_caa4_e4bb_2091, - ]); - let c = Fp([ - 0x6d8d_33e6_3b43_4d3d, - 0xeb12_82fd_b766_dd39, - 0x8534_7bb6_f133_d6d5, - 0xa21d_aa5a_9892_f727, - 0x3b25_6cfb_3ad8_ae23, - 0x155d_7199_de7f_8464, - ]); - - assert_eq!(a - b, c); -} - -#[test] -fn test_negation() { - let a = Fp([ - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b, - ]); - let b = Fp([ - 0x669e_44a6_8798_2a79, - 0xa0d9_8a50_37b5_ed71, - 0x0ad5_822f_2861_a854, - 0x96c5_2bf1_ebf7_5781, - 0x87f8_41f0_5c0c_658c, - 0x08a6_e795_afc5_283e, - ]); - - assert_eq!(-a, b); -} - -#[test] -fn test_debug() { - assert_eq!( - format!( - "{:?}", - Fp([ - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b, - ]) - ), - "0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704" - ); -} - -#[test] -fn test_from_bytes() { - let mut a = Fp([ - 0xdc90_6d9b_e3f9_5dc8, - 0x8755_caf7_4596_91a1, - 0xcff1_a7f4_e958_3ab3, - 0x9b43_821f_849e_2284, - 0xf575_54f3_a297_4f3f, - 0x085d_bea8_4ed4_7f79, - ]); - - for _ in 0..100 { - a = a.square(); - let tmp = a.to_bytes(); - let b = Fp::from_bytes(&tmp).unwrap(); - - assert_eq!(a, b); - } - - assert_eq!( - -Fp::one(), - Fp::from_bytes(&[ - 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, - 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, - 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 - ]) - .unwrap() - ); - - assert!( - Fp::from_bytes(&[ - 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, - 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, - 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 - ]) - .is_none() - .unwrap_u8() - == 1 - ); - - assert!(Fp::from_bytes(&[0xff; 48]).is_none().unwrap_u8() == 1); -} - -#[test] -fn test_sqrt() { - // a = 4 - let a = Fp::from_raw_unchecked([ - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e, - ]); - - assert_eq!( - // sqrt(4) = -2 - -a.sqrt().unwrap(), - // 2 - Fp::from_raw_unchecked([ - 0x3213_0000_0006_554f, - 0xb93c_0018_d6c4_0005, - 0x5760_5e0d_b0dd_bb51, - 0x8b25_6521_ed1f_9bcb, - 0x6cf2_8d79_0162_2c03, - 0x11eb_ab9d_bb81_e28c, - ]) - ); -} - -#[test] -fn test_inversion() { - let a = Fp([ - 0x43b4_3a50_78ac_2076, - 0x1ce0_7630_46f8_962b, - 0x724a_5276_486d_735c, - 0x6f05_c2a6_282d_48fd, - 0x2095_bd5b_b4ca_9331, - 0x03b3_5b38_94b0_f7da, - ]); - let b = Fp([ - 0x69ec_d704_0952_148f, - 0x985c_cc20_2219_0f55, - 0xe19b_ba36_a9ad_2f41, - 0x19bb_16c9_5219_dbd8, - 0x14dc_acfd_fb47_8693, - 0x115f_f58a_fff9_a8e1, - ]); - - assert_eq!(a.invert().unwrap(), b); - assert!(Fp::zero().invert().is_none().unwrap_u8() == 1); -} - -#[test] -fn test_lexicographic_largest() { - assert!(!bool::from(Fp::zero().lexicographically_largest())); - assert!(!bool::from(Fp::one().lexicographically_largest())); - assert!(!bool::from( - Fp::from_raw_unchecked([ - 0xa1fa_ffff_fffe_5557, - 0x995b_fff9_76a3_fffe, - 0x03f4_1d24_d174_ceb4, - 0xf654_7998_c199_5dbd, - 0x778a_468f_507a_6034, - 0x0205_5993_1f7f_8103 - ]) - .lexicographically_largest() - )); - assert!(bool::from( - Fp::from_raw_unchecked([ - 0x1804_0000_0001_5554, - 0x8550_0005_3ab0_0001, - 0x633c_b57c_253c_276f, - 0x6e22_d1ec_31eb_b502, - 0xd391_6126_f2d1_4ca2, - 0x17fb_b857_1a00_6596, - ]) - .lexicographically_largest() - )); - assert!(bool::from( - Fp::from_raw_unchecked([ - 0x43f5_ffff_fffc_aaae, - 0x32b7_fff2_ed47_fffd, - 0x07e8_3a49_a2e9_9d69, - 0xeca8_f331_8332_bb7a, - 0xef14_8d1e_a0f4_c069, - 0x040a_b326_3eff_0206, - ]) - .lexicographically_largest() - )); -} diff --git a/bls12_381/src/fp12.rs b/bls12_381/src/fp12.rs deleted file mode 100644 index 735f91e5d8..0000000000 --- a/bls12_381/src/fp12.rs +++ /dev/null @@ -1,635 +0,0 @@ -use crate::fp::*; -use crate::fp2::*; -use crate::fp6::*; - -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$. -pub struct Fp12 { - pub c0: Fp6, - pub c1: Fp6, -} - -impl From for Fp12 { - fn from(f: Fp) -> Fp12 { - Fp12 { - c0: Fp6::from(f), - c1: Fp6::zero(), - } - } -} - -impl From for Fp12 { - fn from(f: Fp2) -> Fp12 { - Fp12 { - c0: Fp6::from(f), - c1: Fp6::zero(), - } - } -} - -impl From for Fp12 { - fn from(f: Fp6) -> Fp12 { - Fp12 { - c0: f, - c1: Fp6::zero(), - } - } -} - -impl PartialEq for Fp12 { - fn eq(&self, other: &Fp12) -> bool { - self.ct_eq(other).into() - } -} - -impl Copy for Fp12 {} -impl Clone for Fp12 { - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl Default for Fp12 { - fn default() -> Self { - Fp12::zero() - } -} - -impl fmt::Debug for Fp12 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?} + ({:?})*w", self.c0, self.c1) - } -} - -impl ConditionallySelectable for Fp12 { - #[inline(always)] - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fp12 { - c0: Fp6::conditional_select(&a.c0, &b.c0, choice), - c1: Fp6::conditional_select(&a.c1, &b.c1, choice), - } - } -} - -impl ConstantTimeEq for Fp12 { - #[inline(always)] - fn ct_eq(&self, other: &Self) -> Choice { - self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) - } -} - -impl Fp12 { - #[inline] - pub fn zero() -> Self { - Fp12 { - c0: Fp6::zero(), - c1: Fp6::zero(), - } - } - - #[inline] - pub fn one() -> Self { - Fp12 { - c0: Fp6::one(), - c1: Fp6::zero(), - } - } - - pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 { - let aa = self.c0.mul_by_01(c0, c1); - let bb = self.c1.mul_by_1(c4); - let o = c1 + c4; - let c1 = self.c1 + self.c0; - let c1 = c1.mul_by_01(c0, &o); - let c1 = c1 - aa - bb; - let c0 = bb; - let c0 = c0.mul_by_nonresidue(); - let c0 = c0 + aa; - - Fp12 { c0, c1 } - } - - #[inline(always)] - pub fn is_zero(&self) -> Choice { - self.c0.is_zero() & self.c1.is_zero() - } - - #[inline(always)] - pub fn conjugate(&self) -> Self { - Fp12 { - c0: self.c0, - c1: -self.c1, - } - } - - /// Raises this element to p. - #[inline(always)] - pub fn frobenius_map(&self) -> Self { - let c0 = self.c0.frobenius_map(); - let c1 = self.c1.frobenius_map(); - - // c1 = c1 * (u + 1)^((p - 1) / 6) - let c1 = c1 - * Fp6::from(Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0708_9552_b319_d465, - 0xc669_5f92_b50a_8313, - 0x97e8_3ccc_d117_228f, - 0xa35b_aeca_b2dc_29ee, - 0x1ce3_93ea_5daa_ce4d, - 0x08f2_220f_b0fb_66eb, - ]), - c1: Fp::from_raw_unchecked([ - 0xb2f6_6aad_4ce5_d646, - 0x5842_a06b_fc49_7cec, - 0xcf48_95d4_2599_d394, - 0xc11b_9cba_40a8_e8d0, - 0x2e38_13cb_e5a0_de89, - 0x110e_efda_8884_7faf, - ]), - }); - - Fp12 { c0, c1 } - } - - #[inline] - pub fn square(&self) -> Self { - let ab = self.c0 * self.c1; - let c0c1 = self.c0 + self.c1; - let c0 = self.c1.mul_by_nonresidue(); - let c0 = c0 + self.c0; - let c0 = c0 * c0c1; - let c0 = c0 - ab; - let c1 = ab + ab; - let c0 = c0 - ab.mul_by_nonresidue(); - - Fp12 { c0, c1 } - } - - pub fn invert(&self) -> CtOption { - (self.c0.square() - self.c1.square().mul_by_nonresidue()) - .invert() - .map(|t| Fp12 { - c0: self.c0 * t, - c1: self.c1 * -t, - }) - } -} - -impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 { - type Output = Fp12; - - #[inline] - fn mul(self, other: &'b Fp12) -> Self::Output { - let aa = self.c0 * other.c0; - let bb = self.c1 * other.c1; - let o = other.c0 + other.c1; - let c1 = self.c1 + self.c0; - let c1 = c1 * o; - let c1 = c1 - aa; - let c1 = c1 - bb; - let c0 = bb.mul_by_nonresidue(); - let c0 = c0 + aa; - - Fp12 { c0, c1 } - } -} - -impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 { - type Output = Fp12; - - #[inline] - fn add(self, rhs: &'b Fp12) -> Self::Output { - Fp12 { - c0: self.c0 + rhs.c0, - c1: self.c1 + rhs.c1, - } - } -} - -impl<'a> Neg for &'a Fp12 { - type Output = Fp12; - - #[inline] - fn neg(self) -> Self::Output { - Fp12 { - c0: -self.c0, - c1: -self.c1, - } - } -} - -impl Neg for Fp12 { - type Output = Fp12; - - #[inline] - fn neg(self) -> Self::Output { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 { - type Output = Fp12; - - #[inline] - fn sub(self, rhs: &'b Fp12) -> Self::Output { - Fp12 { - c0: self.c0 - rhs.c0, - c1: self.c1 - rhs.c1, - } - } -} - -impl_binops_additive!(Fp12, Fp12); -impl_binops_multiplicative!(Fp12, Fp12); - -#[test] -fn test_arithmetic() { - use crate::fp::*; - use crate::fp2::*; - - let a = Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }, - }; - - let b = Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d272_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e348, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd2_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a117_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }, - }; - - let c = Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_71b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0x7791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_133c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_40e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_1744_c040, - ]), - }, - }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d3_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_1040, - ]), - }, - }, - }; - - // because a and b and c are similar to each other and - // I was lazy, this is just some arbitrary way to make - // them a little more different - let a = a.square().invert().unwrap().square() + c; - let b = b.square().invert().unwrap().square() + a; - let c = c.square().invert().unwrap().square() + b; - - assert_eq!(a.square(), a * a); - assert_eq!(b.square(), b * b); - assert_eq!(c.square(), c * c); - - assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b)); - - assert_eq!( - a.invert().unwrap() * b.invert().unwrap(), - (a * b).invert().unwrap() - ); - assert_eq!(a.invert().unwrap() * a, Fp12::one()); - - assert!(a != a.frobenius_map()); - assert_eq!( - a, - a.frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - ); -} diff --git a/bls12_381/src/fp2.rs b/bls12_381/src/fp2.rs deleted file mode 100644 index 3890d31b3d..0000000000 --- a/bls12_381/src/fp2.rs +++ /dev/null @@ -1,868 +0,0 @@ -//! This module implements arithmetic over the quadratic extension field Fp2. - -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::fp::Fp; - -#[derive(Copy, Clone)] -pub struct Fp2 { - pub c0: Fp, - pub c1: Fp, -} - -impl fmt::Debug for Fp2 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?} + {:?}*u", self.c0, self.c1) - } -} - -impl Default for Fp2 { - fn default() -> Self { - Fp2::zero() - } -} - -impl From for Fp2 { - fn from(f: Fp) -> Fp2 { - Fp2 { - c0: f, - c1: Fp::zero(), - } - } -} - -impl ConstantTimeEq for Fp2 { - fn ct_eq(&self, other: &Self) -> Choice { - self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) - } -} - -impl Eq for Fp2 {} -impl PartialEq for Fp2 { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 - } -} - -impl ConditionallySelectable for Fp2 { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fp2 { - c0: Fp::conditional_select(&a.c0, &b.c0, choice), - c1: Fp::conditional_select(&a.c1, &b.c1, choice), - } - } -} - -impl<'a> Neg for &'a Fp2 { - type Output = Fp2; - - #[inline] - fn neg(self) -> Fp2 { - self.neg() - } -} - -impl Neg for Fp2 { - type Output = Fp2; - - #[inline] - fn neg(self) -> Fp2 { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 { - type Output = Fp2; - - #[inline] - fn sub(self, rhs: &'b Fp2) -> Fp2 { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 { - type Output = Fp2; - - #[inline] - fn add(self, rhs: &'b Fp2) -> Fp2 { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 { - type Output = Fp2; - - #[inline] - fn mul(self, rhs: &'b Fp2) -> Fp2 { - self.mul(rhs) - } -} - -impl_binops_additive!(Fp2, Fp2); -impl_binops_multiplicative!(Fp2, Fp2); - -impl Fp2 { - #[inline] - pub const fn zero() -> Fp2 { - Fp2 { - c0: Fp::zero(), - c1: Fp::zero(), - } - } - - #[inline] - pub const fn one() -> Fp2 { - Fp2 { - c0: Fp::one(), - c1: Fp::zero(), - } - } - - pub fn is_zero(&self) -> Choice { - self.c0.is_zero() & self.c1.is_zero() - } - - /// Raises this element to p. - #[inline(always)] - pub fn frobenius_map(&self) -> Self { - // This is always just a conjugation. If you're curious why, here's - // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/ - self.conjugate() - } - - #[inline(always)] - pub fn conjugate(&self) -> Self { - Fp2 { - c0: self.c0, - c1: -self.c1, - } - } - - #[inline(always)] - pub fn mul_by_nonresidue(&self) -> Fp2 { - // Multiply a + bu by u + 1, getting - // au + a + bu^2 + bu - // and because u^2 = -1, we get - // (a - b) + (a + b)u - - Fp2 { - c0: self.c0 - self.c1, - c1: self.c0 + self.c1, - } - } - - /// Returns whether or not this element is strictly lexicographically - /// larger than its negation. - #[inline] - pub fn lexicographically_largest(&self) -> Choice { - // If this element's c1 coefficient is lexicographically largest - // then it is lexicographically largest. Otherwise, in the event - // the c1 coefficient is zero and the c0 coefficient is - // lexicographically largest, then this element is lexicographically - // largest. - - self.c1.lexicographically_largest() - | (self.c1.is_zero() & self.c0.lexicographically_largest()) - } - - pub const fn square(&self) -> Fp2 { - // Complex squaring: - // - // v0 = c0 * c1 - // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0 - // c1' = 2 * v0 - // - // In BLS12-381's F_{p^2}, our \beta is -1 so we - // can modify this formula: - // - // c0' = (c0 + c1) * (c0 - c1) - // c1' = 2 * c0 * c1 - - let a = (&self.c0).add(&self.c1); - let b = (&self.c0).sub(&self.c1); - let c = (&self.c0).add(&self.c0); - - Fp2 { - c0: (&a).mul(&b), - c1: (&c).mul(&self.c1), - } - } - - pub const fn mul(&self, rhs: &Fp2) -> Fp2 { - // Karatsuba multiplication: - // - // v0 = a0 * b0 - // v1 = a1 * b1 - // c0 = v0 + \beta * v1 - // c1 = (a0 + a1) * (b0 + b1) - v0 - v1 - // - // In BLS12-381's F_{p^2}, our \beta is -1 so we - // can modify this formula. (Also, since we always - // subtract v1, we can compute v1 = -a1 * b1.) - // - // v0 = a0 * b0 - // v1 = (-a1) * b1 - // c0 = v0 + v1 - // c1 = (a0 + a1) * (b0 + b1) - v0 + v1 - - let v0 = (&self.c0).mul(&rhs.c0); - let v1 = (&(&self.c1).neg()).mul(&rhs.c1); - let c0 = (&v0).add(&v1); - let c1 = (&(&self.c0).add(&self.c1)).mul(&(&rhs.c0).add(&rhs.c1)); - let c1 = (&c1).sub(&v0); - let c1 = (&c1).add(&v1); - - Fp2 { c0, c1 } - } - - pub const fn add(&self, rhs: &Fp2) -> Fp2 { - Fp2 { - c0: (&self.c0).add(&rhs.c0), - c1: (&self.c1).add(&rhs.c1), - } - } - - pub const fn sub(&self, rhs: &Fp2) -> Fp2 { - Fp2 { - c0: (&self.c0).sub(&rhs.c0), - c1: (&self.c1).sub(&rhs.c1), - } - } - - pub const fn neg(&self) -> Fp2 { - Fp2 { - c0: (&self.c0).neg(), - c1: (&self.c1).neg(), - } - } - - pub fn sqrt(&self) -> CtOption { - // Algorithm 9, https://eprint.iacr.org/2012/685.pdf - // with constant time modifications. - - CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { - // a1 = self^((p - 3) / 4) - let a1 = self.pow_vartime(&[ - 0xee7f_bfff_ffff_eaaa, - 0x07aa_ffff_ac54_ffff, - 0xd9cc_34a8_3dac_3d89, - 0xd91d_d2e1_3ce1_44af, - 0x92c6_e9ed_90d2_eb35, - 0x0680_447a_8e5f_f9a6, - ]); - - // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) - let alpha = a1.square() * self; - - // x0 = self^((p + 1) / 4) - let x0 = a1 * self; - - // In the event that alpha = -1, the element is order p - 1 and so - // we're just trying to get the square of an element of the subfield - // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element - // x0 = a + bu has b = 0, the solution is therefore au. - CtOption::new( - Fp2 { - c0: -x0.c1, - c1: x0.c0, - }, - alpha.ct_eq(&(&Fp2::one()).neg()), - ) - // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0 - .or_else(|| { - CtOption::new( - (alpha + Fp2::one()).pow_vartime(&[ - 0xdcff_7fff_ffff_d555, - 0x0f55_ffff_58a9_ffff, - 0xb398_6950_7b58_7b12, - 0xb23b_a5c2_79c2_895f, - 0x258d_d3db_21a5_d66b, - 0x0d00_88f5_1cbf_f34d, - ]) * x0, - Choice::from(1), - ) - }) - // Only return the result if it's really the square root (and so - // self is actually quadratic nonresidue) - .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self))) - }) - } - - /// Computes the multiplicative inverse of this field - /// element, returning None in the case that this element - /// is zero. - pub fn invert(&self) -> CtOption { - // We wish to find the multiplicative inverse of a nonzero - // element a + bu in Fp2. We leverage an identity - // - // (a + bu)(a - bu) = a^2 + b^2 - // - // which holds because u^2 = -1. This can be rewritten as - // - // (a + bu)(a - bu)/(a^2 + b^2) = 1 - // - // because a^2 + b^2 = 0 has no nonzero solutions for (a, b). - // This gives that (a - bu)/(a^2 + b^2) is the inverse - // of (a + bu). Importantly, this can be computing using - // only a single inversion in Fp. - - (self.c0.square() + self.c1.square()).invert().map(|t| Fp2 { - c0: self.c0 * t, - c1: self.c1 * -t, - }) - } - - /// Although this is labeled "vartime", it is only - /// variable time with respect to the exponent. It - /// is also not exposed in the public API. - pub fn pow_vartime(&self, by: &[u64; 6]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - res *= self; - } - } - } - res - } -} - -#[test] -fn test_conditional_selection() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]), - c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]), - }; - - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_equality() { - fn is_equal(a: &Fp2, b: &Fp2) -> bool { - let eq = a == b; - let ct_eq = a.ct_eq(&b); - - assert_eq!(eq, ct_eq.unwrap_u8() == 1); - - eq - } - - assert!(is_equal( - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - }, - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - } - )); - - assert!(!is_equal( - &Fp2 { - c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - }, - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - } - )); - - assert!(!is_equal( - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]), - }, - &Fp2 { - c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]), - c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]), - } - )); -} - -#[test] -fn test_squaring() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - ]), - }; - - assert_eq!(a.square(), b); -} - -#[test] -fn test_multiplication() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - ]), - }; - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf597_483e_27b4_e0f7, - 0x610f_badf_811d_ae5f, - 0x8432_af91_7714_327a, - 0x6a9a_9603_cf88_f09e, - 0xf05a_7bf8_bad0_eb01, - 0x0954_9131_c003_ffae, - ]), - c1: Fp::from_raw_unchecked([ - 0x963b_02d0_f93d_37cd, - 0xc95c_e1cd_b30a_73d4, - 0x3087_25fa_3126_f9b8, - 0x56da_3c16_7fab_0d50, - 0x6b50_86b5_f4b6_d6af, - 0x09c3_9f06_2f18_e9f2, - ]), - }; - - assert_eq!(a * b, c); -} - -#[test] -fn test_addition() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - ]), - }; - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x6b82_a9a7_08c1_32d2, - 0x476b_1da3_39ba_5ba4, - 0x848c_0e62_4b91_cd87, - 0x11f9_5955_295a_99ec, - 0xf337_6fce_2255_9f06, - 0x0c3f_e3fa_ce8c_8f43, - ]), - c1: Fp::from_raw_unchecked([ - 0x6f99_2c12_73ab_5bc5, - 0x3355_1366_17a1_df33, - 0x8b0e_f74c_0aed_aff9, - 0x062f_9246_8ad2_ca12, - 0xe146_9770_738f_d584, - 0x12c3_c3dd_84bc_a26d, - ]), - }; - - assert_eq!(a + b, c); -} - -#[test] -fn test_subtraction() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - ]), - c1: Fp::from_raw_unchecked([ - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - ]), - }; - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xe1c0_86bb_bf1b_5981, - 0x4faf_c3a9_aa70_5d7e, - 0x2734_b5c1_0bb7_e726, - 0xb2bd_7776_af03_7a3e, - 0x1b89_5fb3_98a8_4164, - 0x1730_4aef_6f11_3cec, - ]), - c1: Fp::from_raw_unchecked([ - 0x74c3_1c79_9519_1204, - 0x3271_aa54_79fd_ad2b, - 0xc9b4_7157_4915_a30f, - 0x65e4_0313_ec44_b8be, - 0x7487_b238_5b70_67cb, - 0x0952_3b26_d0ad_19a4, - ]), - }; - - assert_eq!(a - b, c); -} - -#[test] -fn test_negation() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - ]), - c1: Fp::from_raw_unchecked([ - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - ]), - }; - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf05c_e7ce_9c11_39d7, - 0x6274_8f57_97e8_a36d, - 0xc4e8_d9df_c664_96df, - 0xb457_88e1_8118_9209, - 0x6949_13d0_8772_930d, - 0x1549_836a_3770_f3cf, - ]), - c1: Fp::from_raw_unchecked([ - 0x24d0_5bb9_fb9d_491c, - 0xfb1e_a120_c12e_39d0, - 0x7067_879f_c807_c7b1, - 0x60a9_269a_31bb_dab6, - 0x45c2_56bc_fd71_649b, - 0x18f6_9b5d_2b8a_fbde, - ]), - }; - - assert_eq!(-a, b); -} - -#[test] -fn test_sqrt() { - // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x2bee_d146_27d7_f9e9, - 0xb661_4e06_660e_5dce, - 0x06c4_cc7c_2f91_d42c, - 0x996d_7847_4b7a_63cc, - 0xebae_bc4c_820d_574e, - 0x1886_5e12_d93f_d845, - ]), - c1: Fp::from_raw_unchecked([ - 0x7d82_8664_baf4_f566, - 0xd17e_6639_96ec_7339, - 0x679e_ad55_cb40_78d0, - 0xfe3b_2260_e001_ec28, - 0x3059_93d0_43d9_1b68, - 0x0626_f03c_0489_b72d, - ]), - }; - - assert_eq!(a.sqrt().unwrap().square(), a); - - // b = 5, which is a generator of the p - 1 order - // multiplicative subgroup - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x6631_0000_0010_5545, - 0x2114_0040_0eec_000d, - 0x3fa7_af30_c820_e316, - 0xc52a_8b8d_6387_695d, - 0x9fb4_e61d_1e83_eac5, - 0x005c_b922_afe8_4dc7, - ]), - c1: Fp::zero(), - }; - - assert_eq!(b.sqrt().unwrap().square(), b); - - // c = 25, which is a generator of the (p - 1) / 2 order - // multiplicative subgroup - let c = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x44f6_0000_0051_ffae, - 0x86b8_0141_9948_0043, - 0xd715_9952_f1f3_794a, - 0x755d_6e3d_fe1f_fc12, - 0xd36c_d6db_5547_e905, - 0x02f8_c8ec_bf18_67bb, - ]), - c1: Fp::zero(), - }; - - assert_eq!(c.sqrt().unwrap().square(), c); - - // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 - // is nonsquare. - assert!(bool::from( - Fp2 { - c0: Fp::from_raw_unchecked([ - 0xc5fa_1bc8_fd00_d7f6, - 0x3830_ca45_4606_003b, - 0x2b28_7f11_04b1_02da, - 0xa7fb_30f2_8230_f23e, - 0x339c_db9e_e953_dbf0, - 0x0d78_ec51_d989_fc57, - ]), - c1: Fp::from_raw_unchecked([ - 0x27ec_4898_cf87_f613, - 0x9de1_394e_1abb_05a5, - 0x0947_f85d_c170_fc14, - 0x586f_bc69_6b61_14b7, - 0x2b34_75a4_077d_7169, - 0x13e1_c895_cc4b_6c22, - ]) - } - .sqrt() - .is_none() - )); -} - -#[test] -fn test_inversion() { - let a = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: Fp::from_raw_unchecked([ - 0xd328_e37c_c2f5_8d41, - 0x948d_f085_8a60_5869, - 0x6032_f9d5_6f93_a573, - 0x2be4_83ef_3fff_dc87, - 0x30ef_61f8_8f48_3c2a, - 0x1333_f55a_3572_5be0, - ]), - }; - - let b = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0581_a133_3d4f_48a6, - 0x5824_2f6e_f074_8500, - 0x0292_c955_349e_6da5, - 0xba37_721d_dd95_fcd0, - 0x70d1_6790_3aa5_dfc5, - 0x1189_5e11_8b58_a9d5, - ]), - c1: Fp::from_raw_unchecked([ - 0x0eda_09d2_d7a8_5d17, - 0x8808_e137_a7d1_a2cf, - 0x43ae_2625_c1ff_21db, - 0xf85a_c9fd_f7a7_4c64, - 0x8fcc_dda5_b8da_9738, - 0x08e8_4f0c_b32c_d17d, - ]), - }; - - assert_eq!(a.invert().unwrap(), b); - - assert!(Fp2::zero().invert().is_none().unwrap_u8() == 1); -} - -#[test] -fn test_lexicographic_largest() { - assert!(!bool::from(Fp2::zero().lexicographically_largest())); - assert!(!bool::from(Fp2::one().lexicographically_largest())); - assert!(bool::from( - Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: Fp::from_raw_unchecked([ - 0xd328_e37c_c2f5_8d41, - 0x948d_f085_8a60_5869, - 0x6032_f9d5_6f93_a573, - 0x2be4_83ef_3fff_dc87, - 0x30ef_61f8_8f48_3c2a, - 0x1333_f55a_3572_5be0, - ]), - } - .lexicographically_largest() - )); - assert!(!bool::from( - Fp2 { - c0: -Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: -Fp::from_raw_unchecked([ - 0xd328_e37c_c2f5_8d41, - 0x948d_f085_8a60_5869, - 0x6032_f9d5_6f93_a573, - 0x2be4_83ef_3fff_dc87, - 0x30ef_61f8_8f48_3c2a, - 0x1333_f55a_3572_5be0, - ]), - } - .lexicographically_largest() - )); - assert!(!bool::from( - Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: Fp::zero(), - } - .lexicographically_largest() - )); - assert!(bool::from( - Fp2 { - c0: -Fp::from_raw_unchecked([ - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - ]), - c1: Fp::zero(), - } - .lexicographically_largest() - )); -} diff --git a/bls12_381/src/fp6.rs b/bls12_381/src/fp6.rs deleted file mode 100644 index 3f310dc17b..0000000000 --- a/bls12_381/src/fp6.rs +++ /dev/null @@ -1,504 +0,0 @@ -use crate::fp::*; -use crate::fp2::*; - -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -/// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / v^3 - u - 1$. -pub struct Fp6 { - pub c0: Fp2, - pub c1: Fp2, - pub c2: Fp2, -} - -impl From for Fp6 { - fn from(f: Fp) -> Fp6 { - Fp6 { - c0: Fp2::from(f), - c1: Fp2::zero(), - c2: Fp2::zero(), - } - } -} - -impl From for Fp6 { - fn from(f: Fp2) -> Fp6 { - Fp6 { - c0: f, - c1: Fp2::zero(), - c2: Fp2::zero(), - } - } -} - -impl PartialEq for Fp6 { - fn eq(&self, other: &Fp6) -> bool { - self.ct_eq(other).into() - } -} - -impl Copy for Fp6 {} -impl Clone for Fp6 { - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl Default for Fp6 { - fn default() -> Self { - Fp6::zero() - } -} - -impl fmt::Debug for Fp6 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?} + ({:?})*v + ({:?})*v^2", self.c0, self.c1, self.c2) - } -} - -impl ConditionallySelectable for Fp6 { - #[inline(always)] - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fp6 { - c0: Fp2::conditional_select(&a.c0, &b.c0, choice), - c1: Fp2::conditional_select(&a.c1, &b.c1, choice), - c2: Fp2::conditional_select(&a.c2, &b.c2, choice), - } - } -} - -impl ConstantTimeEq for Fp6 { - #[inline(always)] - fn ct_eq(&self, other: &Self) -> Choice { - self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2) - } -} - -impl Fp6 { - #[inline] - pub fn zero() -> Self { - Fp6 { - c0: Fp2::zero(), - c1: Fp2::zero(), - c2: Fp2::zero(), - } - } - - #[inline] - pub fn one() -> Self { - Fp6 { - c0: Fp2::one(), - c1: Fp2::zero(), - c2: Fp2::zero(), - } - } - - pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 { - let b_b = self.c1 * c1; - - let t1 = (self.c1 + self.c2) * c1 - b_b; - let t1 = t1.mul_by_nonresidue(); - - let t2 = (self.c0 + self.c1) * c1 - b_b; - - Fp6 { - c0: t1, - c1: t2, - c2: b_b, - } - } - - pub fn mul_by_01(&self, c0: &Fp2, c1: &Fp2) -> Fp6 { - let a_a = self.c0 * c0; - let b_b = self.c1 * c1; - - let t1 = (self.c1 + self.c2) * c1 - b_b; - let t1 = t1.mul_by_nonresidue() + a_a; - - let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b; - - let t3 = (self.c0 + self.c2) * c0 - a_a + b_b; - - Fp6 { - c0: t1, - c1: t2, - c2: t3, - } - } - - /// Multiply by quadratic nonresidue v. - pub fn mul_by_nonresidue(&self) -> Self { - // Given a + bv + cv^2, this produces - // av + bv^2 + cv^3 - // but because v^3 = u + 1, we have - // c(u + 1) + av + v^2 - - Fp6 { - c0: self.c2.mul_by_nonresidue(), - c1: self.c0, - c2: self.c1, - } - } - - /// Raises this element to p. - #[inline(always)] - pub fn frobenius_map(&self) -> Self { - let c0 = self.c0.frobenius_map(); - let c1 = self.c1.frobenius_map(); - let c2 = self.c2.frobenius_map(); - - // c1 = c1 * (u + 1)^((p - 1) / 3) - let c1 = c1 - * Fp2 { - c0: Fp::zero(), - c1: Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]), - }; - - // c2 = c2 * (u + 1)^((2p - 2) / 3) - let c2 = c2 - * Fp2 { - c0: Fp::from_raw_unchecked([ - 0x890d_c9e4_8675_45c3, - 0x2af3_2253_3285_a5d5, - 0x5088_0866_309b_7e2c, - 0xa20d_1b8c_7e88_1024, - 0x14e4_f04f_e2db_9068, - 0x14e5_6d3f_1564_853a, - ]), - c1: Fp::zero(), - }; - - Fp6 { c0, c1, c2 } - } - - #[inline(always)] - pub fn is_zero(&self) -> Choice { - self.c0.is_zero() & self.c1.is_zero() & self.c2.is_zero() - } - - #[inline] - pub fn square(&self) -> Self { - let s0 = self.c0.square(); - let ab = self.c0 * self.c1; - let s1 = ab + ab; - let s2 = (self.c0 - self.c1 + self.c2).square(); - let bc = self.c1 * self.c2; - let s3 = bc + bc; - let s4 = self.c2.square(); - - Fp6 { - c0: s3.mul_by_nonresidue() + s0, - c1: s4.mul_by_nonresidue() + s1, - c2: s1 + s2 + s3 - s0 - s4, - } - } - - #[inline] - pub fn invert(&self) -> CtOption { - let c0 = (self.c1 * self.c2).mul_by_nonresidue(); - let c0 = self.c0.square() - c0; - - let c1 = self.c2.square().mul_by_nonresidue(); - let c1 = c1 - (self.c0 * self.c1); - - let c2 = self.c1.square(); - let c2 = c2 - (self.c0 * self.c2); - - let tmp = ((self.c1 * c2) + (self.c2 * c1)).mul_by_nonresidue(); - let tmp = tmp + (self.c0 * c0); - - tmp.invert().map(|t| Fp6 { - c0: t * c0, - c1: t * c1, - c2: t * c2, - }) - } -} - -impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 { - type Output = Fp6; - - #[inline] - fn mul(self, other: &'b Fp6) -> Self::Output { - let aa = self.c0 * other.c0; - let bb = self.c1 * other.c1; - let cc = self.c2 * other.c2; - - let t1 = other.c1 + other.c2; - let tmp = self.c1 + self.c2; - let t1 = t1 * tmp; - let t1 = t1 - bb; - let t1 = t1 - cc; - let t1 = t1.mul_by_nonresidue(); - let t1 = t1 + aa; - - let t3 = other.c0 + other.c2; - let tmp = self.c0 + self.c2; - let t3 = t3 * tmp; - let t3 = t3 - aa; - let t3 = t3 + bb; - let t3 = t3 - cc; - - let t2 = other.c0 + other.c1; - let tmp = self.c0 + self.c1; - let t2 = t2 * tmp; - let t2 = t2 - aa; - let t2 = t2 - bb; - let cc = cc.mul_by_nonresidue(); - let t2 = t2 + cc; - - Fp6 { - c0: t1, - c1: t2, - c2: t3, - } - } -} - -impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 { - type Output = Fp6; - - #[inline] - fn add(self, rhs: &'b Fp6) -> Self::Output { - Fp6 { - c0: self.c0 + rhs.c0, - c1: self.c1 + rhs.c1, - c2: self.c2 + rhs.c2, - } - } -} - -impl<'a> Neg for &'a Fp6 { - type Output = Fp6; - - #[inline] - fn neg(self) -> Self::Output { - Fp6 { - c0: -self.c0, - c1: -self.c1, - c2: -self.c2, - } - } -} - -impl Neg for Fp6 { - type Output = Fp6; - - #[inline] - fn neg(self) -> Self::Output { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 { - type Output = Fp6; - - #[inline] - fn sub(self, rhs: &'b Fp6) -> Self::Output { - Fp6 { - c0: self.c0 - rhs.c0, - c1: self.c1 - rhs.c1, - c2: self.c2 - rhs.c2, - } - } -} - -impl_binops_additive!(Fp6, Fp6); -impl_binops_multiplicative!(Fp6, Fp6); - -#[test] -fn test_arithmetic() { - use crate::fp::*; - - let a = Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d, - ]), - c1: Fp::from_raw_unchecked([ - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8, - ]), - c1: Fp::from_raw_unchecked([ - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6, - ]), - c1: Fp::from_raw_unchecked([ - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040, - ]), - }, - }; - - let b = Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf120_cb98_b16f_d84b, - 0x5fb5_10cf_f3de_1d61, - 0x0f21_a5d0_69d8_c251, - 0xaa1f_d62f_34f2_839a, - 0x5a13_3515_7f89_913f, - 0x14a3_fe32_9643_c247, - ]), - c1: Fp::from_raw_unchecked([ - 0x3516_cb98_b16c_82f9, - 0x926d_10c2_e126_1d5f, - 0x1709_e01a_0cc2_5fba, - 0x96c8_c960_b825_3f14, - 0x4927_c234_207e_51a9, - 0x18ae_b158_d542_c44e, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xbf0d_cb98_b169_82fc, - 0xa679_10b7_1d1a_1d5c, - 0xb7c1_47c2_b8fb_06ff, - 0x1efa_710d_47d2_e7ce, - 0xed20_a79c_7e27_653c, - 0x02b8_5294_dac1_dfba, - ]), - c1: Fp::from_raw_unchecked([ - 0x9d52_cb98_b180_82e5, - 0x621d_1111_5176_1d6f, - 0xe798_8260_3b48_af43, - 0x0ad3_1637_a4f4_da37, - 0xaeac_737c_5ac1_cf2e, - 0x006e_7e73_5b48_b824, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xe148_cb98_b17d_2d93, - 0x94d5_1104_3ebe_1d6c, - 0xef80_bca9_de32_4cac, - 0xf77c_0969_2827_95b1, - 0x9dc1_009a_fbb6_8f97, - 0x0479_3199_9a47_ba2b, - ]), - c1: Fp::from_raw_unchecked([ - 0x253e_cb98_b179_d841, - 0xc78d_10f7_2c06_1d6a, - 0xf768_f6f3_811b_ea15, - 0xe424_fc9a_ab5a_512b, - 0x8cd5_8db9_9cab_5001, - 0x0883_e4bf_d946_bc32, - ]), - }, - }; - - let c = Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x6934_cb98_b176_82ef, - 0xfa45_10ea_194e_1d67, - 0xff51_313d_2405_877e, - 0xd0cd_efcc_2e8d_0ca5, - 0x7bea_1ad8_3da0_106b, - 0x0c8e_97e6_1845_be39, - ]), - c1: Fp::from_raw_unchecked([ - 0x4779_cb98_b18d_82d8, - 0xb5e9_1144_4daa_1d7a, - 0x2f28_6bda_a653_2fc2, - 0xbca6_94f6_8bae_ff0f, - 0x3d75_e6b8_1a3a_7a5d, - 0x0a44_c3c4_98cc_96a3, - ]), - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x8b6f_cb98_b18a_2d86, - 0xe8a1_1137_3af2_1d77, - 0x3710_a624_493c_cd2b, - 0xa94f_8828_0ee1_ba89, - 0x2c8a_73d6_bb2f_3ac7, - 0x0e4f_76ea_d7cb_98aa, - ]), - c1: Fp::from_raw_unchecked([ - 0xcf65_cb98_b186_d834, - 0x1b59_112a_283a_1d74, - 0x3ef8_e06d_ec26_6a95, - 0x95f8_7b59_9214_7603, - 0x1b9f_00f5_5c23_fb31, - 0x125a_2a11_16ca_9ab1, - ]), - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x135b_cb98_b183_82e2, - 0x4e11_111d_1582_1d72, - 0x46e1_1ab7_8f10_07fe, - 0x82a1_6e8b_1547_317d, - 0x0ab3_8e13_fd18_bb9b, - 0x1664_dd37_55c9_9cb8, - ]), - c1: Fp::from_raw_unchecked([ - 0xce65_cb98_b131_8334, - 0xc759_0fdb_7c3a_1d2e, - 0x6fcb_8164_9d1c_8eb3, - 0x0d44_004d_1727_356a, - 0x3746_b738_a7d0_d296, - 0x136c_144a_96b1_34fc, - ]), - }, - }; - - assert_eq!(a.square(), a * a); - assert_eq!(b.square(), b * b); - assert_eq!(c.square(), c * c); - - assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b)); - - assert_eq!( - a.invert().unwrap() * b.invert().unwrap(), - (a * b).invert().unwrap() - ); - assert_eq!(a.invert().unwrap() * a, Fp6::one()); -} diff --git a/bls12_381/src/g1.rs b/bls12_381/src/g1.rs deleted file mode 100644 index 5469fd6080..0000000000 --- a/bls12_381/src/g1.rs +++ /dev/null @@ -1,1347 +0,0 @@ -//! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. - -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::fp::Fp; -use crate::Scalar; - -/// This is an element of $\mathbb{G}_1$ represented in the affine coordinate space. -/// It is ideal to keep elements in this representation to reduce memory usage and -/// improve performance through the use of mixed curve model arithmetic. -/// -/// Values of `G1Affine` are guaranteed to be in the $q$-order subgroup unless an -/// "unchecked" API was misused. -#[derive(Copy, Clone, Debug)] -pub struct G1Affine { - pub(crate) x: Fp, - pub(crate) y: Fp, - infinity: Choice, -} - -impl Default for G1Affine { - fn default() -> G1Affine { - G1Affine::identity() - } -} - -impl<'a> From<&'a G1Projective> for G1Affine { - fn from(p: &'a G1Projective) -> G1Affine { - let zinv = p.z.invert().unwrap_or(Fp::zero()); - let zinv2 = zinv.square(); - let x = p.x * zinv2; - let zinv3 = zinv2 * zinv; - let y = p.y * zinv3; - - let tmp = G1Affine { - x, - y, - infinity: Choice::from(0u8), - }; - - G1Affine::conditional_select(&tmp, &G1Affine::identity(), zinv.is_zero()) - } -} - -impl From for G1Affine { - fn from(p: G1Projective) -> G1Affine { - G1Affine::from(&p) - } -} - -impl ConstantTimeEq for G1Affine { - fn ct_eq(&self, other: &Self) -> Choice { - // The only cases in which two points are equal are - // 1. infinity is set on both - // 2. infinity is not set on both, and their coordinates are equal - - (self.infinity & other.infinity) - | ((!self.infinity) - & (!other.infinity) - & self.x.ct_eq(&other.x) - & self.y.ct_eq(&other.y)) - } -} - -impl ConditionallySelectable for G1Affine { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G1Affine { - x: Fp::conditional_select(&a.x, &b.x, choice), - y: Fp::conditional_select(&a.y, &b.y, choice), - infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), - } - } -} - -impl Eq for G1Affine {} -impl PartialEq for G1Affine { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G1Affine { - type Output = G1Affine; - - #[inline] - fn neg(self) -> G1Affine { - G1Affine { - x: self.x, - y: Fp::conditional_select(&-self.y, &Fp::one(), self.infinity), - infinity: self.infinity, - } - } -} - -impl Neg for G1Affine { - type Output = G1Affine; - - #[inline] - fn neg(self) -> G1Affine { - -&self - } -} - -impl<'a, 'b> Add<&'b G1Projective> for &'a G1Affine { - type Output = G1Projective; - - #[inline] - fn add(self, rhs: &'b G1Projective) -> G1Projective { - rhs.add_mixed(self) - } -} - -impl<'a, 'b> Add<&'b G1Affine> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn add(self, rhs: &'b G1Affine) -> G1Projective { - self.add_mixed(rhs) - } -} - -impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Affine { - type Output = G1Projective; - - #[inline] - fn sub(self, rhs: &'b G1Projective) -> G1Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Sub<&'b G1Affine> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn sub(self, rhs: &'b G1Affine) -> G1Projective { - self + (-rhs) - } -} - -impl_binops_additive!(G1Projective, G1Affine); -impl_binops_additive_specify_output!(G1Affine, G1Projective, G1Projective); - -const B: Fp = Fp::from_raw_unchecked([ - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e, -]); - -impl G1Affine { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G1Affine { - G1Affine { - x: Fp::zero(), - y: Fp::one(), - infinity: Choice::from(1u8), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G1Affine { - G1Affine { - x: Fp::from_raw_unchecked([ - 0x5cb3_8790_fd53_0c16, - 0x7817_fc67_9976_fff5, - 0x154f_95c7_143b_a1c1, - 0xf0ae_6acd_f3d0_e747, - 0xedce_6ecc_21db_f440, - 0x1201_7741_9e0b_fb75, - ]), - y: Fp::from_raw_unchecked([ - 0xbaac_93d5_0ce7_2271, - 0x8c22_631a_7918_fd8e, - 0xdd59_5f13_5707_25ce, - 0x51ac_5829_5040_5194, - 0x0e1c_8c3f_ad00_59c0, - 0x0bbc_3efc_5008_a26a, - ]), - infinity: Choice::from(0u8), - } - } - - /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_compressed(&self) -> [u8; 48] { - // Strictly speaking, self.x is zero already when self.infinity is true, but - // to guard against implementation mistakes we do not assume this. - let mut res = Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes(); - - // This point is in compressed form, so we set the most significant bit. - res[0] |= 1u8 << 7; - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= u8::conditional_select( - &0u8, - &(1u8 << 5), - (!self.infinity) & self.y.lexicographically_largest(), - ); - - res - } - - /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_uncompressed(&self) -> [u8; 96] { - let mut res = [0; 96]; - - res[0..48].copy_from_slice( - &Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes()[..], - ); - res[48..96].copy_from_slice( - &Fp::conditional_select(&self.y, &Fp::zero(), self.infinity).to_bytes()[..], - ); - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - res - } - - /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_uncompressed(bytes: &[u8; 96]) -> CtOption { - Self::from_uncompressed_unchecked(bytes) - .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is on the curve and not checking if it is in the correct subgroup. - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. - pub fn from_uncompressed_unchecked(bytes: &[u8; 96]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let x = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - - // Attempt to obtain the y-coordinate - let y = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[48..96]); - - Fp::from_bytes(&tmp) - }; - - x.and_then(|x| { - y.and_then(|y| { - // Create a point representing this value - let p = G1Affine::conditional_select( - &G1Affine { - x, - y, - infinity: infinity_flag_set, - }, - &G1Affine::identity(), - infinity_flag_set, - ); - - CtOption::new( - p, - // If the infinity flag is set, the x and y coordinates should have been zero. - ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & - // The compression flag should not have been set, as this is an uncompressed element - (!compression_flag_set) & - // The sort flag should not have been set, as this is an uncompressed element - (!sort_flag_set), - ) - }) - }) - } - - /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_compressed(bytes: &[u8; 48]) -> CtOption { - // We already know the point is on the curve because this is established - // by the y-coordinate recovery procedure in from_compressed_unchecked(). - - Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is in the correct subgroup. - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using `from_compressed()` instead. - pub fn from_compressed_unchecked(bytes: &[u8; 48]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let x = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - - x.and_then(|x| { - // If the infinity flag is set, return the value assuming - // the x-coordinate is zero and the sort bit is not set. - // - // Otherwise, return a recovered point (assuming the correct - // y-coordinate can be found) so long as the infinity flag - // was not set. - CtOption::new( - G1Affine::identity(), - infinity_flag_set & // Infinity flag should be set - compression_flag_set & // Compression flag should be set - (!sort_flag_set) & // Sort flag should not be set - x.is_zero(), // The x-coordinate should be zero - ) - .or_else(|| { - // Recover a y-coordinate given x by y = sqrt(x^3 + 4) - ((x.square() * x) + B).sqrt().and_then(|y| { - // Switch to the correct y-coordinate if necessary. - let y = Fp::conditional_select( - &y, - &-y, - y.lexicographically_largest() ^ sort_flag_set, - ); - - CtOption::new( - G1Affine { - x, - y, - infinity: infinity_flag_set, - }, - (!infinity_flag_set) & // Infinity flag should not be set - compression_flag_set, // Compression flag should be set - ) - }) - }) - }) - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.infinity - } - - /// Returns true if this point is free of an $h$-torsion component, and so it - /// exists within the $q$-order subgroup $\mathbb{G}_1$. This should always return true - /// unless an "unchecked" API was used. - pub fn is_torsion_free(&self) -> Choice { - const FQ_MODULUS_BYTES: [u8; 32] = [ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, - ]; - - // Clear the r-torsion from the point and check if it is the identity - G1Projective::from(*self) - .multiply(&FQ_MODULUS_BYTES) - .is_identity() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // y^2 - x^3 ?= 4 - (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity - } -} - -/// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space. -#[derive(Copy, Clone, Debug)] -pub struct G1Projective { - x: Fp, - y: Fp, - z: Fp, -} - -impl<'a> From<&'a G1Affine> for G1Projective { - fn from(p: &'a G1Affine) -> G1Projective { - G1Projective { - x: p.x, - y: p.y, - z: Fp::conditional_select(&Fp::one(), &Fp::zero(), p.infinity), - } - } -} - -impl From for G1Projective { - fn from(p: G1Affine) -> G1Projective { - G1Projective::from(&p) - } -} - -impl ConstantTimeEq for G1Projective { - fn ct_eq(&self, other: &Self) -> Choice { - // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? - - let z = other.z.square(); - let x1 = self.x * z; - let z = z * other.z; - let y1 = self.y * z; - let z = self.z.square(); - let x2 = other.x * z; - let z = z * self.z; - let y2 = other.y * z; - - let self_is_zero = self.z.is_zero(); - let other_is_zero = other.z.is_zero(); - - (self_is_zero & other_is_zero) // Both point at infinity - | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) - // Neither point at infinity, coordinates are the same - } -} - -impl ConditionallySelectable for G1Projective { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G1Projective { - x: Fp::conditional_select(&a.x, &b.x, choice), - y: Fp::conditional_select(&a.y, &b.y, choice), - z: Fp::conditional_select(&a.z, &b.z, choice), - } - } -} - -impl Eq for G1Projective {} -impl PartialEq for G1Projective { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn neg(self) -> G1Projective { - G1Projective { - x: self.x, - y: -self.y, - z: self.z, - } - } -} - -impl Neg for G1Projective { - type Output = G1Projective; - - #[inline] - fn neg(self) -> G1Projective { - -&self - } -} - -impl<'a, 'b> Add<&'b G1Projective> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn add(self, rhs: &'b G1Projective) -> G1Projective { - self.add(rhs) - } -} - -impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn sub(self, rhs: &'b G1Projective) -> G1Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G1Projective { - type Output = G1Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - self.multiply(&other.to_bytes()) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G1Affine { - type Output = G1Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - G1Projective::from(self).multiply(&other.to_bytes()) - } -} - -impl_binops_additive!(G1Projective, G1Projective); -impl_binops_multiplicative!(G1Projective, Scalar); -impl_binops_multiplicative_mixed!(G1Affine, Scalar, G1Projective); - -impl G1Projective { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G1Projective { - G1Projective { - x: Fp::zero(), - y: Fp::one(), - z: Fp::zero(), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G1Projective { - G1Projective { - x: Fp::from_raw_unchecked([ - 0x5cb3_8790_fd53_0c16, - 0x7817_fc67_9976_fff5, - 0x154f_95c7_143b_a1c1, - 0xf0ae_6acd_f3d0_e747, - 0xedce_6ecc_21db_f440, - 0x1201_7741_9e0b_fb75, - ]), - y: Fp::from_raw_unchecked([ - 0xbaac_93d5_0ce7_2271, - 0x8c22_631a_7918_fd8e, - 0xdd59_5f13_5707_25ce, - 0x51ac_5829_5040_5194, - 0x0e1c_8c3f_ad00_59c0, - 0x0bbc_3efc_5008_a26a, - ]), - z: Fp::one(), - } - } - - /// Computes the doubling of this point. - pub fn double(&self) -> G1Projective { - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - // - // There are no points of order 2. - - let a = self.x.square(); - let b = self.y.square(); - let c = b.square(); - let d = self.x + b; - let d = d.square(); - let d = d - a - c; - let d = d + d; - let e = a + a + a; - let f = e.square(); - let z3 = self.z * self.y; - let z3 = z3 + z3; - let x3 = f - (d + d); - let c = c + c; - let c = c + c; - let c = c + c; - let y3 = e * (d - x3) - c; - - let tmp = G1Projective { - x: x3, - y: y3, - z: z3, - }; - - G1Projective::conditional_select(&tmp, &G1Projective::identity(), self.is_identity()) - } - - /// Adds this point to another point. - pub fn add(&self, rhs: &G1Projective) -> G1Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G1Projective::conditional_select(self, rhs, f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let z = rhs.z.square(); - let u1 = self.x * z; - let z = z * rhs.z; - let s1 = self.y * z; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G1Projective { - x: x3, - y: y3, - z: z3, - }; - - G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - /// Adds this point to another point in the affine model. - pub fn add_mixed(&self, rhs: &G1Affine) -> G1Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G1Projective::conditional_select(self, &G1Projective::from(rhs), f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let u1 = self.x; - let s1 = self.y; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z; - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G1Projective { - x: x3, - y: y3, - z: z3, - }; - - G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - fn multiply(&self, by: &[u8; 32]) -> G1Projective { - let mut acc = G1Projective::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading bit because it's always unset for Fq - // elements. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = G1Projective::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - - /// Converts a batch of `G1Projective` elements into `G1Affine` elements. This - /// function will panic if `p.len() != q.len()`. - pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) { - assert_eq!(p.len(), q.len()); - - let mut acc = Fp::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `x` field of `G1Affine` to store the product - // of previous z-coordinates seen. - q.x = acc; - - // We will end up skipping all identities in p - acc = Fp::conditional_select(&(acc * p.z), &acc, p.is_identity()); - } - - // This is the inverse, as all z-coordinates are nonzero and the ones - // that are not are skipped. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { - let skip = p.is_identity(); - - // Compute tmp = 1/z - let tmp = q.x * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc = Fp::conditional_select(&(acc * p.z), &acc, skip); - - // Set the coordinates to the correct value - let tmp2 = tmp.square(); - let tmp3 = tmp2 * tmp; - - q.x = p.x * tmp2; - q.y = p.y * tmp3; - q.infinity = Choice::from(0u8); - - *q = G1Affine::conditional_select(&q, &G1Affine::identity(), skip); - } - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.z.is_zero() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // Y^2 - X^3 = 4(Z^6) - - (self.y.square() - (self.x.square() * self.x)) - .ct_eq(&((self.z.square() * self.z).square() * B)) - | self.z.is_zero() - } -} - -#[test] -fn test_is_on_curve() { - assert!(bool::from(G1Affine::identity().is_on_curve())); - assert!(bool::from(G1Affine::generator().is_on_curve())); - assert!(bool::from(G1Projective::identity().is_on_curve())); - assert!(bool::from(G1Projective::generator().is_on_curve())); - - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - let gen = G1Affine::generator(); - let mut test = G1Projective { - x: gen.x * (z.square()), - y: gen.y * (z.square() * z), - z, - }; - - assert!(bool::from(test.is_on_curve())); - - test.x = z; - assert!(!bool::from(test.is_on_curve())); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_affine_point_equality() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_point_equality() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - let mut c = G1Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - assert!(bool::from(c.is_on_curve())); - - assert!(a == c); - assert!(b != c); - assert!(c == a); - assert!(c != b); - - c.y = -c.y; - assert!(bool::from(c.is_on_curve())); - - assert!(a != c); - assert!(b != c); - assert!(c != a); - assert!(c != b); - - c.y = -c.y; - c.x = z; - assert!(!bool::from(c.is_on_curve())); - assert!(a != b); - assert!(a != c); - assert!(b != c); -} - -#[test] -fn test_conditionally_select_affine() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(0u8)), a); - assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(1u8)), b); -} - -#[test] -fn test_conditionally_select_projective() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); - - assert_eq!( - G1Projective::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - G1Projective::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_projective_to_affine() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); - - assert!(bool::from(G1Affine::from(a).is_on_curve())); - assert!(!bool::from(G1Affine::from(a).is_identity())); - assert!(bool::from(G1Affine::from(b).is_on_curve())); - assert!(bool::from(G1Affine::from(b).is_identity())); - - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - let c = G1Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - - assert_eq!(G1Affine::from(c), G1Affine::generator()); -} - -#[test] -fn test_affine_to_projective() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert!(bool::from(G1Projective::from(a).is_on_curve())); - assert!(!bool::from(G1Projective::from(a).is_identity())); - assert!(bool::from(G1Projective::from(b).is_on_curve())); - assert!(bool::from(G1Projective::from(b).is_identity())); -} - -#[test] -fn test_doubling() { - { - let tmp = G1Projective::identity().double(); - assert!(bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - } - { - let tmp = G1Projective::generator().double(); - assert!(!bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - - assert_eq!( - G1Affine::from(tmp), - G1Affine { - x: Fp::from_raw_unchecked([ - 0x53e9_78ce_58a9_ba3c, - 0x3ea0_583c_4f3d_65f9, - 0x4d20_bb47_f001_2960, - 0xa54c_664a_e5b2_b5d9, - 0x26b5_52a3_9d7e_b21f, - 0x0008_895d_26e6_8785, - ]), - y: Fp::from_raw_unchecked([ - 0x7011_0b32_9829_3940, - 0xda33_c539_3f1f_6afc, - 0xb86e_dfd1_6a5a_a785, - 0xaec6_d1c9_e7b1_c895, - 0x25cf_c2b5_22d1_1720, - 0x0636_1c83_f8d0_9b15, - ]), - infinity: Choice::from(0u8) - } - ); - } -} - -#[test] -fn test_projective_addition() { - { - let a = G1Projective::identity(); - let b = G1Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G1Projective::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::generator().double().double(); // 4P - let b = G1Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G1Projective::generator(); - for _ in 0..5 { - d += G1Projective::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]); - let beta = beta.square(); - let a = G1Projective::generator().double().double(); - let b = G1Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G1Affine::from(c), - G1Affine::from(G1Projective { - x: Fp::from_raw_unchecked([ - 0x29e1_e987_ef68_f2d0, - 0xc5f3_ec53_1db0_3233, - 0xacd6_c4b6_ca19_730f, - 0x18ad_9e82_7bc2_bab7, - 0x46e3_b2c5_785c_c7a9, - 0x07e5_71d4_2d22_ddd6, - ]), - y: Fp::from_raw_unchecked([ - 0x94d1_17a7_e5a5_39e7, - 0x8e17_ef67_3d4b_5d22, - 0x9d74_6aaf_508a_33ea, - 0x8c6d_883d_2516_c9a2, - 0x0bc3_b8d5_fb04_47f7, - 0x07bf_a4c7_210f_4f44, - ]), - z: Fp::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -fn test_mixed_addition() { - { - let a = G1Affine::identity(); - let b = G1Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G1Affine::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Affine::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::generator().double().double(); // 4P - let b = G1Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G1Projective::generator(); - for _ in 0..5 { - d += G1Affine::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]); - let beta = beta.square(); - let a = G1Projective::generator().double().double(); - let b = G1Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - let a = G1Affine::from(a); - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G1Affine::from(c), - G1Affine::from(G1Projective { - x: Fp::from_raw_unchecked([ - 0x29e1_e987_ef68_f2d0, - 0xc5f3_ec53_1db0_3233, - 0xacd6_c4b6_ca19_730f, - 0x18ad_9e82_7bc2_bab7, - 0x46e3_b2c5_785c_c7a9, - 0x07e5_71d4_2d22_ddd6, - ]), - y: Fp::from_raw_unchecked([ - 0x94d1_17a7_e5a5_39e7, - 0x8e17_ef67_3d4b_5d22, - 0x9d74_6aaf_508a_33ea, - 0x8c6d_883d_2516_c9a2, - 0x0bc3_b8d5_fb04_47f7, - 0x07bf_a4c7_210f_4f44, - ]), - z: Fp::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_negation_and_subtraction() { - let a = G1Projective::generator().double(); - assert_eq!(a + (-a), G1Projective::identity()); - assert_eq!(a + (-a), a - a); -} - -#[test] -fn test_affine_negation_and_subtraction() { - let a = G1Affine::generator(); - assert_eq!(G1Projective::from(a) + (-a), G1Projective::identity()); - assert_eq!(G1Projective::from(a) + (-a), G1Projective::from(a) - a); -} - -#[test] -fn test_projective_scalar_multiplication() { - let g = G1Projective::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!((g * a) * b, g * c); -} - -#[test] -fn test_affine_scalar_multiplication() { - let g = G1Affine::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!(G1Affine::from(g * a) * b, g * c); -} - -#[test] -fn test_is_torsion_free() { - let a = G1Affine { - x: Fp::from_raw_unchecked([ - 0x0aba_f895_b97e_43c8, - 0xba4c_6432_eb9b_61b0, - 0x1250_6f52_adfe_307f, - 0x7502_8c34_3933_6b72, - 0x8474_4f05_b8e9_bd71, - 0x113d_554f_b095_54f7, - ]), - y: Fp::from_raw_unchecked([ - 0x73e9_0e88_f5cf_01c0, - 0x3700_7b65_dd31_97e2, - 0x5cf9_a199_2f0d_7c78, - 0x4f83_c10b_9eb3_330d, - 0xf6a6_3f6f_07f6_0961, - 0x0c53_b5b9_7e63_4df3, - ]), - infinity: Choice::from(0u8), - }; - assert!(!bool::from(a.is_torsion_free())); - - assert!(bool::from(G1Affine::identity().is_torsion_free())); - assert!(bool::from(G1Affine::generator().is_torsion_free())); -} - -#[test] -fn test_batch_normalize() { - let a = G1Projective::generator().double(); - let b = a.double(); - let c = b.double(); - - for a_identity in (0..1).map(|n| n == 1) { - for b_identity in (0..1).map(|n| n == 1) { - for c_identity in (0..1).map(|n| n == 1) { - let mut v = [a, b, c]; - if a_identity { - v[0] = G1Projective::identity() - } - if b_identity { - v[1] = G1Projective::identity() - } - if c_identity { - v[2] = G1Projective::identity() - } - - let mut t = [ - G1Affine::identity(), - G1Affine::identity(), - G1Affine::identity(), - ]; - let expected = [ - G1Affine::from(v[0]), - G1Affine::from(v[1]), - G1Affine::from(v[2]), - ]; - - G1Projective::batch_normalize(&v[..], &mut t[..]); - - assert_eq!(&t[..], &expected[..]); - } - } - } -} diff --git a/bls12_381/src/g2.rs b/bls12_381/src/g2.rs deleted file mode 100644 index d3f505b46b..0000000000 --- a/bls12_381/src/g2.rs +++ /dev/null @@ -1,1595 +0,0 @@ -//! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. - -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::fp::Fp; -use crate::fp2::Fp2; -use crate::Scalar; - -/// This is an element of $\mathbb{G}_2$ represented in the affine coordinate space. -/// It is ideal to keep elements in this representation to reduce memory usage and -/// improve performance through the use of mixed curve model arithmetic. -/// -/// Values of `G2Affine` are guaranteed to be in the $q$-order subgroup unless an -/// "unchecked" API was misused. -#[derive(Copy, Clone, Debug)] -pub struct G2Affine { - pub(crate) x: Fp2, - pub(crate) y: Fp2, - infinity: Choice, -} - -impl Default for G2Affine { - fn default() -> G2Affine { - G2Affine::identity() - } -} - -impl<'a> From<&'a G2Projective> for G2Affine { - fn from(p: &'a G2Projective) -> G2Affine { - let zinv = p.z.invert().unwrap_or(Fp2::zero()); - let zinv2 = zinv.square(); - let x = p.x * zinv2; - let zinv3 = zinv2 * zinv; - let y = p.y * zinv3; - - let tmp = G2Affine { - x, - y, - infinity: Choice::from(0u8), - }; - - G2Affine::conditional_select(&tmp, &G2Affine::identity(), zinv.is_zero()) - } -} - -impl From for G2Affine { - fn from(p: G2Projective) -> G2Affine { - G2Affine::from(&p) - } -} - -impl ConstantTimeEq for G2Affine { - fn ct_eq(&self, other: &Self) -> Choice { - // The only cases in which two points are equal are - // 1. infinity is set on both - // 2. infinity is not set on both, and their coordinates are equal - - (self.infinity & other.infinity) - | ((!self.infinity) - & (!other.infinity) - & self.x.ct_eq(&other.x) - & self.y.ct_eq(&other.y)) - } -} - -impl ConditionallySelectable for G2Affine { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G2Affine { - x: Fp2::conditional_select(&a.x, &b.x, choice), - y: Fp2::conditional_select(&a.y, &b.y, choice), - infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), - } - } -} - -impl Eq for G2Affine {} -impl PartialEq for G2Affine { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G2Affine { - type Output = G2Affine; - - #[inline] - fn neg(self) -> G2Affine { - G2Affine { - x: self.x, - y: Fp2::conditional_select(&-self.y, &Fp2::one(), self.infinity), - infinity: self.infinity, - } - } -} - -impl Neg for G2Affine { - type Output = G2Affine; - - #[inline] - fn neg(self) -> G2Affine { - -&self - } -} - -impl<'a, 'b> Add<&'b G2Projective> for &'a G2Affine { - type Output = G2Projective; - - #[inline] - fn add(self, rhs: &'b G2Projective) -> G2Projective { - rhs.add_mixed(self) - } -} - -impl<'a, 'b> Add<&'b G2Affine> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn add(self, rhs: &'b G2Affine) -> G2Projective { - self.add_mixed(rhs) - } -} - -impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Affine { - type Output = G2Projective; - - #[inline] - fn sub(self, rhs: &'b G2Projective) -> G2Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Sub<&'b G2Affine> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn sub(self, rhs: &'b G2Affine) -> G2Projective { - self + (-rhs) - } -} - -impl_binops_additive!(G2Projective, G2Affine); -impl_binops_additive_specify_output!(G2Affine, G2Projective, G2Projective); - -const B: Fp2 = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e, - ]), - c1: Fp::from_raw_unchecked([ - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e, - ]), -}; - -impl G2Affine { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G2Affine { - G2Affine { - x: Fp2::zero(), - y: Fp2::one(), - infinity: Choice::from(1u8), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G2Affine { - G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf5f2_8fa2_0294_0a10, - 0xb3f5_fb26_87b4_961a, - 0xa1a8_93b5_3e2a_e580, - 0x9894_999d_1a3c_aee9, - 0x6f67_b763_1863_366b, - 0x0581_9192_4350_bcd7, - ]), - c1: Fp::from_raw_unchecked([ - 0xa5a9_c075_9e23_f606, - 0xaaa0_c59d_bccd_60c3, - 0x3bb1_7e18_e286_7806, - 0x1b1a_b6cc_8541_b367, - 0xc2b6_ed0e_f215_8547, - 0x1192_2a09_7360_edf3, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x4c73_0af8_6049_4c4a, - 0x597c_fa1f_5e36_9c5a, - 0xe7e6_856c_aa0a_635a, - 0xbbef_b5e9_6e0d_495f, - 0x07d3_a975_f0ef_25a2, - 0x0083_fd8e_7e80_dae5, - ]), - c1: Fp::from_raw_unchecked([ - 0xadc0_fc92_df64_b05d, - 0x18aa_270a_2b14_61dc, - 0x86ad_ac6a_3be4_eba0, - 0x7949_5c4e_c93d_a33a, - 0xe717_5850_a43c_caed, - 0x0b2b_c2a1_63de_1bf2, - ]), - }, - infinity: Choice::from(0u8), - } - } - - /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_compressed(&self) -> [u8; 96] { - // Strictly speaking, self.x is zero already when self.infinity is true, but - // to guard against implementation mistakes we do not assume this. - let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); - - let mut res = [0; 96]; - - (&mut res[0..48]).copy_from_slice(&x.c1.to_bytes()[..]); - (&mut res[48..96]).copy_from_slice(&x.c0.to_bytes()[..]); - - // This point is in compressed form, so we set the most significant bit. - res[0] |= 1u8 << 7; - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= u8::conditional_select( - &0u8, - &(1u8 << 5), - (!self.infinity) & self.y.lexicographically_largest(), - ); - - res - } - - /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_uncompressed(&self) -> [u8; 192] { - let mut res = [0; 192]; - - let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); - let y = Fp2::conditional_select(&self.y, &Fp2::zero(), self.infinity); - - res[0..48].copy_from_slice(&x.c1.to_bytes()[..]); - res[48..96].copy_from_slice(&x.c0.to_bytes()[..]); - res[96..144].copy_from_slice(&y.c1.to_bytes()[..]); - res[144..192].copy_from_slice(&y.c0.to_bytes()[..]); - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - res - } - - /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_uncompressed(bytes: &[u8; 192]) -> CtOption { - Self::from_uncompressed_unchecked(bytes) - .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is on the curve and not checking if it is in the correct subgroup. - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using `from_uncompressed()` instead. - pub fn from_uncompressed_unchecked(bytes: &[u8; 192]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let xc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - let xc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[48..96]); - - Fp::from_bytes(&tmp) - }; - - // Attempt to obtain the y-coordinate - let yc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[96..144]); - - Fp::from_bytes(&tmp) - }; - let yc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[144..192]); - - Fp::from_bytes(&tmp) - }; - - xc1.and_then(|xc1| { - xc0.and_then(|xc0| { - yc1.and_then(|yc1| { - yc0.and_then(|yc0| { - let x = Fp2 { - c0: xc0, - c1: xc1 - }; - let y = Fp2 { - c0: yc0, - c1: yc1 - }; - - // Create a point representing this value - let p = G2Affine::conditional_select( - &G2Affine { - x, - y, - infinity: infinity_flag_set, - }, - &G2Affine::identity(), - infinity_flag_set, - ); - - CtOption::new( - p, - // If the infinity flag is set, the x and y coordinates should have been zero. - ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & - // The compression flag should not have been set, as this is an uncompressed element - (!compression_flag_set) & - // The sort flag should not have been set, as this is an uncompressed element - (!sort_flag_set), - ) - }) - }) - }) - }) - } - - /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_compressed(bytes: &[u8; 96]) -> CtOption { - // We already know the point is on the curve because this is established - // by the y-coordinate recovery procedure in from_compressed_unchecked(). - - Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is in the correct subgroup. - /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, - /// API invariants may be broken.** Please consider using `from_compressed()` instead. - pub fn from_compressed_unchecked(bytes: &[u8; 96]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let xc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - let xc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[48..96]); - - Fp::from_bytes(&tmp) - }; - - xc1.and_then(|xc1| { - xc0.and_then(|xc0| { - let x = Fp2 { c0: xc0, c1: xc1 }; - - // If the infinity flag is set, return the value assuming - // the x-coordinate is zero and the sort bit is not set. - // - // Otherwise, return a recovered point (assuming the correct - // y-coordinate can be found) so long as the infinity flag - // was not set. - CtOption::new( - G2Affine::identity(), - infinity_flag_set & // Infinity flag should be set - compression_flag_set & // Compression flag should be set - (!sort_flag_set) & // Sort flag should not be set - x.is_zero(), // The x-coordinate should be zero - ) - .or_else(|| { - // Recover a y-coordinate given x by y = sqrt(x^3 + 4) - ((x.square() * x) + B).sqrt().and_then(|y| { - // Switch to the correct y-coordinate if necessary. - let y = Fp2::conditional_select( - &y, - &-y, - y.lexicographically_largest() ^ sort_flag_set, - ); - - CtOption::new( - G2Affine { - x, - y, - infinity: infinity_flag_set, - }, - (!infinity_flag_set) & // Infinity flag should not be set - compression_flag_set, // Compression flag should be set - ) - }) - }) - }) - }) - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.infinity - } - - /// Returns true if this point is free of an $h$-torsion component, and so it - /// exists within the $q$-order subgroup $\mathbb{G}_2$. This should always return true - /// unless an "unchecked" API was used. - pub fn is_torsion_free(&self) -> Choice { - const FQ_MODULUS_BYTES: [u8; 32] = [ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, - ]; - - // Clear the r-torsion from the point and check if it is the identity - G2Projective::from(*self) - .multiply(&FQ_MODULUS_BYTES) - .is_identity() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // y^2 - x^3 ?= 4(u + 1) - (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity - } -} - -/// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space. -#[derive(Copy, Clone, Debug)] -pub struct G2Projective { - pub(crate) x: Fp2, - pub(crate) y: Fp2, - pub(crate) z: Fp2, -} - -impl<'a> From<&'a G2Affine> for G2Projective { - fn from(p: &'a G2Affine) -> G2Projective { - G2Projective { - x: p.x, - y: p.y, - z: Fp2::conditional_select(&Fp2::one(), &Fp2::zero(), p.infinity), - } - } -} - -impl From for G2Projective { - fn from(p: G2Affine) -> G2Projective { - G2Projective::from(&p) - } -} - -impl ConstantTimeEq for G2Projective { - fn ct_eq(&self, other: &Self) -> Choice { - // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? - - let z = other.z.square(); - let x1 = self.x * z; - let z = z * other.z; - let y1 = self.y * z; - let z = self.z.square(); - let x2 = other.x * z; - let z = z * self.z; - let y2 = other.y * z; - - let self_is_zero = self.z.is_zero(); - let other_is_zero = other.z.is_zero(); - - (self_is_zero & other_is_zero) // Both point at infinity - | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) - // Neither point at infinity, coordinates are the same - } -} - -impl ConditionallySelectable for G2Projective { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G2Projective { - x: Fp2::conditional_select(&a.x, &b.x, choice), - y: Fp2::conditional_select(&a.y, &b.y, choice), - z: Fp2::conditional_select(&a.z, &b.z, choice), - } - } -} - -impl Eq for G2Projective {} -impl PartialEq for G2Projective { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn neg(self) -> G2Projective { - G2Projective { - x: self.x, - y: -self.y, - z: self.z, - } - } -} - -impl Neg for G2Projective { - type Output = G2Projective; - - #[inline] - fn neg(self) -> G2Projective { - -&self - } -} - -impl<'a, 'b> Add<&'b G2Projective> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn add(self, rhs: &'b G2Projective) -> G2Projective { - self.add(rhs) - } -} - -impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn sub(self, rhs: &'b G2Projective) -> G2Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G2Projective { - type Output = G2Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - self.multiply(&other.to_bytes()) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G2Affine { - type Output = G2Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - G2Projective::from(self).multiply(&other.to_bytes()) - } -} - -impl_binops_additive!(G2Projective, G2Projective); -impl_binops_multiplicative!(G2Projective, Scalar); -impl_binops_multiplicative_mixed!(G2Affine, Scalar, G2Projective); - -impl G2Projective { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G2Projective { - G2Projective { - x: Fp2::zero(), - y: Fp2::one(), - z: Fp2::zero(), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G2Projective { - G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf5f2_8fa2_0294_0a10, - 0xb3f5_fb26_87b4_961a, - 0xa1a8_93b5_3e2a_e580, - 0x9894_999d_1a3c_aee9, - 0x6f67_b763_1863_366b, - 0x0581_9192_4350_bcd7, - ]), - c1: Fp::from_raw_unchecked([ - 0xa5a9_c075_9e23_f606, - 0xaaa0_c59d_bccd_60c3, - 0x3bb1_7e18_e286_7806, - 0x1b1a_b6cc_8541_b367, - 0xc2b6_ed0e_f215_8547, - 0x1192_2a09_7360_edf3, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x4c73_0af8_6049_4c4a, - 0x597c_fa1f_5e36_9c5a, - 0xe7e6_856c_aa0a_635a, - 0xbbef_b5e9_6e0d_495f, - 0x07d3_a975_f0ef_25a2, - 0x0083_fd8e_7e80_dae5, - ]), - c1: Fp::from_raw_unchecked([ - 0xadc0_fc92_df64_b05d, - 0x18aa_270a_2b14_61dc, - 0x86ad_ac6a_3be4_eba0, - 0x7949_5c4e_c93d_a33a, - 0xe717_5850_a43c_caed, - 0x0b2b_c2a1_63de_1bf2, - ]), - }, - z: Fp2::one(), - } - } - - /// Computes the doubling of this point. - pub fn double(&self) -> G2Projective { - // http://www.hyperelliptic.org/EFD/g2p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - // - // There are no points of order 2. - - let a = self.x.square(); - let b = self.y.square(); - let c = b.square(); - let d = self.x + b; - let d = d.square(); - let d = d - a - c; - let d = d + d; - let e = a + a + a; - let f = e.square(); - let z3 = self.z * self.y; - let z3 = z3 + z3; - let x3 = f - (d + d); - let c = c + c; - let c = c + c; - let c = c + c; - let y3 = e * (d - x3) - c; - - let tmp = G2Projective { - x: x3, - y: y3, - z: z3, - }; - - G2Projective::conditional_select(&tmp, &G2Projective::identity(), self.is_identity()) - } - - /// Adds this point to another point. - pub fn add(&self, rhs: &G2Projective) -> G2Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G2Projective::conditional_select(self, rhs, f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let z = rhs.z.square(); - let u1 = self.x * z; - let z = z * rhs.z; - let s1 = self.y * z; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp2::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G2Projective { - x: x3, - y: y3, - z: z3, - }; - - G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - /// Adds this point to another point in the affine model. - pub fn add_mixed(&self, rhs: &G2Affine) -> G2Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G2Projective::conditional_select(self, &G2Projective::from(rhs), f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let u1 = self.x; - let s1 = self.y; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp2::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z; - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G2Projective { - x: x3, - y: y3, - z: z3, - }; - - G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - fn multiply(&self, by: &[u8; 32]) -> G2Projective { - let mut acc = G2Projective::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading bit because it's always unset for Fq - // elements. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = G2Projective::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - - /// Converts a batch of `G2Projective` elements into `G2Affine` elements. This - /// function will panic if `p.len() != q.len()`. - pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) { - assert_eq!(p.len(), q.len()); - - let mut acc = Fp2::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `x` field of `G2Affine` to store the product - // of previous z-coordinates seen. - q.x = acc; - - // We will end up skipping all identities in p - acc = Fp2::conditional_select(&(acc * p.z), &acc, p.is_identity()); - } - - // This is the inverse, as all z-coordinates are nonzero and the ones - // that are not are skipped. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { - let skip = p.is_identity(); - - // Compute tmp = 1/z - let tmp = q.x * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc = Fp2::conditional_select(&(acc * p.z), &acc, skip); - - // Set the coordinates to the correct value - let tmp2 = tmp.square(); - let tmp3 = tmp2 * tmp; - - q.x = p.x * tmp2; - q.y = p.y * tmp3; - q.infinity = Choice::from(0u8); - - *q = G2Affine::conditional_select(&q, &G2Affine::identity(), skip); - } - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.z.is_zero() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // Y^2 - X^3 = 4(u + 1)(Z^6) - - (self.y.square() - (self.x.square() * self.x)) - .ct_eq(&((self.z.square() * self.z).square() * B)) - | self.z.is_zero() - } -} - -#[test] -fn test_is_on_curve() { - assert!(bool::from(G2Affine::identity().is_on_curve())); - assert!(bool::from(G2Affine::generator().is_on_curve())); - assert!(bool::from(G2Projective::identity().is_on_curve())); - assert!(bool::from(G2Projective::generator().is_on_curve())); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - let gen = G2Affine::generator(); - let mut test = G2Projective { - x: gen.x * (z.square()), - y: gen.y * (z.square() * z), - z, - }; - - assert!(bool::from(test.is_on_curve())); - - test.x = z; - assert!(!bool::from(test.is_on_curve())); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_affine_point_equality() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_point_equality() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - let mut c = G2Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - assert!(bool::from(c.is_on_curve())); - - assert!(a == c); - assert!(b != c); - assert!(c == a); - assert!(c != b); - - c.y = -c.y; - assert!(bool::from(c.is_on_curve())); - - assert!(a != c); - assert!(b != c); - assert!(c != a); - assert!(c != b); - - c.y = -c.y; - c.x = z; - assert!(!bool::from(c.is_on_curve())); - assert!(a != b); - assert!(a != c); - assert!(b != c); -} - -#[test] -fn test_conditionally_select_affine() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(0u8)), a); - assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(1u8)), b); -} - -#[test] -fn test_conditionally_select_projective() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); - - assert_eq!( - G2Projective::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - G2Projective::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_projective_to_affine() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); - - assert!(bool::from(G2Affine::from(a).is_on_curve())); - assert!(!bool::from(G2Affine::from(a).is_identity())); - assert!(bool::from(G2Affine::from(b).is_on_curve())); - assert!(bool::from(G2Affine::from(b).is_identity())); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - let c = G2Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - - assert_eq!(G2Affine::from(c), G2Affine::generator()); -} - -#[test] -fn test_affine_to_projective() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert!(bool::from(G2Projective::from(a).is_on_curve())); - assert!(!bool::from(G2Projective::from(a).is_identity())); - assert!(bool::from(G2Projective::from(b).is_on_curve())); - assert!(bool::from(G2Projective::from(b).is_identity())); -} - -#[test] -fn test_doubling() { - { - let tmp = G2Projective::identity().double(); - assert!(bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - } - { - let tmp = G2Projective::generator().double(); - assert!(!bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - - assert_eq!( - G2Affine::from(tmp), - G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xe9d9_e2da_9620_f98b, - 0x54f1_1993_46b9_7f36, - 0x3db3_b820_376b_ed27, - 0xcfdb_31c9_b0b6_4f4c, - 0x41d7_c127_8635_4493, - 0x0571_0794_c255_c064, - ]), - c1: Fp::from_raw_unchecked([ - 0xd6c1_d3ca_6ea0_d06e, - 0xda0c_bd90_5595_489f, - 0x4f53_52d4_3479_221d, - 0x8ade_5d73_6f8c_97e0, - 0x48cc_8433_925e_f70e, - 0x08d7_ea71_ea91_ef81, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x15ba_26eb_4b0d_186f, - 0x0d08_6d64_b7e9_e01e, - 0xc8b8_48dd_652f_4c78, - 0xeecf_46a6_123b_ae4f, - 0x255e_8dd8_b6dc_812a, - 0x1641_42af_21dc_f93f, - ]), - c1: Fp::from_raw_unchecked([ - 0xf9b4_a1a8_9598_4db4, - 0xd417_b114_cccf_f748, - 0x6856_301f_c89f_086e, - 0x41c7_7787_8931_e3da, - 0x3556_b155_066a_2105, - 0x00ac_f7d3_25cb_89cf, - ]), - }, - infinity: Choice::from(0u8) - } - ); - } -} - -#[test] -fn test_projective_addition() { - { - let a = G2Projective::identity(); - let b = G2Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G2Projective::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::generator().double().double(); // 4P - let b = G2Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G2Projective::generator(); - for _ in 0..5 { - d += G2Projective::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]), - c1: Fp::zero(), - }; - let beta = beta.square(); - let a = G2Projective::generator().double().double(); - let b = G2Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G2Affine::from(c), - G2Affine::from(G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x705a_bc79_9ca7_73d3, - 0xfe13_2292_c1d4_bf08, - 0xf37e_ce3e_07b2_b466, - 0x887e_1c43_f447_e301, - 0x1e09_70d0_33bc_77e8, - 0x1985_c81e_20a6_93f2, - ]), - c1: Fp::from_raw_unchecked([ - 0x1d79_b25d_b36a_b924, - 0x2394_8e4d_5296_39d3, - 0x471b_a7fb_0d00_6297, - 0x2c36_d4b4_465d_c4c0, - 0x82bb_c3cf_ec67_f538, - 0x051d_2728_b67b_f952, - ]) - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x41b1_bbf6_576c_0abf, - 0xb6cc_9371_3f7a_0f9a, - 0x6b65_b43e_48f3_f01f, - 0xfb7a_4cfc_af81_be4f, - 0x3e32_dadc_6ec2_2cb6, - 0x0bb0_fc49_d798_07e3, - ]), - c1: Fp::from_raw_unchecked([ - 0x7d13_9778_8f5f_2ddf, - 0xab29_0714_4ff0_d8e8, - 0x5b75_73e0_cdb9_1f92, - 0x4cb8_932d_d31d_af28, - 0x62bb_fac6_db05_2a54, - 0x11f9_5c16_d14c_3bbe, - ]) - }, - z: Fp2::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -fn test_mixed_addition() { - { - let a = G2Affine::identity(); - let b = G2Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G2Affine::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Affine::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::generator().double().double(); // 4P - let b = G2Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G2Projective::generator(); - for _ in 0..5 { - d += G2Affine::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741, - ]), - c1: Fp::zero(), - }; - let beta = beta.square(); - let a = G2Projective::generator().double().double(); - let b = G2Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - let a = G2Affine::from(a); - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G2Affine::from(c), - G2Affine::from(G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x705a_bc79_9ca7_73d3, - 0xfe13_2292_c1d4_bf08, - 0xf37e_ce3e_07b2_b466, - 0x887e_1c43_f447_e301, - 0x1e09_70d0_33bc_77e8, - 0x1985_c81e_20a6_93f2, - ]), - c1: Fp::from_raw_unchecked([ - 0x1d79_b25d_b36a_b924, - 0x2394_8e4d_5296_39d3, - 0x471b_a7fb_0d00_6297, - 0x2c36_d4b4_465d_c4c0, - 0x82bb_c3cf_ec67_f538, - 0x051d_2728_b67b_f952, - ]) - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x41b1_bbf6_576c_0abf, - 0xb6cc_9371_3f7a_0f9a, - 0x6b65_b43e_48f3_f01f, - 0xfb7a_4cfc_af81_be4f, - 0x3e32_dadc_6ec2_2cb6, - 0x0bb0_fc49_d798_07e3, - ]), - c1: Fp::from_raw_unchecked([ - 0x7d13_9778_8f5f_2ddf, - 0xab29_0714_4ff0_d8e8, - 0x5b75_73e0_cdb9_1f92, - 0x4cb8_932d_d31d_af28, - 0x62bb_fac6_db05_2a54, - 0x11f9_5c16_d14c_3bbe, - ]) - }, - z: Fp2::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_negation_and_subtraction() { - let a = G2Projective::generator().double(); - assert_eq!(a + (-a), G2Projective::identity()); - assert_eq!(a + (-a), a - a); -} - -#[test] -fn test_affine_negation_and_subtraction() { - let a = G2Affine::generator(); - assert_eq!(G2Projective::from(a) + (-a), G2Projective::identity()); - assert_eq!(G2Projective::from(a) + (-a), G2Projective::from(a) - a); -} - -#[test] -fn test_projective_scalar_multiplication() { - let g = G2Projective::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!((g * a) * b, g * c); -} - -#[test] -fn test_affine_scalar_multiplication() { - let g = G2Affine::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!(G2Affine::from(g * a) * b, g * c); -} - -#[test] -fn test_is_torsion_free() { - let a = G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x89f5_50c8_13db_6431, - 0xa50b_e8c4_56cd_8a1a, - 0xa45b_3741_14ca_e851, - 0xbb61_90f5_bf7f_ff63, - 0x970c_a02c_3ba8_0bc7, - 0x02b8_5d24_e840_fbac, - ]), - c1: Fp::from_raw_unchecked([ - 0x6888_bc53_d707_16dc, - 0x3dea_6b41_1768_2d70, - 0xd8f5_f930_500c_a354, - 0x6b5e_cb65_56f5_c155, - 0xc96b_ef04_3477_8ab0, - 0x0508_1505_5150_06ad, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x3cf1_ea0d_434b_0f40, - 0x1a0d_c610_e603_e333, - 0x7f89_9561_60c7_2fa0, - 0x25ee_03de_cf64_31c5, - 0xeee8_e206_ec0f_e137, - 0x0975_92b2_26df_ef28, - ]), - c1: Fp::from_raw_unchecked([ - 0x71e8_bb5f_2924_7367, - 0xa5fe_049e_2118_31ce, - 0x0ce6_b354_502a_3896, - 0x93b0_1200_0997_314e, - 0x6759_f3b6_aa5b_42ac, - 0x1569_44c4_dfe9_2bbb, - ]), - }, - infinity: Choice::from(0u8), - }; - assert!(!bool::from(a.is_torsion_free())); - - assert!(bool::from(G2Affine::identity().is_torsion_free())); - assert!(bool::from(G2Affine::generator().is_torsion_free())); -} - -#[test] -fn test_batch_normalize() { - let a = G2Projective::generator().double(); - let b = a.double(); - let c = b.double(); - - for a_identity in (0..1).map(|n| n == 1) { - for b_identity in (0..1).map(|n| n == 1) { - for c_identity in (0..1).map(|n| n == 1) { - let mut v = [a, b, c]; - if a_identity { - v[0] = G2Projective::identity() - } - if b_identity { - v[1] = G2Projective::identity() - } - if c_identity { - v[2] = G2Projective::identity() - } - - let mut t = [ - G2Affine::identity(), - G2Affine::identity(), - G2Affine::identity(), - ]; - let expected = [ - G2Affine::from(v[0]), - G2Affine::from(v[1]), - G2Affine::from(v[2]), - ]; - - G2Projective::batch_normalize(&v[..], &mut t[..]); - - assert_eq!(&t[..], &expected[..]); - } - } - } -} diff --git a/bls12_381/src/lib.rs b/bls12_381/src/lib.rs deleted file mode 100644 index d5b4d512fb..0000000000 --- a/bls12_381/src/lib.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! # `bls12_381` -//! -//! This crate provides an implementation of the BLS12-381 pairing-friendly elliptic -//! curve construction. -//! -//! * **This implementation has not been reviewed or audited. Use at your own risk.** -//! * This implementation targets Rust `1.36` or later. -//! * This implementation does not require the Rust standard library. -//! * All operations are constant time unless explicitly noted. - -#![no_std] -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] -#![deny(missing_debug_implementations)] -#![deny(missing_docs)] -#![deny(unsafe_code)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::many_single_char_names)] -// This lint is described at -// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl -// In our library, some of the arithmetic involving extension fields will necessarily -// involve various binary operators, and so this lint is triggered unnecessarily. -#![allow(clippy::suspicious_arithmetic_impl)] - -#[cfg(feature = "alloc")] -extern crate alloc; - -#[cfg(test)] -#[macro_use] -extern crate std; - -#[cfg(test)] -#[cfg(feature = "groups")] -mod tests; - -#[macro_use] -mod util; - -/// Notes about how the BLS12-381 elliptic curve is designed, specified -/// and implemented by this library. -pub mod notes { - pub mod design; - pub mod serialization; -} - -mod scalar; - -pub use scalar::Scalar; - -#[cfg(feature = "groups")] -mod fp; -#[cfg(feature = "groups")] -mod fp2; -#[cfg(feature = "groups")] -mod g1; -#[cfg(feature = "groups")] -mod g2; - -#[cfg(feature = "groups")] -pub use g1::{G1Affine, G1Projective}; -#[cfg(feature = "groups")] -pub use g2::{G2Affine, G2Projective}; - -#[cfg(feature = "groups")] -mod fp12; -#[cfg(feature = "groups")] -mod fp6; - -// The BLS parameter x for BLS12-381 is -0xd201000000010000 -const BLS_X: u64 = 0xd201_0000_0001_0000; -const BLS_X_IS_NEGATIVE: bool = true; - -#[cfg(feature = "pairings")] -mod pairings; - -#[cfg(feature = "pairings")] -pub use pairings::{pairing, Gt, MillerLoopResult}; - -#[cfg(all(feature = "pairings", feature = "alloc"))] -pub use pairings::{multi_miller_loop, G2Prepared}; diff --git a/bls12_381/src/notes/design.rs b/bls12_381/src/notes/design.rs deleted file mode 100644 index d245260ef6..0000000000 --- a/bls12_381/src/notes/design.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! # Design of BLS12-381 -//! ## Fixed Generators -//! -//! Although any generator produced by hashing to $\mathbb{G}_1$ or $\mathbb{G}_2$ is -//! safe to use in a cryptographic protocol, we specify some simple, fixed generators. -//! -//! In order to derive these generators, we select the lexicographically smallest -//! valid $x$-coordinate and the lexicographically smallest corresponding $y$-coordinate, -//! and then scale the resulting point by the cofactor, such that the result is not the -//! identity. This results in the following fixed generators: -//! -//! 1. $\mathbb{G}_1$ -//! * $x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507$ -//! * $y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569$ -//! 2. $\mathbb{G}_2$ -//! * $x = 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 + 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758 u$ -//! * $y = 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 + 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582 u$ -//! -//! This can be derived using the following sage script: -//! -//! ```norun -//! param = -0xd201000000010000 -//! def r(x): -//! return (x**4) - (x**2) + 1 -//! def q(x): -//! return (((x - 1) ** 2) * ((x**4) - (x**2) + 1) // 3) + x -//! def g1_h(x): -//! return ((x-1)**2) // 3 -//! def g2_h(x): -//! return ((x**8) - (4 * (x**7)) + (5 * (x**6)) - (4 * (x**4)) + (6 * (x**3)) - (4 * (x**2)) - (4*x) + 13) // 9 -//! q = q(param) -//! r = r(param) -//! Fq = GF(q) -//! ec = EllipticCurve(Fq, [0, 4]) -//! def psqrt(v): -//! assert(not v.is_zero()) -//! a = sqrt(v) -//! b = -a -//! if a < b: -//! return a -//! else: -//! return b -//! for x in range(0,100): -//! rhs = Fq(x)^3 + 4 -//! if rhs.is_square(): -//! y = psqrt(rhs) -//! p = ec(x, y) * g1_h(param) -//! if (not p.is_zero()) and (p * r).is_zero(): -//! print "g1 generator: %s" % p -//! break -//! Fqx. = PolynomialRing(Fq, 'j') -//! Fq2. = GF(q^2, modulus=j^2 + 1) -//! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))]) -//! assert(ec2.order() == (r * g2_h(param))) -//! for x in range(0,100): -//! rhs = (Fq2(x))^3 + (4 * (1 + i)) -//! if rhs.is_square(): -//! y = psqrt(rhs) -//! p = ec2(Fq2(x), y) * g2_h(param) -//! if (not p.is_zero()) and (p * r).is_zero(): -//! print "g2 generator: %s" % p -//! break -//! ``` diff --git a/bls12_381/src/notes/serialization.rs b/bls12_381/src/notes/serialization.rs deleted file mode 100644 index ded752e6e8..0000000000 --- a/bls12_381/src/notes/serialization.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! # BLS12-381 serialization -//! -//! * $\mathbb{F}\_p$ elements are encoded in big-endian form. They occupy 48 -//! bytes in this form. -//! * $\mathbb{F}\_{p^2}$ elements are encoded in big-endian form, meaning that -//! the $\mathbb{F}\_{p^2}$ element $c\_0 + c\_1 \cdot u$ is represented by the -//! $\mathbb{F}\_p$ element $c\_1$ followed by the $\mathbb{F}\_p$ element $c\_0$. -//! This means $\mathbb{F}_{p^2}$ elements occupy 96 bytes in this form. -//! * The group $\mathbb{G}\_1$ uses $\mathbb{F}\_p$ elements for coordinates. The -//! group $\mathbb{G}\_2$ uses $\mathbb{F}_{p^2}$ elements for coordinates. -//! * $\mathbb{G}\_1$ and $\mathbb{G}\_2$ elements can be encoded in uncompressed -//! form (the x-coordinate followed by the y-coordinate) or in compressed form -//! (just the x-coordinate). $\mathbb{G}\_1$ elements occupy 96 bytes in -//! uncompressed form, and 48 bytes in compressed form. $\mathbb{G}\_2$ -//! elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed -//! form. -//! -//! The most-significant three bits of a $\mathbb{G}\_1$ or $\mathbb{G}\_2$ -//! 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. diff --git a/bls12_381/src/pairings.rs b/bls12_381/src/pairings.rs deleted file mode 100644 index ef7180a5e3..0000000000 --- a/bls12_381/src/pairings.rs +++ /dev/null @@ -1,648 +0,0 @@ -use crate::fp12::Fp12; -use crate::fp2::Fp2; -use crate::fp6::Fp6; -use crate::{G1Affine, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE}; - -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; - -#[cfg(feature = "alloc")] -use alloc::vec::Vec; - -/// Represents results of a Miller loop, one of the most expensive portions -/// of the pairing function. `MillerLoopResult`s cannot be compared with each -/// other until `.final_exponentiation()` is called, which is also expensive. -#[derive(Copy, Clone, Debug)] -pub struct MillerLoopResult(pub(crate) Fp12); - -impl ConditionallySelectable for MillerLoopResult { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - MillerLoopResult(Fp12::conditional_select(&a.0, &b.0, choice)) - } -} - -impl MillerLoopResult { - /// This performs a "final exponentiation" routine to convert the result - /// of a Miller loop into an element of `Gt` with help of efficient squaring - /// operation in the so-called `cyclotomic subgroup` of `Fq6` so that - /// it can be compared with other elements of `Gt`. - pub fn final_exponentiation(&self) -> Gt { - #[must_use] - fn fp4_square(a: Fp2, b: Fp2) -> (Fp2, Fp2) { - let t0 = a.square(); - let t1 = b.square(); - let mut t2 = t1.mul_by_nonresidue(); - let c0 = t2 + t0; - t2 = a + b; - t2 = t2.square(); - t2 -= t0; - let c1 = t2 - t1; - - (c0, c1) - } - // Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography - // Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions - // https://eprint.iacr.org/2009/565.pdf - #[must_use] - fn cyclotomic_square(f: Fp12) -> Fp12 { - let mut z0 = f.c0.c0; - let mut z4 = f.c0.c1; - let mut z3 = f.c0.c2; - let mut z2 = f.c1.c0; - let mut z1 = f.c1.c1; - let mut z5 = f.c1.c2; - - let (t0, t1) = fp4_square(z0, z1); - - // For A - z0 = t0 - z0; - z0 = z0 + z0 + t0; - - z1 = t1 + z1; - z1 = z1 + z1 + t1; - - let (mut t0, t1) = fp4_square(z2, z3); - let (t2, t3) = fp4_square(z4, z5); - - // For C - z4 = t0 - z4; - z4 = z4 + z4 + t0; - - z5 = t1 + z5; - z5 = z5 + z5 + t1; - - // For B - t0 = t3.mul_by_nonresidue(); - z2 = t0 + z2; - z2 = z2 + z2 + t0; - - z3 = t2 - z3; - z3 = z3 + z3 + t2; - - Fp12 { - c0: Fp6 { - c0: z0, - c1: z4, - c2: z3, - }, - c1: Fp6 { - c0: z2, - c1: z1, - c2: z5, - }, - } - } - #[must_use] - fn cycolotomic_exp(f: Fp12) -> Fp12 { - let x = BLS_X; - let mut tmp = Fp12::one(); - let mut found_one = false; - for i in (0..64).rev().map(|b| ((x >> b) & 1) == 1) { - if found_one { - tmp = cyclotomic_square(tmp) - } else { - found_one = i; - } - - if i { - tmp *= f; - } - } - - tmp.conjugate() - } - - let mut f = self.0; - let mut t0 = f - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map() - .frobenius_map(); - Gt(f.invert() - .map(|mut t1| { - let mut t2 = t0 * t1; - t1 = t2; - t2 = t2.frobenius_map().frobenius_map(); - t2 *= t1; - t1 = cyclotomic_square(t2).conjugate(); - let mut t3 = cycolotomic_exp(t2); - let mut t4 = cyclotomic_square(t3); - let mut t5 = t1 * t3; - t1 = cycolotomic_exp(t5); - t0 = cycolotomic_exp(t1); - let mut t6 = cycolotomic_exp(t0); - t6 *= t4; - t4 = cycolotomic_exp(t6); - t5 = t5.conjugate(); - t4 *= t5 * t2; - t5 = t2.conjugate(); - t1 *= t2; - t1 = t1.frobenius_map().frobenius_map().frobenius_map(); - t6 *= t5; - t6 = t6.frobenius_map(); - t3 *= t0; - t3 = t3.frobenius_map().frobenius_map(); - t3 *= t1; - t3 *= t6; - f = t3 * t4; - - f - }) - // We unwrap() because `MillerLoopResult` can only be constructed - // by a function within this crate, and we uphold the invariant - // that the enclosed value is nonzero. - .unwrap()) - } -} - -impl<'a, 'b> Add<&'b MillerLoopResult> for &'a MillerLoopResult { - type Output = MillerLoopResult; - - #[inline] - fn add(self, rhs: &'b MillerLoopResult) -> MillerLoopResult { - MillerLoopResult(self.0 * rhs.0) - } -} - -impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopResult); - -/// This is an element of $\mathbb{G}_T$, the target group of the pairing function. As with -/// $\mathbb{G}_1$ and $\mathbb{G}_2$ this group has order $q$. -/// -/// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to -/// keep code and abstractions consistent. -#[derive(Copy, Clone, Debug)] -pub struct Gt(pub(crate) Fp12); - -impl ConstantTimeEq for Gt { - fn ct_eq(&self, other: &Self) -> Choice { - self.0.ct_eq(&other.0) - } -} - -impl ConditionallySelectable for Gt { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Gt(Fp12::conditional_select(&a.0, &b.0, choice)) - } -} - -impl Eq for Gt {} -impl PartialEq for Gt { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl Gt { - /// Returns the group identity, which is $1$. - pub fn identity() -> Gt { - Gt(Fp12::one()) - } - - /// Doubles this group element. - pub fn double(&self) -> Gt { - Gt(self.0.square()) - } -} - -impl<'a> Neg for &'a Gt { - type Output = Gt; - - #[inline] - fn neg(self) -> Gt { - // The element is unitary, so we just conjugate. - Gt(self.0.conjugate()) - } -} - -impl Neg for Gt { - type Output = Gt; - - #[inline] - fn neg(self) -> Gt { - -&self - } -} - -impl<'a, 'b> Add<&'b Gt> for &'a Gt { - type Output = Gt; - - #[inline] - fn add(self, rhs: &'b Gt) -> Gt { - Gt(self.0 * rhs.0) - } -} - -impl<'a, 'b> Sub<&'b Gt> for &'a Gt { - type Output = Gt; - - #[inline] - fn sub(self, rhs: &'b Gt) -> Gt { - self + (-rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a Gt { - type Output = Gt; - - fn mul(self, other: &'b Scalar) -> Self::Output { - let mut acc = Gt::identity(); - - // This is a simple double-and-add implementation of group element - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading bit because it's always unset for Fq - // elements. - for bit in other - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = Gt::conditional_select(&acc, &(acc + self), bit); - } - - acc - } -} - -impl_binops_additive!(Gt, Gt); -impl_binops_multiplicative!(Gt, Scalar); - -#[cfg(feature = "alloc")] -#[derive(Clone, Debug)] -/// This structure contains cached computations pertaining to a $\mathbb{G}_2$ -/// element as part of the pairing function (specifically, the Miller loop) and -/// so should be computed whenever a $\mathbb{G}_2$ element is being used in -/// multiple pairings or is otherwise known in advance. This should be used in -/// conjunction with the [`multi_miller_loop`](crate::multi_miller_loop) -/// function provided by this crate. -/// -/// Requires the `alloc` and `pairing` crate features to be enabled. -pub struct G2Prepared { - infinity: Choice, - coeffs: Vec<(Fp2, Fp2, Fp2)>, -} - -#[cfg(feature = "alloc")] -impl From for G2Prepared { - fn from(q: G2Affine) -> G2Prepared { - struct Adder { - cur: G2Projective, - base: G2Affine, - coeffs: Vec<(Fp2, Fp2, Fp2)>, - } - - impl MillerLoopDriver for Adder { - type Output = (); - - fn doubling_step(&mut self, _: Self::Output) -> Self::Output { - let coeffs = doubling_step(&mut self.cur); - self.coeffs.push(coeffs); - } - fn addition_step(&mut self, _: Self::Output) -> Self::Output { - let coeffs = addition_step(&mut self.cur, &self.base); - self.coeffs.push(coeffs); - } - fn square_output(_: Self::Output) -> Self::Output {} - fn conjugate(_: Self::Output) -> Self::Output {} - fn one() -> Self::Output {} - } - - let is_identity = q.is_identity(); - let q = G2Affine::conditional_select(&q, &G2Affine::generator(), is_identity); - - let mut adder = Adder { - cur: G2Projective::from(q), - base: q, - coeffs: Vec::with_capacity(68), - }; - - miller_loop(&mut adder); - - assert_eq!(adder.coeffs.len(), 68); - - G2Prepared { - infinity: is_identity, - coeffs: adder.coeffs, - } - } -} - -#[cfg(feature = "alloc")] -/// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms -/// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ -/// -/// Requires the `alloc` and `pairing` crate features to be enabled. -pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> MillerLoopResult { - struct Adder<'a, 'b, 'c> { - terms: &'c [(&'a G1Affine, &'b G2Prepared)], - index: usize, - } - - impl<'a, 'b, 'c> MillerLoopDriver for Adder<'a, 'b, 'c> { - type Output = Fp12; - - fn doubling_step(&mut self, mut f: Self::Output) -> Self::Output { - let index = self.index; - for term in self.terms { - let either_identity = term.0.is_identity() | term.1.infinity; - - let new_f = ell(f, &term.1.coeffs[index], term.0); - f = Fp12::conditional_select(&new_f, &f, either_identity); - } - self.index += 1; - - f - } - fn addition_step(&mut self, mut f: Self::Output) -> Self::Output { - let index = self.index; - for term in self.terms { - let either_identity = term.0.is_identity() | term.1.infinity; - - let new_f = ell(f, &term.1.coeffs[index], term.0); - f = Fp12::conditional_select(&new_f, &f, either_identity); - } - self.index += 1; - - f - } - fn square_output(f: Self::Output) -> Self::Output { - f.square() - } - fn conjugate(f: Self::Output) -> Self::Output { - f.conjugate() - } - fn one() -> Self::Output { - Fp12::one() - } - } - - let mut adder = Adder { terms, index: 0 }; - - let tmp = miller_loop(&mut adder); - - MillerLoopResult(tmp) -} - -/// Invoke the pairing function without the use of precomputation and other optimizations. -pub fn pairing(p: &G1Affine, q: &G2Affine) -> Gt { - struct Adder { - cur: G2Projective, - base: G2Affine, - p: G1Affine, - } - - impl MillerLoopDriver for Adder { - type Output = Fp12; - - fn doubling_step(&mut self, f: Self::Output) -> Self::Output { - let coeffs = doubling_step(&mut self.cur); - ell(f, &coeffs, &self.p) - } - fn addition_step(&mut self, f: Self::Output) -> Self::Output { - let coeffs = addition_step(&mut self.cur, &self.base); - ell(f, &coeffs, &self.p) - } - fn square_output(f: Self::Output) -> Self::Output { - f.square() - } - fn conjugate(f: Self::Output) -> Self::Output { - f.conjugate() - } - fn one() -> Self::Output { - Fp12::one() - } - } - - let either_identity = p.is_identity() | q.is_identity(); - let p = G1Affine::conditional_select(&p, &G1Affine::generator(), either_identity); - let q = G2Affine::conditional_select(&q, &G2Affine::generator(), either_identity); - - let mut adder = Adder { - cur: G2Projective::from(q), - base: q, - p, - }; - - let tmp = miller_loop(&mut adder); - let tmp = MillerLoopResult(Fp12::conditional_select( - &tmp, - &Fp12::one(), - either_identity, - )); - tmp.final_exponentiation() -} - -trait MillerLoopDriver { - type Output; - - fn doubling_step(&mut self, f: Self::Output) -> Self::Output; - fn addition_step(&mut self, f: Self::Output) -> Self::Output; - fn square_output(f: Self::Output) -> Self::Output; - fn conjugate(f: Self::Output) -> Self::Output; - fn one() -> Self::Output; -} - -/// This is a "generic" implementation of the Miller loop to avoid duplicating code -/// structure elsewhere; instead, we'll write concrete instantiations of -/// `MillerLoopDriver` for whatever purposes we need (such as caching modes). -fn miller_loop(driver: &mut D) -> D::Output { - let mut f = D::one(); - - let mut found_one = false; - for i in (0..64).rev().map(|b| (((BLS_X >> 1) >> b) & 1) == 1) { - if !found_one { - found_one = i; - continue; - } - - f = driver.doubling_step(f); - - if i { - f = driver.addition_step(f); - } - - f = D::square_output(f); - } - - f = driver.doubling_step(f); - - if BLS_X_IS_NEGATIVE { - f = D::conjugate(f); - } - - f -} - -fn ell(f: Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) -> Fp12 { - let mut c0 = coeffs.0; - let mut c1 = coeffs.1; - - c0.c0 *= p.y; - c0.c1 *= p.y; - - c1.c0 *= p.x; - c1.c1 *= p.x; - - f.mul_by_014(&coeffs.2, &c1, &c0) -} - -fn doubling_step(r: &mut G2Projective) -> (Fp2, Fp2, Fp2) { - // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf - let tmp0 = r.x.square(); - let tmp1 = r.y.square(); - let tmp2 = tmp1.square(); - let tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2; - let tmp3 = tmp3 + tmp3; - let tmp4 = tmp0 + tmp0 + tmp0; - let tmp6 = r.x + tmp4; - let tmp5 = tmp4.square(); - let zsquared = r.z.square(); - r.x = tmp5 - tmp3 - tmp3; - r.z = (r.z + r.y).square() - tmp1 - zsquared; - r.y = (tmp3 - r.x) * tmp4; - let tmp2 = tmp2 + tmp2; - let tmp2 = tmp2 + tmp2; - let tmp2 = tmp2 + tmp2; - r.y -= tmp2; - let tmp3 = tmp4 * zsquared; - let tmp3 = tmp3 + tmp3; - let tmp3 = -tmp3; - let tmp6 = tmp6.square() - tmp0 - tmp5; - let tmp1 = tmp1 + tmp1; - let tmp1 = tmp1 + tmp1; - let tmp6 = tmp6 - tmp1; - let tmp0 = r.z * zsquared; - let tmp0 = tmp0 + tmp0; - - (tmp0, tmp3, tmp6) -} - -fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) { - // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf - let zsquared = r.z.square(); - let ysquared = q.y.square(); - let t0 = zsquared * q.x; - let t1 = ((q.y + r.z).square() - ysquared - zsquared) * zsquared; - let t2 = t0 - r.x; - let t3 = t2.square(); - let t4 = t3 + t3; - let t4 = t4 + t4; - let t5 = t4 * t2; - let t6 = t1 - r.y - r.y; - let t9 = t6 * q.x; - let t7 = t4 * r.x; - r.x = t6.square() - t5 - t7 - t7; - r.z = (r.z + t2).square() - zsquared - t3; - let t10 = q.y + r.z; - let t8 = (t7 - r.x) * t6; - let t0 = r.y * t5; - let t0 = t0 + t0; - r.y = t8 - t0; - let t10 = t10.square() - ysquared; - let ztsquared = r.z.square(); - let t10 = t10 - ztsquared; - let t9 = t9 + t9 - t10; - let t10 = r.z + r.z; - let t6 = -t6; - let t1 = t6 + t6; - - (t10, t1, t9) -} - -#[test] -fn test_bilinearity() { - use crate::Scalar; - - let a = Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(); - let b = Scalar::from_raw([5, 6, 7, 8]).invert().unwrap().square(); - let c = a * b; - - let g = G1Affine::from(G1Affine::generator() * a); - let h = G2Affine::from(G2Affine::generator() * b); - let p = pairing(&g, &h); - - assert!(p != Gt::identity()); - - let expected = G1Affine::from(G1Affine::generator() * c); - - assert_eq!(p, pairing(&expected, &G2Affine::generator())); - assert_eq!( - p, - pairing(&G1Affine::generator(), &G2Affine::generator()) * c - ); -} - -#[test] -fn test_unitary() { - let g = G1Affine::generator(); - let h = G2Affine::generator(); - let p = -pairing(&g, &h); - let q = pairing(&g, &-h); - let r = pairing(&-g, &h); - - assert_eq!(p, q); - assert_eq!(q, r); -} - -#[cfg(feature = "alloc")] -#[test] -fn test_multi_miller_loop() { - let a1 = G1Affine::generator(); - let b1 = G2Affine::generator(); - - let a2 = G1Affine::from( - G1Affine::generator() * Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(), - ); - let b2 = G2Affine::from( - G2Affine::generator() * Scalar::from_raw([4, 2, 2, 4]).invert().unwrap().square(), - ); - - let a3 = G1Affine::identity(); - let b3 = G2Affine::from( - G2Affine::generator() * Scalar::from_raw([9, 2, 2, 4]).invert().unwrap().square(), - ); - - let a4 = G1Affine::from( - G1Affine::generator() * Scalar::from_raw([5, 5, 5, 5]).invert().unwrap().square(), - ); - let b4 = G2Affine::identity(); - - let a5 = G1Affine::from( - G1Affine::generator() * Scalar::from_raw([323, 32, 3, 1]).invert().unwrap().square(), - ); - let b5 = G2Affine::from( - G2Affine::generator() * Scalar::from_raw([4, 2, 2, 9099]).invert().unwrap().square(), - ); - - let b1_prepared = G2Prepared::from(b1); - let b2_prepared = G2Prepared::from(b2); - let b3_prepared = G2Prepared::from(b3); - let b4_prepared = G2Prepared::from(b4); - let b5_prepared = G2Prepared::from(b5); - - let expected = pairing(&a1, &b1) - + pairing(&a2, &b2) - + pairing(&a3, &b3) - + pairing(&a4, &b4) - + pairing(&a5, &b5); - - let test = multi_miller_loop(&[ - (&a1, &b1_prepared), - (&a2, &b2_prepared), - (&a3, &b3_prepared), - (&a4, &b4_prepared), - (&a5, &b5_prepared), - ]) - .final_exponentiation(); - - assert_eq!(expected, test); -} diff --git a/bls12_381/src/scalar.rs b/bls12_381/src/scalar.rs deleted file mode 100644 index 91f88a49b8..0000000000 --- a/bls12_381/src/scalar.rs +++ /dev/null @@ -1,1076 +0,0 @@ -//! This module provides an implementation of the BLS12-381 scalar field $\mathbb{F}_q$ -//! where `q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` - -use core::convert::TryFrom; -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::util::{adc, mac, sbb}; - -/// Represents an element of the scalar field $\mathbb{F}_q$ of the BLS12-381 elliptic -/// curve construction. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. `Scalar` values are always in -// Montgomery form; i.e., Scalar(a) = aR mod q, with R = 2^256. -#[derive(Clone, Copy, Eq)] -pub struct Scalar(pub(crate) [u64; 4]); - -impl fmt::Debug for Scalar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_bytes(); - write!(f, "0x")?; - for &b in tmp.iter().rev() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } -} - -impl From for Scalar { - fn from(val: u64) -> Scalar { - Scalar([val, 0, 0, 0]) * R2 - } -} - -impl ConstantTimeEq for Scalar { - fn ct_eq(&self, other: &Self) -> Choice { - self.0[0].ct_eq(&other.0[0]) - & self.0[1].ct_eq(&other.0[1]) - & self.0[2].ct_eq(&other.0[2]) - & self.0[3].ct_eq(&other.0[3]) - } -} - -impl PartialEq for Scalar { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 - } -} - -impl ConditionallySelectable for Scalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Scalar([ - u64::conditional_select(&a.0[0], &b.0[0], choice), - u64::conditional_select(&a.0[1], &b.0[1], choice), - u64::conditional_select(&a.0[2], &b.0[2], choice), - u64::conditional_select(&a.0[3], &b.0[3], choice), - ]) - } -} - -/// Constant representing the modulus -/// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 -const MODULUS: Scalar = Scalar([ - 0xffff_ffff_0000_0001, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, -]); - -impl<'a> Neg for &'a Scalar { - type Output = Scalar; - - #[inline] - fn neg(self) -> Scalar { - self.neg() - } -} - -impl Neg for Scalar { - type Output = Scalar; - - #[inline] - fn neg(self) -> Scalar { - -&self - } -} - -impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn sub(self, rhs: &'b Scalar) -> Scalar { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn add(self, rhs: &'b Scalar) -> Scalar { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn mul(self, rhs: &'b Scalar) -> Scalar { - self.mul(rhs) - } -} - -impl_binops_additive!(Scalar, Scalar); -impl_binops_multiplicative!(Scalar, Scalar); - -/// INV = -(q^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0xffff_fffe_ffff_ffff; - -/// R = 2^256 mod q -const R: Scalar = Scalar([ - 0x0000_0001_ffff_fffe, - 0x5884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff5, - 0x1824_b159_acc5_056f, -]); - -/// R^2 = 2^512 mod q -const R2: Scalar = Scalar([ - 0xc999_e990_f3f2_9c6d, - 0x2b6c_edcb_8792_5c23, - 0x05d3_1496_7254_398f, - 0x0748_d9d9_9f59_ff11, -]); - -/// R^3 = 2^768 mod q -const R3: Scalar = Scalar([ - 0xc62c_1807_439b_73af, - 0x1b3e_0d18_8cf0_6990, - 0x73d1_3c71_c7b5_f418, - 0x6e2a_5bb9_c8db_33e9, -]); - -const S: u32 = 32; - -/// GENERATOR^t where t * 2^s + 1 = q -/// with t odd. In other words, this -/// is a 2^s root of unity. -/// -/// `GENERATOR = 7 mod q` is a generator -/// of the q - 1 order multiplicative -/// subgroup. -const ROOT_OF_UNITY: Scalar = Scalar([ - 0xb9b5_8d8c_5f0e_466a, - 0x5b1b_4c80_1819_d7ec, - 0x0af5_3ae3_52a3_1e64, - 0x5bf3_adda_19e9_b27b, -]); - -impl Default for Scalar { - #[inline] - fn default() -> Self { - Self::zero() - } -} - -impl Scalar { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> Scalar { - Scalar([0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> Scalar { - R - } - - /// Doubles this field element. - #[inline] - pub const fn double(&self) -> Scalar { - // TODO: This can be achieved more efficiently with a bitshift. - self.add(self) - } - - /// Attempts to convert a little-endian byte representation of - /// a scalar into a `Scalar`, failing if the input is not canonical. - pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - let mut tmp = Scalar([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); - tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); - tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); - tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); - - // If the element is smaller than MODULUS then the - // subtraction will underflow, producing a borrow value - // of 0xffff...ffff. Otherwise, it'll be zero. - let is_some = (borrow as u8) & 1; - - // Convert to Montgomery form by computing - // (a.R^0 * R^2) / R = a.R - tmp *= &R2; - - CtOption::new(tmp, Choice::from(is_some)) - } - - /// Converts an element of `Scalar` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 32] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - let mut res = [0; 32]; - res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); - - res - } - - /// Converts a 512-bit little endian integer into - /// a `Scalar` by reducing by the modulus. - pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar { - Scalar::from_u512([ - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()), - ]) - } - - fn from_u512(limbs: [u64; 8]) -> Scalar { - // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits - // with the higher bits multiplied by 2^256. Thus, we perform two reductions - // - // 1. the lower bits are multiplied by R^2, as normal - // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 - // - // and computing their sum in the field. It remains to see that arbitrary 256-bit - // numbers can be placed into Montgomery form safely using the reduction. The - // reduction works so long as the product is less than R=2^256 multipled by - // the modulus. This holds because for any `c` smaller than the modulus, we have - // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the - // reduction always works so long as `c` is in the field; in this case it is either the - // constant `R2` or `R3`. - let d0 = Scalar([limbs[0], limbs[1], limbs[2], limbs[3]]); - let d1 = Scalar([limbs[4], limbs[5], limbs[6], limbs[7]]); - // Convert to Montgomery form - d0 * R2 + d1 * R3 - } - - /// Converts from an integer represented in little endian - /// into its (congruent) `Scalar` representation. - pub const fn from_raw(val: [u64; 4]) -> Self { - (&Scalar(val)).mul(&R2) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> Scalar { - let (r1, carry) = mac(0, self.0[0], self.0[1], 0); - let (r2, carry) = mac(0, self.0[0], self.0[2], carry); - let (r3, r4) = mac(0, self.0[0], self.0[3], carry); - - let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); - let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); - - let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); - - let r7 = r6 >> 63; - let r6 = (r6 << 1) | (r5 >> 63); - let r5 = (r5 << 1) | (r4 >> 63); - let r4 = (r4 << 1) | (r3 >> 63); - let r3 = (r3 << 1) | (r2 >> 63); - let r2 = (r2 << 1) | (r1 >> 63); - let r1 = r1 << 1; - - let (r0, carry) = mac(0, self.0[0], self.0[0], 0); - let (r1, carry) = adc(0, r1, carry); - let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); - let (r3, carry) = adc(0, r3, carry); - let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); - let (r5, carry) = adc(0, r5, carry); - let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); - let (r7, _) = adc(0, r7, carry); - - Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Computes the square root of this element, if it exists. - pub fn sqrt(&self) -> CtOption { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - - // w = self^((t - 1) // 2) - // = self^6104339283789297388802252303364915521546564123189034618274734669823 - let w = self.pow_vartime(&[ - 0x7fff_2dff_7fff_ffff, - 0x04d0_ec02_a9de_d201, - 0x94ce_bea4_199c_ec04, - 0x0000_0000_39f6_d3a9, - ]); - - let mut v = S; - let mut x = self * w; - let mut b = x * w; - - // Initialize z as the 2^S root of unity. - let mut z = ROOT_OF_UNITY; - - for max_v in (1..=S).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&Scalar::one()); - let squared = Scalar::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = Scalar::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = Scalar::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = Scalar::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * z; - x = Scalar::conditional_select(&result, &x, b.ct_eq(&Scalar::one())); - z = z.square(); - b *= z; - v = k; - } - - CtOption::new( - x, - (x * x).ct_eq(self), // Only return Some if it's the square root. - ) - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - pub fn pow(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - let mut tmp = res; - tmp *= self; - res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); - } - } - res - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - /// - /// **This operation is variable time with respect - /// to the exponent.** If the exponent is fixed, - /// this operation is effectively constant time. - pub fn pow_vartime(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - res.mul_assign(self); - } - } - } - res - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - pub fn invert(&self) -> CtOption { - #[inline(always)] - fn square_assign_multi(n: &mut Scalar, num_times: usize) { - for _ in 0..num_times { - *n = n.square(); - } - } - // found using https://github.com/kwantam/addchain - let mut t0 = self.square(); - let mut t1 = t0 * self; - let mut t16 = t0.square(); - let mut t6 = t16.square(); - let mut t5 = t6 * t0; - t0 = t6 * t16; - let mut t12 = t5 * t16; - let mut t2 = t6.square(); - let mut t7 = t5 * t6; - let mut t15 = t0 * t5; - let mut t17 = t12.square(); - t1 *= t17; - let mut t3 = t7 * t2; - let t8 = t1 * t17; - let t4 = t8 * t2; - let t9 = t8 * t7; - t7 = t4 * t5; - let t11 = t4 * t17; - t5 = t9 * t17; - let t14 = t7 * t15; - let t13 = t11 * t12; - t12 = t11 * t17; - t15 *= &t12; - t16 *= &t15; - t3 *= &t16; - t17 *= &t3; - t0 *= &t17; - t6 *= &t0; - t2 *= &t6; - square_assign_multi(&mut t0, 8); - t0 *= &t17; - square_assign_multi(&mut t0, 9); - t0 *= &t16; - square_assign_multi(&mut t0, 9); - t0 *= &t15; - square_assign_multi(&mut t0, 9); - t0 *= &t15; - square_assign_multi(&mut t0, 7); - t0 *= &t14; - square_assign_multi(&mut t0, 7); - t0 *= &t13; - square_assign_multi(&mut t0, 10); - t0 *= &t12; - square_assign_multi(&mut t0, 9); - t0 *= &t11; - square_assign_multi(&mut t0, 8); - t0 *= &t8; - square_assign_multi(&mut t0, 8); - t0 *= self; - square_assign_multi(&mut t0, 14); - t0 *= &t9; - square_assign_multi(&mut t0, 10); - t0 *= &t8; - square_assign_multi(&mut t0, 15); - t0 *= &t7; - square_assign_multi(&mut t0, 10); - t0 *= &t6; - square_assign_multi(&mut t0, 8); - t0 *= &t5; - square_assign_multi(&mut t0, 16); - t0 *= &t3; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 7); - t0 *= &t4; - square_assign_multi(&mut t0, 9); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t3; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t3; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 5); - t0 *= &t1; - square_assign_multi(&mut t0, 5); - t0 *= &t1; - - CtOption::new(t0, !self.ct_eq(&Self::zero())) - } - - #[inline(always)] - const fn montgomery_reduce( - r0: u64, - r1: u64, - r2: u64, - r3: u64, - r4: u64, - r5: u64, - r6: u64, - r7: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = r0.wrapping_mul(INV); - let (_, carry) = mac(r0, k, MODULUS.0[0], 0); - let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); - let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); - let (r4, carry2) = adc(r4, 0, carry); - - let k = r1.wrapping_mul(INV); - let (_, carry) = mac(r1, k, MODULUS.0[0], 0); - let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); - let (r5, carry2) = adc(r5, carry2, carry); - - let k = r2.wrapping_mul(INV); - let (_, carry) = mac(r2, k, MODULUS.0[0], 0); - let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); - let (r6, carry2) = adc(r6, carry2, carry); - - let k = r3.wrapping_mul(INV); - let (_, carry) = mac(r3, k, MODULUS.0[0], 0); - let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); - let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); - let (r7, _) = adc(r7, carry2, carry); - - // Result may be within MODULUS of the correct value - (&Scalar([r4, r5, r6, r7])).sub(&MODULUS) - } - - /// Multiplies `rhs` by `self`, returning the result. - #[inline] - pub const fn mul(&self, rhs: &Self) -> Self { - // Schoolbook multiplication - - let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); - let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); - let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); - let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); - - let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); - let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); - let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); - let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); - - let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); - let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); - let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); - let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); - - let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); - let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); - let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); - let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); - - Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Subtracts `rhs` from `self`, returning the result. - #[inline] - pub const fn sub(&self, rhs: &Self) -> Self { - let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); - let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); - let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); - let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); - - // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise - // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. - let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); - let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); - let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); - let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); - - Scalar([d0, d1, d2, d3]) - } - - /// Adds `rhs` to `self`, returning the result. - #[inline] - pub const fn add(&self, rhs: &Self) -> Self { - let (d0, carry) = adc(self.0[0], rhs.0[0], 0); - let (d1, carry) = adc(self.0[1], rhs.0[1], carry); - let (d2, carry) = adc(self.0[2], rhs.0[2], carry); - let (d3, _) = adc(self.0[3], rhs.0[3], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Scalar([d0, d1, d2, d3])).sub(&MODULUS) - } - - /// Negates `self`. - #[inline] - pub const fn neg(&self) -> Self { - // Subtract `self` from `MODULUS` to negate. Ignore the final - // borrow because it cannot underflow; self is guaranteed to - // be in the field. - let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); - let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); - let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); - let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); - - // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is - // zero if `self` was zero, and `u64::max_value()` if self was nonzero. - let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); - - Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) - } -} - -impl<'a> From<&'a Scalar> for [u8; 32] { - fn from(value: &'a Scalar) -> [u8; 32] { - value.to_bytes() - } -} - -#[test] -fn test_inv() { - // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating - // by totient(2**64) - 1 - - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(MODULUS.0[0]); - } - inv = inv.wrapping_neg(); - - assert_eq!(inv, INV); -} - -#[cfg(feature = "std")] -#[test] -fn test_debug() { - assert_eq!( - format!("{:?}", Scalar::zero()), - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format!("{:?}", Scalar::one()), - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - assert_eq!( - format!("{:?}", R2), - "0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe" - ); -} - -#[test] -fn test_equality() { - assert_eq!(Scalar::zero(), Scalar::zero()); - assert_eq!(Scalar::one(), Scalar::one()); - assert_eq!(R2, R2); - - assert!(Scalar::zero() != Scalar::one()); - assert!(Scalar::one() != R2); -} - -#[test] -fn test_to_bytes() { - assert_eq!( - Scalar::zero().to_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - Scalar::one().to_bytes(), - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - R2.to_bytes(), - [ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - ] - ); - - assert_eq!( - (-&Scalar::one()).to_bytes(), - [ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ] - ); -} - -#[test] -fn test_from_bytes() { - assert_eq!( - Scalar::from_bytes(&[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Scalar::zero() - ); - - assert_eq!( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Scalar::one() - ); - - assert_eq!( - Scalar::from_bytes(&[ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - ]) - .unwrap(), - R2 - ); - - // -1 should work - assert!( - Scalar::from_bytes(&[ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_some() - .unwrap_u8() - == 1 - ); - - // modulus is invalid - assert!( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_none() - .unwrap_u8() - == 1 - ); - - // Anything larger than the modulus is invalid - assert!( - Scalar::from_bytes(&[ - 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_none() - .unwrap_u8() - == 1 - ); - assert!( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_none() - .unwrap_u8() - == 1 - ); - assert!( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 - ]) - .is_none() - .unwrap_u8() - == 1 - ); -} - -#[test] -fn test_from_u512_zero() { - assert_eq!( - Scalar::zero(), - Scalar::from_u512([ - MODULUS.0[0], - MODULUS.0[1], - MODULUS.0[2], - MODULUS.0[3], - 0, - 0, - 0, - 0 - ]) - ); -} - -#[test] -fn test_from_u512_r() { - assert_eq!(R, Scalar::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); -} - -#[test] -fn test_from_u512_r2() { - assert_eq!(R2, Scalar::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); -} - -#[test] -fn test_from_u512_max() { - let max_u64 = 0xffff_ffff_ffff_ffff; - assert_eq!( - R3 - R, - Scalar::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) - ); -} - -#[test] -fn test_from_bytes_wide_r2() { - assert_eq!( - R2, - Scalar::from_bytes_wide(&[ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_negative_one() { - assert_eq!( - -&Scalar::one(), - Scalar::from_bytes_wide(&[ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_maximum() { - assert_eq!( - Scalar([ - 0xc62c_1805_439b_73b1, - 0xc2b9_551e_8ced_218e, - 0xda44_ec81_daf9_a422, - 0x5605_aa60_1c16_2e79, - ]), - Scalar::from_bytes_wide(&[0xff; 64]) - ); -} - -#[test] -fn test_zero() { - assert_eq!(Scalar::zero(), -&Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() + Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() - Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() * Scalar::zero()); -} - -#[cfg(test)] -const LARGEST: Scalar = Scalar([ - 0xffff_ffff_0000_0000, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, -]); - -#[test] -fn test_addition() { - let mut tmp = LARGEST; - tmp += &LARGEST; - - assert_eq!( - tmp, - Scalar([ - 0xffff_fffe_ffff_ffff, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, - ]) - ); - - let mut tmp = LARGEST; - tmp += &Scalar([1, 0, 0, 0]); - - assert_eq!(tmp, Scalar::zero()); -} - -#[test] -fn test_negation() { - let tmp = -&LARGEST; - - assert_eq!(tmp, Scalar([1, 0, 0, 0])); - - let tmp = -&Scalar::zero(); - assert_eq!(tmp, Scalar::zero()); - let tmp = -&Scalar([1, 0, 0, 0]); - assert_eq!(tmp, LARGEST); -} - -#[test] -fn test_subtraction() { - let mut tmp = LARGEST; - tmp -= &LARGEST; - - assert_eq!(tmp, Scalar::zero()); - - let mut tmp = Scalar::zero(); - tmp -= &LARGEST; - - let mut tmp2 = MODULUS; - tmp2 -= &LARGEST; - - assert_eq!(tmp, tmp2); -} - -#[test] -fn test_multiplication() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp *= &cur; - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_squaring() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp = tmp.square(); - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_inversion() { - assert_eq!(Scalar::zero().invert().is_none().unwrap_u8(), 1); - assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); - assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); - - let mut tmp = R2; - - for _ in 0..100 { - let mut tmp2 = tmp.invert().unwrap(); - tmp2.mul_assign(&tmp); - - assert_eq!(tmp2, Scalar::one()); - - tmp.add_assign(&R2); - } -} - -#[test] -fn test_invert_is_pow() { - let q_minus_2 = [ - 0xffff_fffe_ffff_ffff, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, - ]; - - let mut r1 = R; - let mut r2 = R; - let mut r3 = R; - - for _ in 0..100 { - r1 = r1.invert().unwrap(); - r2 = r2.pow_vartime(&q_minus_2); - r3 = r3.pow(&q_minus_2); - - assert_eq!(r1, r2); - assert_eq!(r2, r3); - // Add R so we check something different next time around - r1.add_assign(&R); - r2 = r1; - r3 = r1; - } -} - -#[test] -fn test_sqrt() { - { - assert_eq!(Scalar::zero().sqrt().unwrap(), Scalar::zero()); - } - - let mut square = Scalar([ - 0x46cd_85a5_f273_077e, - 0x1d30_c47d_d68f_c735, - 0x77f6_56f6_0bec_a0eb, - 0x494a_a01b_df32_468d, - ]); - - let mut none_count = 0; - - for _ in 0..100 { - let square_root = square.sqrt(); - if square_root.is_none().unwrap_u8() == 1 { - none_count += 1; - } else { - assert_eq!(square_root.unwrap() * square_root.unwrap(), square); - } - square -= Scalar::one(); - } - - assert_eq!(49, none_count); -} - -#[test] -fn test_from_raw() { - assert_eq!( - Scalar::from_raw([ - 0x0001_ffff_fffd, - 0x5884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff5, - 0x1824_b159_acc5_056f, - ]), - Scalar::from_raw([0xffff_ffff_ffff_ffff; 4]) - ); - - assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero()); - - assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R); -} - -#[test] -fn test_double() { - let a = Scalar::from_raw([ - 0x1fff_3231_233f_fffd, - 0x4884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff3, - 0x1824_b159_acc5_0562, - ]); - - assert_eq!(a.double(), a + a); -} diff --git a/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat deleted file mode 100644 index ea8cd67652..0000000000 Binary files a/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat and /dev/null differ diff --git a/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 86abfba945..0000000000 Binary files a/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat and /dev/null differ diff --git a/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat deleted file mode 100644 index a40bbe251d..0000000000 Binary files a/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat and /dev/null differ diff --git a/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 92e4bc528e..0000000000 Binary files a/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat and /dev/null differ diff --git a/bls12_381/src/tests/mod.rs b/bls12_381/src/tests/mod.rs deleted file mode 100644 index d6ab86d285..0000000000 --- a/bls12_381/src/tests/mod.rs +++ /dev/null @@ -1,230 +0,0 @@ -use super::*; - -macro_rules! test_vectors { - ($projective:ident, $affine:ident, $serialize:ident, $deserialize:ident, $expected:ident) => { - let mut e = $projective::identity(); - - let mut v = vec![]; - { - let mut expected = $expected; - for _ in 0..1000 { - let e_affine = $affine::from(e); - let encoded = e_affine.$serialize(); - v.extend_from_slice(&encoded[..]); - - let mut decoded = encoded; - let len_of_encoding = decoded.len(); - (&mut decoded[..]).copy_from_slice(&expected[0..len_of_encoding]); - expected = &expected[len_of_encoding..]; - let decoded = $affine::$deserialize(&decoded).unwrap(); - assert_eq!(e_affine, decoded); - - e = &e + &$projective::generator(); - } - } - - assert_eq!(&v[..], $expected); - }; -} - -#[test] -fn g1_uncompressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g1_uncompressed_valid_test_vectors.dat"); - test_vectors!( - G1Projective, - G1Affine, - to_uncompressed, - from_uncompressed, - bytes - ); -} - -#[test] -fn g1_compressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g1_compressed_valid_test_vectors.dat"); - test_vectors!( - G1Projective, - G1Affine, - to_compressed, - from_compressed, - bytes - ); -} - -#[test] -fn g2_uncompressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g2_uncompressed_valid_test_vectors.dat"); - test_vectors!( - G2Projective, - G2Affine, - to_uncompressed, - from_uncompressed, - bytes - ); -} - -#[test] -fn g2_compressed_valid_test_vectors() { - let bytes: &'static [u8] = include_bytes!("g2_compressed_valid_test_vectors.dat"); - test_vectors!( - G2Projective, - G2Affine, - to_compressed, - from_compressed, - bytes - ); -} - -#[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 - */ - - let a = G1Affine::generator(); - let b = G2Affine::generator(); - - use super::fp::Fp; - use super::fp12::Fp12; - use super::fp2::Fp2; - use super::fp6::Fp6; - - let res = pairing(&a, &b); - - let prep = G2Prepared::from(b); - - assert_eq!( - res, - multi_miller_loop(&[(&a, &prep)]).final_exponentiation() - ); - - assert_eq!( - res.0, - Fp12 { - c0: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x1972_e433_a01f_85c5, - 0x97d3_2b76_fd77_2538, - 0xc8ce_546f_c96b_cdf9, - 0xcef6_3e73_66d4_0614, - 0xa611_3427_8184_3780, - 0x13f3_448a_3fc6_d825, - ]), - c1: Fp::from_raw_unchecked([ - 0xd263_31b0_2e9d_6995, - 0x9d68_a482_f779_7e7d, - 0x9c9b_2924_8d39_ea92, - 0xf480_1ca2_e131_07aa, - 0xa16c_0732_bdbc_b066, - 0x083c_a4af_ba36_0478, - ]) - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x59e2_61db_0916_b641, - 0x2716_b6f4_b23e_960d, - 0xc8e5_5b10_a0bd_9c45, - 0x0bdb_0bd9_9c4d_eda8, - 0x8cf8_9ebf_57fd_aac5, - 0x12d6_b792_9e77_7a5e, - ]), - c1: Fp::from_raw_unchecked([ - 0x5fc8_5188_b0e1_5f35, - 0x34a0_6e3a_8f09_6365, - 0xdb31_26a6_e02a_d62c, - 0xfc6f_5aa9_7d9a_990b, - 0xa12f_55f5_eb89_c210, - 0x1723_703a_926f_8889, - ]) - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x9358_8f29_7182_8778, - 0x43f6_5b86_11ab_7585, - 0x3183_aaf5_ec27_9fdf, - 0xfa73_d7e1_8ac9_9df6, - 0x64e1_76a6_a64c_99b0, - 0x179f_a78c_5838_8f1f, - ]), - c1: Fp::from_raw_unchecked([ - 0x672a_0a11_ca2a_ef12, - 0x0d11_b9b5_2aa3_f16b, - 0xa444_12d0_699d_056e, - 0xc01d_0177_221a_5ba5, - 0x66e0_cede_6c73_5529, - 0x05f5_a71e_9fdd_c339, - ]) - } - }, - c1: Fp6 { - c0: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xd30a_88a1_b062_c679, - 0x5ac5_6a5d_35fc_8304, - 0xd0c8_34a6_a81f_290d, - 0xcd54_30c2_da37_07c7, - 0xf0c2_7ff7_8050_0af0, - 0x0924_5da6_e2d7_2eae, - ]), - c1: Fp::from_raw_unchecked([ - 0x9f2e_0676_791b_5156, - 0xe2d1_c823_4918_fe13, - 0x4c9e_459f_3c56_1bf4, - 0xa3e8_5e53_b9d3_e3c1, - 0x820a_121e_21a7_0020, - 0x15af_6183_41c5_9acc, - ]) - }, - c1: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7c95_658c_2499_3ab1, - 0x73eb_3872_1ca8_86b9, - 0x5256_d749_4774_34bc, - 0x8ba4_1902_ea50_4a8b, - 0x04a3_d3f8_0c86_ce6d, - 0x18a6_4a87_fb68_6eaa, - ]), - c1: Fp::from_raw_unchecked([ - 0xbb83_e71b_b920_cf26, - 0x2a52_77ac_92a7_3945, - 0xfc0e_e59f_94f0_46a0, - 0x7158_cdf3_7860_58f7, - 0x7cc1_061b_82f9_45f6, - 0x03f8_47aa_9fdb_e567, - ]) - }, - c2: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x8078_dba5_6134_e657, - 0x1cd7_ec9a_4399_8a6e, - 0xb1aa_599a_1a99_3766, - 0xc9a0_f62f_0842_ee44, - 0x8e15_9be3_b605_dffa, - 0x0c86_ba0d_4af1_3fc2, - ]), - c1: Fp::from_raw_unchecked([ - 0xe80f_f2a0_6a52_ffb1, - 0x7694_ca48_721a_906c, - 0x7583_183e_03b0_8514, - 0xf567_afdd_40ce_e4e2, - 0x9a6d_96d2_e526_a5fc, - 0x197e_9f49_861f_2242, - ]) - } - } - } - ); -} diff --git a/bls12_381/src/util.rs b/bls12_381/src/util.rs deleted file mode 100644 index bd25dd06a2..0000000000 --- a/bls12_381/src/util.rs +++ /dev/null @@ -1,174 +0,0 @@ -/// Compute a + b + carry, returning the result and the new carry over. -#[inline(always)] -pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { - let ret = (a as u128) + (b as u128) + (carry as u128); - (ret as u64, (ret >> 64) as u64) -} - -/// Compute a - (b + borrow), returning the result and the new borrow. -#[inline(always)] -pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { - let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); - (ret as u64, (ret >> 64) as u64) -} - -/// Compute a + (b * c) + carry, returning the result and the new carry over. -#[inline(always)] -pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { - let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); - (ret as u64, (ret >> 64) as u64) -} - -macro_rules! impl_add_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Add<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: &'b $rhs) -> $output { - &self + rhs - } - } - - impl<'a> Add<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - self + &rhs - } - } - - impl Add<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - &self + &rhs - } - } - }; -} - -macro_rules! impl_sub_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Sub<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: &'b $rhs) -> $output { - &self - rhs - } - } - - impl<'a> Sub<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - self - &rhs - } - } - - impl Sub<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - &self - &rhs - } - } - }; -} - -macro_rules! impl_binops_additive_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl_add_binop_specify_output!($lhs, $rhs, $output); - impl_sub_binop_specify_output!($lhs, $rhs, $output); - }; -} - -macro_rules! impl_binops_multiplicative_mixed { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Mul<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: &'b $rhs) -> $output { - &self * rhs - } - } - - impl<'a> Mul<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - self * &rhs - } - } - - impl Mul<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - &self * &rhs - } - } - }; -} - -macro_rules! impl_binops_additive { - ($lhs:ident, $rhs:ident) => { - impl_binops_additive_specify_output!($lhs, $rhs, $lhs); - - impl SubAssign<$rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: $rhs) { - *self = &*self - &rhs; - } - } - - impl AddAssign<$rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: $rhs) { - *self = &*self + &rhs; - } - } - - impl<'b> SubAssign<&'b $rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: &'b $rhs) { - *self = &*self - rhs; - } - } - - impl<'b> AddAssign<&'b $rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: &'b $rhs) { - *self = &*self + rhs; - } - } - }; -} - -macro_rules! impl_binops_multiplicative { - ($lhs:ident, $rhs:ident) => { - impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); - - impl MulAssign<$rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: $rhs) { - *self = &*self * &rhs; - } - } - - impl<'b> MulAssign<&'b $rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: &'b $rhs) { - *self = &*self * rhs; - } - } - }; -} diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml new file mode 100644 index 0000000000..86bacb9e37 --- /dev/null +++ b/components/equihash/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "equihash" +description = "The Equihash Proof-of-Work function" +version = "0.1.0" +authors = ["Jack Grigg "] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +license = "MIT OR Apache-2.0" +edition = "2018" + +[dependencies] +blake2b_simd = "0.5" +byteorder = "1" diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs new file mode 100644 index 0000000000..59c287305a --- /dev/null +++ b/components/equihash/src/lib.rs @@ -0,0 +1,25 @@ +//! Equihash is a Proof-of-Work algorithm, based on a generalization of the Birthday +//! problem which finds colliding hash values. It was designed to be memory-hard; more +//! specifically, the bottle-neck for parallel implementations of Equihash solvers would +//! be memory bandwidth. +//! +//! This crate implements Equihash as specified for the Zcash consensus rules. It can +//! verify solutions for any valid `(n, k)` parameters, as long as the row indices are no +//! larger than 32 bits (that is, `ceiling(((n / (k + 1)) + 1) / 8) <= 4`). +//! +//! References +//! ========== +//! - [Section 7.6.1: Equihash.] Zcash Protocol Specification, version 2020.1.10 or later. +//! - Alex Biryukov and Dmitry Khovratovich. +//! [*Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem.*][BK16] +//! NDSS ’16. +//! +//! [Section 7.6.1: Equihash.]: https://zips.z.cash/protocol/protocol.pdf#equihash +//! [BK16]: https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf + +mod verify; + +#[cfg(test)] +mod test_vectors; + +pub use verify::{is_valid_solution, Error}; diff --git a/components/equihash/src/test_vectors.rs b/components/equihash/src/test_vectors.rs new file mode 100644 index 0000000000..99299fb264 --- /dev/null +++ b/components/equihash/src/test_vectors.rs @@ -0,0 +1,5 @@ +mod invalid; +mod valid; + +pub(crate) use invalid::INVALID_TEST_VECTORS; +pub(crate) use valid::VALID_TEST_VECTORS; diff --git a/components/equihash/src/test_vectors/invalid.rs b/components/equihash/src/test_vectors/invalid.rs new file mode 100644 index 0000000000..11da849e0d --- /dev/null +++ b/components/equihash/src/test_vectors/invalid.rs @@ -0,0 +1,153 @@ +use crate::verify::{Kind, Params}; + +pub(crate) struct TestVector { + pub(crate) params: Params, + pub(crate) input: &'static [u8], + pub(crate) nonce: [u8; 32], + pub(crate) solution: &'static [u32], + pub(crate) error: Kind, +} + +pub(crate) const INVALID_TEST_VECTORS: &[TestVector] = &[ + // Original valid solution: [ + // 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + // 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + // 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + // ] + + // Change one index + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2262, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + error: Kind::Collision, + }, + // Swap two arbitrary indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 45858, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 2261, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + error: Kind::Collision, + }, + // Reverse the first pair of indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 15185, 2261, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + error: Kind::OutOfOrder, + }, + // Swap the first and second pairs of indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 36112, 104243, 2261, 15185, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + error: Kind::OutOfOrder, + }, + // Swap the second-to-last and last pairs of indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 104973, 122568, 69567, 114474, + ], + error: Kind::OutOfOrder, + }, + // Swap the first half and second half + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, 23460, 49807, 52426, 80391, + 69567, 114474, 104973, 122568, 2261, 15185, 36112, 104243, 23779, 118390, 118332, + 130041, 32642, 69878, 76925, 80080, 45858, 116805, 92842, 111026, + ], + error: Kind::OutOfOrder, + }, + // Sort the indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2261, 15185, 15972, 23460, 23779, 32642, 36112, 45858, 49807, 52426, 68190, 69567, + 69878, 76925, 80080, 80391, 81830, 85191, 90330, 91132, 92842, 104243, 104973, 111026, + 114474, 115059, 116805, 118332, 118390, 122568, 122819, 130041, + ], + error: Kind::Collision, + }, + // Duplicate indices + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2261, 2261, 15185, 15185, 36112, 36112, 104243, 104243, 23779, 23779, 118390, 118390, + 118332, 118332, 130041, 130041, 32642, 32642, 69878, 69878, 76925, 76925, 80080, 80080, + 45858, 45858, 116805, 116805, 92842, 92842, 111026, 111026, + ], + error: Kind::DuplicateIdxs, + }, + // Duplicate first half + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solution: &[ + 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 2261, 15185, 36112, 104243, 23779, 118390, 118332, + 130041, 32642, 69878, 76925, 80080, 45858, 116805, 92842, 111026, + ], + error: Kind::DuplicateIdxs, + }, +]; diff --git a/components/equihash/src/test_vectors/valid.rs b/components/equihash/src/test_vectors/valid.rs new file mode 100644 index 0000000000..a55de1b96a --- /dev/null +++ b/components/equihash/src/test_vectors/valid.rs @@ -0,0 +1,841 @@ +use crate::verify::Params; + +pub(crate) struct TestVector { + pub(crate) params: Params, + pub(crate) input: &'static [u8], + pub(crate) nonce: [u8; 32], + pub(crate) solutions: &'static [&'static [u32]], +} + +pub(crate) const VALID_TEST_VECTORS: &[TestVector] = &[ + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [0; 32], + solutions: &[ + &[ + 976, 126621, 100174, 123328, 38477, 105390, 38834, 90500, 6411, 116489, 51107, + 129167, 25557, 92292, 38525, 56514, 1110, 98024, 15426, 74455, 3185, 84007, 24328, + 36473, 17427, 129451, 27556, 119967, 31704, 62448, 110460, 117894, + ], + &[ + 1008, 18280, 34711, 57439, 3903, 104059, 81195, 95931, 58336, 118687, 67931, + 123026, 64235, 95595, 84355, 122946, 8131, 88988, 45130, 58986, 59899, 78278, + 94769, 118158, 25569, 106598, 44224, 96285, 54009, 67246, 85039, 127667, + ], + &[ + 1278, 107636, 80519, 127719, 19716, 130440, 83752, 121810, 15337, 106305, 96940, + 117036, 46903, 101115, 82294, 118709, 4915, 70826, 40826, 79883, 37902, 95324, + 101092, 112254, 15536, 68760, 68493, 125640, 67620, 108562, 68035, 93430, + ], + &[ + 3976, 108868, 80426, 109742, 33354, 55962, 68338, 80112, 26648, 28006, 64679, + 130709, 41182, 126811, 56563, 129040, 4013, 80357, 38063, 91241, 30768, 72264, + 97338, 124455, 5607, 36901, 67672, 87377, 17841, 66985, 77087, 85291, + ], + &[ + 5970, 21862, 34861, 102517, 11849, 104563, 91620, 110653, 7619, 52100, 21162, + 112513, 74964, 79553, 105558, 127256, 21905, 112672, 81803, 92086, 43695, 97911, + 66587, 104119, 29017, 61613, 97690, 106345, 47428, 98460, 53655, 109002, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 1911, 96020, 94086, 96830, 7895, 51522, 56142, 62444, 15441, 100732, 48983, 64776, + 27781, 85932, 101138, 114362, 4497, 14199, 36249, 41817, 23995, 93888, 35798, 96337, + 5530, 82377, 66438, 85247, 39332, 78978, 83015, 123505, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 165, 27290, 87424, 123403, 5344, 35125, 49154, 108221, 8882, 90328, 77359, 92348, + 54692, 81690, 115200, 121929, 18968, 122421, 32882, 128517, 56629, 88083, 88022, + 102461, 35665, 62833, 95988, 114502, 39965, 119818, 45010, 94889, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [ + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 1855, 37525, 81472, 112062, 11831, 38873, 45382, 82417, 11571, 47965, 71385, + 119369, 13049, 64810, 26995, 34659, 6423, 67533, 88972, 105540, 30672, 80244, + 39493, 94598, 17858, 78496, 35376, 118645, 50186, 51838, 70421, 103703, + ], + &[ + 3671, 125813, 31502, 78587, 25500, 83138, 74685, 98796, 8873, 119842, 21142, 55332, + 25571, 122204, 31433, 80719, 3955, 49477, 4225, 129562, 11837, 21530, 75841, + 120644, 4653, 101217, 19230, 113175, 16322, 24384, 21271, 96965, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"block header", + nonce: [ + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 2570, 20946, 61727, 130667, 16426, 62291, 107177, 112384, 18464, 125099, 120313, + 127545, 35035, 73082, 118591, 120800, 13800, 32837, 23607, 86516, 17339, 114578, 22053, + 85510, 14913, 42826, 25168, 121262, 33673, 114773, 77592, 83471, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [0; 32], + solutions: &[ + &[ + 3130, 83179, 30454, 107686, 71240, 88412, 109700, 114639, 10024, 32706, 38019, + 113013, 18399, 92942, 21094, 112263, 4146, 30807, 10631, 73192, 22216, 90216, + 45581, 125042, 11256, 119455, 93603, 110112, 59851, 91545, 97403, 111102, + ], + &[ + 3822, 35317, 47508, 119823, 37652, 117039, 69087, 72058, 13147, 111794, 65435, + 124256, 22247, 66272, 30298, 108956, 13157, 109175, 37574, 50978, 31258, 91519, + 52568, 107874, 14999, 103687, 27027, 109468, 36918, 109660, 42196, 100424, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, + 80080, 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, + 81830, 91132, 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ], + &[ + 16700, 46276, 21232, 43153, 22398, 58511, 47922, 71816, 23370, 26222, 39248, 40137, + 65375, 85794, 69749, 73259, 23599, 72821, 42250, 52383, 35267, 75893, 52152, 57181, + 27137, 101117, 45804, 92838, 29548, 29574, 37737, 113624, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 6005, 59843, 55560, 70361, 39140, 77856, 44238, 57702, 32125, 121969, 108032, 116542, + 37925, 75404, 48671, 111682, 6937, 93582, 53272, 77545, 13715, 40867, 73187, 77853, + 7348, 70313, 24935, 24978, 25967, 41062, 58694, 110036, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 968, 90691, 70664, 112581, 17233, 79239, 66772, 92199, 27801, 44198, 58712, 122292, + 28227, 126747, 70925, 118108, 2876, 76082, 39335, 113764, 26643, 60579, 50853, + 70300, 19640, 31848, 28672, 87870, 33574, 50308, 40291, 61593, + ], + &[ + 1181, 61261, 75793, 96302, 36209, 113590, 79236, 108781, 8275, 106510, 11877, + 74550, 45593, 80595, 71247, 95783, 2991, 99117, 56413, 71287, 10235, 68286, 22016, + 104685, 51588, 53344, 56822, 63386, 63527, 75772, 93100, 108542, + ], + &[ + 2229, 30387, 14573, 115700, 20018, 124283, 84929, 91944, 26341, 64220, 69433, + 82466, 29778, 101161, 59334, 79798, 2533, 104985, 50731, 111094, 10619, 80909, + 15555, 119911, 29028, 42966, 51958, 86784, 34561, 97709, 77126, 127250, + ], + &[ + 15465, 59017, 93851, 112478, 24940, 128791, 26154, 107289, 24050, 78626, 51948, + 111573, 35117, 113754, 36317, 67606, 21508, 91486, 28293, 126983, 23989, 39722, + 60567, 97243, 26720, 56243, 60444, 107530, 40329, 56467, 91943, 93737, + ], + ], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.", + nonce: [ + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 1120, 77433, 58243, 76860, 11411, 96068, 13150, 35878, 15049, 88928, 20101, 104706, + 29215, 73328, 39498, 83529, 9233, 124174, 66731, 97423, 10823, 92444, 25647, 127742, + 12207, 46292, 22018, 120758, 14411, 46485, 21828, 57591, + ]], + }, + TestVector { + params: Params { n: 96, k: 5 }, + input: b"Test case with 3+-way collision in the final round.", + nonce: [ + 0xf0, 0x07, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + ], + solutions: &[ + &[ + 1162, 129543, 57488, 82745, 18311, 115612, 20603, 112899, 5635, 103373, 101651, + 125986, 52160, 70847, 65152, 101720, 5810, 43165, 64589, 105333, 11347, 63836, + 55495, 96392, 40767, 81019, 53976, 94184, 41650, 114374, 45109, 57038, + ], + &[ + 2321, 121781, 36792, 51959, 21685, 67596, 27992, 59307, 13462, 118550, 37537, + 55849, 48994, 58515, 78703, 100100, 11189, 98120, 45242, 116128, 33260, 47351, + 61550, 116649, 11927, 20590, 35907, 107966, 28779, 57407, 54793, 104108, + ], + &[ + 2321, 121781, 36792, 51959, 21685, 67596, 27992, 59307, 13462, 118550, 37537, + 55849, 48994, 78703, 58515, 100100, 11189, 98120, 45242, 116128, 33260, 47351, + 61550, 116649, 11927, 20590, 35907, 107966, 28779, 57407, 54793, 104108, + ], + &[ + 2321, 121781, 36792, 51959, 21685, 67596, 27992, 59307, 13462, 118550, 37537, + 55849, 48994, 100100, 58515, 78703, 11189, 98120, 45242, 116128, 33260, 47351, + 61550, 116649, 11927, 20590, 35907, 107966, 28779, 57407, 54793, 104108, + ], + &[ + 4488, 83544, 24912, 62564, 43206, 62790, 68462, 125162, 6805, 8886, 46937, 54588, + 15509, 126232, 19426, 27845, 5959, 56839, 38806, 102580, 11255, 63258, 23442, + 39750, 13022, 22271, 24110, 52077, 17422, 124996, 35725, 101509, + ], + &[ + 8144, 33053, 33933, 77498, 21356, 110495, 42805, 116575, 27360, 48574, 100682, + 102629, 50754, 64608, 96899, 120978, 11924, 74422, 49240, 106822, 12787, 68290, + 44314, 50005, 38056, 49716, 83299, 95307, 41798, 82309, 94504, 96161, + ], + ], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [0; 32], + solutions: &[&[ + 4313, 223176, 448870, 1692641, 214911, 551567, 1696002, 1768726, 500589, 938660, + 724628, 1319625, 632093, 1474613, 665376, 1222606, 244013, 528281, 1741992, 1779660, + 313314, 996273, 435612, 1270863, 337273, 1385279, 1031587, 1147423, 349396, 734528, + 902268, 1678799, 10902, 1231236, 1454381, 1873452, 120530, 2034017, 948243, 1160178, + 198008, 1704079, 1087419, 1734550, 457535, 698704, 649903, 1029510, 75564, 1860165, + 1057819, 1609847, 449808, 527480, 1106201, 1252890, 207200, 390061, 1557573, 1711408, + 396772, 1026145, 652307, 1712346, 10680, 1027631, 232412, 974380, 457702, 1827006, + 1316524, 1400456, 91745, 2032682, 192412, 710106, 556298, 1963798, 1329079, 1504143, + 102455, 974420, 639216, 1647860, 223846, 529637, 425255, 680712, 154734, 541808, + 443572, 798134, 322981, 1728849, 1306504, 1696726, 57884, 913814, 607595, 1882692, + 236616, 1439683, 420968, 943170, 1014827, 1446980, 1468636, 1559477, 1203395, 1760681, + 1439278, 1628494, 195166, 198686, 349906, 1208465, 917335, 1361918, 937682, 1885495, + 494922, 1745948, 1320024, 1826734, 847745, 894084, 1484918, 1523367, 7981, 1450024, + 861459, 1250305, 226676, 329669, 339783, 1935047, 369590, 1564617, 939034, 1908111, + 1147449, 1315880, 1276715, 1428599, 168956, 1442649, 766023, 1171907, 273361, 1902110, + 1169410, 1786006, 413021, 1465354, 707998, 1134076, 977854, 1604295, 1369720, 1486036, + 330340, 1587177, 502224, 1313997, 400402, 1667228, 889478, 946451, 470672, 2019542, + 1023489, 2067426, 658974, 876859, 794443, 1667524, 440815, 1099076, 897391, 1214133, + 953386, 1932936, 1100512, 1362504, 874364, 975669, 1277680, 1412800, 1227580, 1857265, + 1312477, 1514298, 12478, 219890, 534265, 1351062, 65060, 651682, 627900, 1331192, + 123915, 865936, 1218072, 1732445, 429968, 1097946, 947293, 1323447, 157573, 1212459, + 923792, 1943189, 488881, 1697044, 915443, 2095861, 333566, 732311, 336101, 1600549, + 575434, 1978648, 1071114, 1473446, 50017, 54713, 367891, 2055483, 561571, 1714951, + 715652, 1347279, 584549, 1642138, 1002587, 1125289, 1364767, 1382627, 1387373, 2054399, + 97237, 1677265, 707752, 1265819, 121088, 1810711, 1755448, 1858538, 444653, 1130822, + 514258, 1669752, 578843, 729315, 1164894, 1691366, 15609, 1917824, 173620, 587765, + 122779, 2024998, 804857, 1619761, 110829, 1514369, 410197, 493788, 637666, 1765683, + 782619, 1186388, 494761, 1536166, 1582152, 1868968, 825150, 1709404, 1273757, 1657222, + 817285, 1955796, 1014018, 1961262, 873632, 1689675, 985486, 1008905, 130394, 897076, + 419669, 535509, 980696, 1557389, 1244581, 1738170, 197814, 1879515, 297204, 1165124, + 883018, 1677146, 1545438, 2017790, 345577, 1821269, 761785, 1014134, 746829, 751041, + 930466, 1627114, 507500, 588000, 1216514, 1501422, 991142, 1378804, 1797181, 1976685, + 60742, 780804, 383613, 645316, 770302, 952908, 1105447, 1878268, 504292, 1961414, + 693833, 1198221, 906863, 1733938, 1315563, 2049718, 230826, 2064804, 1224594, 1434135, + 897097, 1961763, 993758, 1733428, 306643, 1402222, 532661, 627295, 453009, 973231, + 1746809, 1857154, 263652, 1683026, 1082106, 1840879, 768542, 1056514, 888164, 1529401, + 327387, 1708909, 961310, 1453127, 375204, 878797, 1311831, 1969930, 451358, 1229838, + 583937, 1537472, 467427, 1305086, 812115, 1065593, 532687, 1656280, 954202, 1318066, + 1164182, 1963300, 1232462, 1722064, 17572, 923473, 1715089, 2079204, 761569, 1557392, + 1133336, 1183431, 175157, 1560762, 418801, 927810, 734183, 825783, 1844176, 1951050, + 317246, 336419, 711727, 1630506, 634967, 1595955, 683333, 1461390, 458765, 1834140, + 1114189, 1761250, 459168, 1897513, 1403594, 1478683, 29456, 1420249, 877950, 1371156, + 767300, 1848863, 1607180, 1819984, 96859, 1601334, 171532, 2068307, 980009, 2083421, + 1329455, 2030243, 69434, 1965626, 804515, 1339113, 396271, 1252075, 619032, 2080090, + 84140, 658024, 507836, 772757, 154310, 1580686, 706815, 1024831, 66704, 614858, 256342, + 957013, 1488503, 1615769, 1515550, 1888497, 245610, 1333432, 302279, 776959, 263110, + 1523487, 623933, 2013452, 68977, 122033, 680726, 1849411, 426308, 1292824, 460128, + 1613657, 234271, 971899, 1320730, 1559313, 1312540, 1837403, 1690310, 2040071, 149918, + 380012, 785058, 1675320, 267071, 1095925, 1149690, 1318422, 361557, 1376579, 1587551, + 1715060, 1224593, 1581980, 1354420, 1850496, 151947, 748306, 1987121, 2070676, 273794, + 981619, 683206, 1485056, 766481, 2047708, 930443, 2040726, 1136227, 1945705, 1722044, + 1971986, + ]], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 1505, 1380774, 200806, 1787044, 101056, 1697952, 281464, 374899, 263712, 1532496, + 264180, 637056, 734225, 1882676, 1112004, 2093109, 193394, 1459136, 525171, 657480, + 214528, 1221365, 574444, 594726, 501919, 1309358, 1740268, 1989610, 654491, + 1068055, 919416, 1993208, 17599, 1858176, 1315176, 1901532, 108258, 109600, + 1117445, 1936058, 70247, 1036984, 628234, 1800109, 149791, 365740, 345683, 563554, + 21678, 822781, 1423722, 1644228, 792912, 1409641, 805060, 2041985, 453824, 1003179, + 934427, 1068834, 629003, 1456111, 670049, 1558594, 19016, 1343657, 1698188, + 1865216, 45723, 1820952, 1160970, 1585983, 422549, 1973097, 1296271, 2006382, + 650084, 809838, 871727, 1080419, 28500, 1471829, 384406, 619459, 212041, 1466258, + 481435, 866461, 145340, 1403843, 1339592, 1405761, 163425, 1073771, 285027, + 1488210, 167744, 1182267, 1354059, 2089602, 921700, 2059931, 1704721, 1853088, + 585171, 739246, 747551, 1520527, 590255, 1175747, 705292, 998433, 522014, 1931179, + 1629531, 1692879, 588830, 1799457, 963672, 1664237, 775408, 1926741, 907030, + 1466738, 784179, 1972599, 1494787, 1598114, 1736, 1039487, 88704, 1302687, 579526, + 1476728, 1677992, 1854526, 432470, 2062305, 1471132, 1747579, 1521894, 1917599, + 1590975, 1936227, 151871, 1999775, 224664, 461809, 704084, 1306665, 1316156, + 1529628, 876811, 2086004, 1986383, 2012147, 1039505, 1637502, 1432721, 1565477, + 110385, 342650, 659137, 1285167, 367416, 2007586, 445677, 2084877, 285692, 1144365, + 988840, 1990372, 748425, 1617758, 1267712, 1510433, 152291, 1256291, 1722179, + 1995439, 864844, 1623380, 1071853, 1731862, 699978, 1407662, 1048047, 1849702, + 962900, 1083340, 1378752, 1534902, 11843, 115329, 454796, 548919, 148184, 1686936, + 862432, 873854, 60753, 999864, 385959, 1528101, 534420, 678401, 590419, 1962518, + 54984, 1141820, 243305, 1349970, 599681, 1817233, 1632537, 1698724, 580004, 673073, + 1403350, 2026104, 758881, 970056, 1717966, 2062827, 19624, 148580, 609748, 1588928, + 456321, 834920, 700532, 1682606, 20012, 441139, 1591072, 1923394, 194034, 1741063, + 1156906, 1983067, 20703, 1939972, 604581, 963600, 128170, 731716, 606773, 1626824, + 139460, 1386775, 521911, 2043473, 392180, 449532, 895678, 1453340, 7085, 598416, + 1514260, 2061068, 279532, 678363, 943255, 1405306, 119114, 2075865, 592839, + 1972064, 254647, 2078288, 946282, 1567138, 120422, 767626, 213242, 448366, 438457, + 1768467, 853790, 1509505, 735780, 1979631, 1461410, 1462050, 739008, 1572606, + 920754, 1507358, 12883, 1681167, 1308399, 1839490, 85599, 1387522, 703262, 1949514, + 18523, 1236125, 669105, 1464132, 68670, 2085647, 333393, 1731573, 21714, 637827, + 985912, 2091029, 84065, 1688993, 1574405, 1899543, 134032, 179206, 671016, 1118310, + 288960, 861994, 622074, 1738892, 10936, 343910, 598016, 1741971, 586348, 1956071, + 851053, 1715626, 531385, 1213667, 1093995, 1863757, 630365, 1851894, 1328101, + 1770446, 31900, 734027, 1078651, 1701535, 123276, 1916343, 581822, 1681706, 573135, + 818091, 1454710, 2052521, 1150284, 1451159, 1482280, 1811430, 26321, 785837, + 877980, 2073103, 107324, 727248, 1785460, 1840517, 184560, 185640, 364103, 1878753, + 518459, 1984029, 964109, 1884200, 74003, 527272, 516232, 711247, 148582, 209254, + 634610, 1534140, 376714, 1573267, 421225, 1265101, 1078858, 1374310, 1806283, + 2091298, 23392, 389637, 413663, 1066737, 226164, 762552, 1048220, 1583397, 40092, + 277435, 775449, 1533894, 202582, 390703, 346741, 1027320, 523034, 809424, 584882, + 1296934, 528062, 733331, 1212771, 1958651, 653372, 1313962, 1366332, 1784489, + 1542466, 1580386, 1628948, 2000957, 57069, 1398636, 1250431, 1698486, 57289, + 596009, 582428, 966130, 167657, 1025537, 1227498, 1630134, 234060, 1285209, 265623, + 1165779, 68485, 632055, 96019, 1854676, 98410, 158575, 168035, 1296171, 158847, + 1243959, 977212, 1113647, 363568, 891940, 954593, 1987111, 90101, 133251, 1136222, + 1255117, 543075, 732768, 749576, 1174878, 422226, 1854657, 1143029, 1457135, + 927105, 1137382, 1566306, 1661926, 103057, 425126, 698089, 1774942, 911019, + 1793511, 1623559, 2002409, 457796, 1196971, 724257, 1811147, 956269, 1165590, + 1137531, 1381215, 201063, 1938529, 986021, 1297857, 921334, 1259083, 1440074, + 1939366, 232907, 747213, 1349009, 1945364, 689906, 1116453, 1904207, 1916192, + 229793, 1576982, 1420059, 1644978, 278248, 2024807, 297914, 419798, 555747, 712605, + 1012424, 1428921, 890113, 1822645, 1082368, 1392894, + ], + &[ + 13396, 1502141, 934546, 1419227, 445798, 1676403, 643830, 1421927, 45286, 1160795, + 117864, 542369, 501065, 1834465, 544881, 1258964, 157233, 888851, 1707333, 2042954, + 1067373, 1959382, 1081841, 1528092, 355787, 1506512, 488244, 1901282, 842029, + 1045169, 1014084, 1718668, 184257, 1419101, 572200, 1554883, 240034, 1489590, + 1108495, 1346106, 357644, 1206700, 1019151, 1817512, 827374, 945127, 885925, + 1320873, 292347, 918571, 436037, 973478, 321703, 1853464, 595802, 894629, 426733, + 849916, 1618173, 1877920, 1260413, 1913655, 1413450, 1821154, 39823, 934676, + 1415329, 1899092, 248682, 1500533, 603937, 2061626, 126829, 490133, 1491924, + 1591619, 474135, 1328233, 878943, 2058462, 86988, 911439, 767925, 1499098, 323431, + 335282, 1398512, 1561768, 142731, 1568252, 459703, 1378588, 443971, 730680, 723742, + 1301913, 120606, 402225, 659692, 878779, 967663, 1022156, 1573638, 1710098, 228789, + 483630, 565528, 1137688, 569834, 1100519, 1315274, 1613960, 323958, 1035040, + 342578, 1395004, 693886, 778875, 1128411, 1424459, 470124, 1001591, 1265290, + 1962023, 898247, 1156840, 1424257, 1463363, 29922, 1694756, 109027, 1777528, + 115450, 1525306, 1347575, 1835714, 279549, 1320423, 1162056, 1668629, 934706, + 1283078, 1115477, 1231121, 60882, 1677375, 729195, 1560584, 199789, 770485, 532152, + 1939788, 225610, 1517013, 1952643, 2085389, 453439, 1202000, 902342, 1303946, + 208859, 493534, 1185220, 1943780, 639093, 1123038, 1247020, 1359794, 354070, + 1207821, 463570, 650864, 846889, 875335, 1220142, 1929715, 222307, 849285, 859051, + 1961534, 419719, 1330637, 441693, 1566326, 590324, 1106956, 1198428, 1208601, + 1194514, 1714365, 1549673, 1766871, 36406, 315544, 313443, 1553541, 769731, + 1608444, 1457538, 1893815, 192649, 1030632, 282298, 846640, 287483, 336140, 541071, + 1079100, 139955, 404958, 1306603, 1734441, 843559, 1448208, 1273901, 1715116, + 547237, 1060287, 629301, 1036790, 733471, 1236082, 1073097, 1137292, 78721, 365289, + 321250, 730155, 505572, 1570719, 1272465, 1762855, 200602, 2002477, 376698, 586952, + 574825, 1774405, 1739907, 1910482, 321686, 1711532, 514504, 769967, 419504, + 1565290, 586517, 1474371, 394689, 802951, 1529816, 2003281, 1025904, 1789059, + 1557849, 1649570, 22351, 1020349, 799545, 1167424, 372784, 822777, 1331906, + 1487316, 147912, 779616, 503790, 591742, 831949, 1009009, 846119, 1154281, 160278, + 1306827, 972322, 1632998, 925417, 1862659, 1167562, 1487753, 225867, 1170797, + 595379, 1116885, 580973, 823382, 825674, 1756426, 123619, 578961, 1105801, 1478751, + 615784, 1248585, 1555596, 1823147, 291494, 1439342, 1362238, 2020172, 416541, + 808362, 1087192, 1279428, 145445, 1476821, 589962, 1007778, 502785, 578289, + 1347895, 1468692, 397887, 1422580, 873197, 1547340, 544937, 1706334, 1240996, + 1394517, 60961, 173017, 1198379, 1875710, 164983, 1244552, 786241, 1163907, 182496, + 445793, 765554, 1044380, 274547, 1626395, 1621315, 1632207, 168375, 924818, + 1355514, 1888896, 1060612, 1309397, 1247169, 2065604, 537737, 849561, 690965, + 1778198, 834911, 1341821, 1239610, 1468591, 90037, 516053, 906189, 1302107, 544896, + 1319300, 1944402, 2080316, 332213, 469952, 949830, 1187792, 400278, 459757, + 1306417, 1772258, 135309, 995973, 571189, 2041352, 375936, 389143, 1350844, + 1611928, 294058, 577712, 820473, 2062905, 435690, 1918441, 1160042, 1437969, 60304, + 817774, 956977, 1785565, 116153, 479573, 1291516, 1468918, 260814, 1568016, 695030, + 1082881, 1175273, 1679631, 1219376, 1680268, 173493, 2060876, 1391045, 1554572, + 795587, 931051, 1528751, 1810166, 1049000, 1089918, 1780849, 2080247, 1409007, + 1720471, 1726930, 2048510, 202301, 1229629, 910467, 1501208, 249207, 758814, + 386350, 1680387, 259000, 824989, 268126, 416050, 304924, 1411322, 534678, 883265, + 211439, 1915547, 1954195, 1987528, 682149, 1798395, 766421, 1964526, 345778, + 739906, 1698586, 1980612, 1207132, 1863904, 1586749, 1798519, 89627, 322371, + 1448894, 1821630, 771946, 845162, 1627288, 1848711, 247727, 1578777, 425393, + 1141980, 738940, 2054340, 1156205, 1662664, 315398, 956023, 867847, 935583, 559412, + 1538514, 1291871, 1371526, 925807, 2038575, 1099192, 1213875, 989415, 1590364, + 1137142, 1456911, 148404, 1829533, 171283, 355992, 231384, 1712830, 380050, + 1531904, 205155, 785766, 794141, 1821460, 536969, 826105, 915018, 1727720, 473495, + 575484, 1511034, 1752204, 1030559, 1173930, 1566670, 1684100, 545434, 1644431, + 838096, 1830099, 714438, 1058571, 986457, 1275490, + ], + ], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 85, 2041581, 739509, 1038120, 95814, 1449199, 566808, 1970271, 22351, 1033277, + 351539, 378679, 370613, 1217658, 744902, 2054863, 128384, 2048133, 1422405, + 1711301, 266020, 338919, 1851784, 1923279, 344519, 939493, 1254831, 1365416, + 658643, 1827109, 742476, 2019543, 7557, 1416156, 42164, 1108616, 1324398, 1502720, + 1471471, 1734206, 51676, 532090, 634806, 1747514, 481844, 1488478, 690106, 1838033, + 93690, 1442016, 977262, 1136782, 239698, 1964439, 1032494, 2041403, 463135, + 1204579, 693303, 1522068, 880410, 2021579, 1108504, 1718764, 3462, 1916805, + 1727074, 1789966, 562318, 1651780, 1332270, 1995649, 295751, 2023013, 1119902, + 1690352, 1293091, 2056850, 1974345, 2044869, 78574, 899703, 1106267, 1286448, + 1303134, 1850087, 1355112, 1776010, 1031239, 1851498, 1153488, 1243952, 1163993, + 1977728, 1328544, 1612102, 9487, 233220, 998029, 1173368, 549226, 2073453, 871154, + 1572100, 46216, 739886, 1234167, 1572986, 374817, 878325, 910917, 1079476, 9548, + 961914, 1057590, 1411096, 973096, 1060957, 1188074, 1721366, 465242, 2055339, + 971225, 1830281, 526459, 2042659, 746133, 1985292, 7405, 1510070, 385903, 2095485, + 468941, 1679477, 757944, 1622263, 246823, 695851, 444054, 846202, 321170, 1678719, + 928172, 1531270, 13258, 342299, 639214, 1919221, 412214, 430924, 787608, 1968276, + 32804, 791991, 524319, 1083379, 568152, 1875970, 753609, 1958222, 44322, 324266, + 1072444, 1182703, 133944, 1208050, 900653, 1614070, 373367, 1363285, 663351, + 1459703, 578444, 1419137, 1163520, 1922722, 65157, 1631833, 1034031, 1487396, + 723173, 1724173, 1482982, 1644877, 384747, 909984, 1275503, 2036514, 610392, + 1093084, 913780, 1924334, 11137, 1546273, 61787, 295562, 319377, 2057614, 1229059, + 2010647, 209286, 1287454, 1013313, 1747506, 271940, 1520544, 1018674, 1063669, + 185227, 1219872, 1288529, 1548657, 344601, 1898125, 1755668, 1992858, 890818, + 1100957, 1565899, 1575128, 1207190, 1821158, 1999048, 2022807, 19362, 2055304, + 757990, 2088728, 478320, 1006345, 509532, 1966851, 160002, 648308, 414679, 1022972, + 528460, 1898952, 919894, 1918492, 154904, 1997802, 1528735, 1687070, 240714, + 1414676, 1400402, 1763165, 381766, 1044133, 619868, 1519386, 1248422, 1409298, + 1754871, 2015118, 1739, 499886, 1642104, 2069348, 437356, 609873, 491378, 1137963, + 89811, 1626714, 873752, 1548730, 1114856, 1941590, 1481869, 1625018, 59629, 668173, + 315591, 733560, 803171, 1801431, 1294776, 1914531, 253597, 1771037, 650342, + 1014718, 375289, 519529, 1447780, 1900126, 8241, 1229781, 777968, 1198408, 104296, + 2030372, 683340, 1454000, 91445, 100079, 645496, 824897, 392258, 1740230, 1525343, + 2069444, 110826, 1097701, 1069615, 1960595, 530572, 1028831, 999251, 1458171, + 146008, 1135021, 867825, 1398554, 397922, 818160, 587611, 1867232, 11088, 414753, + 572774, 2060307, 407170, 687100, 1002378, 1924055, 225264, 1608839, 792486, + 1925598, 470948, 519691, 700762, 1434860, 164901, 1277475, 377305, 1816065, 526937, + 1419265, 639397, 690184, 259943, 444998, 672324, 836053, 601877, 1693911, 1108479, + 1809555, 147947, 796744, 732775, 1441222, 325070, 1809776, 1873763, 2013982, + 481882, 1288648, 1653390, 1654906, 532739, 2062844, 758222, 1372565, 339507, + 1224640, 1392890, 1850326, 1130365, 1924596, 1177208, 1363642, 384241, 515152, + 1164040, 2004909, 609791, 1575213, 1671915, 1691266, 3039, 1774544, 200172, 273877, + 420816, 737235, 986055, 1164239, 165598, 265509, 1009133, 2062342, 758743, 1489470, + 1260158, 1924360, 208628, 1135455, 794209, 1067104, 469480, 1795800, 1183662, + 1360938, 335183, 822888, 831116, 2088169, 399584, 1836326, 1174096, 2034335, 95734, + 1427706, 1593344, 2070787, 305103, 459806, 1134106, 1581586, 304533, 1761123, + 454382, 1620968, 974160, 1661165, 1984968, 2006168, 143936, 1576427, 1420916, + 2050868, 239423, 1955755, 713829, 1553644, 613116, 653092, 957406, 1332874, 634343, + 1504804, 1539492, 1652920, 29255, 84313, 134872, 1722963, 936125, 1636028, 1518342, + 1910113, 74089, 1517035, 141099, 1837859, 91886, 1841153, 483590, 1276988, 94868, + 209194, 613253, 1062768, 289463, 1150432, 1216070, 2086920, 226473, 1630691, + 482394, 1837175, 389596, 2002601, 395772, 870173, 43107, 688649, 936340, 1235157, + 189041, 1855656, 597803, 1251423, 472775, 1688197, 1286637, 1760949, 930937, + 1072689, 1187497, 1784673, 58620, 1436417, 146777, 1677387, 66982, 746844, 945993, + 1703252, 347963, 945075, 445864, 1694069, 946355, 1646534, 1769893, 1806674, + ], + &[ + 2280, 1675737, 1241864, 1426081, 529325, 1356538, 1546188, 2018466, 113218, + 1885133, 750288, 1896938, 785567, 802607, 1597047, 1985969, 132292, 1612427, + 551147, 1732380, 1140541, 1246254, 1371957, 1567864, 369405, 1582447, 1726106, + 1947007, 435161, 1369789, 928581, 1556123, 241507, 1653097, 395601, 975278, 633072, + 1541996, 1250446, 1740729, 545122, 1930170, 980220, 1098305, 1158689, 1369613, + 1570322, 1726377, 438386, 783277, 968764, 1057094, 559257, 1187476, 1228488, + 2074064, 813312, 1810708, 1064164, 2087729, 923610, 1552562, 1327854, 1735362, + 32630, 1981975, 310780, 1158178, 263597, 363824, 2052751, 2073086, 45706, 847451, + 584418, 813295, 716033, 968866, 854478, 1868422, 594854, 1676760, 1410774, 2055426, + 661611, 1138770, 997952, 1309381, 984769, 1370253, 1649486, 1920232, 1080026, + 1654268, 1212179, 1834060, 58487, 971078, 424085, 1175474, 687490, 1317807, 713352, + 1985958, 142307, 1854880, 1309882, 1540711, 487396, 904606, 975430, 1385196, 88528, + 1099752, 372012, 1708451, 207227, 1674648, 1476751, 1547086, 138190, 738504, + 779891, 1107444, 505099, 1265858, 613613, 1884841, 17621, 1157648, 209013, 526174, + 971607, 1004381, 1202861, 1494745, 274131, 982841, 729228, 886096, 478622, 1293202, + 539968, 885395, 152578, 1647348, 494562, 1327036, 342817, 1698049, 725707, 1547591, + 767029, 1290077, 1546025, 1736585, 1219491, 1852307, 1555669, 1883327, 61829, + 813247, 482047, 1362746, 93496, 1467091, 1070897, 1559668, 305281, 690664, 326883, + 444914, 443937, 1762042, 614124, 1309010, 148533, 1571755, 497978, 2074730, + 1545845, 1666088, 1757232, 1900305, 189111, 802199, 203091, 881152, 926582, + 1675352, 1478644, 1677015, 20277, 435117, 396319, 834104, 370396, 1445594, 1161835, + 2054329, 230752, 644229, 568858, 1963813, 872483, 974796, 984693, 1105289, 94040, + 1781444, 278075, 1901607, 165376, 834499, 951353, 1932215, 146197, 664541, 383107, + 1743858, 287583, 1810757, 570459, 1739938, 24296, 1527677, 1742586, 2000245, + 392111, 1596429, 915955, 1501951, 590908, 1689547, 752882, 1853220, 749276, + 1941184, 1507459, 1578707, 167457, 1313516, 428750, 1165032, 324442, 368759, + 1283775, 2017161, 364763, 2053902, 905316, 1800528, 581102, 2050600, 818573, + 1762293, 2711, 622959, 813485, 831354, 41471, 1324329, 862647, 1007821, 26246, + 1120632, 1156719, 1215948, 907527, 1144579, 1042530, 1287508, 138588, 1538967, + 1265927, 1735492, 229904, 1039415, 999131, 1826370, 392686, 730051, 623787, + 2047295, 409449, 1711230, 462385, 1992127, 42656, 880450, 42729, 1973289, 479692, + 1993383, 673152, 1885841, 170786, 748129, 521360, 818751, 982929, 1234321, 1113308, + 1907714, 98953, 1728051, 947493, 1289327, 199255, 1474247, 681622, 1416365, 352728, + 1796749, 603909, 811763, 946618, 1104470, 973691, 1152770, 21297, 639834, 594075, + 1017433, 133138, 630545, 537386, 1347734, 365214, 844135, 1341949, 1460424, 673293, + 758707, 754735, 1456846, 40018, 993218, 91092, 1539083, 1526483, 1757364, 1595990, + 1598593, 230413, 1897770, 538469, 1395634, 396644, 1893699, 442435, 571726, 52013, + 1125704, 1098095, 2080341, 200259, 2046061, 1241415, 1755598, 573138, 735184, + 795832, 1567959, 680511, 1310894, 1103029, 1516884, 135655, 1326571, 692790, + 1190347, 397690, 1163996, 793521, 1677407, 307698, 1644146, 476490, 480901, 939619, + 1485684, 1154733, 1255691, 3690, 219420, 282792, 439050, 145272, 576914, 712049, + 1304645, 573928, 1772866, 591113, 1393852, 1035141, 1950385, 1660238, 2048950, + 156755, 1608179, 1548683, 1672961, 305784, 1862688, 321105, 1025297, 452008, + 1005531, 843560, 1190325, 856621, 1117370, 1130365, 1527816, 49720, 747725, 88191, + 532153, 387221, 2046361, 562281, 1174973, 58051, 665523, 1087404, 1953295, 288049, + 2095453, 843437, 1391132, 71028, 684791, 905101, 983353, 869595, 952262, 1639398, + 1914119, 419401, 1543571, 1072120, 1589356, 1156347, 1293818, 1377687, 1462379, + 15074, 1544101, 82479, 277248, 608805, 980975, 627710, 2071408, 210587, 2050837, + 837017, 1832082, 1225589, 1331877, 1546243, 1710461, 263463, 1118404, 1070140, + 1965733, 573307, 1351499, 983779, 1981322, 606434, 1595416, 765946, 1106775, + 909539, 1338917, 1288935, 1474260, 360272, 448135, 848529, 1128620, 425058, + 1408729, 552440, 908024, 536460, 1031151, 1355099, 1690286, 758818, 965662, + 1568718, 1811172, 394608, 1421319, 528521, 983018, 1098180, 2033658, 1555936, + 2016847, 558752, 1627758, 1319354, 1973293, 844477, 2092434, 1252290, 1572207, + ], + ], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [ + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 4864, 1199590, 193812, 1249880, 714671, 1837456, 1535571, 1708457, 232772, 1454167, + 457525, 637002, 332079, 1501386, 718273, 1615866, 77066, 484710, 167020, 1302858, + 478350, 1147611, 1080786, 1765391, 192941, 989350, 946203, 2030362, 741168, + 1308632, 981242, 1278097, 37040, 342963, 569456, 668433, 603930, 933078, 698729, + 1622250, 64884, 1692110, 592064, 1876950, 1033063, 1611191, 1595251, 1620759, + 85174, 1366524, 780855, 1565061, 499859, 1083320, 659353, 2024776, 245240, 653208, + 610593, 1607404, 692246, 1235766, 1256907, 1797417, 50473, 131951, 1598065, + 1624695, 1111152, 1515152, 1388781, 1891337, 160469, 432196, 393547, 812762, + 176550, 1413868, 509501, 1271887, 318995, 1437575, 991689, 2006157, 539568, + 1719313, 794460, 1908039, 535401, 1235620, 1455510, 1626953, 1091062, 1782601, + 1467126, 1580102, 170783, 1348866, 1353402, 1491258, 772707, 1966772, 826506, + 1152761, 449546, 1960316, 1722182, 2080464, 585763, 1674095, 609247, 843509, + 172788, 1720636, 435897, 1922357, 500566, 1040796, 613483, 1370281, 256465, + 1230426, 769634, 1233698, 338802, 1256346, 1127412, 1606391, 28572, 1971791, + 1444448, 1918707, 97182, 1965648, 257599, 1202977, 93470, 966723, 905279, 1233897, + 113192, 1584168, 1161311, 2032889, 256253, 1722369, 1073588, 1855590, 346680, + 1584579, 1743391, 1934763, 312281, 1549608, 994193, 1602606, 387880, 725528, + 934703, 1266574, 65484, 576232, 286914, 1159487, 67947, 120057, 1292895, 1703617, + 78516, 561032, 324727, 964901, 207979, 1061901, 669782, 1333235, 206098, 1887692, + 556487, 1825223, 311381, 1950933, 346751, 900814, 428287, 460877, 839356, 2060346, + 812004, 1046964, 1855974, 2094549, 104606, 743195, 954206, 1215739, 741600, + 1861587, 743395, 1346010, 494866, 1045735, 519272, 678086, 1500989, 1559455, + 1804386, 1954175, 125590, 1395720, 187846, 573761, 537850, 1166248, 1075068, + 1546494, 227070, 2020913, 1609765, 1905314, 387408, 1087195, 506841, 1499006, + 139388, 1837001, 877345, 1809095, 1352203, 1418350, 1850889, 1990899, 280451, + 1765615, 510671, 1352059, 764606, 1764448, 926933, 1636061, 211736, 1525591, + 655004, 1579428, 751390, 1914755, 1108881, 1741132, 312225, 1280938, 1278149, + 1812658, 1150817, 1667245, 1332384, 1891064, 9140, 1966916, 777375, 1545630, + 490282, 1629876, 1169924, 1800146, 343120, 369449, 1598222, 1975938, 1275395, + 1417808, 1349883, 1709635, 248136, 1064213, 248531, 419620, 395583, 934164, 957401, + 1441780, 436644, 1282126, 1185318, 1225662, 793359, 1337722, 1738888, 1910757, + 77536, 946595, 193413, 1714318, 245074, 944172, 433226, 1320914, 251395, 474279, + 837087, 1103199, 297673, 606529, 657996, 1316189, 115773, 1039045, 677050, 1570537, + 293787, 644796, 1550861, 1957774, 601959, 1591943, 1745446, 2025220, 1240950, + 1633250, 1879694, 2058372, 60446, 1323729, 575091, 1342903, 807201, 1631118, + 980227, 1144536, 105511, 2067126, 618548, 1233793, 676117, 1129668, 1416791, + 1774930, 106539, 1150242, 1145224, 1200128, 805479, 1751155, 995027, 1982253, + 767482, 1244117, 944753, 2047440, 1095507, 1299247, 1460212, 2038990, 88843, + 530639, 646346, 1179485, 1044312, 1797756, 1456669, 1549970, 496644, 1959677, + 822589, 1619867, 671946, 2071372, 839018, 1504389, 115789, 175266, 1467790, + 1972030, 1513638, 1832563, 1563718, 1897413, 203986, 391698, 1340000, 1715426, + 1050764, 1249824, 1276431, 2015035, 62131, 1545007, 525280, 717401, 177592, 661746, + 348866, 1689238, 420259, 632760, 1244376, 1935559, 465968, 1601759, 1732708, + 1760778, 99049, 302037, 183591, 822581, 196077, 680305, 1585363, 2091013, 383849, + 437456, 571005, 1266155, 611968, 1457147, 868500, 1231186, 147047, 1237994, 240469, + 1095717, 267068, 1421991, 431598, 1915282, 445827, 525511, 1327175, 1774033, + 716456, 1233031, 1061413, 1122461, 206655, 572078, 924087, 1310789, 684611, + 1287277, 1773797, 1913313, 309030, 1878386, 473275, 1003742, 1260118, 1576334, + 1318332, 1864421, 121871, 254982, 1668894, 1981433, 720578, 1301056, 935612, + 1117955, 497019, 1086159, 879102, 2090471, 872907, 1471132, 1837356, 1946140, + 132945, 1223450, 983306, 1258235, 211937, 884317, 776845, 1230188, 256819, 1715032, + 1175550, 1373727, 546648, 1594562, 1079893, 2013428, 121905, 419443, 1270708, + 2001104, 231117, 1687963, 581051, 755823, 150994, 201680, 277505, 1571629, 210684, + 267883, 346662, 1369722, 210410, 450237, 889519, 1927159, 428761, 1261947, 1112506, + 2072336, 328126, 919149, 422720, 523141, 480263, 948885, 888698, 1228888, + ], + &[ + 23901, 617413, 355244, 1975506, 132019, 973669, 1226248, 1573681, 122421, 1065428, + 582124, 909665, 558651, 1926639, 991115, 1811101, 53484, 471380, 1166430, 1172099, + 768898, 1980136, 1308533, 2043797, 225770, 1560246, 282139, 1511423, 252319, + 1591786, 1281863, 1792727, 62057, 1262311, 152547, 1040143, 854194, 1671967, + 1041149, 1607891, 65257, 1134934, 1022871, 1271798, 1447250, 1983171, 1491959, + 1874069, 179856, 1219906, 1267725, 1654491, 735071, 939234, 766437, 900743, 430964, + 2070358, 1146633, 1654102, 694284, 1822630, 762226, 1448725, 43801, 1286582, + 1375355, 2031392, 904812, 1027586, 1334970, 1596137, 1178798, 1845585, 2024912, + 2075007, 1327104, 1704913, 1924277, 2050791, 213166, 509295, 797674, 1025109, + 844789, 1735309, 1228897, 1966440, 726212, 789029, 853855, 1647548, 924001, + 1849322, 1783876, 2001548, 302290, 1172889, 501308, 2003083, 742289, 1147883, + 808789, 1707834, 681978, 1238187, 1467872, 1679417, 854573, 1431629, 2053677, + 2085589, 473258, 1062845, 1237630, 1794734, 1037815, 2094316, 1495253, 1743060, + 772774, 1993150, 1098941, 1165463, 812798, 845435, 1604066, 1982214, 36051, 613993, + 1171428, 1458641, 282798, 328217, 1168775, 1871314, 238601, 1759957, 1307623, + 1970904, 281444, 294479, 348563, 1135891, 88431, 1319495, 453854, 657030, 343548, + 1764131, 1061870, 1704218, 260179, 345854, 1307098, 1823763, 602183, 1577697, + 1809954, 2027412, 51269, 57467, 1325003, 1335582, 1311661, 1392464, 1684355, + 2053786, 534916, 1468056, 1606406, 1802740, 1162295, 1458329, 1580464, 1802771, + 62963, 1265622, 1079825, 1523318, 697498, 1137974, 1153192, 1248533, 129604, + 1178809, 1073871, 1104755, 311330, 517501, 535836, 1379634, 40198, 647811, 675812, + 936605, 486329, 765127, 775567, 1414124, 613964, 1577815, 683996, 1689517, 996979, + 2083259, 1556180, 2058600, 210016, 1789429, 275149, 2088979, 726542, 1285888, + 939385, 1301015, 305279, 339990, 307825, 523852, 332361, 1577829, 914883, 1501568, + 67628, 390418, 842350, 1733296, 1075292, 1541991, 1118111, 1412896, 133657, 421233, + 334008, 1150183, 617485, 1236606, 787610, 1451290, 179174, 1512540, 554916, + 1239556, 543320, 623168, 668527, 836286, 288147, 724320, 632809, 1666001, 989270, + 1332112, 1491347, 1690153, 47022, 628361, 1666493, 1972048, 521980, 1695996, + 1230445, 1557971, 107339, 1669410, 441113, 1464999, 675537, 2082149, 967630, + 1727034, 73959, 1283937, 1221350, 1586488, 532533, 2054356, 1372304, 1965748, + 276873, 623244, 439869, 527242, 532370, 861699, 1128671, 1814005, 81006, 451273, + 906992, 1525688, 892238, 1673990, 1173231, 1289145, 361337, 1728127, 1240059, + 1744405, 800364, 1979466, 1166409, 1340859, 185949, 556989, 600344, 692557, 316208, + 1531398, 545306, 1748454, 225612, 1139376, 646851, 1064772, 1272849, 1363216, + 1646428, 1770462, 54905, 1005365, 805801, 1395824, 75735, 1795449, 912629, 1502495, + 188676, 351070, 828616, 1590802, 406154, 1290478, 1172204, 1600863, 130475, 598371, + 612509, 784904, 489833, 563167, 504536, 1887655, 963575, 1812104, 1618265, 1771485, + 982915, 1024970, 1728069, 1928492, 83664, 988553, 347128, 1880498, 212168, 1305561, + 1862267, 1899554, 194973, 1507295, 940166, 1752636, 251583, 2049162, 692749, + 762955, 118916, 1119413, 274739, 470862, 292880, 414786, 1218416, 1709535, 279622, + 1285940, 469442, 1987188, 675485, 740244, 1341026, 1852022, 59670, 1259557, 284173, + 2062010, 483778, 1897192, 1651813, 1718729, 388128, 957145, 1285996, 1831671, + 497806, 1832924, 559068, 784092, 170059, 1212079, 237095, 734694, 767545, 871328, + 1197522, 1369692, 221296, 1792535, 907705, 1233817, 571488, 2033222, 636603, + 1104823, 354658, 1213051, 1424476, 1684826, 643531, 1615184, 659947, 1061345, + 412620, 605072, 904987, 1590212, 1558823, 1587538, 1785968, 2088976, 452351, + 1338442, 460036, 616323, 857148, 1950194, 1087766, 1868668, 558337, 1358354, + 636084, 788194, 566027, 675396, 1825562, 2095311, 63285, 1910522, 709285, 1519032, + 167798, 474629, 225257, 1897035, 155141, 2043903, 959984, 1559487, 698805, 1800434, + 1062227, 1271147, 144322, 1580619, 1386291, 1521451, 852563, 957301, 1446230, + 1523630, 364335, 2071342, 1235960, 1310200, 449297, 601907, 634608, 2014188, 81019, + 839298, 474427, 1831684, 210231, 1370197, 1261151, 1816924, 329886, 699000, 372746, + 1891547, 671945, 1301809, 873541, 1034843, 377922, 680790, 1280670, 1510355, + 771245, 1966090, 1483940, 2001054, 426637, 1257587, 815969, 1277447, 631165, + 722596, 1283260, 1948358, + ], + ], + }, + TestVector { + params: Params { n: 200, k: 9 }, + input: b"block header", + nonce: [ + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 4069, 463738, 457648, 1094395, 353041, 845577, 1338264, 1960371, 269620, 856637, + 560213, 877722, 450359, 826410, 714399, 1983540, 126148, 1080255, 1138998, 1761600, + 427345, 1932802, 881882, 2068285, 447232, 829316, 903027, 1193156, 541454, 1521796, + 1078148, 1889459, 124993, 1347183, 581805, 1443350, 179226, 1577228, 651270, 1325494, + 683884, 1089322, 793100, 1463641, 1237884, 1941965, 1796434, 1863807, 230979, 828024, + 1428386, 1491953, 711293, 1450044, 951912, 1655589, 756010, 1309938, 1041860, 1350970, + 814780, 1539264, 1035122, 1455759, 6420, 774570, 1625325, 1729420, 753562, 1103469, + 829724, 1258289, 568974, 756682, 583406, 738962, 570441, 658047, 1879798, 1920132, + 288029, 716348, 771535, 1896895, 750194, 1355985, 1833382, 1937010, 445480, 519375, + 1044543, 1447869, 684723, 1795442, 760755, 1087503, 27203, 401767, 515774, 2096530, + 220510, 1531978, 682810, 1202695, 102739, 159328, 249040, 1376984, 714328, 1532836, + 1849127, 1900521, 64133, 118448, 1010819, 1789329, 1399572, 1772831, 1485143, 2060346, + 128615, 1309550, 1573721, 1746738, 757879, 1130173, 793709, 1847068, 44228, 228896, + 412976, 1276115, 293492, 737005, 1674045, 1944525, 331939, 1510392, 369242, 1077155, + 426460, 1673372, 431032, 1274877, 60341, 294808, 695707, 2027632, 240042, 1252879, + 1725057, 1913005, 313836, 526303, 515786, 1468016, 545971, 1193874, 875617, 1497527, + 112951, 733634, 1406066, 1866156, 476895, 1582936, 1613761, 1810957, 555593, 1637664, + 1205103, 2010551, 801136, 1350946, 1416652, 1881050, 251209, 1831040, 485897, 1321291, + 526290, 759360, 833919, 1197480, 275949, 437926, 1352279, 1907841, 1174293, 1232730, + 1591652, 1724868, 64129, 638812, 164647, 1927780, 226966, 1708268, 725951, 1398329, + 404993, 1261809, 933394, 1045040, 448233, 673005, 481885, 1180107, 135281, 1803975, + 1109497, 1803585, 532625, 1135255, 1119381, 1966487, 557305, 2051233, 615421, 1411567, + 610639, 1787653, 1241629, 1372160, 127977, 1274377, 556987, 822140, 886894, 1502085, + 1317350, 1334492, 221984, 990436, 1138067, 1488903, 525849, 1181021, 1763583, 1797031, + 391839, 736791, 1471289, 1549100, 483303, 750308, 1537541, 1566177, 659853, 1733147, + 864458, 1431423, 1528133, 1779129, 1945881, 2012012, 7510, 2047486, 1071115, 1190891, + 209477, 1579776, 452538, 1743654, 61983, 1504258, 1687339, 2091843, 336350, 524705, + 1133333, 1162168, 20622, 107880, 657023, 1441283, 352745, 1959673, 1253812, 1948787, + 57245, 2008612, 128314, 429920, 106537, 2057002, 2060106, 2090515, 15120, 1229368, + 1691769, 1924728, 90639, 562555, 626612, 1794414, 218678, 1909493, 980759, 1591385, + 714695, 1440327, 1320404, 1828751, 84296, 1186698, 440022, 1235700, 576284, 1694165, + 1568289, 2002405, 956662, 1163003, 1235075, 1791789, 1364965, 1669818, 1464088, + 1574228, 47568, 911259, 975879, 1880881, 98599, 303565, 1387907, 1576200, 92625, + 1959308, 333801, 1231633, 772862, 1284483, 1534241, 2013255, 74617, 1025792, 1135731, + 1451877, 473457, 1073612, 1045423, 1746842, 494445, 1703635, 1306798, 2003521, 949685, + 1927757, 1496880, 1857562, 161571, 1044916, 640458, 1053372, 719774, 1292407, 1131831, + 1741201, 374920, 1893201, 915430, 2028608, 679984, 1165018, 1065417, 2046590, 464822, + 1353906, 810126, 1433305, 882328, 1630701, 1519924, 1596363, 560399, 2011590, 1306858, + 1629806, 1287457, 1615912, 1712644, 1924703, 7943, 1336360, 1610242, 1822737, 474625, + 2088181, 491226, 2067675, 200602, 1281328, 1025938, 1898799, 697976, 1208680, 863807, + 1728279, 235165, 1134723, 1062949, 1834188, 1078943, 1832179, 1383472, 2066945, 278003, + 1551243, 328896, 1274678, 329449, 894853, 1807715, 2007736, 37987, 878450, 1503193, + 1885025, 82368, 1588984, 205464, 1958945, 352190, 581519, 977218, 1948110, 605118, + 1616091, 1170257, 1979591, 350625, 935037, 1171330, 1579630, 503298, 1825558, 667693, + 1930990, 406035, 475116, 840775, 1489431, 669255, 1365102, 1615968, 1714470, 37008, + 1765323, 694838, 1838285, 1254886, 1713979, 1376019, 1683443, 189227, 2040007, 212830, + 1846721, 328541, 675639, 1117953, 1173356, 174554, 1453766, 1016781, 1380416, 284565, + 916510, 770336, 1613349, 313668, 622218, 1284478, 1831739, 404140, 728071, 834045, + 2069311, 51289, 1600412, 412477, 2062451, 130962, 2053156, 465929, 709254, 117855, + 1392948, 136673, 374064, 121591, 1688669, 846029, 994804, 675059, 1448777, 1045103, + 1240057, 816462, 1499323, 1890697, 1896908, 826192, 959241, 833980, 1301853, 1149691, + 1227108, 1164702, 1520364, + ]], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [0; 32], + solutions: &[ + &[ + 592534, 16727887, 7453057, 25925862, 3112444, 22940957, 11281555, 31775301, + 1334223, 20443726, 11070438, 27290152, 4163350, 8213747, 9315696, 19739115, + 1204738, 23545872, 1776094, 13506389, 6697536, 27749507, 11388567, 14622750, + 4026870, 14622947, 8538779, 27133048, 11652285, 21221152, 22429643, 26529065, + ], + &[ + 893099, 8838806, 28398733, 31357275, 16596368, 25123776, 18326148, 31682454, + 924167, 27761424, 20546064, 30880786, 2931034, 11343701, 17011529, 26876917, + 4915668, 8097132, 17630254, 19828133, 5205703, 15014329, 13248799, 31182371, + 7887075, 22909946, 28758238, 31473391, 14791659, 33348545, 23436578, 26267836, + ], + &[ + 1948823, 6927010, 5051182, 16853572, 7567151, 8174004, 9697548, 30082354, 4126978, + 11219458, 12046931, 26296269, 6960990, 7513282, 15641819, 29553952, 2075509, + 3917596, 26506206, 32423116, 20172796, 26046597, 21008102, 32605968, 7733247, + 28951159, 19401173, 22240259, 12527355, 25816053, 26924563, 30167801, + ], + ], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 370176, 12908777, 1114179, 27164301, 6258700, 25604518, 23849263, 25098550, + 3874837, 22260519, 6421829, 20746376, 9010370, 14958301, 11701370, 20286183, + 3453033, 22917202, 17399732, 25201320, 12365907, 25599116, 12861876, 16581537, + 6291684, 17504753, 17494629, 17928408, 10119629, 10615318, 17868827, 20213583, + ], + &[ + 582263, 3783052, 2313999, 19478261, 1954747, 14513744, 5696384, 9983371, 770696, + 25399708, 2469656, 31060031, 1409486, 25011708, 6197016, 24800042, 4526208, + 20923264, 22532911, 24458988, 19054856, 19620962, 21223763, 25258694, 5339436, + 15681349, 11143785, 21451088, 8434833, 30577236, 27811311, 32663733, + ], + &[ + 635733, 25222820, 21014930, 29574076, 1000985, 5604521, 6974734, 19935829, 3041402, + 6498908, 27180330, 29522758, 3065872, 28403257, 5814381, 33337207, 1920304, + 16178841, 24948948, 25474220, 14568607, 30131615, 16282584, 28097350, 6277286, + 20609353, 13688741, 20448955, 8669674, 28133172, 18969419, 33014245, + ], + &[ + 1784595, 8730569, 2952232, 8311088, 3848398, 24535350, 6741302, 15864803, 5653320, + 16018355, 17835034, 29486303, 5823367, 20140719, 7233264, 33483182, 3117353, + 20053611, 3338894, 15846604, 7165521, 28162236, 8412349, 11018248, 7341551, + 18365873, 16351743, 22192468, 8662075, 9732645, 14238971, 22027130, + ], + &[ + 1791926, 8318711, 26251202, 32356717, 6997365, 25735638, 21576954, 30111878, + 3898334, 19905391, 5033991, 16030336, 5245813, 26522082, 5669465, 16635645, + 8277609, 22422842, 13069153, 29511907, 20365907, 23921315, 24326546, 32342867, + 12058103, 23466254, 19021516, 19329156, 15273796, 15658582, 15782868, 30953403, + ], + ], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[ + &[ + 631838, 32379030, 12115828, 15370934, 1071098, 28542374, 3749356, 23094728, + 1030877, 4102154, 3296262, 16677836, 7373429, 23553272, 13706818, 22718294, + 1600870, 2009968, 24236940, 26722391, 3296672, 30961726, 8361013, 20154770, + 3094572, 28709268, 6668495, 32281682, 11480232, 24080407, 21721486, 26351116, + ], + &[ + 2024468, 30788885, 16549044, 31105157, 10766172, 27803398, 14188383, 18350597, + 8340166, 12112117, 9771703, 16475394, 15638163, 19852515, 16164133, 21283881, + 3012382, 10164383, 4371003, 27267590, 4579840, 32997246, 17142413, 27563106, + 4959833, 19397820, 7489484, 26132602, 7957443, 27721944, 26669199, 27861139, + ], + &[ + 5378620, 10759970, 17807788, 29226493, 11529006, 22674062, 17704747, 23436136, + 10872275, 25829134, 15459988, 21678082, 17603136, 22657822, 22774669, 23569569, + 6492083, 20372131, 27398382, 33053456, 21986403, 23346432, 29327458, 33052852, + 13637567, 26765408, 26834306, 29589598, 17363888, 31088383, 17860587, 20580709, + ], + ], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [ + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[], + }, + TestVector { + params: Params { n: 144, k: 5 }, + input: b"block header", + nonce: [ + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + solutions: &[&[ + 911662, 22138389, 8210265, 31274530, 8029780, 20878462, 18256796, 24246891, 1935509, + 22500647, 18385714, 21573406, 5314654, 11279996, 7578895, 12470048, 3573289, 16335834, + 8230824, 25830081, 4414521, 7228234, 20359437, 21115537, 11102213, 12353678, 19277545, + 26893604, 13111251, 30773720, 14408826, 26047501, + ]], + }, +]; diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs new file mode 100644 index 0000000000..939b483782 --- /dev/null +++ b/components/equihash/src/verify.rs @@ -0,0 +1,529 @@ +//! Verification functions for the [Equihash] proof-of-work algorithm. +//! +//! [Equihash]: https://zips.z.cash/protocol/protocol.pdf#equihash + +use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams, State as Blake2bState}; +use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use std::fmt; +use std::io::Cursor; +use std::mem::size_of; + +#[derive(Clone, Copy)] +pub(crate) struct Params { + pub(crate) n: u32, + pub(crate) k: u32, +} + +#[derive(Clone)] +struct Node { + hash: Vec, + indices: Vec, +} + +impl Params { + fn new(n: u32, k: u32) -> Result { + // We place the following requirements on the parameters: + // - n is a multiple of 8, so the hash output has an exact byte length. + // - k >= 3 so the encoded solutions have an exact byte length. + // - k < n, so the collision bit length is at least 1. + // - n is a multiple of k + 1, so we have an integer collision bit length. + if (n % 8 == 0) && (k >= 3) && (k < n) && (n % (k + 1) == 0) { + Ok(Params { n, k }) + } else { + Err(Error(Kind::InvalidParams)) + } + } + fn indices_per_hash_output(&self) -> u32 { + 512 / self.n + } + fn hash_output(&self) -> u8 { + (self.indices_per_hash_output() * self.n / 8) as u8 + } + fn collision_bit_length(&self) -> usize { + (self.n / (self.k + 1)) as usize + } + fn collision_byte_length(&self) -> usize { + (self.collision_bit_length() + 7) / 8 + } + #[cfg(test)] + fn hash_length(&self) -> usize { + ((self.k as usize) + 1) * self.collision_byte_length() + } +} + +impl Node { + fn new(p: &Params, state: &Blake2bState, i: u32) -> Self { + let hash = generate_hash(state, i / p.indices_per_hash_output()); + let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize; + let end = start + (p.n as usize) / 8; + Node { + hash: expand_array(&hash.as_bytes()[start..end], p.collision_bit_length(), 0), + indices: vec![i], + } + } + + fn from_children(a: Node, b: Node, trim: usize) -> Self { + let hash: Vec<_> = a + .hash + .iter() + .zip(b.hash.iter()) + .skip(trim) + .map(|(a, b)| a ^ b) + .collect(); + let indices = if a.indices_before(&b) { + let mut indices = a.indices; + indices.extend(b.indices.iter()); + indices + } else { + let mut indices = b.indices; + indices.extend(a.indices.iter()); + indices + }; + Node { hash, indices } + } + + #[cfg(test)] + fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self { + let hash: Vec<_> = a + .hash + .iter() + .zip(b.hash.iter()) + .skip(trim) + .map(|(a, b)| a ^ b) + .collect(); + let mut indices = Vec::with_capacity(a.indices.len() + b.indices.len()); + if a.indices_before(b) { + indices.extend(a.indices.iter()); + indices.extend(b.indices.iter()); + } else { + indices.extend(b.indices.iter()); + indices.extend(a.indices.iter()); + } + Node { hash, indices } + } + + fn indices_before(&self, other: &Node) -> bool { + // Indices are serialized in big-endian so that integer + // comparison is equivalent to array comparison + self.indices[0] < other.indices[0] + } + + fn is_zero(&self, len: usize) -> bool { + self.hash.iter().take(len).all(|v| *v == 0) + } +} + +/// An Equihash solution failed to verify. +#[derive(Debug)] +pub struct Error(Kind); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Invalid solution: {}", self.0) + } +} + +impl std::error::Error for Error {} + +#[derive(Debug, PartialEq)] +pub(crate) enum Kind { + InvalidParams, + Collision, + OutOfOrder, + DuplicateIdxs, + NonZeroRootHash, +} + +impl fmt::Display for Kind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Kind::InvalidParams => f.write_str("invalid parameters"), + Kind::Collision => f.write_str("invalid collision length between StepRows"), + Kind::OutOfOrder => f.write_str("Index tree incorrectly ordered"), + Kind::DuplicateIdxs => f.write_str("duplicate indices"), + Kind::NonZeroRootHash => f.write_str("root hash of tree is non-zero"), + } + } +} + +fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2bState { + let mut personalization: Vec = Vec::from("ZcashPoW"); + personalization.write_u32::(n).unwrap(); + personalization.write_u32::(k).unwrap(); + + Blake2bParams::new() + .hash_length(digest_len as usize) + .personal(&personalization) + .to_state() +} + +fn generate_hash(base_state: &Blake2bState, i: u32) -> Blake2bHash { + let mut lei = [0u8; 4]; + (&mut lei[..]).write_u32::(i).unwrap(); + + let mut state = base_state.clone(); + state.update(&lei); + state.finalize() +} + +fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { + assert!(bit_len >= 8); + assert!(8 * size_of::() >= 7 + bit_len); + + let out_width = (bit_len + 7) / 8 + byte_pad; + let out_len = 8 * out_width * vin.len() / bit_len; + + // Shortcut for parameters where expansion is a no-op + if out_len == vin.len() { + return vin.to_vec(); + } + + let mut vout: Vec = vec![0; out_len]; + let bit_len_mask: u32 = (1 << bit_len) - 1; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + let mut acc_bits = 0; + let mut acc_value: u32 = 0; + + let mut j = 0; + for b in vin { + acc_value = (acc_value << 8) | u32::from(*b); + acc_bits += 8; + + // When we have bit_len or more bits in the accumulator, write the next + // output element. + if acc_bits >= bit_len { + acc_bits -= bit_len; + for x in byte_pad..out_width { + 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; + } + j += out_width; + } + } + + vout +} + +fn indices_from_minimal(p: Params, minimal: &[u8]) -> Result, Error> { + let c_bit_len = p.collision_bit_length(); + // Division is exact because k >= 3. + if minimal.len() != ((1 << p.k) * (c_bit_len + 1)) / 8 { + return Err(Error(Kind::InvalidParams)); + } + + assert!(((c_bit_len + 1) + 7) / 8 <= size_of::()); + let len_indices = 8 * size_of::() * minimal.len() / (c_bit_len + 1); + let byte_pad = size_of::() - ((c_bit_len + 1) + 7) / 8; + + let mut csr = Cursor::new(expand_array(minimal, c_bit_len + 1, byte_pad)); + let mut ret = Vec::with_capacity(len_indices); + + // Big-endian so that lexicographic array comparison is equivalent to integer + // comparison + while let Ok(i) = csr.read_u32::() { + ret.push(i); + } + + Ok(ret) +} + +fn has_collision(a: &Node, b: &Node, len: usize) -> bool { + a.hash + .iter() + .zip(b.hash.iter()) + .take(len) + .all(|(a, b)| a == b) +} + +fn distinct_indices(a: &Node, b: &Node) -> bool { + for i in &(a.indices) { + for j in &(b.indices) { + if i == j { + return false; + } + } + } + true +} + +fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> Result<(), Kind> { + if !has_collision(a, b, p.collision_byte_length()) { + Err(Kind::Collision) + } else if b.indices_before(a) { + Err(Kind::OutOfOrder) + } else if !distinct_indices(a, b) { + Err(Kind::DuplicateIdxs) + } else { + Ok(()) + } +} + +#[cfg(test)] +fn is_valid_solution_iterative( + p: Params, + input: &[u8], + nonce: &[u8], + indices: &[u32], +) -> Result<(), Error> { + let mut state = initialise_state(p.n, p.k, p.hash_output()); + state.update(input); + state.update(nonce); + + let mut rows = Vec::new(); + for i in indices { + rows.push(Node::new(&p, &state, *i)); + } + + let mut hash_len = p.hash_length(); + while rows.len() > 1 { + let mut cur_rows = Vec::new(); + for pair in rows.chunks(2) { + let a = &pair[0]; + let b = &pair[1]; + validate_subtrees(&p, a, b).map_err(Error)?; + cur_rows.push(Node::from_children_ref(a, b, p.collision_byte_length())); + } + rows = cur_rows; + hash_len -= p.collision_byte_length(); + } + + assert!(rows.len() == 1); + + if rows[0].is_zero(hash_len) { + Ok(()) + } else { + Err(Error(Kind::NonZeroRootHash)) + } +} + +fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Result { + if indices.len() > 1 { + let end = indices.len(); + let mid = end / 2; + let a = tree_validator(p, state, &indices[0..mid])?; + let b = tree_validator(p, state, &indices[mid..end])?; + validate_subtrees(p, &a, &b).map_err(Error)?; + Ok(Node::from_children(a, b, p.collision_byte_length())) + } else { + Ok(Node::new(&p, &state, indices[0])) + } +} + +fn is_valid_solution_recursive( + p: Params, + input: &[u8], + nonce: &[u8], + indices: &[u32], +) -> Result<(), Error> { + let mut state = initialise_state(p.n, p.k, p.hash_output()); + state.update(input); + state.update(nonce); + + let root = tree_validator(&p, &state, indices)?; + + // Hashes were trimmed, so only need to check remaining length + if root.is_zero(p.collision_byte_length()) { + Ok(()) + } else { + Err(Error(Kind::NonZeroRootHash)) + } +} + +/// Checks whether `soln` is a valid solution for `(input, nonce)` with the +/// parameters `(n, k)`. +pub fn is_valid_solution( + n: u32, + k: u32, + input: &[u8], + nonce: &[u8], + soln: &[u8], +) -> Result<(), Error> { + let p = Params::new(n, k)?; + let indices = indices_from_minimal(p, soln)?; + + // Recursive validation is faster + is_valid_solution_recursive(p, input, nonce, &indices) +} + +#[cfg(test)] +mod tests { + use super::{ + expand_array, indices_from_minimal, is_valid_solution, is_valid_solution_iterative, + is_valid_solution_recursive, Params, + }; + use crate::test_vectors::{INVALID_TEST_VECTORS, VALID_TEST_VECTORS}; + + #[test] + fn array_expansion() { + let check_array = |(bit_len, byte_pad), compact, expanded| { + assert_eq!(expand_array(compact, bit_len, byte_pad), expanded); + }; + + // 8 11-bit chunks, all-ones + check_array( + (11, 0), + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ], + &[ + 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff, + 0x07, 0xff, + ][..], + ); + // 8 21-bit chunks, alternating 1s and 0s + check_array( + (21, 0), + &[ + 0xaa, 0xaa, 0xad, 0x55, 0x55, 0x6a, 0xaa, 0xab, 0x55, 0x55, 0x5a, 0xaa, 0xaa, 0xd5, + 0x55, 0x56, 0xaa, 0xaa, 0xb5, 0x55, 0x55, + ], + &[ + 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, + 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, 0x15, 0x55, 0x55, + ][..], + ); + // 8 21-bit chunks, based on example in the spec + check_array( + (21, 0), + &[ + 0x00, 0x02, 0x20, 0x00, 0x0a, 0x7f, 0xff, 0xfe, 0x00, 0x12, 0x30, 0x22, 0xb3, 0x82, + 0x26, 0xac, 0x19, 0xbd, 0xf2, 0x34, 0x56, + ], + &[ + 0x00, 0x00, 0x44, 0x00, 0x00, 0x29, 0x1f, 0xff, 0xff, 0x00, 0x01, 0x23, 0x00, 0x45, + 0x67, 0x00, 0x89, 0xab, 0x00, 0xcd, 0xef, 0x12, 0x34, 0x56, + ][..], + ); + // 16 14-bit chunks, alternating 11s and 00s + check_array( + (14, 0), + &[ + 0xcc, 0xcf, 0x33, 0x3c, 0xcc, 0xf3, 0x33, 0xcc, 0xcf, 0x33, 0x3c, 0xcc, 0xf3, 0x33, + 0xcc, 0xcf, 0x33, 0x3c, 0xcc, 0xf3, 0x33, 0xcc, 0xcf, 0x33, 0x3c, 0xcc, 0xf3, 0x33, + ], + &[ + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, + ][..], + ); + // 8 11-bit chunks, all-ones, 2-byte padding + check_array( + (11, 2), + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ], + &[ + 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, + 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, + 0x00, 0x00, 0x07, 0xff, + ][..], + ); + } + + #[test] + fn minimal_solution_repr() { + let check_repr = |minimal, indices| { + assert_eq!( + indices_from_minimal(Params { n: 80, k: 3 }, minimal).unwrap(), + indices, + ); + }; + + // The solutions here are not intended to be valid. + check_repr( + &[ + 0x00, 0x00, 0x08, 0x00, 0x00, 0x40, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x80, + 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x01, + ], + &[1, 1, 1, 1, 1, 1, 1, 1], + ); + check_repr( + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ], + &[ + 2097151, 2097151, 2097151, 2097151, 2097151, 2097151, 2097151, 2097151, + ], + ); + check_repr( + &[ + 0x0f, 0xff, 0xf8, 0x00, 0x20, 0x03, 0xff, 0xfe, 0x00, 0x08, 0x00, 0xff, 0xff, 0x80, + 0x02, 0x00, 0x3f, 0xff, 0xe0, 0x00, 0x80, + ], + &[131071, 128, 131071, 128, 131071, 128, 131071, 128], + ); + check_repr( + &[ + 0x00, 0x02, 0x20, 0x00, 0x0a, 0x7f, 0xff, 0xfe, 0x00, 0x4d, 0x10, 0x01, 0x4c, 0x80, + 0x0f, 0xfc, 0x00, 0x00, 0x2f, 0xff, 0xff, + ], + &[68, 41, 2097151, 1233, 665, 1023, 1, 1048575], + ); + } + + #[test] + fn valid_test_vectors() { + for tv in VALID_TEST_VECTORS { + for soln in tv.solutions { + is_valid_solution_iterative(tv.params, tv.input, &tv.nonce, soln).unwrap(); + is_valid_solution_recursive(tv.params, tv.input, &tv.nonce, soln).unwrap(); + } + } + } + + #[test] + fn invalid_test_vectors() { + for tv in INVALID_TEST_VECTORS { + assert_eq!( + is_valid_solution_iterative(tv.params, tv.input, &tv.nonce, &tv.solution) + .unwrap_err() + .0, + tv.error + ); + assert_eq!( + is_valid_solution_recursive(tv.params, tv.input, &tv.nonce, &tv.solution) + .unwrap_err() + .0, + tv.error + ); + } + } + + #[test] + fn all_bits_matter() { + // Initialize the state according to one of the valid test vectors. + let n = 96; + let k = 5; + let input = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem."; + let nonce = [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ]; + let soln = &[ + 0x04, 0x6a, 0x8e, 0xd4, 0x51, 0xa2, 0x19, 0x73, 0x32, 0xe7, 0x1f, 0x39, 0xdb, 0x9c, + 0x79, 0xfb, 0xf9, 0x3f, 0xc1, 0x44, 0x3d, 0xa5, 0x8f, 0xb3, 0x8d, 0x05, 0x99, 0x17, + 0x21, 0x16, 0xd5, 0x55, 0xb1, 0xb2, 0x1f, 0x32, 0x70, 0x5c, 0xe9, 0x98, 0xf6, 0x0d, + 0xa8, 0x52, 0xf7, 0x7f, 0x0e, 0x7f, 0x4d, 0x63, 0xfc, 0x2d, 0xd2, 0x30, 0xa3, 0xd9, + 0x99, 0x53, 0xa0, 0x78, 0x7d, 0xfe, 0xfc, 0xab, 0x34, 0x1b, 0xde, 0xc8, + ]; + + // Prove that the solution is valid. + is_valid_solution(n, k, input, &nonce, soln).unwrap(); + + // Changing any single bit of the encoded solution should make it invalid. + for i in 0..soln.len() * 8 { + let mut mutated = soln.to_vec(); + mutated[i / 8] ^= 1 << (i % 8); + is_valid_solution(n, k, input, &nonce, &mutated).unwrap_err(); + } + } +} diff --git a/ff/.gitignore b/ff/.gitignore deleted file mode 100644 index 4308d82204..0000000000 --- a/ff/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -target/ -**/*.rs.bk -Cargo.lock diff --git a/ff/Cargo.toml b/ff/Cargo.toml deleted file mode 100644 index 9dbf514ca3..0000000000 --- a/ff/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "ff" -version = "0.6.0" -authors = ["Sean Bowe "] -description = "Library for building and interfacing with finite fields" -readme = "README.md" -documentation = "https://docs.rs/ff/" -homepage = "https://github.com/ebfull/ff" -license = "MIT/Apache-2.0" -repository = "https://github.com/ebfull/ff" -edition = "2018" - -[dependencies] -byteorder = { version = "1", optional = true } -ff_derive = { version = "0.6", path = "ff_derive", optional = true } -rand_core = { version = "0.5", default-features = false } -subtle = { version = "2.2.1", default-features = false, features = ["i128"] } - -[features] -default = ["std"] -derive = ["ff_derive"] -std = ["byteorder"] - -[badges] -maintenance = { status = "actively-developed" } diff --git a/ff/LICENSE-APACHE b/ff/LICENSE-APACHE deleted file mode 100644 index 1e5006dc14..0000000000 --- a/ff/LICENSE-APACHE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - diff --git a/ff/LICENSE-MIT b/ff/LICENSE-MIT deleted file mode 100644 index ed3a13fdd9..0000000000 --- a/ff/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -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 -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. diff --git a/ff/README.md b/ff/README.md deleted file mode 100644 index 57ef693f35..0000000000 --- a/ff/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# 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.5" -``` - -The `ff` crate contains `Field`, `PrimeField`, `PrimeFieldRepr` and `SqrtField` traits. -See the **[documentation](https://docs.rs/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. diff --git a/ff/ff_derive/Cargo.toml b/ff/ff_derive/Cargo.toml deleted file mode 100644 index 4adf28b31c..0000000000 --- a/ff/ff_derive/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "ff_derive" -version = "0.6.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -description = "Procedural macro library used to build custom prime field implementations" -documentation = "https://docs.rs/ff/" -homepage = "https://github.com/ebfull/ff" -license = "MIT/Apache-2.0" -repository = "https://github.com/ebfull/ff" -edition = "2018" - -[lib] -proc-macro = true - -[dependencies] -addchain = "0.1" -num-bigint = "0.2" -num-traits = "0.2" -num-integer = "0.1" -proc-macro2 = "1" -quote = "1" -syn = "1" - -[badges] -maintenance = { status = "passively-maintained" } diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs deleted file mode 100644 index 121c296dc7..0000000000 --- a/ff/ff_derive/src/lib.rs +++ /dev/null @@ -1,1140 +0,0 @@ -#![recursion_limit = "1024"] - -extern crate proc_macro; -extern crate proc_macro2; - -use num_bigint::BigUint; -use num_integer::Integer; -use num_traits::{One, ToPrimitive, Zero}; -use quote::quote; -use quote::TokenStreamExt; -use std::str::FromStr; - -mod pow_fixed; - -#[proc_macro_derive(PrimeField, attributes(PrimeFieldModulus, PrimeFieldGenerator))] -pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - // Parse the type definition - let ast: syn::DeriveInput = syn::parse(input).unwrap(); - - // The struct we're deriving for is a wrapper around a "Repr" type we must construct. - let repr_ident = fetch_wrapped_ident(&ast.data) - .expect("PrimeField derive only operates over tuple structs of a single item"); - - // We're given the modulus p of the prime field - let modulus: BigUint = fetch_attr("PrimeFieldModulus", &ast.attrs) - .expect("Please supply a PrimeFieldModulus attribute") - .parse() - .expect("PrimeFieldModulus should be a number"); - - // We may be provided with a generator of p - 1 order. It is required that this generator be quadratic - // nonresidue. - let generator: BigUint = fetch_attr("PrimeFieldGenerator", &ast.attrs) - .expect("Please supply a PrimeFieldGenerator attribute") - .parse() - .expect("PrimeFieldGenerator should be a number"); - - // The arithmetic in this library only works if the modulus*2 is smaller than the backing - // representation. Compute the number of limbs we need. - let mut limbs = 1; - { - let mod2 = (&modulus) << 1; // modulus * 2 - let mut cur = BigUint::one() << 64; // always 64-bit limbs for now - while cur < mod2 { - limbs += 1; - cur <<= 64; - } - } - - let mut gen = proc_macro2::TokenStream::new(); - - let (constants_impl, sqrt_impl) = - prime_field_constants_and_sqrt(&ast.ident, &repr_ident, &modulus, limbs, generator); - - gen.extend(constants_impl); - gen.extend(prime_field_repr_impl(&repr_ident, limbs)); - gen.extend(prime_field_impl(&ast.ident, &repr_ident, &modulus, limbs)); - gen.extend(sqrt_impl); - - // Return the generated impl - gen.into() -} - -/// Fetches the ident being wrapped by the type we're deriving. -fn fetch_wrapped_ident(body: &syn::Data) -> Option { - if let syn::Data::Struct(ref variant_data) = body { - if let syn::Fields::Unnamed(ref fields) = variant_data.fields { - if fields.unnamed.len() == 1 { - if let syn::Type::Path(ref path) = fields.unnamed[0].ty { - if path.path.segments.len() == 1 { - return Some(path.path.segments[0].ident.clone()); - } - } - } - } - }; - - None -} - -/// Fetch an attribute string from the derived struct. -fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option { - for attr in attrs { - if let Ok(meta) = attr.parse_meta() { - match meta { - syn::Meta::NameValue(nv) => { - if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) { - match nv.lit { - syn::Lit::Str(ref s) => return Some(s.value()), - _ => { - panic!("attribute {} should be a string", name); - } - } - } - } - _ => { - panic!("attribute {} should be a string", name); - } - } - } - } - - None -} - -// Implement PrimeFieldRepr for the wrapped ident `repr` with `limbs` limbs. -fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenStream { - quote! { - #[derive(Copy, Clone, PartialEq, Eq, Default)] - pub struct #repr(pub [u64; #limbs]); - - impl ::core::fmt::Debug for #repr - { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "0x")?; - for i in self.0.iter().rev() { - write!(f, "{:016x}", *i)?; - } - - Ok(()) - } - } - - impl ::core::fmt::Display for #repr { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "0x")?; - for i in self.0.iter().rev() { - write!(f, "{:016x}", *i)?; - } - - Ok(()) - } - } - - impl AsRef<[u64]> for #repr { - #[inline(always)] - fn as_ref(&self) -> &[u64] { - &self.0 - } - } - - impl AsMut<[u64]> for #repr { - #[inline(always)] - fn as_mut(&mut self) -> &mut [u64] { - &mut self.0 - } - } - - impl From for #repr { - #[inline(always)] - fn from(val: u64) -> #repr { - use core::default::Default; - - let mut repr = Self::default(); - repr.0[0] = val; - repr - } - } - - impl Ord for #repr { - #[inline(always)] - fn cmp(&self, other: &#repr) -> ::core::cmp::Ordering { - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if a < b { - return ::core::cmp::Ordering::Less - } else if a > b { - return ::core::cmp::Ordering::Greater - } - } - - ::core::cmp::Ordering::Equal - } - } - - impl PartialOrd for #repr { - #[inline(always)] - fn partial_cmp(&self, other: &#repr) -> Option<::core::cmp::Ordering> { - Some(self.cmp(other)) - } - } - - impl ::ff::PrimeFieldRepr for #repr { - #[inline(always)] - fn is_odd(&self) -> bool { - self.0[0] & 1 == 1 - } - - #[inline(always)] - fn is_even(&self) -> bool { - !self.is_odd() - } - - #[inline(always)] - fn is_zero(&self) -> bool { - self.0.iter().all(|&e| e == 0) - } - - #[inline(always)] - fn shr(&mut self, mut n: u32) { - if n as usize >= 64 * #limbs { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - ::core::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn div2(&mut self) { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << 63; - *i >>= 1; - *i |= t; - t = t2; - } - } - - #[inline(always)] - fn mul2(&mut self) { - let mut last = 0; - for i in &mut self.0 { - let tmp = *i >> 63; - *i <<= 1; - *i |= last; - last = tmp; - } - } - - #[inline(always)] - fn shl(&mut self, mut n: u32) { - if n as usize >= 64 * #limbs { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in &mut self.0 { - ::core::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in &mut self.0 { - let t2 = *i >> (64 - n); - *i <<= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn num_bits(&self) -> u32 { - let mut ret = (#limbs as u32) * 64; - for i in self.0.iter().rev() { - let leading = i.leading_zeros(); - ret -= leading; - if leading != 64 { - break; - } - } - - ret - } - - #[inline(always)] - fn add_nocarry(&mut self, other: &#repr) { - let mut carry = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::adc(*a, *b, &mut carry); - } - } - - #[inline(always)] - fn sub_noborrow(&mut self, other: &#repr) { - let mut borrow = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::sbb(*a, *b, &mut borrow); - } - } - } - } -} - -/// Convert BigUint into a vector of 64-bit limbs. -fn biguint_to_real_u64_vec(mut v: BigUint, limbs: usize) -> Vec { - let m = BigUint::one() << 64; - let mut ret = vec![]; - - while v > BigUint::zero() { - ret.push((&v % &m).to_u64().unwrap()); - v >>= 64; - } - - while ret.len() < limbs { - ret.push(0); - } - - assert!(ret.len() == limbs); - - ret -} - -/// Convert BigUint into a tokenized vector of 64-bit limbs. -fn biguint_to_u64_vec(v: BigUint, limbs: usize) -> proc_macro2::TokenStream { - let ret = biguint_to_real_u64_vec(v, limbs); - quote!([#(#ret,)*]) -} - -fn biguint_num_bits(mut v: BigUint) -> u32 { - let mut bits = 0; - - while v != BigUint::zero() { - v >>= 1; - bits += 1; - } - - bits -} - -/// BigUint modular exponentiation by square-and-multiply. -fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { - let mut ret = BigUint::one(); - - for i in exp - .to_bytes_be() - .into_iter() - .flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd())) - { - ret = (&ret * &ret) % modulus; - if i { - ret = (ret * &base) % modulus; - } - } - - ret -} - -#[test] -fn test_exp() { - assert_eq!( - exp( - BigUint::from_str("4398572349857239485729348572983472345").unwrap(), - &BigUint::from_str("5489673498567349856734895").unwrap(), - &BigUint::from_str( - "52435875175126190479447740508185965837690552500527637822603658699938581184513" - ) - .unwrap() - ), - BigUint::from_str( - "4371221214068404307866768905142520595925044802278091865033317963560480051536" - ) - .unwrap() - ); -} - -fn prime_field_constants_and_sqrt( - name: &syn::Ident, - repr: &syn::Ident, - modulus: &BigUint, - limbs: usize, - generator: BigUint, -) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { - let modulus_num_bits = biguint_num_bits(modulus.clone()); - - // The number of bits we should "shave" from a randomly sampled reputation, i.e., - // if our modulus is 381 bits and our representation is 384 bits, we should shave - // 3 bits from the beginning of a randomly sampled 384 bit representation to - // reduce the cost of rejection sampling. - let repr_shave_bits = (64 * limbs as u32) - biguint_num_bits(modulus.clone()); - - // Compute R = 2**(64 * limbs) mod m - let r = (BigUint::one() << (limbs * 64)) % modulus; - - // modulus - 1 = 2^s * t - let mut s: u32 = 0; - let mut t = modulus - BigUint::from_str("1").unwrap(); - while t.is_even() { - t >>= 1; - s += 1; - } - - // Compute 2^s root of unity given the generator - let root_of_unity = - biguint_to_u64_vec((exp(generator.clone(), &t, &modulus) * &r) % modulus, limbs); - let generator = biguint_to_u64_vec((generator.clone() * &r) % modulus, limbs); - - let sqrt_impl = if (modulus % BigUint::from_str("4").unwrap()) - == BigUint::from_str("3").unwrap() - { - // Addition chain for (r + 1) // 4 - let mod_plus_1_over_4 = pow_fixed::generate( - "e! {self}, - (modulus + BigUint::from_str("1").unwrap()) >> 2, - ); - - quote! { - impl ::ff::SqrtField for #name { - fn sqrt(&self) -> ::subtle::CtOption { - use ::subtle::ConstantTimeEq; - - // Because r = 3 (mod 4) - // sqrt can be done with only one exponentiation, - // via the computation of self^((r + 1) // 4) (mod r) - let sqrt = { - #mod_plus_1_over_4 - }; - - ::subtle::CtOption::new( - sqrt, - (sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root. - ) - } - } - } - } else if (modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() { - // Addition chain for (t - 1) // 2 - let t_minus_1_over_2 = pow_fixed::generate("e! {self}, (&t - BigUint::one()) >> 1); - - quote! { - impl ::ff::SqrtField for #name { - fn sqrt(&self) -> ::subtle::CtOption { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - use ::subtle::{ConditionallySelectable, ConstantTimeEq}; - - // w = self^((t - 1) // 2) - let w = { - #t_minus_1_over_2 - }; - - let mut v = S; - let mut x = *self * &w; - let mut b = x * &w; - - // Initialize z as the 2^S root of unity. - let mut z = #name(ROOT_OF_UNITY); - - for max_v in (1..=S).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: ::subtle::Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&#name::one()); - let squared = #name::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = #name::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = #name::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = #name::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * &z; - x = #name::conditional_select(&result, &x, b.ct_eq(&#name::one())); - z = z.square(); - b *= &z; - v = k; - } - - ::subtle::CtOption::new( - x, - (x * &x).ct_eq(self), // Only return Some if it's the square root. - ) - } - } - } - } else { - quote! {} - }; - - // Compute R^2 mod m - let r2 = biguint_to_u64_vec((&r * &r) % modulus, limbs); - - let r = biguint_to_u64_vec(r, limbs); - let modulus = biguint_to_real_u64_vec(modulus.clone(), limbs); - - // Compute -m^-1 mod 2**64 by exponentiating by totient(2**64) - 1 - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(modulus[0]); - } - inv = inv.wrapping_neg(); - - ( - quote! { - /// This is the modulus m of the prime field - const MODULUS: #repr = #repr([#(#modulus,)*]); - - /// The number of bits needed to represent the modulus. - const MODULUS_BITS: u32 = #modulus_num_bits; - - /// The number of bits that must be shaved from the beginning of - /// the representation when randomly sampling. - const REPR_SHAVE_BITS: u32 = #repr_shave_bits; - - /// 2^{limbs*64} mod m - const R: #repr = #repr(#r); - - /// 2^{limbs*64*2} mod m - const R2: #repr = #repr(#r2); - - /// -(m^{-1} mod m) mod m - const INV: u64 = #inv; - - /// Multiplicative generator of `MODULUS` - 1 order, also quadratic - /// nonresidue. - const GENERATOR: #repr = #repr(#generator); - - /// 2^s * t = MODULUS - 1 with t odd - const S: u32 = #s; - - /// 2^s root of unity computed by GENERATOR^t - const ROOT_OF_UNITY: #repr = #repr(#root_of_unity); - }, - sqrt_impl, - ) -} - -/// Implement PrimeField for the derived type. -fn prime_field_impl( - name: &syn::Ident, - repr: &syn::Ident, - modulus: &BigUint, - limbs: usize, -) -> proc_macro2::TokenStream { - // Returns r{n} as an ident. - fn get_temp(n: usize) -> syn::Ident { - syn::Ident::new(&format!("r{}", n), proc_macro2::Span::call_site()) - } - - // The parameter list for the mont_reduce() internal method. - // r0: u64, mut r1: u64, mut r2: u64, ... - let mut mont_paramlist = proc_macro2::TokenStream::new(); - mont_paramlist.append_separated( - (0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| { - if i != 0 { - quote! {mut #x: u64} - } else { - quote! {#x: u64} - } - }), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - // Implement montgomery reduction for some number of limbs - fn mont_impl(limbs: usize) -> proc_macro2::TokenStream { - let mut gen = proc_macro2::TokenStream::new(); - - for i in 0..limbs { - { - let temp = get_temp(i); - gen.extend(quote! { - let k = #temp.wrapping_mul(INV); - let mut carry = 0; - ::ff::mac_with_carry(#temp, k, MODULUS.0[0], &mut carry); - }); - } - - for j in 1..limbs { - let temp = get_temp(i + j); - gen.extend(quote! { - #temp = ::ff::mac_with_carry(#temp, k, MODULUS.0[#j], &mut carry); - }); - } - - let temp = get_temp(i + limbs); - - if i == 0 { - gen.extend(quote! { - #temp = ::ff::adc(#temp, 0, &mut carry); - }); - } else { - gen.extend(quote! { - #temp = ::ff::adc(#temp, carry2, &mut carry); - }); - } - - if i != (limbs - 1) { - gen.extend(quote! { - let carry2 = carry; - }); - } - } - - for i in 0..limbs { - let temp = get_temp(limbs + i); - - gen.extend(quote! { - (self.0).0[#i] = #temp; - }); - } - - gen - } - - fn sqr_impl(a: proc_macro2::TokenStream, limbs: usize) -> proc_macro2::TokenStream { - let mut gen = proc_macro2::TokenStream::new(); - - for i in 0..(limbs - 1) { - gen.extend(quote! { - let mut carry = 0; - }); - - for j in (i + 1)..limbs { - let temp = get_temp(i + j); - if i == 0 { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#j], &mut carry); - }); - } else { - gen.extend(quote!{ - let #temp = ::ff::mac_with_carry(#temp, (#a.0).0[#i], (#a.0).0[#j], &mut carry); - }); - } - } - - let temp = get_temp(i + limbs); - - gen.extend(quote! { - let #temp = carry; - }); - } - - for i in 1..(limbs * 2) { - let temp0 = get_temp(limbs * 2 - i); - let temp1 = get_temp(limbs * 2 - i - 1); - - if i == 1 { - gen.extend(quote! { - let #temp0 = #temp1 >> 63; - }); - } else if i == (limbs * 2 - 1) { - gen.extend(quote! { - let #temp0 = #temp0 << 1; - }); - } else { - gen.extend(quote! { - let #temp0 = (#temp0 << 1) | (#temp1 >> 63); - }); - } - } - - gen.extend(quote! { - let mut carry = 0; - }); - - for i in 0..limbs { - let temp0 = get_temp(i * 2); - let temp1 = get_temp(i * 2 + 1); - if i == 0 { - gen.extend(quote! { - let #temp0 = ::ff::mac_with_carry(0, (#a.0).0[#i], (#a.0).0[#i], &mut carry); - }); - } else { - gen.extend(quote!{ - let #temp0 = ::ff::mac_with_carry(#temp0, (#a.0).0[#i], (#a.0).0[#i], &mut carry); - }); - } - - gen.extend(quote! { - let #temp1 = ::ff::adc(#temp1, 0, &mut carry); - }); - } - - let mut mont_calling = proc_macro2::TokenStream::new(); - mont_calling.append_separated( - (0..(limbs * 2)).map(get_temp), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - gen.extend(quote! { - let mut ret = *self; - ret.mont_reduce(#mont_calling); - ret - }); - - gen - } - - fn mul_impl( - a: proc_macro2::TokenStream, - b: proc_macro2::TokenStream, - limbs: usize, - ) -> proc_macro2::TokenStream { - let mut gen = proc_macro2::TokenStream::new(); - - for i in 0..limbs { - gen.extend(quote! { - let mut carry = 0; - }); - - for j in 0..limbs { - let temp = get_temp(i + j); - - if i == 0 { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, (#a.0).0[#i], (#b.0).0[#j], &mut carry); - }); - } else { - gen.extend(quote!{ - let #temp = ::ff::mac_with_carry(#temp, (#a.0).0[#i], (#b.0).0[#j], &mut carry); - }); - } - } - - let temp = get_temp(i + limbs); - - gen.extend(quote! { - let #temp = carry; - }); - } - - let mut mont_calling = proc_macro2::TokenStream::new(); - mont_calling.append_separated( - (0..(limbs * 2)).map(get_temp), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - gen.extend(quote! { - self.mont_reduce(#mont_calling); - }); - - gen - } - - /// Generates an implementation of multiplicative inversion within the target prime - /// field. - fn inv_impl( - a: proc_macro2::TokenStream, - name: &syn::Ident, - modulus: &BigUint, - ) -> proc_macro2::TokenStream { - // Addition chain for p - 2 - let mod_minus_2 = pow_fixed::generate(&a, modulus - BigUint::from(2u64)); - - quote! { - use ::subtle::ConstantTimeEq; - - // By Euler's theorem, if `a` is coprime to `p` (i.e. `gcd(a, p) = 1`), then: - // a^-1 ≡ a^(phi(p) - 1) mod p - // - // `ff_derive` requires that `p` is prime; in this case, `phi(p) = p - 1`, and - // thus: - // a^-1 ≡ a^(p - 2) mod p - let inv = { - #mod_minus_2 - }; - - ::subtle::CtOption::new(inv, !#a.ct_eq(&#name::zero())) - } - } - - let squaring_impl = sqr_impl(quote! {self}, limbs); - let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs); - let invert_impl = inv_impl(quote! {self}, name, modulus); - let montgomery_impl = mont_impl(limbs); - - // (self.0).0[0].ct_eq(&(other.0).0[0]) & (self.0).0[1].ct_eq(&(other.0).0[1]) & ... - let mut ct_eq_impl = proc_macro2::TokenStream::new(); - ct_eq_impl.append_separated( - (0..limbs).map(|i| quote! { (self.0).0[#i].ct_eq(&(other.0).0[#i]) }), - proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone), - ); - - // (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ... - let mut into_repr_params = proc_macro2::TokenStream::new(); - into_repr_params.append_separated( - (0..limbs) - .map(|i| quote! { (self.0).0[#i] }) - .chain((0..limbs).map(|_| quote! {0})), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - let top_limb_index = limbs - 1; - - quote! { - impl ::core::marker::Copy for #name { } - - impl ::core::clone::Clone for #name { - fn clone(&self) -> #name { - *self - } - } - - impl ::core::default::Default for #name { - fn default() -> #name { - #name::zero() - } - } - - impl ::subtle::ConstantTimeEq for #name { - fn ct_eq(&self, other: &#name) -> ::subtle::Choice { - #ct_eq_impl - } - } - - impl ::core::cmp::PartialEq for #name { - fn eq(&self, other: &#name) -> bool { - self.0 == other.0 - } - } - - impl ::core::cmp::Eq for #name { } - - impl ::core::fmt::Debug for #name - { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:?})", stringify!(#name), self.into_repr()) - } - } - - /// Elements are ordered lexicographically. - impl Ord for #name { - #[inline(always)] - fn cmp(&self, other: &#name) -> ::core::cmp::Ordering { - self.into_repr().cmp(&other.into_repr()) - } - } - - impl PartialOrd for #name { - #[inline(always)] - fn partial_cmp(&self, other: &#name) -> Option<::core::cmp::Ordering> { - Some(self.cmp(other)) - } - } - - impl ::core::fmt::Display for #name { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({})", stringify!(#name), self.into_repr()) - } - } - - impl From<#name> for #repr { - fn from(e: #name) -> #repr { - e.into_repr() - } - } - - impl ::subtle::ConditionallySelectable for #name { - fn conditional_select(a: &#name, b: &#name, choice: ::subtle::Choice) -> #name { - let mut res = [0u64; #limbs]; - for i in 0..#limbs { - res[i] = u64::conditional_select(&(a.0).0[i], &(b.0).0[i], choice); - } - #name(#repr(res)) - } - } - - impl ::core::ops::Neg for #name { - type Output = #name; - - #[inline] - fn neg(self) -> #name { - let mut ret = self; - if !ret.is_zero() { - let mut tmp = MODULUS; - tmp.sub_noborrow(&ret.0); - ret.0 = tmp; - } - ret - } - } - - impl<'r> ::core::ops::Add<&'r #name> for #name { - type Output = #name; - - #[inline] - fn add(self, other: &#name) -> #name { - let mut ret = self; - ret.add_assign(other); - ret - } - } - - impl ::core::ops::Add for #name { - type Output = #name; - - #[inline] - fn add(self, other: #name) -> Self { - self + &other - } - } - - impl<'r> ::core::ops::AddAssign<&'r #name> for #name { - #[inline] - fn add_assign(&mut self, other: &#name) { - // This cannot exceed the backing capacity. - self.0.add_nocarry(&other.0); - - // However, it may need to be reduced. - self.reduce(); - } - } - - impl ::core::ops::AddAssign for #name { - #[inline] - fn add_assign(&mut self, other: #name) { - self.add_assign(&other); - } - } - - impl<'r> ::core::ops::Sub<&'r #name> for #name { - type Output = #name; - - #[inline] - fn sub(self, other: &#name) -> Self { - let mut ret = self; - ret.sub_assign(other); - ret - } - } - - impl ::core::ops::Sub for #name { - type Output = #name; - - #[inline] - fn sub(self, other: #name) -> Self { - self - &other - } - } - - impl<'r> ::core::ops::SubAssign<&'r #name> for #name { - #[inline] - fn sub_assign(&mut self, other: &#name) { - // If `other` is larger than `self`, we'll need to add the modulus to self first. - if other.0 > self.0 { - self.0.add_nocarry(&MODULUS); - } - - self.0.sub_noborrow(&other.0); - } - } - - impl ::core::ops::SubAssign for #name { - #[inline] - fn sub_assign(&mut self, other: #name) { - self.sub_assign(&other); - } - } - - impl<'r> ::core::ops::Mul<&'r #name> for #name { - type Output = #name; - - #[inline] - fn mul(self, other: &#name) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } - } - - impl ::core::ops::Mul for #name { - type Output = #name; - - #[inline] - fn mul(self, other: #name) -> Self { - self * &other - } - } - - impl<'r> ::core::ops::MulAssign<&'r #name> for #name { - #[inline] - fn mul_assign(&mut self, other: &#name) - { - #multiply_impl - } - } - - impl ::core::ops::MulAssign for #name { - #[inline] - fn mul_assign(&mut self, other: #name) - { - self.mul_assign(&other); - } - } - - impl ::ff::PrimeField for #name { - type Repr = #repr; - - fn from_repr(r: #repr) -> Result<#name, PrimeFieldDecodingError> { - let mut r = #name(r); - if r.is_valid() { - r.mul_assign(&#name(R2)); - - Ok(r) - } else { - Err(PrimeFieldDecodingError::NotInField) - } - } - - fn into_repr(&self) -> #repr { - let mut r = *self; - r.mont_reduce( - #into_repr_params - ); - - r.0 - } - - fn char() -> #repr { - MODULUS - } - - const NUM_BITS: u32 = MODULUS_BITS; - - const CAPACITY: u32 = Self::NUM_BITS - 1; - - fn multiplicative_generator() -> Self { - #name(GENERATOR) - } - - const S: u32 = S; - - fn root_of_unity() -> Self { - #name(ROOT_OF_UNITY) - } - } - - impl ::ff::Field for #name { - /// Computes a uniformly random element using rejection sampling. - fn random(rng: &mut R) -> Self { - loop { - let mut tmp = { - let mut repr = [0u64; #limbs]; - for i in 0..#limbs { - repr[i] = rng.next_u64(); - } - #name(#repr(repr)) - }; - - // Mask away the unused most-significant bits. - tmp.0.as_mut()[#top_limb_index] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; - - if tmp.is_valid() { - return tmp - } - } - } - - #[inline] - fn zero() -> Self { - #name(#repr::from(0)) - } - - #[inline] - fn one() -> Self { - #name(R) - } - - #[inline] - fn is_zero(&self) -> bool { - self.0.is_zero() - } - - #[inline] - fn double(&self) -> Self { - let mut ret = *self; - - // This cannot exceed the backing capacity. - ret.0.mul2(); - - // However, it may need to be reduced. - ret.reduce(); - - ret - } - - fn invert(&self) -> ::subtle::CtOption { - #invert_impl - } - - #[inline(always)] - fn frobenius_map(&mut self, _: usize) { - // This has no effect in a prime field. - } - - #[inline] - fn square(&self) -> Self - { - #squaring_impl - } - } - - impl #name { - /// Determines if the element is really in the field. This is only used - /// internally. - #[inline(always)] - fn is_valid(&self) -> bool { - self.0 < MODULUS - } - - /// Subtracts the modulus from this element if this element is not in the - /// field. Only used interally. - #[inline(always)] - fn reduce(&mut self) { - if !self.is_valid() { - self.0.sub_noborrow(&MODULUS); - } - } - - #[inline(always)] - fn mont_reduce( - &mut self, - #mont_paramlist - ) - { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - #montgomery_impl - - self.reduce(); - } - } - } -} diff --git a/ff/ff_derive/src/pow_fixed.rs b/ff/ff_derive/src/pow_fixed.rs deleted file mode 100644 index 1d2b37ad29..0000000000 --- a/ff/ff_derive/src/pow_fixed.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Fixed-exponent variable-base exponentiation using addition chains. - -use addchain::{build_addition_chain, Step}; -use num_bigint::BigUint; -use quote::quote; -use syn::Ident; - -/// Returns t{n} as an ident. -fn get_temp(n: usize) -> Ident { - Ident::new(&format!("t{}", n), proc_macro2::Span::call_site()) -} - -pub(crate) fn generate( - base: &proc_macro2::TokenStream, - exponent: BigUint, -) -> proc_macro2::TokenStream { - let steps = build_addition_chain(exponent); - - let mut gen = proc_macro2::TokenStream::new(); - - // First entry in chain is one, i.e. the base. - let start = get_temp(0); - gen.extend(quote! { - let #start = #base; - }); - - let mut tmps = vec![start]; - for (i, step) in steps.into_iter().enumerate() { - let out = get_temp(i + 1); - - gen.extend(match step { - Step::Double { index } => { - let val = &tmps[index]; - quote! { - let #out = #val.square(); - } - } - Step::Add { left, right } => { - let left = &tmps[left]; - let right = &tmps[right]; - quote! { - let #out = #left * #right; - } - } - }); - - tmps.push(out.clone()); - } - - let end = tmps.last().expect("have last"); - gen.extend(quote! { - #end - }); - - gen -} diff --git a/ff/src/lib.rs b/ff/src/lib.rs deleted file mode 100644 index 65c32b2d64..0000000000 --- a/ff/src/lib.rs +++ /dev/null @@ -1,405 +0,0 @@ -//! This crate provides traits for working with finite fields. - -// Catch documentation errors caused by code changes. -#![no_std] -#![deny(intra_doc_link_resolution_failure)] -#![allow(unused_imports)] - -#[cfg(feature = "std")] -#[macro_use] -extern crate std; - -#[cfg(feature = "derive")] -pub use ff_derive::*; - -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use rand_core::RngCore; -#[cfg(feature = "std")] -use std::io::{self, Read, Write}; -use subtle::{ConditionallySelectable, CtOption}; - -/// This trait represents an element of a field. -pub trait Field: - Sized - + Eq - + Copy - + Clone - + Default - + Send - + Sync - + fmt::Debug - + fmt::Display - + 'static - + ConditionallySelectable - + Add - + Sub - + Mul - + Neg - + for<'a> Add<&'a Self, Output = Self> - + for<'a> Mul<&'a Self, Output = Self> - + for<'a> Sub<&'a Self, Output = Self> - + MulAssign - + AddAssign - + SubAssign - + for<'a> MulAssign<&'a Self> - + for<'a> AddAssign<&'a Self> - + for<'a> SubAssign<&'a Self> -{ - /// Returns an element chosen uniformly at random using a user-provided RNG. - fn random(rng: &mut R) -> Self; - - /// 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. - #[must_use] - fn square(&self) -> Self; - - /// Doubles this element. - #[must_use] - fn double(&self) -> Self; - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - fn invert(&self) -> CtOption; - - /// Exponentiates this element by a power of the base prime modulus via - /// the Frobenius automorphism. - fn frobenius_map(&mut self, power: usize); - - /// Exponentiates `self` by `exp`, where `exp` is a little-endian order - /// integer exponent. - /// - /// **This operation is variable time with respect to the exponent.** If the - /// exponent is fixed, this operation is effectively constant time. - fn pow_vartime>(&self, exp: S) -> Self { - let mut res = Self::one(); - for e in exp.as_ref().iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - 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 square root of the field element, if it is - /// quadratic residue. - fn sqrt(&self) -> CtOption; -} - -/// 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 - + AsRef<[u64]> - + AsMut<[u64]> - + From -{ - /// 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. - #[cfg(feature = "std")] - fn write_be(&self, mut writer: W) -> io::Result<()> { - use byteorder::{BigEndian, WriteBytesExt}; - - for digit in self.as_ref().iter().rev() { - writer.write_u64::(*digit)?; - } - - Ok(()) - } - - /// Reads a big endian integer into this representation. - #[cfg(feature = "std")] - fn read_be(&mut self, mut reader: R) -> io::Result<()> { - use byteorder::{BigEndian, ReadBytesExt}; - - for digit in self.as_mut().iter_mut().rev() { - *digit = reader.read_u64::()?; - } - - Ok(()) - } - - /// Writes this `PrimeFieldRepr` as a little endian integer. - #[cfg(feature = "std")] - fn write_le(&self, mut writer: W) -> io::Result<()> { - use byteorder::{LittleEndian, WriteBytesExt}; - - for digit in self.as_ref().iter() { - writer.write_u64::(*digit)?; - } - - Ok(()) - } - - /// Reads a little endian integer into this representation. - #[cfg(feature = "std")] - fn read_le(&mut self, mut reader: R) -> io::Result<()> { - use byteorder::{LittleEndian, ReadBytesExt}; - - for digit in self.as_mut().iter_mut() { - *digit = reader.read_u64::()?; - } - - Ok(()) - } -} - -/// 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, -} - -#[cfg(feature = "std")] -impl std::error::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 => write!(f, "not an element of the field"), - } - } -} - -/// 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; - - /// 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 { - 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; - - /// 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 { - t: E, - n: usize, -} - -impl> BitIterator { - pub fn new(t: E) -> Self { - let n = t.as_ref().len() * 64; - - BitIterator { t, n } - } -} - -impl> Iterator for BitIterator { - type Item = bool; - - fn next(&mut self) -> Option { - 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([0xa953_d79b_83f6_ab59, 0x6dea_2059_e200_bd39]); - 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([ - 0x429d_5f3a_c3a3_b759, - 0xb10f_4c66_768b_1c92, - 0x9236_8b6d_16ec_d3b4, - 0xa57e_a85a_e877_5219, - ]); - - 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 - } -} diff --git a/group/.gitignore b/group/.gitignore deleted file mode 100644 index 693699042b..0000000000 --- a/group/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock diff --git a/group/COPYRIGHT b/group/COPYRIGHT deleted file mode 100644 index 849e327475..0000000000 --- a/group/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -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. diff --git a/group/Cargo.toml b/group/Cargo.toml deleted file mode 100644 index 9b8fe5f766..0000000000 --- a/group/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "group" -version = "0.6.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -readme = "README.md" -license = "MIT/Apache-2.0" - -description = "Elliptic curve group traits and utilities" -documentation = "https://docs.rs/group/" -homepage = "https://github.com/ebfull/group" -repository = "https://github.com/ebfull/group" -edition = "2018" - -[dependencies] -ff = { version = "0.6", path = "../ff" } -rand = "0.7" -rand_xorshift = "0.2" - -[badges] -maintenance = { status = "actively-developed" } diff --git a/group/LICENSE-APACHE b/group/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/group/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/group/LICENSE-MIT b/group/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/group/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/group/README.md b/group/README.md deleted file mode 100644 index 5c2398b2b2..0000000000 --- a/group/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# group [![Crates.io](https://img.shields.io/crates/v/group.svg)](https://crates.io/crates/group) # - -`group` is a crate for working with groups over elliptic curves. - -## License - -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. diff --git a/group/src/lib.rs b/group/src/lib.rs deleted file mode 100644 index 34c8ac208b..0000000000 --- a/group/src/lib.rs +++ /dev/null @@ -1,211 +0,0 @@ -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] - -use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField}; -use rand::RngCore; -use std::error::Error; -use std::fmt; -use std::ops::{Add, AddAssign, Neg, Sub, SubAssign}; - -pub mod tests; - -mod wnaf; -pub use self::wnaf::Wnaf; - -/// A helper trait for types implementing group addition. -pub trait CurveOps: - Add + Sub + AddAssign + SubAssign -{ -} - -impl CurveOps for T where - T: Add + Sub + AddAssign + SubAssign -{ -} - -/// A helper trait for references implementing group addition. -pub trait CurveOpsOwned: for<'r> CurveOps<&'r Rhs, Output> {} -impl CurveOpsOwned for T where T: for<'r> CurveOps<&'r Rhs, Output> {} - -/// 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 - + 'static - + Neg - + CurveOps - + CurveOpsOwned - + CurveOps<::Affine> - + CurveOpsOwned<::Affine> -{ - type Engine: ScalarEngine; - type Scalar: PrimeField + SqrtField; - type Base: SqrtField; - type Affine: CurveAffine; - - /// Returns an element chosen uniformly at random using a user-provided RNG. - fn random(rng: &mut R) -> Self; - - /// 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); - - /// Performs scalar multiplication of this element. - fn mul_assign::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: ::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 - + Neg -{ - type Engine: ScalarEngine; - type Scalar: PrimeField + SqrtField; - type Base: SqrtField; - type Projective: CurveProjective; - type Uncompressed: EncodedPoint; - type Compressed: EncodedPoint; - - /// 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; - - /// Performs scalar multiplication of this element with mixed addition. - fn mul::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 { - ::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 { - ::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; - - /// 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; - - /// 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()), - } - } -} diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs deleted file mode 100644 index c53ba762f7..0000000000 --- a/group/src/tests/mod.rs +++ /dev/null @@ -1,447 +0,0 @@ -use ff::{Field, PrimeField}; -use rand::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::Neg; - -use crate::{CurveAffine, CurveProjective, EncodedPoint}; - -pub fn curve_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - // Negation edge case with zero. - { - let z = G::zero().neg(); - 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::random(&mut rng); - let rcopy = r; - r.add_assign(&G::zero()); - assert_eq!(r, rcopy); - r.add_assign(&G::Affine::zero()); - assert_eq!(r, rcopy); - - let mut z = G::zero(); - z.add_assign(&G::zero()); - assert!(z.is_zero()); - z.add_assign(&G::Affine::zero()); - assert!(z.is_zero()); - - let mut z2 = z; - z2.add_assign(&r); - - z.add_assign(&r.into_affine()); - - assert_eq!(z, z2); - assert_eq!(z, r); - } - - // Transformations - { - let a = G::random(&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::(); - random_multiplication_tests::(); - random_doubling_tests::(); - random_negation_tests::(); - random_transformation_tests::(); - random_wnaf_tests::(); - random_encoding_tests::(); -} - -fn random_wnaf_tests() { - use crate::wnaf::*; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - { - let mut table = vec![]; - let mut wnaf = vec![]; - - for w in 2..14 { - for _ in 0..100 { - let g = G::random(&mut rng); - let s = G::Scalar::random(&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) {} - - for _ in 0..100 { - let g = G::random(&mut rng); - let s = G::Scalar::random(&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(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); - } - wnaf.base(g, 1).scalar(s) - }; - let g7 = { - let mut wnaf = Wnaf::new(); - { - // Populate the vectors. - wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); - } - wnaf.scalar(s).base(g) - }; - let g8 = { - let mut wnaf = Wnaf::new(); - { - // Populate the vectors. - wnaf.base(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); - } - 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(G::random(&mut rng), 1) - .scalar(G::Scalar::random(&mut rng).into_repr()); - } - 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() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let r = G::random(&mut rng); - - let s = G::Scalar::random(&mut rng); - let sneg = s.neg(); - - 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(&t2.into_affine()); - assert!(t4.is_zero()); - - assert_eq!(t1.neg(), t2); - } -} - -fn random_doubling_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let mut a = G::random(&mut rng); - let mut b = G::random(&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(&b.into_affine()); - - assert_eq!(tmp1, tmp2); - assert_eq!(tmp1, tmp3); - } -} - -fn random_multiplication_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let mut a = G::random(&mut rng); - let mut b = G::random(&mut rng); - let a_affine = a.into_affine(); - let b_affine = b.into_affine(); - - let s = G::Scalar::random(&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() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = G::random(&mut rng); - let b = G::random(&mut rng); - let c = G::random(&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(&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(&b_affine); - tmp[3].add_assign(&c_affine); - - // a + (b + c) - tmp[4] = b_affine.into_projective(); - tmp[4].add_assign(&c_affine); - tmp[4].add_assign(&a_affine); - - // (a + c) + b - tmp[5] = a_affine.into_projective(); - tmp[5].add_assign(&c_affine); - tmp[5].add_assign(&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() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let g = G::random(&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::random(&mut rng)).collect::>(); - - for i in &v { - assert!(!i.is_normalized()); - } - - use rand::distributions::{Distribution, Uniform}; - let between = Uniform::new(0, 1000); - // Sprinkle in some normalized points - for _ in 0..5 { - v[between.sample(&mut rng)] = G::zero(); - } - for _ in 0..5 { - let s = between.sample(&mut rng); - v[s] = v[s].into_affine().into_projective(); - } - - let expected_v = v - .iter() - .map(|v| v.into_affine().into_projective()) - .collect::>(); - G::batch_normalization(&mut v); - - for i in &v { - assert!(i.is_normalized()); - } - - assert_eq!(v, expected_v); - } -} - -fn random_encoding_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - assert_eq!( - G::Affine::zero().into_uncompressed().into_affine().unwrap(), - G::Affine::zero() - ); - - assert_eq!( - G::Affine::zero().into_compressed().into_affine().unwrap(), - G::Affine::zero() - ); - - for _ in 0..1000 { - let mut r = G::random(&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 = r.neg(); - - let compressed = r.into_compressed(); - let de_compressed = compressed.into_affine().unwrap(); - assert_eq!(de_compressed, r); - } -} diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs deleted file mode 100644 index 381cd106b8..0000000000 --- a/group/src/wnaf.rs +++ /dev/null @@ -1,181 +0,0 @@ -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(table: &mut Vec, 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(wnaf: &mut Vec, 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(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 { - base: B, - scalar: S, - window_size: W, -} - -impl Wnaf<(), Vec, Vec> { - /// 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> { - // 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: <::Scalar as PrimeField>::Repr, - ) -> Wnaf, &[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> { - /// 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> { - Wnaf { - base: self.base, - scalar: vec![], - window_size: self.window_size, - } - } -} - -impl<'a, G: CurveProjective> Wnaf, &'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, &'a [i64]> { - Wnaf { - base: vec![], - scalar: self.scalar, - window_size: self.window_size, - } - } -} - -impl> Wnaf { - /// Performs exponentiation given a base. - pub fn base(&mut self, base: G) -> G - where - B: AsMut>, - { - wnaf_table(self.base.as_mut(), base, self.window_size); - wnaf_exp(self.base.as_mut(), self.scalar.as_ref()) - } -} - -impl>> Wnaf { - /// Performs exponentiation given a scalar. - pub fn scalar( - &mut self, - scalar: <::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()) - } -} diff --git a/jubjub/.github/workflows/ci.yml b/jubjub/.github/workflows/ci.yml deleted file mode 100644 index 5d0efb3a21..0000000000 --- a/jubjub/.github/workflows/ci.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: CI checks - -on: [push, pull_request] - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - # Ensure all code has been formatted with rustfmt - - run: rustup component add rustfmt - - name: Check formatting - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check --color always - - test: - name: Test on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - name: Build tests - uses: actions-rs/cargo@v1 - with: - command: build - args: --verbose --release --tests - - name: Run tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose --release - - no-std: - name: Check no-std compatibility - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.36.0 - override: true - - run: rustup target add thumbv6m-none-eabi - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --verbose --target thumbv6m-none-eabi --no-default-features - - doc-links: - name: Nightly lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - # Ensure intra-documentation links all resolve correctly - # Requires #![deny(intra_doc_link_resolution_failure)] in crate. - - name: Check intra-doc links - uses: actions-rs/cargo@v1 - with: - command: doc - args: --document-private-items diff --git a/jubjub/.gitignore b/jubjub/.gitignore deleted file mode 100644 index 693699042b..0000000000 --- a/jubjub/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock diff --git a/jubjub/COPYRIGHT b/jubjub/COPYRIGHT deleted file mode 100644 index aaca1cc674..0000000000 --- a/jubjub/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyrights in the "jubjub" library are retained by their contributors. No -copyright assignment is required to contribute to the "jubjub" library. - -The "jubjub" 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. diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml deleted file mode 100644 index 31221efc77..0000000000 --- a/jubjub/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -authors = [ - "Sean Bowe ", - "Eirik Ogilvie-Wigley ", - "Jack Grigg ", -] -description = "Implementation of the Jubjub elliptic curve group" -documentation = "https://docs.rs/jubjub/" -homepage = "https://github.com/zkcrypto/jubjub" -license = "MIT/Apache-2.0" -name = "jubjub" -repository = "https://github.com/zkcrypto/jubjub" -version = "0.3.0" -edition = "2018" - -[dependencies.bls12_381] -path = "../bls12_381" -version = "0.1" -default-features = false - -[dependencies.subtle] -version = "^2.2.1" -default-features = false - -[dev-dependencies] -criterion = "0.3" - -[dev-dependencies.rand_core] -version = "0.5" -default-features = false - -[dev-dependencies.rand_xorshift] -version = "0.2" -default-features = false - -[features] -default = [] - -[[bench]] -name = "fq_bench" -harness = false - -[[bench]] -name = "fr_bench" -harness = false - -[[bench]] -name = "point_bench" -harness = false diff --git a/jubjub/LICENSE-APACHE b/jubjub/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/jubjub/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/jubjub/LICENSE-MIT b/jubjub/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/jubjub/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/jubjub/README.md b/jubjub/README.md deleted file mode 100644 index da5bd530ad..0000000000 --- a/jubjub/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# jubjub [![Crates.io](https://img.shields.io/crates/v/jubjub.svg)](https://crates.io/crates/jubjub) # - - - -This is a pure Rust implementation of the Jubjub elliptic curve group and its associated fields. - -* **This implementation has not been reviewed or audited. Use at your own risk.** -* This implementation targets Rust `1.36` or later. -* All operations are constant time unless explicitly noted. -* This implementation does not require the Rust standard library. - -## [Documentation](https://docs.rs/jubjub) - -## Curve Description - -Jubjub is the [twisted Edwards curve](https://en.wikipedia.org/wiki/Twisted_Edwards_curve) `-u^2 + v^2 = 1 + d.u^2.v^2` of rational points over `GF(q)` with a subgroup of prime order `r` and cofactor `8`. - -``` -q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 -r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 -d = -(10240/10241) -``` - -The choice of `GF(q)` is made to be the scalar field of the BLS12-381 elliptic curve construction. - -Jubjub is birationally equivalent to a [Montgomery curve](https://en.wikipedia.org/wiki/Montgomery_curve) `y^2 = x^3 + Ax^2 + x` over the same field with `A = 40962`. This value of `A` is the smallest integer such that `(A - 2) / 4` is a small integer, `A^2 - 4` is nonsquare in `GF(q)`, and the Montgomery curve and its quadratic twist have small cofactors `8` and `4`, respectively. This is identical to the relationship between Curve25519 and ed25519. - -Please see [./doc/evidence/](./doc/evidence/) for supporting evidence that Jubjub meets the [SafeCurves](https://safecurves.cr.yp.to/index.html) criteria. The tool in [./doc/derive/](./doc/derive/) will derive the curve parameters via the above criteria to demonstrate rigidity. - -## Acknowledgements - -Jubjub was designed by Sean Bowe. Daira Hopwood is responsible for its name and specification. The security evidence in [./doc/evidence/](./doc/evidence/) is the product of Daira Hopwood and based on SafeCurves by Daniel J. Bernstein and Tanja Lange. Peter Newell and Daira Hopwood are responsible for the Jubjub bird image. - -Please see `Cargo.toml` for a list of primary authors of this codebase. - -## 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. diff --git a/jubjub/RELEASES.md b/jubjub/RELEASES.md deleted file mode 100644 index 45db61c1ff..0000000000 --- a/jubjub/RELEASES.md +++ /dev/null @@ -1,24 +0,0 @@ -# 0.3.0 - -This release now depends on the `bls12_381` crate, which exposes the `Fq` field type that we re-export. - -* The `Fq` and `Fr` field types now have better constant function support for various operations and constructors. -* We no longer depend on the `byteorder` crate. -* We've bumped our `rand_core` dev-dependency up to 0.5. -* We've removed the `std` and `nightly` features. -* We've bumped our dependency of `subtle` up to `^2.2.1`. - -# 0.2.0 - -This release switches to `subtle 2.1` to bring in the `CtOption` type, and also makes a few useful API changes. - -* Implemented `Mul` for `AffineNielsPoint` and `ExtendedNielsPoint` -* Changed `AffinePoint::to_niels()` to be a `const` function so that constant curve points can be constructed without statics. -* Implemented `multiply_bits` for `AffineNielsPoint`, `ExtendedNielsPoint` -* Removed `CtOption` and replaced it with `CtOption` from `subtle` crate. -* Modified receivers of some methods to reduce stack usage -* Changed various `into_bytes` methods into `to_bytes` - -# 0.1.0 - -Initial release. diff --git a/jubjub/benches/fq_bench.rs b/jubjub/benches/fq_bench.rs deleted file mode 100644 index 65eceafc84..0000000000 --- a/jubjub/benches/fq_bench.rs +++ /dev/null @@ -1,58 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use jubjub::*; - -fn bench_add_assign(c: &mut Criterion) { - let mut n = Fq::one(); - let neg_one = -Fq::one(); - c.bench_function("Fq add_assign", |b| { - b.iter(move || { - n += &neg_one; - }) - }); -} - -fn bench_sub_assign(c: &mut Criterion) { - let mut n = Fq::one(); - let neg_one = -Fq::one(); - c.bench_function("Fq sub_assign", |b| { - b.iter(move || { - n -= &neg_one; - }) - }); -} - -fn bench_mul_assign(c: &mut Criterion) { - let mut n = Fq::one(); - let neg_one = -Fq::one(); - c.bench_function("Fq mul_assign", |b| { - b.iter(move || { - n *= &neg_one; - }) - }); -} - -fn bench_square(c: &mut Criterion) { - let n = Fq::one(); - c.bench_function("Fq square", |b| b.iter(move || n.square())); -} - -fn bench_invert(c: &mut Criterion) { - let n = Fq::one(); - c.bench_function("Fq invert", |b| b.iter(move || n.invert())); -} - -fn bench_sqrt(c: &mut Criterion) { - let n = Fq::one().double().double(); - c.bench_function("Fq sqrt", |b| b.iter(move || n.sqrt())); -} - -criterion_group!( - benches, - bench_add_assign, - bench_sub_assign, - bench_mul_assign, - bench_square, - bench_invert, - bench_sqrt, -); -criterion_main!(benches); diff --git a/jubjub/benches/fr_bench.rs b/jubjub/benches/fr_bench.rs deleted file mode 100644 index 8dc9ce2216..0000000000 --- a/jubjub/benches/fr_bench.rs +++ /dev/null @@ -1,58 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use jubjub::*; - -fn bench_add_assign(c: &mut Criterion) { - let mut n = Fr::one(); - let neg_one = -Fr::one(); - c.bench_function("Fr add_assign", |b| { - b.iter(move || { - n += &neg_one; - }) - }); -} - -fn bench_sub_assign(c: &mut Criterion) { - let mut n = Fr::one(); - let neg_one = -Fr::one(); - c.bench_function("Fr sub_assign", |b| { - b.iter(move || { - n -= &neg_one; - }) - }); -} - -fn bench_mul_assign(c: &mut Criterion) { - let mut n = Fr::one(); - let neg_one = -Fr::one(); - c.bench_function("Fr mul_assign", |b| { - b.iter(move || { - n *= &neg_one; - }) - }); -} - -fn bench_square(c: &mut Criterion) { - let n = Fr::one(); - c.bench_function("Fr square", |b| b.iter(move || n.square())); -} - -fn bench_invert(c: &mut Criterion) { - let n = Fr::one(); - c.bench_function("Fr invert", |b| b.iter(move || n.invert())); -} - -fn bench_sqrt(c: &mut Criterion) { - let n = Fr::one().double().double(); - c.bench_function("Fr sqrt", |b| b.iter(move || n.sqrt())); -} - -criterion_group!( - benches, - bench_add_assign, - bench_sub_assign, - bench_mul_assign, - bench_square, - bench_invert, - bench_sqrt, -); -criterion_main!(benches); diff --git a/jubjub/benches/point_bench.rs b/jubjub/benches/point_bench.rs deleted file mode 100644 index 1659ea5f97..0000000000 --- a/jubjub/benches/point_bench.rs +++ /dev/null @@ -1,73 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use jubjub::*; - -// Non-Niels - -fn bench_point_doubling(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - c.bench_function("Jubjub point doubling", |bencher| { - bencher.iter(move || a.double()) - }); -} - -fn bench_point_addition(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = -ExtendedPoint::identity(); - c.bench_function("Jubjub point addition", |bencher| { - bencher.iter(move || a + b) - }); -} - -fn bench_point_subtraction(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = -ExtendedPoint::identity(); - c.bench_function("Jubjub point subtraction", |bencher| { - bencher.iter(move || a + b) - }); -} - -// Niels - -fn bench_cached_point_addition(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = ExtendedPoint::identity().to_niels(); - c.bench_function("Jubjub cached point addition", |bencher| { - bencher.iter(move || a + b) - }); -} - -fn bench_cached_point_subtraction(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = ExtendedPoint::identity().to_niels(); - c.bench_function("Jubjub cached point subtraction", |bencher| { - bencher.iter(move || a + b) - }); -} - -fn bench_cached_affine_point_addition(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = AffinePoint::identity().to_niels(); - c.bench_function("Jubjub cached affine point addition", |bencher| { - bencher.iter(move || a + b) - }); -} - -fn bench_cached_affine_point_subtraction(c: &mut Criterion) { - let a = ExtendedPoint::identity(); - let b = AffinePoint::identity().to_niels(); - c.bench_function("Jubjub cached affine point subtraction", |bencher| { - bencher.iter(move || a + b) - }); -} - -criterion_group!( - benches, - bench_point_doubling, - bench_point_addition, - bench_point_subtraction, - bench_cached_point_addition, - bench_cached_point_subtraction, - bench_cached_affine_point_addition, - bench_cached_affine_point_subtraction, -); -criterion_main!(benches); diff --git a/jubjub/doc/derive/.gitignore b/jubjub/doc/derive/.gitignore deleted file mode 100644 index 7c974cf29c..0000000000 --- a/jubjub/doc/derive/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.sage.py diff --git a/jubjub/doc/derive/derive.sage b/jubjub/doc/derive/derive.sage deleted file mode 100644 index c0c5310bf4..0000000000 --- a/jubjub/doc/derive/derive.sage +++ /dev/null @@ -1,32 +0,0 @@ -q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 -Fq = GF(q) - -# We wish to find a Montgomery curve with B = 1 and A the smallest such -# that (A - 2) / 4 is a small integer. -def get_A(n): - return (n * 4) + 2 - -# A = 2 is invalid (singular curve), so we start at i = 1 (A = 6) -i = 1 - -while True: - A = Fq(get_A(i)) - i = i + 1 - - # We also want that A^2 - 4 is nonsquare. - if ((A^2) - 4).is_square(): - continue - - ec = EllipticCurve(Fq, [0, A, 0, 1, 0]) - o = ec.order() - - if (o % 8 == 0): - o = o // 8 - if is_prime(o): - twist = ec.quadratic_twist() - otwist = twist.order() - if (otwist % 4 == 0): - otwist = otwist // 4 - if is_prime(otwist): - print "A = %s" % A - exit(0) diff --git a/jubjub/doc/evidence/.gitignore b/jubjub/doc/evidence/.gitignore deleted file mode 100644 index 9a0d287f2c..0000000000 --- a/jubjub/doc/evidence/.gitignore +++ /dev/null @@ -1,102 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# dotenv -.env - -# virtualenv -.venv -venv/ -ENV/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - diff --git a/jubjub/doc/evidence/LICENSE b/jubjub/doc/evidence/LICENSE deleted file mode 100644 index 9e181634a2..0000000000 --- a/jubjub/doc/evidence/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2017 The Zcash developers - -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. diff --git a/jubjub/doc/evidence/README.md b/jubjub/doc/evidence/README.md deleted file mode 100644 index 26b2e36f23..0000000000 --- a/jubjub/doc/evidence/README.md +++ /dev/null @@ -1,28 +0,0 @@ -Jubjub supporting evidence --------------------------- - -This repository contains supporting evidence that the twisted Edwards curve --x^2 + y^2 = 1 - (10240/10241).x^2.y^2 of rational points over -GF(52435875175126190479447740508185965837690552500527637822603658699938581184513), -[also called "Jubjub"](https://z.cash/technology/jubjub.html), -satisfies the [SafeCurves criteria](https://safecurves.cr.yp.to/index.html). - -The script ``verify.sage`` is based on -[this script from the SafeCurves site](https://safecurves.cr.yp.to/verify.html), -modified - -* to support twisted Edwards curves; -* to generate a file 'primes' containing the primes needed for primality proofs, - if it is not already present; -* to change the directory in which Pocklington proof files are generated - (``proof/`` rather than ``../../../proof``), and to create that directory - if it does not exist. - -Prerequisites: - -* apt-get install sagemath -* pip install sortedcontainers - -Run ``sage verify.sage .``, or ``./run.sh`` to also print out the results. - -Note that the "rigidity" criterion cannot be checked automatically. diff --git a/jubjub/doc/evidence/a b/jubjub/doc/evidence/a deleted file mode 100644 index 3a2e3f4984..0000000000 --- a/jubjub/doc/evidence/a +++ /dev/null @@ -1 +0,0 @@ --1 diff --git a/jubjub/doc/evidence/d b/jubjub/doc/evidence/d deleted file mode 100644 index 767309a726..0000000000 --- a/jubjub/doc/evidence/d +++ /dev/null @@ -1 +0,0 @@ -19257038036680949359750312669786877991949435402254120286184196891950884077233 diff --git a/jubjub/doc/evidence/l b/jubjub/doc/evidence/l deleted file mode 100644 index 83f92d5797..0000000000 --- a/jubjub/doc/evidence/l +++ /dev/null @@ -1 +0,0 @@ -6554484396890773809930967563523245729705921265872317281365359162392183254199 diff --git a/jubjub/doc/evidence/p b/jubjub/doc/evidence/p deleted file mode 100644 index 1dc0557a71..0000000000 --- a/jubjub/doc/evidence/p +++ /dev/null @@ -1 +0,0 @@ -52435875175126190479447740508185965837690552500527637822603658699938581184513 diff --git a/jubjub/doc/evidence/rigid b/jubjub/doc/evidence/rigid deleted file mode 100644 index e560e4087a..0000000000 --- a/jubjub/doc/evidence/rigid +++ /dev/null @@ -1 +0,0 @@ -fully rigid diff --git a/jubjub/doc/evidence/run.sh b/jubjub/doc/evidence/run.sh deleted file mode 100644 index 817f2faf8e..0000000000 --- a/jubjub/doc/evidence/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -sage verify.sage . -grep -Rn '.' verify-* |grep '^verify-.*:1:' |sed 's/:1:/ = /' - diff --git a/jubjub/doc/evidence/shape b/jubjub/doc/evidence/shape deleted file mode 100644 index 796f74d092..0000000000 --- a/jubjub/doc/evidence/shape +++ /dev/null @@ -1 +0,0 @@ -tedwards diff --git a/jubjub/doc/evidence/verify.sage b/jubjub/doc/evidence/verify.sage deleted file mode 100644 index 1717c0bc6e..0000000000 --- a/jubjub/doc/evidence/verify.sage +++ /dev/null @@ -1,444 +0,0 @@ -import os -import sys -from errno import ENOENT, EEXIST -from sortedcontainers import SortedSet - - -def readfile(fn): - fd = open(fn,'r') - r = fd.read() - fd.close() - return r - -def writefile(fn,s): - fd = open(fn,'w') - fd.write(s) - fd.close() - -def expand2(n): - s = "" - - while n != 0: - j = 16 - while 2**j < abs(n): j += 1 - if 2**j - abs(n) > abs(n) - 2**(j-1): j -= 1 - - if abs(abs(n) - 2**j) > 2**(j - 10): - if n > 0: - if s != "": s += " + " - s += str(n) - else: - s += " - " + str(-n) - n = 0 - elif n > 0: - if s != "": s += " + " - s += "2^" + str(j) - n -= 2**j - else: - s += " - 2^" + str(j) - n += 2**j - - return s - -def requirement(fn,istrue): - writefile(fn,str(istrue) + '\n') - return istrue - -def verify(): - try: - os.mkdir('proof') - except OSError as e: - if e.errno != EEXIST: raise - - try: - s = set(map(Integer, readfile('primes').split())) - except IOError, e: - if e.errno != ENOENT: raise - s = set() - - needtofactor = SortedSet() - V = SortedSet() # distinct verified primes - verify_primes(V, s, needtofactor) - verify_pass(V, needtofactor) - - old = V - needtofactor.update(V) - while len(needtofactor) > len(old): - k = len(needtofactor) - len(old) - sys.stdout.write('Factoring %d integer%s' % (k, '' if k == 1 else 's')) - sys.stdout.flush() - for x in needtofactor: - if x not in old: - for (y, z) in factor(x): - s.add(y) - sys.stdout.write('.') - sys.stdout.flush() - - print('') - - old = needtofactor.copy() - verify_primes(V, s, needtofactor) - - writefile('primes', '\n'.join(map(str, s)) + '\n') - writefile('verify-primes', '\n' + - ''.join(('2\n' if v == 2 else - '%s\n' % (v,v)) for v in V) + - '\n') - - verify_pass(V, needtofactor) - - -def verify_primes(V, s, needtofactor): - for n in sorted(s): - if not n.is_prime() or n in V: continue - needtofactor.add(n-1) - if n == 2: - V.add(n) - continue - for trybase in primes(2,10000): - base = Integers(n)(trybase) - if base^(n-1) != 1: continue - proof = 'Primality proof for n = %s:\n' % n - proof += '

Take b = %s.\n' % base - proof += '

b^(n-1) mod n = 1.\n' - f = factor(1) - for v in reversed(V): - if f.prod()^2 <= n: - if n % v == 1: - u = base^((n-1)/v)-1 - if u.is_unit(): - if v == 2: - proof += '

2 is prime.\n' - else: - proof += '

%s is prime.\n' % (v,v) - proof += '
b^((n-1)/%s)-1 mod n = %s, which is a unit, inverse %s.\n' % (v,u,1/u) - f *= factor(v)^(n-1).valuation(v) - if f.prod()^2 <= n: continue - if n % f.prod() != 1: continue - proof += '

(%s) divides n-1.\n' % f - proof += '

(%s)^2 > n.\n' % f - proof += "

n is prime by Pocklington's theorem.\n" - proof += '\n' - writefile('proof/%s.html' % n,proof) - V.add(n) - break - - -def verify_pass(V, needtofactor): - p = Integer(readfile('p')) - k = GF(p) - kz. = k[] - l = Integer(readfile('l')) - x0 = Integer(readfile('x0')) - y0 = Integer(readfile('y0')) - x1 = Integer(readfile('x1')) - y1 = Integer(readfile('y1')) - shape = readfile('shape').strip() - rigid = readfile('rigid').strip() - - safefield = True - safeeq = True - safebase = True - saferho = True - safetransfer = True - safedisc = True - saferigid = True - safeladder = True - safetwist = True - safecomplete = True - safeind = True - - pstatus = 'Unverified' - if not p.is_prime(): pstatus = 'False' - needtofactor.add(p) - if p in V: pstatus = 'True' - if pstatus != 'True': safefield = False - writefile('verify-pisprime',pstatus + '\n') - - pstatus = 'Unverified' - if not l.is_prime(): pstatus = 'False' - needtofactor.add(l) - if l in V: pstatus = 'True' - if pstatus != 'True': safebase = False - writefile('verify-lisprime',pstatus + '\n') - - writefile('expand2-p','= %s\n' % expand2(p)) - writefile('expand2-l','
= %s\n' % expand2(l)) - - writefile('hex-p',hex(p) + '\n') - writefile('hex-l',hex(l) + '\n') - writefile('hex-x0',hex(x0) + '\n') - writefile('hex-x1',hex(x1) + '\n') - writefile('hex-y0',hex(y0) + '\n') - writefile('hex-y1',hex(y1) + '\n') - - gcdlpis1 = gcd(l,p) == 1 - safetransfer &= requirement('verify-gcdlp1',gcdlpis1) - - writefile('verify-movsafe','Unverified\n') - writefile('verify-embeddingdegree','Unverified\n') - if gcdlpis1 and l.is_prime(): - u = Integers(l)(p) - d = l-1 - needtofactor.add(d) - for v in V: - while d % v == 0: d /= v - if d == 1: - d = l-1 - for v in V: - while d % v == 0: - if u^(d/v) != 1: break - d /= v - safetransfer &= requirement('verify-movsafe',(l-1)/d <= 100) - writefile('verify-embeddingdegree','%s
= (l-1)/%s\n' % (d,(l-1)/d)) - - t = p+1-l*round((p+1)/l) - if l^2 > 16*p: - writefile('verify-trace','%s\n' % t) - f = factor(1) - d = (p+1-t)/l - needtofactor.add(d) - for v in V: - while d % v == 0: - d //= v - f *= factor(v) - writefile('verify-cofactor','%s\n' % f) - else: - writefile('verify-trace','Unverified\n') - writefile('verify-cofactor','Unverified\n') - - D = t^2-4*p - needtofactor.add(D) - for v in V: - while D % v^2 == 0: D /= v^2 - if prod([v for v in V if D % v == 0]) != -D: - writefile('verify-disc','Unverified\n') - writefile('verify-discisbig','Unverified\n') - safedisc = False - else: - f = -prod([factor(v) for v in V if D % v == 0]) - if D % 4 != 1: - D *= 4 - f = factor(4) * f - Dbits = (log(-D)/log(2)).numerical_approx() - writefile('verify-disc','%s
= %s
≈ -2^%.1f\n' % (D,f,Dbits)) - safedisc &= requirement('verify-discisbig',D < -2^100) - - pi4 = 0.78539816339744830961566084581987572105 - rho = log(pi4*l)/log(4) - writefile('verify-rho','%.1f\n' % rho) - saferho &= requirement('verify-rhoabove100',rho.numerical_approx() >= 100) - - twistl = 'Unverified' - d = p+1+t - needtofactor.add(d) - for v in V: - while d % v == 0: d /= v - if d == 1: - d = p+1+t - for v in V: - if d % v == 0: - if twistl == 'Unverified' or v > twistl: twistl = v - - writefile('verify-twistl','%s\n' % twistl) - writefile('verify-twistembeddingdegree','Unverified\n') - writefile('verify-twistmovsafe','Unverified\n') - if twistl == 'Unverified': - writefile('hex-twistl','Unverified\n') - writefile('expand2-twistl','Unverified\n') - writefile('verify-twistcofactor','Unverified\n') - writefile('verify-gcdtwistlp1','Unverified\n') - writefile('verify-twistrho','Unverified\n') - safetwist = False - else: - writefile('hex-twistl',hex(twistl) + '\n') - writefile('expand2-twistl','
= %s\n' % expand2(twistl)) - f = factor(1) - d = (p+1+t)/twistl - needtofactor.add(d) - for v in V: - while d % v == 0: - d //= v - f *= factor(v) - writefile('verify-twistcofactor','%s\n' % f) - gcdtwistlpis1 = gcd(twistl,p) == 1 - safetwist &= requirement('verify-gcdtwistlp1',gcdtwistlpis1) - - movsafe = 'Unverified' - embeddingdegree = 'Unverified' - if gcdtwistlpis1 and twistl.is_prime(): - u = Integers(twistl)(p) - d = twistl-1 - needtofactor.add(d) - for v in V: - while d % v == 0: d /= v - if d == 1: - d = twistl-1 - for v in V: - while d % v == 0: - if u^(d/v) != 1: break - d /= v - safetwist &= requirement('verify-twistmovsafe',(twistl-1)/d <= 100) - writefile('verify-twistembeddingdegree',"%s
= (l'-1)/%s\n" % (d,(twistl-1)/d)) - - rho = log(pi4*twistl)/log(4) - writefile('verify-twistrho','%.1f\n' % rho) - safetwist &= requirement('verify-twistrhoabove100',rho.numerical_approx() >= 100) - - precomp = 0 - joint = l - needtofactor.add(p+1-t) - needtofactor.add(p+1+t) - for v in V: - d1 = p+1-t - d2 = p+1+t - while d1 % v == 0 or d2 % v == 0: - if d1 % v == 0: d1 //= v - if d2 % v == 0: d2 //= v - # best case for attack: cyclic; each power is usable - # also assume that kangaroo is as efficient as rho - if v + sqrt(pi4*joint/v) < sqrt(pi4*joint): - precomp += v - joint /= v - - rho = log(precomp + sqrt(pi4 * joint))/log(2) - writefile('verify-jointrho','%.1f\n' % rho) - safetwist &= requirement('verify-jointrhoabove100',rho.numerical_approx() >= 100) - - - x0 = k(x0) - y0 = k(y0) - x1 = k(x1) - y1 = k(y1) - - if shape in ('edwards', 'tedwards'): - d = Integer(readfile('d')) - a = 1 - if shape == 'tedwards': - a = Integer(readfile('a')) - - writefile('verify-shape','Twisted Edwards\n') - writefile('verify-equation','%sx^2+y^2 = 1%+dx^2y^2\n' % (a, d)) - if a == 1: - writefile('verify-shape','Edwards\n') - writefile('verify-equation','x^2+y^2 = 1%+dx^2y^2\n' % d) - - a = k(a) - d = k(d) - elliptic = a*d*(a-d) - level0 = a*x0^2+y0^2-1-d*x0^2*y0^2 - level1 = a*x1^2+y1^2-1-d*x1^2*y1^2 - - if shape == 'montgomery': - writefile('verify-shape','Montgomery\n') - A = Integer(readfile('A')) - B = Integer(readfile('B')) - equation = '%sy^2 = x^3%+dx^2+x' % (B,A) - if B == 1: - equation = 'y^2 = x^3%+dx^2+x' % A - writefile('verify-equation',equation + '\n') - - A = k(A) - B = k(B) - elliptic = B*(A^2-4) - level0 = B*y0^2-x0^3-A*x0^2-x0 - level1 = B*y1^2-x1^3-A*x1^2-x1 - - if shape == 'shortw': - writefile('verify-shape','short Weierstrass\n') - a = Integer(readfile('a')) - b = Integer(readfile('b')) - writefile('verify-equation','y^2 = x^3%+dx%+d\n' % (a,b)) - - a = k(a) - b = k(b) - elliptic = 4*a^3+27*b^2 - level0 = y0^2-x0^3-a*x0-b - level1 = y1^2-x1^3-a*x1-b - - writefile('verify-elliptic',str(elliptic) + '\n') - safeeq &= requirement('verify-iselliptic',elliptic != 0) - safebase &= requirement('verify-isoncurve0',level0 == 0) - safebase &= requirement('verify-isoncurve1',level1 == 0) - - if shape in ('edwards', 'tedwards'): - A = 2*(a+d)/(a-d) - B = 4/(a-d) - x0,y0 = (1+y0)/(1-y0),((1+y0)/(1-y0))/x0 - x1,y1 = (1+y1)/(1-y1),((1+y1)/(1-y1))/x1 - shape = 'montgomery' - - if shape == 'montgomery': - a = (3-A^2)/(3*B^2) - b = (2*A^3-9*A)/(27*B^3) - x0,y0 = (x0+A/3)/B,y0/B - x1,y1 = (x1+A/3)/B,y1/B - shape = 'shortw' - - try: - E = EllipticCurve([a,b]) - numorder2 = 0 - numorder4 = 0 - for P in E(0).division_points(4): - if P != 0 and 2*P == 0: - numorder2 += 1 - if 2*P != 0 and 4*P == 0: - numorder4 += 1 - writefile('verify-numorder2',str(numorder2) + '\n') - writefile('verify-numorder4',str(numorder4) + '\n') - completesingle = False - completemulti = False - if numorder4 == 2 and numorder2 == 1: - # complete edwards form, and montgomery with unique point of order 2 - completesingle = True - completemulti = True - # should extend this to allow complete twisted hessian - safecomplete &= requirement('verify-completesingle',completesingle) - safecomplete &= requirement('verify-completemulti',completemulti) - safecomplete &= requirement('verify-ltimesbase1is0',l * E([x1,y1]) == 0) - writefile('verify-ltimesbase1',str(l * E([x1,y1])) + '\n') - writefile('verify-cofactorbase01',str(((p+1-t)//l) * E([x0,y0]) == E([x1,y1])) + '\n') - except: - writefile('verify-numorder2','Unverified\n') - writefile('verify-numorder4','Unverified\n') - writefile('verify-ltimesbase1','Unverified\n') - writefile('verify-cofactorbase01','Unverified\n') - safecomplete = False - - montladder = False - for r,e in (z^3+a*z+b).roots(): - if (3*r^2+a).is_square(): - montladder = True - safeladder &= requirement('verify-montladder',montladder) - - indistinguishability = False - elligator2 = False - if (p+1-t) % 2 == 0: - if b != 0: - indistinguishability = True - elligator2 = True - safeind &= requirement('verify-indistinguishability',indistinguishability) - writefile('verify-ind-notes','Elligator 2: %s.\n' % ['No','Yes'][elligator2]) - - saferigid &= (rigid == 'fully rigid' or rigid == 'somewhat rigid') - - safecurve = True - safecurve &= requirement('verify-safefield',safefield) - safecurve &= requirement('verify-safeeq',safeeq) - safecurve &= requirement('verify-safebase',safebase) - safecurve &= requirement('verify-saferho',saferho) - safecurve &= requirement('verify-safetransfer',safetransfer) - safecurve &= requirement('verify-safedisc',safedisc) - safecurve &= requirement('verify-saferigid',saferigid) - safecurve &= requirement('verify-safeladder',safeladder) - safecurve &= requirement('verify-safetwist',safetwist) - safecurve &= requirement('verify-safecomplete',safecomplete) - safecurve &= requirement('verify-safeind',safeind) - requirement('verify-safecurve',safecurve) - -originaldir = os.open('.',os.O_RDONLY) -for i in range(1,len(sys.argv)): - os.fchdir(originaldir) - os.chdir(sys.argv[i]) - verify() - diff --git a/jubjub/doc/evidence/x0 b/jubjub/doc/evidence/x0 deleted file mode 100644 index 3b2097a36c..0000000000 --- a/jubjub/doc/evidence/x0 +++ /dev/null @@ -1 +0,0 @@ -11076627216317271660298050606127911965867021807910416450833192264015104452986 diff --git a/jubjub/doc/evidence/x1 b/jubjub/doc/evidence/x1 deleted file mode 100644 index c8c8fc3088..0000000000 --- a/jubjub/doc/evidence/x1 +++ /dev/null @@ -1 +0,0 @@ -8076246640662884909881801758704306714034609987455869804520522091855516602923 diff --git a/jubjub/doc/evidence/y0 b/jubjub/doc/evidence/y0 deleted file mode 100644 index b47cd2764f..0000000000 --- a/jubjub/doc/evidence/y0 +++ /dev/null @@ -1 +0,0 @@ -44412834903739585386157632289020980010620626017712148233229312325549216099227 diff --git a/jubjub/doc/evidence/y1 b/jubjub/doc/evidence/y1 deleted file mode 100644 index a46479fb58..0000000000 --- a/jubjub/doc/evidence/y1 +++ /dev/null @@ -1 +0,0 @@ -13262374693698910701929044844600465831413122818447359594527400194675274060458 diff --git a/jubjub/src/fr.rs b/jubjub/src/fr.rs deleted file mode 100644 index e46953027e..0000000000 --- a/jubjub/src/fr.rs +++ /dev/null @@ -1,1024 +0,0 @@ -//! This module provides an implementation of the Jubjub scalar field $\mathbb{F}_r$ -//! where `r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` - -use core::convert::TryInto; -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::util::{adc, mac, sbb}; - -/// Represents an element of the scalar field $\mathbb{F}_r$ of the Jubjub elliptic -/// curve construction. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. Elements of Fr are always in -// Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. -#[derive(Clone, Copy, Eq)] -pub struct Fr(pub(crate) [u64; 4]); - -impl fmt::Debug for Fr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_bytes(); - write!(f, "0x")?; - for &b in tmp.iter().rev() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } -} - -impl From for Fr { - fn from(val: u64) -> Fr { - Fr([val, 0, 0, 0]) * R2 - } -} - -impl ConstantTimeEq for Fr { - fn ct_eq(&self, other: &Self) -> Choice { - self.0[0].ct_eq(&other.0[0]) - & self.0[1].ct_eq(&other.0[1]) - & self.0[2].ct_eq(&other.0[2]) - & self.0[3].ct_eq(&other.0[3]) - } -} - -impl PartialEq for Fr { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 - } -} - -impl ConditionallySelectable for Fr { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fr([ - u64::conditional_select(&a.0[0], &b.0[0], choice), - u64::conditional_select(&a.0[1], &b.0[1], choice), - u64::conditional_select(&a.0[2], &b.0[2], choice), - u64::conditional_select(&a.0[3], &b.0[3], choice), - ]) - } -} - -/// Constant representing the modulus -/// r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 -pub const MODULUS: Fr = Fr([ - 0xd097_0e5e_d6f7_2cb7, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, -]); - -impl<'a> Neg for &'a Fr { - type Output = Fr; - - #[inline] - fn neg(self) -> Fr { - self.neg() - } -} - -impl Neg for Fr { - type Output = Fr; - - #[inline] - fn neg(self) -> Fr { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fr> for &'a Fr { - type Output = Fr; - - #[inline] - fn sub(self, rhs: &'b Fr) -> Fr { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fr> for &'a Fr { - type Output = Fr; - - #[inline] - fn add(self, rhs: &'b Fr) -> Fr { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a Fr { - type Output = Fr; - - #[inline] - fn mul(self, rhs: &'b Fr) -> Fr { - // Schoolbook multiplication - - self.mul(rhs) - } -} - -impl_binops_additive!(Fr, Fr); -impl_binops_multiplicative!(Fr, Fr); - -/// INV = -(r^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0x1ba3_a358_ef78_8ef9; - -/// R = 2^256 mod r -const R: Fr = Fr([ - 0x25f8_0bb3_b996_07d9, - 0xf315_d62f_66b6_e750, - 0x9325_14ee_eb88_14f4, - 0x09a6_fc6f_4791_55c6, -]); - -/// R^2 = 2^512 mod r -const R2: Fr = Fr([ - 0x6771_9aa4_95e5_7731, - 0x51b0_cef0_9ce3_fc26, - 0x69da_b7fa_c026_e9a5, - 0x04f6_547b_8d12_7688, -]); - -/// R^2 = 2^768 mod r -const R3: Fr = Fr([ - 0xe0d6_c656_3d83_0544, - 0x323e_3883_598d_0f85, - 0xf0fe_a300_4c2e_2ba8, - 0x0587_4f84_9467_37ec, -]); - -impl Default for Fr { - fn default() -> Self { - Self::zero() - } -} - -impl Fr { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> Fr { - Fr([0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> Fr { - R - } - - /// Doubles this field element. - #[inline] - pub const fn double(&self) -> Fr { - self.add(self) - } - - /// Attempts to convert a little-endian byte representation of - /// a field element into an element of `Fr`, failing if the input - /// is not canonical (is not smaller than r). - pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - let mut tmp = Fr([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); - tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); - tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); - tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); - - // If the element is smaller than MODULUS then the - // subtraction will underflow, producing a borrow value - // of 0xffff...ffff. Otherwise, it'll be zero. - let is_some = (borrow as u8) & 1; - - // Convert to Montgomery form by computing - // (a.R^0 * R^2) / R = a.R - tmp *= &R2; - - CtOption::new(tmp, Choice::from(is_some)) - } - - /// Converts an element of `Fr` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 32] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Fr::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - let mut res = [0; 32]; - res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); - - res - } - - /// Converts a 512-bit little endian integer into - /// an element of Fr by reducing modulo r. - pub fn from_bytes_wide(bytes: &[u8; 64]) -> Fr { - Fr::from_u512([ - u64::from_le_bytes(bytes[0..8].try_into().unwrap()), - u64::from_le_bytes(bytes[8..16].try_into().unwrap()), - u64::from_le_bytes(bytes[16..24].try_into().unwrap()), - u64::from_le_bytes(bytes[24..32].try_into().unwrap()), - u64::from_le_bytes(bytes[32..40].try_into().unwrap()), - u64::from_le_bytes(bytes[40..48].try_into().unwrap()), - u64::from_le_bytes(bytes[48..56].try_into().unwrap()), - u64::from_le_bytes(bytes[56..64].try_into().unwrap()), - ]) - } - - fn from_u512(limbs: [u64; 8]) -> Fr { - // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits - // with the higher bits multiplied by 2^256. Thus, we perform two reductions - // - // 1. the lower bits are multiplied by R^2, as normal - // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 - // - // and computing their sum in the field. It remains to see that arbitrary 256-bit - // numbers can be placed into Montgomery form safely using the reduction. The - // reduction works so long as the product is less than R=2^256 multipled by - // the modulus. This holds because for any `c` smaller than the modulus, we have - // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the - // reduction always works so long as `c` is in the field; in this case it is either the - // constant `R2` or `R3`. - let d0 = Fr([limbs[0], limbs[1], limbs[2], limbs[3]]); - let d1 = Fr([limbs[4], limbs[5], limbs[6], limbs[7]]); - // Convert to Montgomery form - d0 * R2 + d1 * R3 - } - - /// Converts from an integer represented in little endian - /// into its (congruent) `Fr` representation. - pub const fn from_raw(val: [u64; 4]) -> Self { - (&Fr(val)).mul(&R2) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> Fr { - let (r1, carry) = mac(0, self.0[0], self.0[1], 0); - let (r2, carry) = mac(0, self.0[0], self.0[2], carry); - let (r3, r4) = mac(0, self.0[0], self.0[3], carry); - - let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); - let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); - - let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); - - let r7 = r6 >> 63; - let r6 = (r6 << 1) | (r5 >> 63); - let r5 = (r5 << 1) | (r4 >> 63); - let r4 = (r4 << 1) | (r3 >> 63); - let r3 = (r3 << 1) | (r2 >> 63); - let r2 = (r2 << 1) | (r1 >> 63); - let r1 = r1 << 1; - - let (r0, carry) = mac(0, self.0[0], self.0[0], 0); - let (r1, carry) = adc(0, r1, carry); - let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); - let (r3, carry) = adc(0, r3, carry); - let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); - let (r5, carry) = adc(0, r5, carry); - let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); - let (r7, _) = adc(0, r7, carry); - - Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Computes the square root of this element, if it exists. - pub fn sqrt(&self) -> CtOption { - // Because r = 3 (mod 4) - // sqrt can be done with only one exponentiation, - // via the computation of self^((r + 1) // 4) (mod r) - let sqrt = self.pow_vartime(&[ - 0xb425_c397_b5bd_cb2e, - 0x299a_0824_f332_0420, - 0x4199_cec0_404d_0ec0, - 0x039f_6d3a_994c_ebea, - ]); - - CtOption::new( - sqrt, - (sqrt * sqrt).ct_eq(self), // Only return Some if it's the square root. - ) - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - pub fn pow(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - let mut tmp = res; - tmp.mul_assign(self); - res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); - } - } - res - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - /// - /// **This operation is variable time with respect - /// to the exponent.** If the exponent is fixed, - /// this operation is effectively constant time. - pub fn pow_vartime(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - - if ((*e >> i) & 1) == 1 { - res.mul_assign(self); - } - } - } - res - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - pub fn invert(&self) -> CtOption { - #[inline(always)] - fn square_assign_multi(n: &mut Fr, num_times: usize) { - for _ in 0..num_times { - *n = n.square(); - } - } - // found using https://github.com/kwantam/addchain - let mut t1 = self.square(); - let mut t0 = t1.square(); - let mut t3 = t0 * t1; - let t6 = t3 * self; - let t7 = t6 * t1; - let t12 = t7 * t3; - let t13 = t12 * t0; - let t16 = t12 * t3; - let t2 = t13 * t3; - let t15 = t16 * t3; - let t19 = t2 * t0; - let t9 = t15 * t3; - let t18 = t9 * t3; - let t14 = t18 * t1; - let t4 = t18 * t0; - let t8 = t18 * t3; - let t17 = t14 * t3; - let t11 = t8 * t3; - t1 = t17 * t3; - let t5 = t11 * t3; - t3 = t5 * t0; - t0 = t5.square(); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t3); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t8); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t19); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t13); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t14); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t18); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t17); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t16); - square_assign_multi(&mut t0, 3); - t0.mul_assign(self); - square_assign_multi(&mut t0, 11); - t0.mul_assign(&t11); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t5); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t15); - square_assign_multi(&mut t0, 8); - t0.mul_assign(self); - square_assign_multi(&mut t0, 12); - t0.mul_assign(&t13); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t9); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t15); - square_assign_multi(&mut t0, 14); - t0.mul_assign(&t14); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t13); - square_assign_multi(&mut t0, 2); - t0.mul_assign(self); - square_assign_multi(&mut t0, 6); - t0.mul_assign(self); - square_assign_multi(&mut t0, 9); - t0.mul_assign(&t7); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t12); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t11); - square_assign_multi(&mut t0, 3); - t0.mul_assign(self); - square_assign_multi(&mut t0, 12); - t0.mul_assign(&t9); - square_assign_multi(&mut t0, 11); - t0.mul_assign(&t8); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t7); - square_assign_multi(&mut t0, 4); - t0.mul_assign(&t6); - square_assign_multi(&mut t0, 10); - t0.mul_assign(&t5); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t3); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t4); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t3); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t2); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t2); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t1); - - CtOption::new(t0, !self.ct_eq(&Self::zero())) - } - - #[inline] - #[allow(clippy::too_many_arguments)] - const fn montgomery_reduce( - r0: u64, - r1: u64, - r2: u64, - r3: u64, - r4: u64, - r5: u64, - r6: u64, - r7: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = r0.wrapping_mul(INV); - let (_, carry) = mac(r0, k, MODULUS.0[0], 0); - let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); - let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); - let (r4, carry2) = adc(r4, 0, carry); - - let k = r1.wrapping_mul(INV); - let (_, carry) = mac(r1, k, MODULUS.0[0], 0); - let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); - let (r5, carry2) = adc(r5, carry2, carry); - - let k = r2.wrapping_mul(INV); - let (_, carry) = mac(r2, k, MODULUS.0[0], 0); - let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); - let (r6, carry2) = adc(r6, carry2, carry); - - let k = r3.wrapping_mul(INV); - let (_, carry) = mac(r3, k, MODULUS.0[0], 0); - let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); - let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); - let (r7, _) = adc(r7, carry2, carry); - - // Result may be within MODULUS of the correct value - (&Fr([r4, r5, r6, r7])).sub(&MODULUS) - } - - /// Multiplies this element by another element - #[inline] - pub const fn mul(&self, rhs: &Self) -> Self { - // Schoolbook multiplication - - let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); - let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); - let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); - let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); - - let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); - let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); - let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); - let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); - - let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); - let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); - let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); - let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); - - let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); - let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); - let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); - let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); - - Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Subtracts another element from this element. - #[inline] - pub const fn sub(&self, rhs: &Self) -> Self { - let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); - let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); - let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); - let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); - - // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise - // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. - let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); - let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); - let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); - let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); - - Fr([d0, d1, d2, d3]) - } - - /// Adds this element to another element. - #[inline] - pub const fn add(&self, rhs: &Self) -> Self { - let (d0, carry) = adc(self.0[0], rhs.0[0], 0); - let (d1, carry) = adc(self.0[1], rhs.0[1], carry); - let (d2, carry) = adc(self.0[2], rhs.0[2], carry); - let (d3, _) = adc(self.0[3], rhs.0[3], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Fr([d0, d1, d2, d3])).sub(&MODULUS) - } - - /// Negates this element. - #[inline] - pub const fn neg(&self) -> Self { - // Subtract `self` from `MODULUS` to negate. Ignore the final - // borrow because it cannot underflow; self is guaranteed to - // be in the field. - let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); - let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); - let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); - let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); - - // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is - // zero if `self` was zero, and `u64::max_value()` if self was nonzero. - let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); - - Fr([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) - } -} - -impl<'a> From<&'a Fr> for [u8; 32] { - fn from(value: &'a Fr) -> [u8; 32] { - value.to_bytes() - } -} - -#[test] -fn test_inv() { - // Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating - // by totient(2**64) - 1 - - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(MODULUS.0[0]); - } - inv = inv.wrapping_neg(); - - assert_eq!(inv, INV); -} - -#[test] -fn test_debug() { - assert_eq!( - format!("{:?}", Fr::zero()), - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format!("{:?}", Fr::one()), - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - assert_eq!( - format!("{:?}", R2), - "0x09a6fc6f479155c6932514eeeb8814f4f315d62f66b6e75025f80bb3b99607d9" - ); -} - -#[test] -fn test_equality() { - assert_eq!(Fr::zero(), Fr::zero()); - assert_eq!(Fr::one(), Fr::one()); - assert_eq!(R2, R2); - - assert!(Fr::zero() != Fr::one()); - assert!(Fr::one() != R2); -} - -#[test] -fn test_to_bytes() { - assert_eq!( - Fr::zero().to_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - Fr::one().to_bytes(), - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - R2.to_bytes(), - [ - 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, - 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 - ] - ); - - assert_eq!( - (-&Fr::one()).to_bytes(), - [ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ] - ); -} - -#[test] -fn test_from_bytes() { - assert_eq!( - Fr::from_bytes(&[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Fr::zero() - ); - - assert_eq!( - Fr::from_bytes(&[ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Fr::one() - ); - - assert_eq!( - Fr::from_bytes(&[ - 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, - 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 - ]) - .unwrap(), - R2 - ); - - // -1 should work - assert!( - Fr::from_bytes(&[ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_some() - .unwrap_u8() - == 1 - ); - - // modulus is invalid - assert!( - Fr::from_bytes(&[ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_none() - .unwrap_u8() - == 1 - ); - - // Anything larger than the modulus is invalid - assert!( - Fr::from_bytes(&[ - 184, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_none() - .unwrap_u8() - == 1 - ); - - assert!( - Fr::from_bytes(&[ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 104, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_none() - .unwrap_u8() - == 1 - ); - - assert!( - Fr::from_bytes(&[ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 15 - ]) - .is_none() - .unwrap_u8() - == 1 - ); -} - -#[test] -fn test_from_u512_zero() { - assert_eq!( - Fr::zero(), - Fr::from_u512([ - MODULUS.0[0], - MODULUS.0[1], - MODULUS.0[2], - MODULUS.0[3], - 0, - 0, - 0, - 0 - ]) - ); -} - -#[test] -fn test_from_u512_r() { - assert_eq!(R, Fr::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); -} - -#[test] -fn test_from_u512_r2() { - assert_eq!(R2, Fr::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); -} - -#[test] -fn test_from_u512_max() { - let max_u64 = 0xffff_ffff_ffff_ffff; - assert_eq!( - R3 - R, - Fr::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) - ); -} - -#[test] -fn test_from_bytes_wide_r2() { - assert_eq!( - R2, - Fr::from_bytes_wide(&[ - 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, - 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_negative_one() { - assert_eq!( - -&Fr::one(), - Fr::from_bytes_wide(&[ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_maximum() { - assert_eq!( - Fr([ - 0x8b75_c901_5ae4_2a22, - 0xe590_82e7_bf9e_38b8, - 0x6440_c912_61da_51b3, - 0x0a5e_07ff_b209_91cf, - ]), - Fr::from_bytes_wide(&[0xff; 64]) - ); -} - -#[test] -fn test_zero() { - assert_eq!(Fr::zero(), -&Fr::zero()); - assert_eq!(Fr::zero(), Fr::zero() + Fr::zero()); - assert_eq!(Fr::zero(), Fr::zero() - Fr::zero()); - assert_eq!(Fr::zero(), Fr::zero() * Fr::zero()); -} - -#[cfg(test)] -const LARGEST: Fr = Fr([ - 0xd097_0e5e_d6f7_2cb6, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, -]); - -#[test] -fn test_addition() { - let mut tmp = LARGEST; - tmp += &LARGEST; - - assert_eq!( - tmp, - Fr([ - 0xd097_0e5e_d6f7_2cb5, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9 - ]) - ); - - let mut tmp = LARGEST; - tmp += &Fr([1, 0, 0, 0]); - - assert_eq!(tmp, Fr::zero()); -} - -#[test] -fn test_negation() { - let tmp = -&LARGEST; - - assert_eq!(tmp, Fr([1, 0, 0, 0])); - - let tmp = -&Fr::zero(); - assert_eq!(tmp, Fr::zero()); - let tmp = -&Fr([1, 0, 0, 0]); - assert_eq!(tmp, LARGEST); -} - -#[test] -fn test_subtraction() { - let mut tmp = LARGEST; - tmp -= &LARGEST; - - assert_eq!(tmp, Fr::zero()); - - let mut tmp = Fr::zero(); - tmp -= &LARGEST; - - let mut tmp2 = MODULUS; - tmp2 -= &LARGEST; - - assert_eq!(tmp, tmp2); -} - -#[test] -fn test_multiplication() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp *= &cur; - - let mut tmp2 = Fr::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_squaring() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp = tmp.square(); - - let mut tmp2 = Fr::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_inversion() { - assert_eq!(Fr::zero().invert().is_none().unwrap_u8(), 1); - assert_eq!(Fr::one().invert().unwrap(), Fr::one()); - assert_eq!((-&Fr::one()).invert().unwrap(), -&Fr::one()); - - let mut tmp = R2; - - for _ in 0..100 { - let mut tmp2 = tmp.invert().unwrap(); - tmp2.mul_assign(&tmp); - - assert_eq!(tmp2, Fr::one()); - - tmp.add_assign(&R2); - } -} - -#[test] -fn test_invert_is_pow() { - let r_minus_2 = [ - 0xd097_0e5e_d6f7_2cb5, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, - ]; - - let mut r1 = R; - let mut r2 = R; - let mut r3 = R; - - for _ in 0..100 { - r1 = r1.invert().unwrap(); - r2 = r2.pow_vartime(&r_minus_2); - r3 = r3.pow(&r_minus_2); - - assert_eq!(r1, r2); - assert_eq!(r2, r3); - // Add R so we check something different next time around - r1.add_assign(&R); - r2 = r1; - r3 = r1; - } -} - -#[test] -fn test_sqrt() { - let mut square = Fr([ - // r - 2 - 0xd097_0e5e_d6f7_2cb5, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, - ]); - - let mut none_count = 0; - - for _ in 0..100 { - let square_root = square.sqrt(); - if square_root.is_none().unwrap_u8() == 1 { - none_count += 1; - } else { - assert_eq!(square_root.unwrap() * square_root.unwrap(), square); - } - square -= Fr::one(); - } - - assert_eq!(47, none_count); -} - -#[test] -fn test_from_raw() { - assert_eq!( - Fr::from_raw([ - 0x25f8_0bb3_b996_07d8, - 0xf315_d62f_66b6_e750, - 0x9325_14ee_eb88_14f4, - 0x09a6_fc6f_4791_55c6, - ]), - Fr::from_raw([0xffff_ffff_ffff_ffff; 4]) - ); - - assert_eq!(Fr::from_raw(MODULUS.0), Fr::zero()); - - assert_eq!(Fr::from_raw([1, 0, 0, 0]), R); -} diff --git a/jubjub/src/lib.rs b/jubjub/src/lib.rs deleted file mode 100644 index 09cb97ee23..0000000000 --- a/jubjub/src/lib.rs +++ /dev/null @@ -1,1328 +0,0 @@ -//! This crate provides an implementation of the **Jubjub** elliptic curve and its associated -//! field arithmetic. See [`README.md`](https://github.com/zkcrypto/jubjub/blob/master/README.md) for more details about Jubjub. -//! -//! # API -//! -//! * `AffinePoint` / `ExtendedPoint` which are implementations of Jubjub group arithmetic -//! * `AffineNielsPoint` / `ExtendedNielsPoint` which are pre-processed Jubjub points -//! * `Fq`, which is the base field of Jubjub -//! * `Fr`, which is the scalar field of Jubjub -//! * `batch_normalize` for converting many `ExtendedPoint`s into `AffinePoint`s efficiently. -//! -//! # Constant Time -//! -//! All operations are constant time unless explicitly noted; these functions will contain -//! "vartime" in their name and they will be documented as variable time. -//! -//! This crate uses the `subtle` crate to perform constant-time operations. - -#![no_std] -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] -#![deny(missing_debug_implementations)] -#![deny(missing_docs)] -#![deny(unsafe_code)] -// This lint is described at -// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl -// In our library, some of the arithmetic will necessarily involve various binary -// operators, and so this lint is triggered unnecessarily. -#![allow(clippy::suspicious_arithmetic_impl)] - -#[cfg(test)] -#[macro_use] -extern crate std; - -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -#[macro_use] -mod util; - -mod fr; -pub use bls12_381::Scalar as Fq; -pub use fr::Fr; - -const FR_MODULUS_BYTES: [u8; 32] = [ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, - 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, -]; - -/// This represents a Jubjub point in the affine `(u, v)` -/// coordinates. -#[derive(Clone, Copy, Debug)] -pub struct AffinePoint { - u: Fq, - v: Fq, -} - -impl Neg for AffinePoint { - type Output = AffinePoint; - - /// This computes the negation of a point `P = (u, v)` - /// as `-P = (-u, v)`. - #[inline] - fn neg(self) -> AffinePoint { - AffinePoint { - u: -self.u, - v: self.v, - } - } -} - -impl ConstantTimeEq for AffinePoint { - fn ct_eq(&self, other: &Self) -> Choice { - self.u.ct_eq(&other.u) & self.v.ct_eq(&other.v) - } -} - -impl PartialEq for AffinePoint { - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 - } -} - -impl ConditionallySelectable for AffinePoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - AffinePoint { - u: Fq::conditional_select(&a.u, &b.u, choice), - v: Fq::conditional_select(&a.v, &b.v, choice), - } - } -} - -/// This represents an extended point `(U, V, Z, T1, T2)` -/// with `Z` nonzero, corresponding to the affine point -/// `(U/Z, V/Z)`. We always have `T1 * T2 = UV/Z`. -/// -/// You can do the following things with a point in this -/// form: -/// -/// * Convert it into a point in the affine form. -/// * Add it to an `ExtendedPoint`, `AffineNielsPoint` or `ExtendedNielsPoint`. -/// * Double it using `double()`. -/// * Compare it with another extended point using `PartialEq` or `ct_eq()`. -#[derive(Clone, Copy, Debug)] -pub struct ExtendedPoint { - u: Fq, - v: Fq, - z: Fq, - t1: Fq, - t2: Fq, -} - -impl ConstantTimeEq for ExtendedPoint { - fn ct_eq(&self, other: &Self) -> Choice { - // (u/z, v/z) = (u'/z', v'/z') is implied by - // (uz'z = u'z'z) and - // (vz'z = v'z'z) - // as z and z' are always nonzero. - - (self.u * other.z).ct_eq(&(other.u * self.z)) - & (self.v * other.z).ct_eq(&(other.v * self.z)) - } -} - -impl ConditionallySelectable for ExtendedPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ExtendedPoint { - u: Fq::conditional_select(&a.u, &b.u, choice), - v: Fq::conditional_select(&a.v, &b.v, choice), - z: Fq::conditional_select(&a.z, &b.z, choice), - t1: Fq::conditional_select(&a.t1, &b.t1, choice), - t2: Fq::conditional_select(&a.t2, &b.t2, choice), - } - } -} - -impl PartialEq for ExtendedPoint { - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 - } -} - -impl Neg for ExtendedPoint { - type Output = ExtendedPoint; - - /// Computes the negation of a point `P = (U, V, Z, T)` - /// as `-P = (-U, V, Z, -T1, T2)`. The choice of `T1` - /// is made without loss of generality. - #[inline] - fn neg(self) -> ExtendedPoint { - ExtendedPoint { - u: -self.u, - v: self.v, - z: self.z, - t1: -self.t1, - t2: self.t2, - } - } -} - -impl From for ExtendedPoint { - /// Constructs an extended point (with `Z = 1`) from - /// an affine point using the map `(u, v) => (u, v, 1, u, v)`. - fn from(affine: AffinePoint) -> ExtendedPoint { - ExtendedPoint { - u: affine.u, - v: affine.v, - z: Fq::one(), - t1: affine.u, - t2: affine.v, - } - } -} - -impl<'a> From<&'a ExtendedPoint> for AffinePoint { - /// Constructs an affine point from an extended point - /// using the map `(U, V, Z, T1, T2) => (U/Z, V/Z)` - /// as Z is always nonzero. **This requires a field inversion - /// and so it is recommended to perform these in a batch - /// using [`batch_normalize`](crate::batch_normalize) instead.** - fn from(extended: &'a ExtendedPoint) -> AffinePoint { - // Z coordinate is always nonzero, so this is - // its inverse. - let zinv = extended.z.invert().unwrap(); - - AffinePoint { - u: extended.u * zinv, - v: extended.v * zinv, - } - } -} - -impl From for AffinePoint { - fn from(extended: ExtendedPoint) -> AffinePoint { - AffinePoint::from(&extended) - } -} - -/// This is a pre-processed version of an affine point `(u, v)` -/// in the form `(v + u, v - u, u * v * 2d)`. This can be added to an -/// [`ExtendedPoint`](crate::ExtendedPoint). -#[derive(Clone, Copy, Debug)] -pub struct AffineNielsPoint { - v_plus_u: Fq, - v_minus_u: Fq, - t2d: Fq, -} - -impl AffineNielsPoint { - /// Constructs this point from the neutral element `(0, 1)`. - pub const fn identity() -> Self { - AffineNielsPoint { - v_plus_u: Fq::one(), - v_minus_u: Fq::one(), - t2d: Fq::zero(), - } - } - - #[inline] - fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { - let zero = AffineNielsPoint::identity(); - - let mut acc = ExtendedPoint::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading four bits because they're always - // unset for Fr. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(4) - { - acc = acc.double(); - acc += AffineNielsPoint::conditional_select(&zero, &self, bit); - } - - acc - } - - /// Multiplies this point by the specific little-endian bit pattern in the - /// given byte array, ignoring the highest four bits. - pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { - self.multiply(by) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a AffineNielsPoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative_mixed!(AffineNielsPoint, Fr, ExtendedPoint); - -impl ConditionallySelectable for AffineNielsPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - AffineNielsPoint { - v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), - v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), - t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), - } - } -} - -/// This is a pre-processed version of an extended point `(U, V, Z, T1, T2)` -/// in the form `(V + U, V - U, Z, T1 * T2 * 2d)`. -#[derive(Clone, Copy, Debug)] -pub struct ExtendedNielsPoint { - v_plus_u: Fq, - v_minus_u: Fq, - z: Fq, - t2d: Fq, -} - -impl ConditionallySelectable for ExtendedNielsPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ExtendedNielsPoint { - v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), - v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), - z: Fq::conditional_select(&a.z, &b.z, choice), - t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), - } - } -} - -impl ExtendedNielsPoint { - /// Constructs this point from the neutral element `(0, 1)`. - pub const fn identity() -> Self { - ExtendedNielsPoint { - v_plus_u: Fq::one(), - v_minus_u: Fq::one(), - z: Fq::one(), - t2d: Fq::zero(), - } - } - - #[inline] - fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { - let zero = ExtendedNielsPoint::identity(); - - let mut acc = ExtendedPoint::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading four bits because they're always - // unset for Fr. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(4) - { - acc = acc.double(); - acc += ExtendedNielsPoint::conditional_select(&zero, &self, bit); - } - - acc - } - - /// Multiplies this point by the specific little-endian bit pattern in the - /// given byte array, ignoring the highest four bits. - pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { - self.multiply(by) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, ExtendedPoint); - -// `d = -(10240/10241)` -const EDWARDS_D: Fq = Fq::from_raw([ - 0x0106_5fd6_d634_3eb1, - 0x292d_7f6d_3757_9d26, - 0xf5fd_9207_e6bd_7fd4, - 0x2a93_18e7_4bfa_2b48, -]); - -// `2*d` -const EDWARDS_D2: Fq = Fq::from_raw([ - 0x020c_bfad_ac68_7d62, - 0x525a_feda_6eaf_3a4c, - 0xebfb_240f_cd7a_ffa8, - 0x5526_31ce_97f4_5691, -]); - -impl AffinePoint { - /// Constructs the neutral element `(0, 1)`. - pub const fn identity() -> Self { - AffinePoint { - u: Fq::zero(), - v: Fq::one(), - } - } - - /// Multiplies this point by the cofactor, producing an - /// `ExtendedPoint` - pub fn mul_by_cofactor(&self) -> ExtendedPoint { - ExtendedPoint::from(*self).mul_by_cofactor() - } - - /// Determines if this point is of small order. - pub fn is_small_order(&self) -> Choice { - ExtendedPoint::from(*self).is_small_order() - } - - /// Determines if this point is torsion free and so is - /// in the prime order subgroup. - pub fn is_torsion_free(&self) -> Choice { - ExtendedPoint::from(*self).is_torsion_free() - } - - /// Determines if this point is prime order, or in other words that - /// the smallest scalar multiplied by this point that produces the - /// identity is `r`. This is equivalent to checking that the point - /// is both torsion free and not the identity. - pub fn is_prime_order(&self) -> Choice { - let extended = ExtendedPoint::from(*self); - extended.is_torsion_free() & (!extended.is_identity()) - } - - /// Converts this element into its byte representation. - pub fn to_bytes(&self) -> [u8; 32] { - let mut tmp = self.v.to_bytes(); - let u = self.u.to_bytes(); - - // Encode the sign of the u-coordinate in the most - // significant bit. - tmp[31] |= u[0] << 7; - - tmp - } - - /// Attempts to interpret a byte representation of an - /// affine point, failing if the element is not on - /// the curve or non-canonical. - pub fn from_bytes(mut b: [u8; 32]) -> CtOption { - // Grab the sign bit from the representation - let sign = b[31] >> 7; - - // Mask away the sign bit - b[31] &= 0b0111_1111; - - // Interpret what remains as the v-coordinate - Fq::from_bytes(&b).and_then(|v| { - // -u^2 + v^2 = 1 + d.u^2.v^2 - // -u^2 = 1 + d.u^2.v^2 - v^2 (rearrange) - // -u^2 - d.u^2.v^2 = 1 - v^2 (rearrange) - // u^2 + d.u^2.v^2 = v^2 - 1 (flip signs) - // u^2 (1 + d.v^2) = v^2 - 1 (factor) - // u^2 = (v^2 - 1) / (1 + d.v^2) (isolate u^2) - // We know that (1 + d.v^2) is nonzero for all v: - // (1 + d.v^2) = 0 - // d.v^2 = -1 - // v^2 = -(1 / d) No solutions, as -(1 / d) is not a square - - let v2 = v.square(); - - ((v2 - Fq::one()) * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) - .sqrt() - .and_then(|u| { - // Fix the sign of `u` if necessary - let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); - let u_negated = -u; - let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); - - CtOption::new(AffinePoint { u: final_u, v }, Choice::from(1u8)) - }) - }) - } - - /// Returns the `u`-coordinate of this point. - pub fn get_u(&self) -> Fq { - self.u - } - - /// Returns the `v`-coordinate of this point. - pub fn get_v(&self) -> Fq { - self.v - } - - /// Performs a pre-processing step that produces an `AffineNielsPoint` - /// for use in multiple additions. - pub const fn to_niels(&self) -> AffineNielsPoint { - AffineNielsPoint { - v_plus_u: Fq::add(&self.v, &self.u), - v_minus_u: Fq::sub(&self.v, &self.u), - t2d: Fq::mul(&Fq::mul(&self.u, &self.v), &EDWARDS_D2), - } - } - - /// Constructs an AffinePoint given `u` and `v` without checking - /// that the point is on the curve. - pub const fn from_raw_unchecked(u: Fq, v: Fq) -> AffinePoint { - AffinePoint { u, v } - } - - /// This is only for debugging purposes and not - /// exposed in the public API. Checks that this - /// point is on the curve. - #[cfg(test)] - fn is_on_curve_vartime(&self) -> bool { - let u2 = self.u.square(); - let v2 = self.v.square(); - - v2 - u2 == Fq::one() + EDWARDS_D * u2 * v2 - } -} - -impl ExtendedPoint { - /// Constructs an extended point from the neutral element `(0, 1)`. - pub const fn identity() -> Self { - ExtendedPoint { - u: Fq::zero(), - v: Fq::one(), - z: Fq::one(), - t1: Fq::zero(), - t2: Fq::zero(), - } - } - - /// Determines if this point is the identity. - pub fn is_identity(&self) -> Choice { - // If this point is the identity, then - // u = 0 * z = 0 - // and v = 1 * z = z - self.u.ct_eq(&Fq::zero()) & self.v.ct_eq(&self.z) - } - - /// Determines if this point is of small order. - pub fn is_small_order(&self) -> Choice { - // We only need to perform two doublings, since the 2-torsion - // points are (0, 1) and (0, -1), and so we only need to check - // that the u-coordinate of the result is zero to see if the - // point is small order. - self.double().double().u.ct_eq(&Fq::zero()) - } - - /// Determines if this point is torsion free and so is contained - /// in the prime order subgroup. - pub fn is_torsion_free(&self) -> Choice { - self.multiply(&FR_MODULUS_BYTES).is_identity() - } - - /// Determines if this point is prime order, or in other words that - /// the smallest scalar multiplied by this point that produces the - /// identity is `r`. This is equivalent to checking that the point - /// is both torsion free and not the identity. - pub fn is_prime_order(&self) -> Choice { - self.is_torsion_free() & (!self.is_identity()) - } - - /// Multiplies this element by the cofactor `8`. - pub fn mul_by_cofactor(&self) -> ExtendedPoint { - self.double().double().double() - } - - /// Performs a pre-processing step that produces an `ExtendedNielsPoint` - /// for use in multiple additions. - pub fn to_niels(&self) -> ExtendedNielsPoint { - ExtendedNielsPoint { - v_plus_u: self.v + self.u, - v_minus_u: self.v - self.u, - z: self.z, - t2d: self.t1 * self.t2 * EDWARDS_D2, - } - } - - /// Computes the doubling of a point more efficiently than a point can - /// be added to itself. - pub fn double(&self) -> ExtendedPoint { - // Doubling is more efficient (three multiplications, four squarings) - // when we work within the projective coordinate space (U:Z, V:Z). We - // rely on the most efficient formula, "dbl-2008-bbjlp", as described - // in Section 6 of "Twisted Edwards Curves" by Bernstein et al. - // - // See - // for more information. - // - // We differ from the literature in that we use (u, v) rather than - // (x, y) coordinates. We also have the constant `a = -1` implied. Let - // us rewrite the procedure of doubling (u, v, z) to produce (U, V, Z) - // as follows: - // - // B = (u + v)^2 - // C = u^2 - // D = v^2 - // F = D - C - // H = 2 * z^2 - // J = F - H - // U = (B - C - D) * J - // V = F * (- C - D) - // Z = F * J - // - // If we compute K = D + C, we can rewrite this: - // - // B = (u + v)^2 - // C = u^2 - // D = v^2 - // F = D - C - // K = D + C - // H = 2 * z^2 - // J = F - H - // U = (B - K) * J - // V = F * (-K) - // Z = F * J - // - // In order to avoid the unnecessary negation of K, - // we will negate J, transforming the result into - // an equivalent point with a negated z-coordinate. - // - // B = (u + v)^2 - // C = u^2 - // D = v^2 - // F = D - C - // K = D + C - // H = 2 * z^2 - // J = H - F - // U = (B - K) * J - // V = F * K - // Z = F * J - // - // Let us rename some variables to simplify: - // - // UV2 = (u + v)^2 - // UU = u^2 - // VV = v^2 - // VVmUU = VV - UU - // VVpUU = VV + UU - // ZZ2 = 2 * z^2 - // J = ZZ2 - VVmUU - // U = (UV2 - VVpUU) * J - // V = VVmUU * VVpUU - // Z = VVmUU * J - // - // We wish to obtain two factors of T = UV/Z. - // - // UV/Z = (UV2 - VVpUU) * (ZZ2 - VVmUU) * VVmUU * VVpUU / VVmUU / (ZZ2 - VVmUU) - // = (UV2 - VVpUU) * VVmUU * VVpUU / VVmUU - // = (UV2 - VVpUU) * VVpUU - // - // and so we have that T1 = (UV2 - VVpUU) and T2 = VVpUU. - - let uu = self.u.square(); - let vv = self.v.square(); - let zz2 = self.z.square().double(); - let uv2 = (self.u + self.v).square(); - let vv_plus_uu = vv + uu; - let vv_minus_uu = vv - uu; - - // The remaining arithmetic is exactly the process of converting - // from a completed point to an extended point. - CompletedPoint { - u: uv2 - vv_plus_uu, - v: vv_plus_uu, - z: vv_minus_uu, - t: zz2 - vv_minus_uu, - } - .into_extended() - } - - #[inline] - fn multiply(self, by: &[u8; 32]) -> Self { - self.to_niels().multiply(by) - } - - /// This is only for debugging purposes and not - /// exposed in the public API. Checks that this - /// point is on the curve. - #[cfg(test)] - fn is_on_curve_vartime(&self) -> bool { - let affine = AffinePoint::from(*self); - - self.z != Fq::zero() - && affine.is_on_curve_vartime() - && (affine.u * affine.v * self.z == self.t1 * self.t2) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative!(ExtendedPoint, Fr); - -impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn add(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { - // We perform addition in the extended coordinates. Here we use - // a formula presented by Hisil, Wong, Carter and Dawson in - // "Twisted Edward Curves Revisited" which only requires 8M. - // - // A = (V1 - U1) * (V2 - U2) - // B = (V1 + U1) * (V2 + U2) - // C = 2d * T1 * T2 - // D = 2 * Z1 * Z2 - // E = B - A - // F = D - C - // G = D + C - // H = B + A - // U3 = E * F - // Y3 = G * H - // Z3 = F * G - // T3 = E * H - - let a = (self.v - self.u) * other.v_minus_u; - let b = (self.v + self.u) * other.v_plus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = (self.z * other.z).double(); - - // The remaining arithmetic is exactly the process of converting - // from a completed point to an extended point. - CompletedPoint { - u: b - a, - v: b + a, - z: d + c, - t: d - c, - } - .into_extended() - } -} - -impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { - let a = (self.v - self.u) * other.v_plus_u; - let b = (self.v + self.u) * other.v_minus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = (self.z * other.z).double(); - - CompletedPoint { - u: b - a, - v: b + a, - z: d - c, - t: d + c, - } - .into_extended() - } -} - -impl_binops_additive!(ExtendedPoint, ExtendedNielsPoint); - -impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn add(self, other: &'b AffineNielsPoint) -> ExtendedPoint { - // This is identical to the addition formula for `ExtendedNielsPoint`, - // except we can assume that `other.z` is one, so that we perform - // 7 multiplications. - - let a = (self.v - self.u) * other.v_minus_u; - let b = (self.v + self.u) * other.v_plus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = self.z.double(); - - // The remaining arithmetic is exactly the process of converting - // from a completed point to an extended point. - CompletedPoint { - u: b - a, - v: b + a, - z: d + c, - t: d - c, - } - .into_extended() - } -} - -impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, other: &'b AffineNielsPoint) -> ExtendedPoint { - let a = (self.v - self.u) * other.v_plus_u; - let b = (self.v + self.u) * other.v_minus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = self.z.double(); - - CompletedPoint { - u: b - a, - v: b + a, - z: d - c, - t: d + c, - } - .into_extended() - } -} - -impl_binops_additive!(ExtendedPoint, AffineNielsPoint); - -impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint { - self + other.to_niels() - } -} - -impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint { - self - other.to_niels() - } -} - -impl_binops_additive!(ExtendedPoint, ExtendedPoint); - -impl<'a, 'b> Add<&'b AffinePoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn add(self, other: &'b AffinePoint) -> ExtendedPoint { - self + other.to_niels() - } -} - -impl<'a, 'b> Sub<&'b AffinePoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn sub(self, other: &'b AffinePoint) -> ExtendedPoint { - self - other.to_niels() - } -} - -impl_binops_additive!(ExtendedPoint, AffinePoint); - -/// This is a "completed" point produced during a point doubling or -/// addition routine. These points exist in the `(U:Z, V:T)` model -/// of the curve. This is not exposed in the API because it is -/// an implementation detail. -struct CompletedPoint { - u: Fq, - v: Fq, - z: Fq, - t: Fq, -} - -impl CompletedPoint { - /// This converts a completed point into an extended point by - /// homogenizing: - /// - /// (u/z, v/t) = (u/z * t/t, v/t * z/z) = (ut/zt, vz/zt) - /// - /// The resulting T coordinate is utvz/zt = uv, and so - /// T1 = u, T2 = v, without loss of generality. - #[inline] - fn into_extended(self) -> ExtendedPoint { - ExtendedPoint { - u: self.u * self.t, - v: self.v * self.z, - z: self.z * self.t, - t1: self.u, - t2: self.v, - } - } -} - -impl Default for AffinePoint { - /// Returns the identity. - fn default() -> AffinePoint { - AffinePoint::identity() - } -} - -impl Default for ExtendedPoint { - /// Returns the identity. - fn default() -> ExtendedPoint { - ExtendedPoint::identity() - } -} - -/// This takes a mutable slice of `ExtendedPoint`s and "normalizes" them using -/// only a single inversion for the entire batch. This normalization results in -/// all of the points having a Z-coordinate of one. Further, an iterator is -/// returned which can be used to obtain `AffinePoint`s for each element in the -/// slice. -/// -/// This costs 5 multiplications per element, and a field inversion. -pub fn batch_normalize<'a>(v: &'a mut [ExtendedPoint]) -> impl Iterator + 'a { - let mut acc = Fq::one(); - for p in v.iter_mut() { - // We use the `t1` field of `ExtendedPoint` to store the product - // of previous z-coordinates seen. - p.t1 = acc; - acc *= &p.z; - } - - // This is the inverse, as all z-coordinates are nonzero. - acc = acc.invert().unwrap(); - - for p in v.iter_mut().rev() { - let mut q = *p; - - // Compute tmp = 1/z - let tmp = q.t1 * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc *= &q.z; - - // Set the coordinates to the correct value - q.u *= &tmp; // Multiply by 1/z - q.v *= &tmp; // Multiply by 1/z - q.z = Fq::one(); // z-coordinate is now one - q.t1 = q.u; - q.t2 = q.v; - - *p = q; - } - - // All extended points are now normalized, but the type - // doesn't encode this fact. Let us offer affine points - // to the caller. - - v.iter().map(|p| AffinePoint { u: p.u, v: p.v }) -} - -#[test] -fn test_is_on_curve_var() { - assert!(AffinePoint::identity().is_on_curve_vartime()); -} - -#[test] -fn test_d_is_non_quadratic_residue() { - assert!(EDWARDS_D.sqrt().is_none().unwrap_u8() == 1); - assert!((-EDWARDS_D).sqrt().is_none().unwrap_u8() == 1); - assert!((-EDWARDS_D).invert().unwrap().sqrt().is_none().unwrap_u8() == 1); -} - -#[test] -fn test_affine_niels_point_identity() { - assert_eq!( - AffineNielsPoint::identity().v_plus_u, - AffinePoint::identity().to_niels().v_plus_u - ); - assert_eq!( - AffineNielsPoint::identity().v_minus_u, - AffinePoint::identity().to_niels().v_minus_u - ); - assert_eq!( - AffineNielsPoint::identity().t2d, - AffinePoint::identity().to_niels().t2d - ); -} - -#[test] -fn test_extended_niels_point_identity() { - assert_eq!( - ExtendedNielsPoint::identity().v_plus_u, - ExtendedPoint::identity().to_niels().v_plus_u - ); - assert_eq!( - ExtendedNielsPoint::identity().v_minus_u, - ExtendedPoint::identity().to_niels().v_minus_u - ); - assert_eq!( - ExtendedNielsPoint::identity().z, - ExtendedPoint::identity().to_niels().z - ); - assert_eq!( - ExtendedNielsPoint::identity().t2d, - ExtendedPoint::identity().to_niels().t2d - ); -} - -#[test] -fn test_assoc() { - let p = ExtendedPoint::from(AffinePoint { - u: Fq::from_raw([ - 0x81c5_71e5_d883_cfb0, - 0x049f_7a68_6f14_7029, - 0xf539_c860_bc3e_a21f, - 0x4284_715b_7ccc_8162, - ]), - v: Fq::from_raw([ - 0xbf09_6275_684b_b8ca, - 0xc7ba_2458_90af_256d, - 0x5911_9f3e_8638_0eb0, - 0x3793_de18_2f9f_b1d2, - ]), - }) - .mul_by_cofactor(); - assert!(p.is_on_curve_vartime()); - - assert_eq!( - (p * Fr::from(1000u64)) * Fr::from(3938u64), - p * (Fr::from(1000u64) * Fr::from(3938u64)), - ); -} - -#[test] -fn test_batch_normalize() { - let mut p = ExtendedPoint::from(AffinePoint { - u: Fq::from_raw([ - 0x81c5_71e5_d883_cfb0, - 0x049f_7a68_6f14_7029, - 0xf539_c860_bc3e_a21f, - 0x4284_715b_7ccc_8162, - ]), - v: Fq::from_raw([ - 0xbf09_6275_684b_b8ca, - 0xc7ba_2458_90af_256d, - 0x5911_9f3e_8638_0eb0, - 0x3793_de18_2f9f_b1d2, - ]), - }) - .mul_by_cofactor(); - - let mut v = vec![]; - for _ in 0..10 { - v.push(p); - p = p.double(); - } - - for p in &v { - assert!(p.is_on_curve_vartime()); - } - - let expected: std::vec::Vec<_> = v.iter().map(|p| AffinePoint::from(*p)).collect(); - let result1: std::vec::Vec<_> = batch_normalize(&mut v).collect(); - for i in 0..10 { - assert!(expected[i] == result1[i]); - assert!(v[i].is_on_curve_vartime()); - assert!(AffinePoint::from(v[i]) == expected[i]); - } - let result2: std::vec::Vec<_> = batch_normalize(&mut v).collect(); - for i in 0..10 { - assert!(expected[i] == result2[i]); - assert!(v[i].is_on_curve_vartime()); - assert!(AffinePoint::from(v[i]) == expected[i]); - } -} - -#[cfg(test)] -const FULL_GENERATOR: AffinePoint = AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xe4b3_d35d_f1a7_adfe, - 0xcaf5_5d1b_29bf_81af, - 0x8b0f_03dd_d60a_8187, - 0x62ed_cbb8_bf37_87c8, - ]), - Fq::from_raw([0xb, 0x0, 0x0, 0x0]), -); - -#[cfg(test)] -const EIGHT_TORSION: [AffinePoint; 8] = [ - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xd92e_6a79_2720_0d43, - 0x7aa4_1ac4_3dae_8582, - 0xeaaa_e086_a166_18d1, - 0x71d4_df38_ba9e_7973, - ]), - Fq::from_raw([ - 0xff0d_2068_eff4_96dd, - 0x9106_ee90_f384_a4a1, - 0x16a1_3035_ad4d_7266, - 0x4958_bdb2_1966_982e, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xfffe_ffff_0000_0001, - 0x67ba_a400_89fb_5bfe, - 0xa5e8_0b39_939e_d334, - 0x73ed_a753_299d_7d47, - ]), - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xd92e_6a79_2720_0d43, - 0x7aa4_1ac4_3dae_8582, - 0xeaaa_e086_a166_18d1, - 0x71d4_df38_ba9e_7973, - ]), - Fq::from_raw([ - 0x00f2_df96_100b_6924, - 0xc2b6_b572_0c79_b75d, - 0x1c98_a7d2_5c54_659e, - 0x2a94_e9a1_1036_e51a, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - Fq::from_raw([ - 0xffff_ffff_0000_0000, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0x26d1_9585_d8df_f2be, - 0xd919_893e_c24f_d67c, - 0x488e_f781_683b_bf33, - 0x0218_c81a_6eff_03d4, - ]), - Fq::from_raw([ - 0x00f2_df96_100b_6924, - 0xc2b6_b572_0c79_b75d, - 0x1c98_a7d2_5c54_659e, - 0x2a94_e9a1_1036_e51a, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0x0001_0000_0000_0000, - 0xec03_0002_7603_0000, - 0x8d51_ccce_7603_04d0, - 0x0, - ]), - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0x26d1_9585_d8df_f2be, - 0xd919_893e_c24f_d67c, - 0x488e_f781_683b_bf33, - 0x0218_c81a_6eff_03d4, - ]), - Fq::from_raw([ - 0xff0d_2068_eff4_96dd, - 0x9106_ee90_f384_a4a1, - 0x16a1_3035_ad4d_7266, - 0x4958_bdb2_1966_982e, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - Fq::from_raw([0x1, 0x0, 0x0, 0x0]), - ), -]; - -#[test] -fn find_eight_torsion() { - let g = ExtendedPoint::from(FULL_GENERATOR); - assert!(g.is_small_order().unwrap_u8() == 0); - let g = g.multiply(&FR_MODULUS_BYTES); - assert!(g.is_small_order().unwrap_u8() == 1); - - let mut cur = g; - - for (i, point) in EIGHT_TORSION.iter().enumerate() { - let tmp = AffinePoint::from(cur); - if &tmp != point { - panic!("{}th torsion point should be {:?}", i, tmp); - } - - cur += &g; - } -} - -#[test] -fn find_curve_generator() { - let mut trial_bytes = [0; 32]; - for _ in 0..255 { - let a = AffinePoint::from_bytes(trial_bytes); - if a.is_some().unwrap_u8() == 1 { - let a = a.unwrap(); - assert!(a.is_on_curve_vartime()); - let b = ExtendedPoint::from(a); - let b = b.multiply(&FR_MODULUS_BYTES); - assert!(b.is_small_order().unwrap_u8() == 1); - let b = b.double(); - assert!(b.is_small_order().unwrap_u8() == 1); - let b = b.double(); - assert!(b.is_small_order().unwrap_u8() == 1); - if b.is_identity().unwrap_u8() == 0 { - let b = b.double(); - assert!(b.is_small_order().unwrap_u8() == 1); - assert!(b.is_identity().unwrap_u8() == 1); - assert_eq!(FULL_GENERATOR, a); - assert!(a.mul_by_cofactor().is_torsion_free().unwrap_u8() == 1); - return; - } - } - - trial_bytes[0] += 1; - } - - panic!("should have found a generator of the curve"); -} - -#[test] -fn test_small_order() { - for point in EIGHT_TORSION.iter() { - assert!(point.is_small_order().unwrap_u8() == 1); - } -} - -#[test] -fn test_is_identity() { - let a = EIGHT_TORSION[0].mul_by_cofactor(); - let b = EIGHT_TORSION[1].mul_by_cofactor(); - - assert_eq!(a.u, b.u); - assert_eq!(a.v, a.z); - assert_eq!(b.v, b.z); - assert!(a.v != b.v); - assert!(a.z != b.z); - - assert!(a.is_identity().unwrap_u8() == 1); - assert!(b.is_identity().unwrap_u8() == 1); - - for point in EIGHT_TORSION.iter() { - assert!(point.mul_by_cofactor().is_identity().unwrap_u8() == 1); - } -} - -#[test] -fn test_mul_consistency() { - let a = Fr([ - 0x21e6_1211_d993_4f2e, - 0xa52c_058a_693c_3e07, - 0x9ccb_77bf_b12d_6360, - 0x07df_2470_ec94_398e, - ]); - let b = Fr([ - 0x0333_6d1c_be19_dbe0, - 0x0153_618f_6156_a536, - 0x2604_c9e1_fc3c_6b15, - 0x04ae_581c_eb02_8720, - ]); - let c = Fr([ - 0xd7ab_f5bb_2468_3f4c, - 0x9d77_12cc_274b_7c03, - 0x9732_93db_9683_789f, - 0x0b67_7e29_380a_97a7, - ]); - assert_eq!(a * b, c); - let p = ExtendedPoint::from(AffinePoint { - u: Fq::from_raw([ - 0x81c5_71e5_d883_cfb0, - 0x049f_7a68_6f14_7029, - 0xf539_c860_bc3e_a21f, - 0x4284_715b_7ccc_8162, - ]), - v: Fq::from_raw([ - 0xbf09_6275_684b_b8ca, - 0xc7ba_2458_90af_256d, - 0x5911_9f3e_8638_0eb0, - 0x3793_de18_2f9f_b1d2, - ]), - }) - .mul_by_cofactor(); - assert_eq!(p * c, (p * a) * b); - - // Test Mul implemented on ExtendedNielsPoint - assert_eq!(p * c, (p.to_niels() * a) * b); - assert_eq!(p.to_niels() * c, (p * a) * b); - assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b); - - // Test Mul implemented on AffineNielsPoint - let p_affine_niels = AffinePoint::from(p).to_niels(); - assert_eq!(p * c, (p_affine_niels * a) * b); - assert_eq!(p_affine_niels * c, (p * a) * b); - assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b); -} - -#[test] -fn test_serialization_consistency() { - let gen = FULL_GENERATOR.mul_by_cofactor(); - let mut p = gen; - - let v = vec![ - [ - 203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110, 170, 185, 179, 108, 97, - 63, 13, 211, 247, 120, 79, 219, 110, 234, 131, 123, 19, 215, - ], - [ - 113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222, 233, 195, 203, 195, - 167, 129, 89, 121, 240, 142, 51, 166, 64, 250, 184, 202, 154, 177, - ], - [ - 197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149, 141, 240, 29, 14, 82, - 86, 225, 126, 129, 186, 157, 148, 162, 219, 51, 156, 199, - ], - [ - 182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97, 200, 139, 192, 83, - 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177, 19, 179, 0, - ], - [ - 118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38, 214, 176, 250, 4, - 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177, 230, 185, - ], - [ - 226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82, 174, 113, 112, 166, - 138, 171, 166, 113, 52, 251, 129, 197, 138, 45, 195, 7, 61, 140, - ], - [ - 38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135, 204, 215, 0, 33, - 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207, 12, 16, 24, - ], - [ - 17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85, 59, 86, 3, 226, 211, - 67, 179, 29, 238, 181, 102, 142, 58, 63, 57, 89, 174, 138, - ], - [ - 210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8, 140, 142, 216, 93, - 190, 183, 116, 174, 63, 33, 242, 177, 118, 148, 40, 241, 203, - ], - [ - 0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121, 185, 29, 152, 14, - 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53, 54, 68, - ], - [ - 178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56, 81, 239, 155, 122, - 59, 188, 237, 253, 11, 169, 208, 236, 12, 4, 163, 211, 88, 97, - ], - [ - 246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115, 144, 12, 90, 150, - 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67, 75, 57, 67, - ], - [ - 212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248, 22, 201, 208, 166, - 2, 83, 134, 130, 85, 129, 166, 136, 185, 191, 163, 38, 54, 10, - ], - [ - 8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19, 219, 143, 64, 161, - 99, 199, 77, 39, 148, 70, 213, 246, 227, 150, 178, 237, 178, - ], - [ - 11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84, 157, 214, 167, 73, - 233, 115, 81, 124, 134, 15, 31, 181, 60, 184, 130, 175, 159, - ], - [ - 141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80, 247, 9, 107, 124, 0, - 26, 203, 16, 237, 34, 214, 147, 133, 15, 29, 236, 37, 88, - ], - ]; - - for expected_serialized in v { - assert!(p.is_on_curve_vartime()); - let affine = AffinePoint::from(p); - let serialized = affine.to_bytes(); - let deserialized = AffinePoint::from_bytes(serialized).unwrap(); - assert_eq!(affine, deserialized); - assert_eq!(expected_serialized, serialized); - p += gen; - } -} diff --git a/jubjub/src/util.rs b/jubjub/src/util.rs deleted file mode 100644 index bd25dd06a2..0000000000 --- a/jubjub/src/util.rs +++ /dev/null @@ -1,174 +0,0 @@ -/// Compute a + b + carry, returning the result and the new carry over. -#[inline(always)] -pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { - let ret = (a as u128) + (b as u128) + (carry as u128); - (ret as u64, (ret >> 64) as u64) -} - -/// Compute a - (b + borrow), returning the result and the new borrow. -#[inline(always)] -pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { - let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); - (ret as u64, (ret >> 64) as u64) -} - -/// Compute a + (b * c) + carry, returning the result and the new carry over. -#[inline(always)] -pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { - let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); - (ret as u64, (ret >> 64) as u64) -} - -macro_rules! impl_add_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Add<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: &'b $rhs) -> $output { - &self + rhs - } - } - - impl<'a> Add<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - self + &rhs - } - } - - impl Add<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - &self + &rhs - } - } - }; -} - -macro_rules! impl_sub_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Sub<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: &'b $rhs) -> $output { - &self - rhs - } - } - - impl<'a> Sub<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - self - &rhs - } - } - - impl Sub<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - &self - &rhs - } - } - }; -} - -macro_rules! impl_binops_additive_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl_add_binop_specify_output!($lhs, $rhs, $output); - impl_sub_binop_specify_output!($lhs, $rhs, $output); - }; -} - -macro_rules! impl_binops_multiplicative_mixed { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> Mul<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: &'b $rhs) -> $output { - &self * rhs - } - } - - impl<'a> Mul<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - self * &rhs - } - } - - impl Mul<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - &self * &rhs - } - } - }; -} - -macro_rules! impl_binops_additive { - ($lhs:ident, $rhs:ident) => { - impl_binops_additive_specify_output!($lhs, $rhs, $lhs); - - impl SubAssign<$rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: $rhs) { - *self = &*self - &rhs; - } - } - - impl AddAssign<$rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: $rhs) { - *self = &*self + &rhs; - } - } - - impl<'b> SubAssign<&'b $rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: &'b $rhs) { - *self = &*self - rhs; - } - } - - impl<'b> AddAssign<&'b $rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: &'b $rhs) { - *self = &*self + rhs; - } - } - }; -} - -macro_rules! impl_binops_multiplicative { - ($lhs:ident, $rhs:ident) => { - impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); - - impl MulAssign<$rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: $rhs) { - *self = &*self * &rhs; - } - } - - impl<'b> MulAssign<&'b $rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: &'b $rhs) { - *self = &*self * rhs; - } - } - }; -} diff --git a/jubjub/tests/common.rs b/jubjub/tests/common.rs deleted file mode 100644 index a4535edbe3..0000000000 --- a/jubjub/tests/common.rs +++ /dev/null @@ -1,29 +0,0 @@ -use jubjub::*; -use rand_core::{RngCore, SeedableRng}; -use rand_xorshift::XorShiftRng; - -pub const NUM_BLACK_BOX_CHECKS: u32 = 2000; - -pub fn new_rng() -> XorShiftRng { - XorShiftRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) -} - -pub trait MyRandom { - fn new_random(rng: &mut T) -> Self; -} - -impl MyRandom for Fq { - fn new_random(rng: &mut T) -> Self { - let mut random_bytes = [0u8; 64]; - rng.fill_bytes(&mut random_bytes); - Fq::from_bytes_wide(&random_bytes) - } -} - -impl MyRandom for Fr { - fn new_random(rng: &mut T) -> Self { - let mut random_bytes = [0u8; 64]; - rng.fill_bytes(&mut random_bytes); - Fr::from_bytes_wide(&random_bytes) - } -} diff --git a/jubjub/tests/fq_blackbox.rs b/jubjub/tests/fq_blackbox.rs deleted file mode 100644 index a823c9b0c5..0000000000 --- a/jubjub/tests/fq_blackbox.rs +++ /dev/null @@ -1,120 +0,0 @@ -mod common; - -use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; -use jubjub::*; - -#[test] -fn test_to_and_from_bytes() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, Fq::from_bytes(&Fq::to_bytes(&a)).unwrap()); - } -} - -#[test] -fn test_additive_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - let c = Fq::new_random(&mut rng); - assert_eq!((a + b) + c, a + (b + c)) - } -} - -#[test] -fn test_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, a + Fq::zero()); - assert_eq!(a, Fq::zero() + a); - } -} - -#[test] -fn test_subtract_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, a - Fq::zero()); - assert_eq!(a, Fq::zero() - -&a); - } -} - -#[test] -fn test_additive_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let a_neg = -&a; - assert_eq!(Fq::zero(), a + a_neg); - assert_eq!(Fq::zero(), a_neg + a); - } -} - -#[test] -fn test_additive_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - assert_eq!(a + b, b + a); - } -} - -#[test] -fn test_multiplicative_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - let c = Fq::new_random(&mut rng); - assert_eq!((a * b) * c, a * (b * c)) - } -} - -#[test] -fn test_multiplicative_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, a * Fq::one()); - assert_eq!(a, Fq::one() * a); - } -} - -#[test] -fn test_multiplicative_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - if a == Fq::zero() { - continue; - } - let a_inv = a.invert().unwrap(); - assert_eq!(Fq::one(), a * a_inv); - assert_eq!(Fq::one(), a_inv * a); - } -} - -#[test] -fn test_multiplicative_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - assert_eq!(a * b, b * a); - } -} - -#[test] -fn test_multiply_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(Fq::zero(), Fq::zero() * a); - assert_eq!(Fq::zero(), a * Fq::zero()); - } -} diff --git a/jubjub/tests/fr_blackbox.rs b/jubjub/tests/fr_blackbox.rs deleted file mode 100644 index 6e36d0f85f..0000000000 --- a/jubjub/tests/fr_blackbox.rs +++ /dev/null @@ -1,120 +0,0 @@ -mod common; - -use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; -use jubjub::*; - -#[test] -fn test_to_and_from_bytes() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, Fr::from_bytes(&Fr::to_bytes(&a)).unwrap()); - } -} - -#[test] -fn test_additive_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - let c = Fr::new_random(&mut rng); - assert_eq!((a + b) + c, a + (b + c)) - } -} - -#[test] -fn test_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, a + Fr::zero()); - assert_eq!(a, Fr::zero() + a); - } -} - -#[test] -fn test_subtract_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, a - Fr::zero()); - assert_eq!(a, Fr::zero() - -&a); - } -} - -#[test] -fn test_additive_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let a_neg = -&a; - assert_eq!(Fr::zero(), a + a_neg); - assert_eq!(Fr::zero(), a_neg + a); - } -} - -#[test] -fn test_additive_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - assert_eq!(a + b, b + a); - } -} - -#[test] -fn test_multiplicative_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - let c = Fr::new_random(&mut rng); - assert_eq!((a * b) * c, a * (b * c)) - } -} - -#[test] -fn test_multiplicative_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, a * Fr::one()); - assert_eq!(a, Fr::one() * a); - } -} - -#[test] -fn test_multiplicative_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - if a == Fr::zero() { - continue; - } - let a_inv = a.invert().unwrap(); - assert_eq!(Fr::one(), a * a_inv); - assert_eq!(Fr::one(), a_inv * a); - } -} - -#[test] -fn test_multiplicative_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - assert_eq!(a * b, b * a); - } -} - -#[test] -fn test_multiply_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(Fr::zero(), Fr::zero() * a); - assert_eq!(Fr::zero(), a * Fr::zero()); - } -} diff --git a/pairing/.gitignore b/pairing/.gitignore deleted file mode 100644 index 4308d82204..0000000000 --- a/pairing/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -target/ -**/*.rs.bk -Cargo.lock diff --git a/pairing/COPYRIGHT b/pairing/COPYRIGHT deleted file mode 100644 index c3876a4d72..0000000000 --- a/pairing/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -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. diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml deleted file mode 100644 index 5bd5e1e2be..0000000000 --- a/pairing/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "pairing" - -# Remember to change version string in README.md. -version = "0.16.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -readme = "README.md" -license = "MIT/Apache-2.0" - -description = "Pairing-friendly elliptic curve library" -documentation = "https://docs.rs/pairing/" -homepage = "https://github.com/ebfull/pairing" -repository = "https://github.com/ebfull/pairing" -edition ="2018" - -[dependencies] -byteorder = "1" -ff = { version = "0.6", path = "../ff", features = ["derive"] } -group = { version = "0.6", path = "../group" } -rand_core = "0.5" -subtle = "2.2.1" - -[dev-dependencies] -criterion = "0.3" -rand_xorshift = "0.2" - -[features] -unstable-features = ["expose-arith"] -expose-arith = [] -default = [] - -[[bench]] -name = "pairing_benches" -harness = false - -[badges] -maintenance = { status = "actively-developed" } diff --git a/pairing/LICENSE-APACHE b/pairing/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/pairing/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/pairing/LICENSE-MIT b/pairing/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/pairing/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/pairing/README.md b/pairing/README.md deleted file mode 100644 index 47a25dc686..0000000000 --- a/pairing/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) # - -`pairing` is a crate for using pairing-friendly elliptic curves. - -Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) -construction is implemented. - -## Roadmap - -`pairing` is being refactored into a generic library for working with -pairing-friendly curves. After the refactor, `pairing` will provide basic traits -for pairing-friendly elliptic curve constructions, while specific curves will be -in separate crates. - -## [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. diff --git a/pairing/benches/bls12_381/ec.rs b/pairing/benches/bls12_381/ec.rs deleted file mode 100644 index 28ce4bf849..0000000000 --- a/pairing/benches/bls12_381/ec.rs +++ /dev/null @@ -1,173 +0,0 @@ -pub(crate) mod g1 { - use criterion::{criterion_group, Criterion}; - use rand_core::SeedableRng; - use rand_xorshift::XorShiftRng; - use std::ops::AddAssign; - - use ff::Field; - use group::CurveProjective; - use pairing::bls12_381::*; - - fn bench_g1_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G1, Fr)> = (0..SAMPLES) - .map(|_| (G1::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("G1::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - fn bench_g1_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G1, G1)> = (0..SAMPLES) - .map(|_| (G1::random(&mut rng), G1::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("G1::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - fn bench_g1_add_assign_mixed(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G1, G1Affine)> = (0..SAMPLES) - .map(|_| (G1::random(&mut rng), G1::random(&mut rng).into())) - .collect(); - - let mut count = 0; - c.bench_function("G1::add_assign_mixed", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - criterion_group!( - benches, - bench_g1_add_assign, - bench_g1_add_assign_mixed, - bench_g1_mul_assign, - ); -} - -pub(crate) mod g2 { - use criterion::{criterion_group, Criterion}; - use rand_core::SeedableRng; - use rand_xorshift::XorShiftRng; - use std::ops::AddAssign; - - use ff::Field; - use group::CurveProjective; - use pairing::bls12_381::*; - - fn bench_g2_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G2, Fr)> = (0..SAMPLES) - .map(|_| (G2::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("G2::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - fn bench_g2_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G2, G2)> = (0..SAMPLES) - .map(|_| (G2::random(&mut rng), G2::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("G2::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - fn bench_g2_add_assign_mixed(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let v: Vec<(G2, G2Affine)> = (0..SAMPLES) - .map(|_| (G2::random(&mut rng), G2::random(&mut rng).into())) - .collect(); - - let mut count = 0; - c.bench_function("G2::add_assign_mixed", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); - } - - criterion_group!( - benches, - bench_g2_add_assign, - bench_g2_add_assign_mixed, - bench_g2_mul_assign, - ); -} diff --git a/pairing/benches/bls12_381/fq.rs b/pairing/benches/bls12_381/fq.rs deleted file mode 100644 index 244c161bcd..0000000000 --- a/pairing/benches/bls12_381/fq.rs +++ /dev/null @@ -1,345 +0,0 @@ -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - -use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; -use pairing::bls12_381::*; - -fn bench_fq_repr_add_nocarry(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) - .map(|_| { - let mut tmp1 = Fq::random(&mut rng).into_repr(); - let mut tmp2 = Fq::random(&mut rng).into_repr(); - // Shave a few bits off to avoid overflow. - for _ in 0..3 { - tmp1.div2(); - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::add_nocarry", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_nocarry(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_repr_sub_noborrow(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) - .map(|_| { - let tmp1 = Fq::random(&mut rng).into_repr(); - let mut tmp2 = tmp1; - // Ensure tmp2 is smaller than tmp1. - for _ in 0..10 { - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::sub_noborrow", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_noborrow(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_repr_num_bits(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::num_bits", |b| { - b.iter(|| { - let tmp = v[count].num_bits(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_repr_mul2(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::mul2", |b| { - b.iter(|| { - let mut tmp = v[count]; - tmp.mul2(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_repr_div2(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FqRepr::div2", |b| { - b.iter(|| { - let mut tmp = v[count]; - tmp.div2(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq, Fq)> = (0..SAMPLES) - .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_sub_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq, Fq)> = (0..SAMPLES) - .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq::sub_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq, Fq)> = (0..SAMPLES) - .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_square(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq::square", |b| { - b.iter(|| { - let tmp = v[count].square(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_invert(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq::invert", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].invert() - }) - }); -} - -fn bench_fq_neg(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq::neg", |b| { - b.iter(|| { - let tmp = v[count].neg(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq_sqrt(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).square()) - .collect(); - - let mut count = 0; - c.bench_function("Fq::sqrt", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].sqrt() - }) - }); -} - -fn bench_fq_into_repr(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq::into_repr", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].into_repr() - }) - }); -} - -fn bench_fq_from_repr(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fq::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("Fq::from_repr", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - Fq::from_repr(v[count]) - }) - }); -} - -criterion_group!( - benches, - bench_fq_repr_add_nocarry, - bench_fq_repr_sub_noborrow, - bench_fq_repr_num_bits, - bench_fq_repr_mul2, - bench_fq_repr_div2, - bench_fq_add_assign, - bench_fq_sub_assign, - bench_fq_mul_assign, - bench_fq_square, - bench_fq_invert, - bench_fq_neg, - bench_fq_sqrt, - bench_fq_into_repr, - bench_fq_from_repr, -); diff --git a/pairing/benches/bls12_381/fq12.rs b/pairing/benches/bls12_381/fq12.rs deleted file mode 100644 index 2d6ed490e2..0000000000 --- a/pairing/benches/bls12_381/fq12.rs +++ /dev/null @@ -1,125 +0,0 @@ -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -use ff::Field; -use pairing::bls12_381::*; - -fn bench_fq12_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) - .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq12::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq12_sub_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) - .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq12::sub_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq12_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) - .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq12::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq12_squaring(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq12::square", |b| { - b.iter(|| { - let tmp = v[count].square(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq12_invert(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq12::invert", |b| { - b.iter(|| { - let tmp = v[count].invert(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -criterion_group!( - benches, - bench_fq12_add_assign, - bench_fq12_sub_assign, - bench_fq12_mul_assign, - bench_fq12_squaring, - bench_fq12_invert, -); diff --git a/pairing/benches/bls12_381/fq2.rs b/pairing/benches/bls12_381/fq2.rs deleted file mode 100644 index 1eebb92db3..0000000000 --- a/pairing/benches/bls12_381/fq2.rs +++ /dev/null @@ -1,146 +0,0 @@ -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -use ff::{Field, SqrtField}; -use pairing::bls12_381::*; - -fn bench_fq2_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) - .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq2::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_sub_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) - .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq2::sub_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) - .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fq2::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_squaring(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq2::square", |b| { - b.iter(|| { - let tmp = v[count].square(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_invert(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq2::invert", |b| { - b.iter(|| { - let tmp = v[count].invert(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fq2_sqrt(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fq2::sqrt", |b| { - b.iter(|| { - let tmp = v[count].sqrt(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -criterion_group!( - benches, - bench_fq2_add_assign, - bench_fq2_sub_assign, - bench_fq2_mul_assign, - bench_fq2_squaring, - bench_fq2_invert, - bench_fq2_sqrt, -); diff --git a/pairing/benches/bls12_381/fr.rs b/pairing/benches/bls12_381/fr.rs deleted file mode 100644 index d2dbc4c210..0000000000 --- a/pairing/benches/bls12_381/fr.rs +++ /dev/null @@ -1,345 +0,0 @@ -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - -use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; -use pairing::bls12_381::*; - -fn bench_fr_repr_add_nocarry(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) - .map(|_| { - let mut tmp1 = Fr::random(&mut rng).into_repr(); - let mut tmp2 = Fr::random(&mut rng).into_repr(); - // Shave a few bits off to avoid overflow. - for _ in 0..3 { - tmp1.div2(); - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::add_nocarry", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_nocarry(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_repr_sub_noborrow(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) - .map(|_| { - let tmp1 = Fr::random(&mut rng).into_repr(); - let mut tmp2 = tmp1; - // Ensure tmp2 is smaller than tmp1. - for _ in 0..10 { - tmp2.div2(); - } - (tmp1, tmp2) - }) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::sub_noborrow", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_noborrow(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_repr_num_bits(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::num_bits", |b| { - b.iter(|| { - let tmp = v[count].num_bits(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_repr_mul2(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::mul2", |b| { - b.iter(|| { - let mut tmp = v[count]; - tmp.mul2(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_repr_div2(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("FrRepr::div2", |b| { - b.iter(|| { - let mut tmp = v[count]; - tmp.div2(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_add_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fr, Fr)> = (0..SAMPLES) - .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fr::add_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.add_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_sub_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fr, Fr)> = (0..SAMPLES) - .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fr::sub_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.sub_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_mul_assign(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(Fr, Fr)> = (0..SAMPLES) - .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Fr::mul_assign", |b| { - b.iter(|| { - let mut tmp = v[count].0; - tmp.mul_assign(&v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_square(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fr::square", |b| { - b.iter(|| { - let tmp = v[count].square(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_invert(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fr::invert", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].invert() - }) - }); -} - -fn bench_fr_neg(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fr::neg", |b| { - b.iter(|| { - let tmp = v[count].neg(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_fr_sqrt(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).square()) - .collect(); - - let mut count = 0; - c.bench_function("Fr::sqrt", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].sqrt() - }) - }); -} - -fn bench_fr_into_repr(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("Fr::into_repr", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - v[count].into_repr() - }) - }); -} - -fn bench_fr_from_repr(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| Fr::random(&mut rng).into_repr()) - .collect(); - - let mut count = 0; - c.bench_function("Fr::from_repr", |b| { - b.iter(|| { - count = (count + 1) % SAMPLES; - Fr::from_repr(v[count]) - }) - }); -} - -criterion_group!( - benches, - bench_fr_repr_add_nocarry, - bench_fr_repr_sub_noborrow, - bench_fr_repr_num_bits, - bench_fr_repr_mul2, - bench_fr_repr_div2, - bench_fr_add_assign, - bench_fr_sub_assign, - bench_fr_mul_assign, - bench_fr_square, - bench_fr_invert, - bench_fr_neg, - bench_fr_sqrt, - bench_fr_into_repr, - bench_fr_from_repr, -); diff --git a/pairing/benches/bls12_381/mod.rs b/pairing/benches/bls12_381/mod.rs deleted file mode 100644 index aa2e2f0873..0000000000 --- a/pairing/benches/bls12_381/mod.rs +++ /dev/null @@ -1,139 +0,0 @@ -pub(crate) mod ec; -pub(crate) mod fq; -pub(crate) mod fq12; -pub(crate) mod fq2; -pub(crate) mod fr; - -use criterion::{criterion_group, Criterion}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; - -use group::CurveProjective; -use pairing::bls12_381::*; -use pairing::{Engine, PairingCurveAffine}; - -fn bench_pairing_g1_preparation(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("G1 preparation", |b| { - b.iter(|| { - let tmp = G1Affine::from(v[count]).prepare(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_pairing_g2_preparation(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect(); - - let mut count = 0; - c.bench_function("G2 preparation", |b| { - b.iter(|| { - let tmp = G2Affine::from(v[count]).prepare(); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_pairing_miller_loop(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES) - .map(|_| { - ( - G1Affine::from(G1::random(&mut rng)).prepare(), - G2Affine::from(G2::random(&mut rng)).prepare(), - ) - }) - .collect(); - - let mut count = 0; - c.bench_function("Miller loop", |b| { - b.iter(|| { - let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_pairing_final_exponentiation(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec = (0..SAMPLES) - .map(|_| { - ( - G1Affine::from(G1::random(&mut rng)).prepare(), - G2Affine::from(G2::random(&mut rng)).prepare(), - ) - }) - .map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)])) - .collect(); - - let mut count = 0; - c.bench_function("Final exponentiation", |b| { - b.iter(|| { - let tmp = Bls12::final_exponentiation(&v[count]); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -fn bench_pairing_full(c: &mut Criterion) { - const SAMPLES: usize = 1000; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let v: Vec<(G1, G2)> = (0..SAMPLES) - .map(|_| (G1::random(&mut rng), G2::random(&mut rng))) - .collect(); - - let mut count = 0; - c.bench_function("Full pairing", |b| { - b.iter(|| { - let tmp = Bls12::pairing(v[count].0, v[count].1); - count = (count + 1) % SAMPLES; - tmp - }) - }); -} - -criterion_group!( - benches, - bench_pairing_g1_preparation, - bench_pairing_g2_preparation, - bench_pairing_miller_loop, - bench_pairing_final_exponentiation, - bench_pairing_full, -); diff --git a/pairing/benches/pairing_benches.rs b/pairing/benches/pairing_benches.rs deleted file mode 100644 index 7abc824537..0000000000 --- a/pairing/benches/pairing_benches.rs +++ /dev/null @@ -1,12 +0,0 @@ -use criterion::criterion_main; -mod bls12_381; - -criterion_main!( - bls12_381::benches, - bls12_381::ec::g1::benches, - bls12_381::ec::g2::benches, - bls12_381::fq::benches, - bls12_381::fq12::benches, - bls12_381::fq2::benches, - bls12_381::fr::benches, -); diff --git a/pairing/src/bls12_381/README.md b/pairing/src/bls12_381/README.md deleted file mode 100644 index d3811a142e..0000000000 --- a/pairing/src/bls12_381/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# 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)2 ((x4 - x2 + 1) / 3) + x -* r = (x4 - x2 + 1) - -Given primes *q* and *r* parameterized as above, we can easily construct an elliptic curve over the prime field F*q* which contains a subgroup of order *r* such that *r* | (*q*12 - 1), giving it an embedding degree of 12. Instantiating its sextic twist over an extension field Fq2 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 Fq12. - -In zk-SNARK schemes, we require Fr with large 2n roots of unity for performing efficient fast-fourier transforms. As such, guaranteeing that large 2n | (r - 1), or equivalently that *x* has a large 2n 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 2383, and *r* should be smaller than 2255, so that the most significant bit is unset when using 64-bit or 32-bit limbs. This allows for cheap reductions. -* Fq12 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. Fq2 is constructed as Fq(u) / (u2 - β) where β = -1. -2. Fq6 is constructed as Fq2(v) / (v3 - ξ) where ξ = u + 1 -3. Fq12 is constructed as Fq6(w) / (w2 - γ) where γ = v - -Now, we instantiate the elliptic curve E(Fq) : y2 = x3 + 4, and the elliptic curve E'(Fq2) : y2 = x3 + 4(u + 1). - -The group G1 is the *r* order subgroup of E, which has cofactor (x - 1)2 / 3. The group G2 is the *r* order subgroup of E', which has cofactor (x8 - 4x7 + 5x6 - 4x4 + 6x3 - 4x2 - 4x + 13) / 9. - -### Generators - -The generators of G1 and G2 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. - diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs deleted file mode 100644 index a87fe11f84..0000000000 --- a/pairing/src/bls12_381/ec.rs +++ /dev/null @@ -1,2209 +0,0 @@ -macro_rules! curve_impl { - ( - $name:expr, - $projective:ident, - $affine:ident, - $prepared:ident, - $basefield:ident, - $scalarfield:ident, - $uncompressed:ident, - $compressed:ident, - $pairing:ident - ) => { - #[derive(Copy, Clone, PartialEq, Eq, Debug)] - pub struct $affine { - pub(crate) x: $basefield, - pub(crate) y: $basefield, - pub(crate) infinity: bool, - } - - impl ::std::fmt::Display for $affine { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - if self.infinity { - write!(f, "{}(Infinity)", $name) - } else { - write!(f, "{}(x={}, y={})", $name, self.x, self.y) - } - } - } - - #[derive(Copy, Clone, Debug, Eq)] - pub struct $projective { - pub(crate) x: $basefield, - pub(crate) y: $basefield, - pub(crate) z: $basefield, - } - - impl ::std::fmt::Display for $projective { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "{}", self.into_affine()) - } - } - - impl PartialEq for $projective { - fn eq(&self, other: &$projective) -> bool { - if self.is_zero() { - return other.is_zero(); - } - - if other.is_zero() { - return false; - } - - // The points (X, Y, Z) and (X', Y', Z') - // are equal when (X * Z^2) = (X' * Z'^2) - // and (Y * Z^3) = (Y' * Z'^3). - - let mut z1 = self.z.square(); - let mut z2 = other.z.square(); - - let mut tmp1 = self.x; - tmp1.mul_assign(&z2); - - let mut tmp2 = other.x; - tmp2.mul_assign(&z1); - - if tmp1 != tmp2 { - return false; - } - - z1.mul_assign(&self.z); - z2.mul_assign(&other.z); - z2.mul_assign(&self.y); - z1.mul_assign(&other.y); - - if z1 != z2 { - return false; - } - - true - } - } - - impl $affine { - fn mul_bits>(&self, bits: BitIterator) -> $projective { - let mut res = $projective::zero(); - for i in bits { - res.double(); - if i { - res.add_assign(self) - } - } - res - } - - /// Attempts to construct an affine point given an x-coordinate. The - /// point is not guaranteed to be in the prime order subgroup. - /// - /// If and only if `greatest` is set will the lexicographically - /// largest y-coordinate be selected. - fn get_point_from_x(x: $basefield, greatest: bool) -> CtOption<$affine> { - // Compute x^3 + b - let mut x3b = x.square(); - x3b.mul_assign(&x); - x3b.add_assign(&$affine::get_coeff_b()); - - x3b.sqrt().map(|y| { - let negy = y.neg(); - - $affine { - x: x, - y: if (y < negy) ^ greatest { y } else { negy }, - infinity: false, - } - }) - } - - fn is_on_curve(&self) -> bool { - if self.is_zero() { - true - } else { - // Check that the point is on the curve - let y2 = self.y.square(); - - let mut x3b = self.x.square(); - x3b.mul_assign(&self.x); - x3b.add_assign(&Self::get_coeff_b()); - - y2 == x3b - } - } - - fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { - self.mul($scalarfield::char()).is_zero() - } - } - - impl ::std::ops::Neg for $affine { - type Output = Self; - - #[inline] - fn neg(self) -> Self { - let mut ret = self; - if !ret.is_zero() { - ret.y = ret.y.neg(); - } - ret - } - } - - impl CurveAffine for $affine { - type Engine = Bls12; - type Scalar = $scalarfield; - type Base = $basefield; - type Projective = $projective; - type Uncompressed = $uncompressed; - type Compressed = $compressed; - - fn zero() -> Self { - $affine { - x: $basefield::zero(), - y: $basefield::one(), - infinity: true, - } - } - - fn one() -> Self { - Self::get_generator() - } - - fn is_zero(&self) -> bool { - self.infinity - } - - fn mul::Repr>>(&self, by: S) -> $projective { - let bits = BitIterator::new(by.into()); - self.mul_bits(bits) - } - - fn into_projective(&self) -> $projective { - (*self).into() - } - } - - impl PairingCurveAffine for $affine { - type Prepared = $prepared; - type Pair = $pairing; - type PairingResult = Fq12; - - fn prepare(&self) -> Self::Prepared { - $prepared::from_affine(*self) - } - - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { - self.perform_pairing(other) - } - } - - impl ::std::ops::Neg for $projective { - type Output = Self; - - #[inline] - fn neg(self) -> Self { - let mut ret = self; - if !ret.is_zero() { - ret.y = ret.y.neg(); - } - ret - } - } - - impl<'r> ::std::ops::Add<&'r $projective> for $projective { - type Output = Self; - - #[inline] - fn add(self, other: &Self) -> Self { - let mut ret = self; - ret.add_assign(other); - ret - } - } - - impl ::std::ops::Add for $projective { - type Output = Self; - - #[inline] - fn add(self, other: Self) -> Self { - self + &other - } - } - - impl<'r> ::std::ops::AddAssign<&'r $projective> for $projective { - fn add_assign(&mut self, other: &Self) { - if self.is_zero() { - *self = *other; - return; - } - - if other.is_zero() { - return; - } - - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl - - // Z1Z1 = Z1^2 - let z1z1 = self.z.square(); - - // Z2Z2 = Z2^2 - let z2z2 = other.z.square(); - - // U1 = X1*Z2Z2 - let mut u1 = self.x; - u1.mul_assign(&z2z2); - - // U2 = X2*Z1Z1 - let mut u2 = other.x; - u2.mul_assign(&z1z1); - - // S1 = Y1*Z2*Z2Z2 - let mut s1 = self.y; - s1.mul_assign(&other.z); - s1.mul_assign(&z2z2); - - // S2 = Y2*Z1*Z1Z1 - let mut s2 = other.y; - s2.mul_assign(&self.z); - s2.mul_assign(&z1z1); - - if u1 == u2 && s1 == s2 { - // The two points are equal, so we double. - self.double(); - } else { - // If we're adding -a and a together, self.z becomes zero as H becomes zero. - - // H = U2-U1 - let mut h = u2; - h.sub_assign(&u1); - - // I = (2*H)^2 - let i = h.double().square(); - - // J = H*I - let mut j = h; - j.mul_assign(&i); - - // r = 2*(S2-S1) - let mut r = s2; - r.sub_assign(&s1); - r = r.double(); - - // V = U1*I - let mut v = u1; - v.mul_assign(&i); - - // X3 = r^2 - J - 2*V - self.x = r.square(); - self.x.sub_assign(&j); - self.x.sub_assign(&v); - self.x.sub_assign(&v); - - // Y3 = r*(V - X3) - 2*S1*J - self.y = v; - self.y.sub_assign(&self.x); - self.y.mul_assign(&r); - s1.mul_assign(&j); // S1 = S1 * J * 2 - s1 = s1.double(); - self.y.sub_assign(&s1); - - // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H - self.z.add_assign(&other.z); - self.z = self.z.square(); - self.z.sub_assign(&z1z1); - self.z.sub_assign(&z2z2); - self.z.mul_assign(&h); - } - } - } - - impl ::std::ops::AddAssign for $projective { - #[inline] - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } - } - - impl<'r> ::std::ops::Sub<&'r $projective> for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: &Self) -> Self { - let mut ret = self; - ret.sub_assign(other); - ret - } - } - - impl ::std::ops::Sub for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: Self) -> Self { - self - &other - } - } - - impl<'r> ::std::ops::SubAssign<&'r $projective> for $projective { - fn sub_assign(&mut self, other: &Self) { - self.add_assign(&other.neg()); - } - } - - impl ::std::ops::SubAssign for $projective { - #[inline] - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } - } - - impl<'r> ::std::ops::Add<&'r <$projective as CurveProjective>::Affine> for $projective { - type Output = Self; - - #[inline] - fn add(self, other: &<$projective as CurveProjective>::Affine) -> Self { - let mut ret = self; - ret.add_assign(other); - ret - } - } - - impl ::std::ops::Add<<$projective as CurveProjective>::Affine> for $projective { - type Output = Self; - - #[inline] - fn add(self, other: <$projective as CurveProjective>::Affine) -> Self { - self + &other - } - } - - impl<'r> ::std::ops::AddAssign<&'r <$projective as CurveProjective>::Affine> - for $projective - { - fn add_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { - if other.is_zero() { - return; - } - - if self.is_zero() { - self.x = other.x; - self.y = other.y; - self.z = $basefield::one(); - return; - } - - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl - - // Z1Z1 = Z1^2 - let z1z1 = self.z.square(); - - // U2 = X2*Z1Z1 - let mut u2 = other.x; - u2.mul_assign(&z1z1); - - // S2 = Y2*Z1*Z1Z1 - let mut s2 = other.y; - s2.mul_assign(&self.z); - s2.mul_assign(&z1z1); - - if self.x == u2 && self.y == s2 { - // The two points are equal, so we double. - self.double(); - } else { - // If we're adding -a and a together, self.z becomes zero as H becomes zero. - - // H = U2-X1 - let mut h = u2; - h.sub_assign(&self.x); - - // HH = H^2 - let hh = h.square(); - - // I = 4*HH - let i = hh.double().double(); - - // J = H*I - let mut j = h; - j.mul_assign(&i); - - // r = 2*(S2-Y1) - let mut r = s2; - r.sub_assign(&self.y); - r = r.double(); - - // V = X1*I - let mut v = self.x; - v.mul_assign(&i); - - // X3 = r^2 - J - 2*V - self.x = r.square(); - self.x.sub_assign(&j); - self.x.sub_assign(&v); - self.x.sub_assign(&v); - - // Y3 = r*(V-X3)-2*Y1*J - j.mul_assign(&self.y); // J = 2*Y1*J - j = j.double(); - self.y = v; - self.y.sub_assign(&self.x); - self.y.mul_assign(&r); - self.y.sub_assign(&j); - - // Z3 = (Z1+H)^2-Z1Z1-HH - self.z.add_assign(&h); - self.z = self.z.square(); - self.z.sub_assign(&z1z1); - self.z.sub_assign(&hh); - } - } - } - - impl ::std::ops::AddAssign<<$projective as CurveProjective>::Affine> for $projective { - #[inline] - fn add_assign(&mut self, other: <$projective as CurveProjective>::Affine) { - self.add_assign(&other); - } - } - - impl<'r> ::std::ops::Sub<&'r <$projective as CurveProjective>::Affine> for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: &<$projective as CurveProjective>::Affine) -> Self { - let mut ret = self; - ret.sub_assign(other); - ret - } - } - - impl ::std::ops::Sub<<$projective as CurveProjective>::Affine> for $projective { - type Output = Self; - - #[inline] - fn sub(self, other: <$projective as CurveProjective>::Affine) -> Self { - self - &other - } - } - - impl<'r> ::std::ops::SubAssign<&'r <$projective as CurveProjective>::Affine> - for $projective - { - fn sub_assign(&mut self, other: &<$projective as CurveProjective>::Affine) { - self.add_assign(&other.neg()); - } - } - - impl ::std::ops::SubAssign<<$projective as CurveProjective>::Affine> for $projective { - #[inline] - fn sub_assign(&mut self, other: <$projective as CurveProjective>::Affine) { - self.sub_assign(&other); - } - } - - impl CurveProjective for $projective { - type Engine = Bls12; - type Scalar = $scalarfield; - type Base = $basefield; - type Affine = $affine; - - fn random(rng: &mut R) -> Self { - loop { - let x = $basefield::random(rng); - let greatest = rng.next_u32() % 2 != 0; - - let p = $affine::get_point_from_x(x, greatest); - if p.is_some().into() { - let p = p.unwrap().scale_by_cofactor(); - - if !p.is_zero() { - return p; - } - } - } - } - - // The point at infinity is always represented by - // Z = 0. - fn zero() -> Self { - $projective { - x: $basefield::zero(), - y: $basefield::one(), - z: $basefield::zero(), - } - } - - fn one() -> Self { - $affine::one().into() - } - - // The point at infinity is always represented by - // Z = 0. - fn is_zero(&self) -> bool { - self.z.is_zero() - } - - fn is_normalized(&self) -> bool { - self.is_zero() || self.z == $basefield::one() - } - - fn batch_normalization(v: &mut [Self]) { - // Montgomery’s Trick and Fast Implementation of Masked AES - // Genelle, Prouff and Quisquater - // Section 3.2 - - // First pass: compute [a, ab, abc, ...] - let mut prod = Vec::with_capacity(v.len()); - let mut tmp = $basefield::one(); - for g in v - .iter_mut() - // Ignore normalized elements - .filter(|g| !g.is_normalized()) - { - tmp.mul_assign(&g.z); - prod.push(tmp); - } - - // Invert `tmp`. - tmp = tmp.invert().unwrap(); // Guaranteed to be nonzero. - - // Second pass: iterate backwards to compute inverses - for (g, s) in v - .iter_mut() - // Backwards - .rev() - // Ignore normalized elements - .filter(|g| !g.is_normalized()) - // Backwards, skip last element, fill in one for last term. - .zip( - prod.into_iter() - .rev() - .skip(1) - .chain(Some($basefield::one())), - ) - { - // tmp := tmp * g.z; g.z := tmp * s = 1/z - let mut newtmp = tmp; - newtmp.mul_assign(&g.z); - g.z = tmp; - g.z.mul_assign(&s); - tmp = newtmp; - } - - // Perform affine transformations - for g in v.iter_mut().filter(|g| !g.is_normalized()) { - let mut z = g.z.square(); // 1/z^2 - g.x.mul_assign(&z); // x/z^2 - z.mul_assign(&g.z); // 1/z^3 - g.y.mul_assign(&z); // y/z^3 - g.z = $basefield::one(); // z = 1 - } - } - - fn double(&mut self) { - if self.is_zero() { - return; - } - - // Other than the point at infinity, no points on E or E' - // can double to equal the point at infinity, as y=0 is - // never true for points on the curve. (-4 and -4u-4 - // are not cubic residue in their respective fields.) - - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - - // A = X1^2 - let a = self.x.square(); - - // B = Y1^2 - let b = self.y.square(); - - // C = B^2 - let mut c = b.square(); - - // D = 2*((X1+B)2-A-C) - let mut d = self.x; - d.add_assign(&b); - d = d.square(); - d.sub_assign(&a); - d.sub_assign(&c); - d = d.double(); - - // E = 3*A - let mut e = a.double(); - e.add_assign(&a); - - // F = E^2 - let f = e.square(); - - // Z3 = 2*Y1*Z1 - self.z.mul_assign(&self.y); - self.z = self.z.double(); - - // X3 = F-2*D - self.x = f; - self.x.sub_assign(&d); - self.x.sub_assign(&d); - - // Y3 = E*(D-X3)-8*C - self.y = d; - self.y.sub_assign(&self.x); - self.y.mul_assign(&e); - c = c.double().double().double(); - self.y.sub_assign(&c); - } - - fn mul_assign::Repr>>(&mut self, other: S) { - let mut res = Self::zero(); - - let mut found_one = false; - - for i in BitIterator::new(other.into()) { - if found_one { - res.double(); - } else { - found_one = i; - } - - if i { - res.add_assign(&*self); - } - } - - *self = res; - } - - fn into_affine(&self) -> $affine { - (*self).into() - } - - fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize { - Self::empirical_recommended_wnaf_for_scalar(scalar) - } - - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - Self::empirical_recommended_wnaf_for_num_scalars(num_scalars) - } - } - - // The affine point X, Y is represented in the jacobian - // coordinates with Z = 1. - impl From<$affine> for $projective { - fn from(p: $affine) -> $projective { - if p.is_zero() { - $projective::zero() - } else { - $projective { - x: p.x, - y: p.y, - z: $basefield::one(), - } - } - } - } - - // The projective point X, Y, Z is represented in the affine - // coordinates as X/Z^2, Y/Z^3. - impl From<$projective> for $affine { - fn from(p: $projective) -> $affine { - if p.is_zero() { - $affine::zero() - } else if p.z == $basefield::one() { - // If Z is one, the point is already normalized. - $affine { - x: p.x, - y: p.y, - infinity: false, - } - } else { - // Z is nonzero, so it must have an inverse in a field. - let zinv = p.z.invert().unwrap(); - let mut zinv_powered = zinv.square(); - - // X/Z^2 - let mut x = p.x; - x.mul_assign(&zinv_powered); - - // Y/Z^3 - let mut y = p.y; - zinv_powered.mul_assign(&zinv); - y.mul_assign(&zinv_powered); - - $affine { - x: x, - y: y, - infinity: false, - } - } - } - } - }; -} - -pub mod g1 { - use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr}; - use super::g2::G2Affine; - use crate::{Engine, PairingCurveAffine}; - use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; - use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; - use rand_core::RngCore; - use std::fmt; - use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - use subtle::CtOption; - - curve_impl!( - "G1", - G1, - G1Affine, - G1Prepared, - Fq, - Fr, - G1Uncompressed, - G1Compressed, - G2Affine - ); - - #[derive(Copy, Clone)] - pub struct G1Uncompressed([u8; 96]); - - impl AsRef<[u8]> for G1Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G1Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - impl fmt::Debug for G1Uncompressed { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0[..].fmt(formatter) - } - } - - impl EncodedPoint for G1Uncompressed { - type Affine = G1Affine; - - fn empty() -> Self { - G1Uncompressed([0; 96]) - } - fn size() -> usize { - 96 - } - fn into_affine(&self) -> Result { - let affine = self.into_affine_unchecked()?; - - if !affine.is_on_curve() { - Err(GroupDecodingError::NotOnCurve) - } else if !affine.is_in_correct_subgroup_assuming_on_curve() { - Err(GroupDecodingError::NotInSubgroup) - } else { - Ok(affine) - } - } - fn into_affine_unchecked(&self) -> Result { - // Create a copy of this representation. - let mut copy = self.0; - - if copy[0] & (1 << 7) != 0 { - // Distinguisher bit is set, but this should be uncompressed! - return Err(GroupDecodingError::UnexpectedCompressionMode); - } - - if copy[0] & (1 << 6) != 0 { - // This is the point at infinity, which means that if we mask away - // the first two bits, the entire representation should consist - // of zeroes. - copy[0] &= 0x3f; - - if copy.iter().all(|b| *b == 0) { - Ok(G1Affine::zero()) - } else { - Err(GroupDecodingError::UnexpectedInformation) - } - } else { - if copy[0] & (1 << 5) != 0 { - // The bit indicating the y-coordinate should be lexicographically - // largest is set, but this is an uncompressed element. - return Err(GroupDecodingError::UnexpectedInformation); - } - - // Unset the three most significant bits. - copy[0] &= 0x1f; - - let mut x = FqRepr([0; 6]); - let mut y = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x.read_be(&mut reader).unwrap(); - y.read_be(&mut reader).unwrap(); - } - - Ok(G1Affine { - x: Fq::from_repr(x).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate", e) - })?, - y: Fq::from_repr(y).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("y coordinate", e) - })?, - infinity: false, - }) - } - } - fn from_affine(affine: G1Affine) -> Self { - let mut res = Self::empty(); - - if affine.is_zero() { - // Set the second-most significant bit to indicate this point - // is at infinity. - res.0[0] |= 1 << 6; - } else { - let mut writer = &mut res.0[..]; - - affine.x.into_repr().write_be(&mut writer).unwrap(); - affine.y.into_repr().write_be(&mut writer).unwrap(); - } - - res - } - } - - #[derive(Copy, Clone)] - pub struct G1Compressed([u8; 48]); - - impl AsRef<[u8]> for G1Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G1Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - impl fmt::Debug for G1Compressed { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0[..].fmt(formatter) - } - } - - impl EncodedPoint for G1Compressed { - type Affine = G1Affine; - - fn empty() -> Self { - G1Compressed([0; 48]) - } - fn size() -> usize { - 48 - } - fn into_affine(&self) -> Result { - let affine = self.into_affine_unchecked()?; - - // NB: Decompression guarantees that it is on the curve already. - - if !affine.is_in_correct_subgroup_assuming_on_curve() { - Err(GroupDecodingError::NotInSubgroup) - } else { - Ok(affine) - } - } - fn into_affine_unchecked(&self) -> Result { - // Create a copy of this representation. - let mut copy = self.0; - - if copy[0] & (1 << 7) == 0 { - // Distinguisher bit isn't set. - return Err(GroupDecodingError::UnexpectedCompressionMode); - } - - if copy[0] & (1 << 6) != 0 { - // This is the point at infinity, which means that if we mask away - // the first two bits, the entire representation should consist - // of zeroes. - copy[0] &= 0x3f; - - if copy.iter().all(|b| *b == 0) { - Ok(G1Affine::zero()) - } else { - Err(GroupDecodingError::UnexpectedInformation) - } - } else { - // Determine if the intended y coordinate must be greater - // lexicographically. - let greatest = copy[0] & (1 << 5) != 0; - - // Unset the three most significant bits. - copy[0] &= 0x1f; - - let mut x = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x.read_be(&mut reader).unwrap(); - } - - // Interpret as Fq element. - let x = Fq::from_repr(x) - .map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?; - - let ret = G1Affine::get_point_from_x(x, greatest); - if ret.is_some().into() { - Ok(ret.unwrap()) - } else { - Err(GroupDecodingError::NotOnCurve) - } - } - } - fn from_affine(affine: G1Affine) -> Self { - let mut res = Self::empty(); - - if affine.is_zero() { - // Set the second-most significant bit to indicate this point - // is at infinity. - res.0[0] |= 1 << 6; - } else { - { - let mut writer = &mut res.0[..]; - - affine.x.into_repr().write_be(&mut writer).unwrap(); - } - - let negy = affine.y.neg(); - - // Set the third most significant bit if the correct y-coordinate - // is lexicographically largest. - if affine.y > negy { - res.0[0] |= 1 << 5; - } - } - - // Set highest bit to distinguish this as a compressed element. - res.0[0] |= 1 << 7; - - res - } - } - - impl G1Affine { - fn scale_by_cofactor(&self) -> G1 { - // G1 cofactor = (x - 1)^2 / 3 = 76329603384216526031706109802092473003 - let cofactor = BitIterator::new([0x8c00aaab0000aaab, 0x396c8c005555e156]); - self.mul_bits(cofactor) - } - - fn get_generator() -> Self { - G1Affine { - x: super::super::fq::G1_GENERATOR_X, - y: super::super::fq::G1_GENERATOR_Y, - infinity: false, - } - } - - fn get_coeff_b() -> Fq { - super::super::fq::B_COEFF - } - - fn perform_pairing(&self, other: &G2Affine) -> Fq12 { - super::super::Bls12::pairing(*self, *other) - } - } - - impl G1 { - fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { - let num_bits = scalar.num_bits() as usize; - - if num_bits >= 130 { - 4 - } else if num_bits >= 34 { - 3 - } else { - 2 - } - } - - fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - const RECOMMENDATIONS: [usize; 12] = - [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } - } - - #[derive(Clone, Debug)] - pub struct G1Prepared(pub(crate) G1Affine); - - impl G1Prepared { - pub fn is_zero(&self) -> bool { - self.0.is_zero() - } - - pub fn from_affine(p: G1Affine) -> Self { - G1Prepared(p) - } - } - - #[test] - fn g1_generator() { - use crate::SqrtField; - - let mut x = Fq::zero(); - let mut i = 0; - loop { - // y^2 = x^3 + b - let mut rhs = x.square(); - rhs.mul_assign(&x); - rhs.add_assign(&G1Affine::get_coeff_b()); - - let y = rhs.sqrt(); - if y.is_some().into() { - let y = y.unwrap(); - let yrepr = y.into_repr(); - let negy = y.neg(); - let negyrepr = negy.into_repr(); - - let p = G1Affine { - x, - y: if yrepr < negyrepr { y } else { negy }, - infinity: false, - }; - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - - let g1 = p.scale_by_cofactor(); - if !g1.is_zero() { - assert_eq!(i, 4); - let g1 = G1Affine::from(g1); - - assert!(g1.is_in_correct_subgroup_assuming_on_curve()); - - assert_eq!(g1, G1Affine::one()); - break; - } - } - - i += 1; - x.add_assign(&Fq::one()); - } - } - - #[test] - fn g1_test_is_valid() { - // Reject point on isomorphic twist (b = 24) - { - let p = G1Affine { - x: Fq::from_repr(FqRepr([ - 0xc58d887b66c035dc, - 0x10cbfd301d553822, - 0xaf23e064f1131ee5, - 0x9fe83b1b4a5d648d, - 0xf583cc5a508f6a40, - 0xc3ad2aefde0bb13, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x60aa6f9552f03aae, - 0xecd01d5181300d35, - 0x8af1cdb8aa8ce167, - 0xe760f57922998c9d, - 0x953703f5795a39e5, - 0xfe3ae0922df702c, - ])) - .unwrap(), - infinity: false, - }; - assert!(!p.is_on_curve()); - assert!(p.is_in_correct_subgroup_assuming_on_curve()); - } - - // Reject point on a twist (b = 3) - { - let p = G1Affine { - x: Fq::from_repr(FqRepr([ - 0xee6adf83511e15f5, - 0x92ddd328f27a4ba6, - 0xe305bd1ac65adba7, - 0xea034ee2928b30a8, - 0xbd8833dc7c79a7f7, - 0xe45c9f0c0438675, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x3b450eb1ab7b5dad, - 0xa65cb81e975e8675, - 0xaa548682b21726e5, - 0x753ddf21a2601d20, - 0x532d0b640bd3ff8b, - 0x118d2c543f031102, - ])) - .unwrap(), - infinity: false, - }; - assert!(!p.is_on_curve()); - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - } - - // Reject point in an invalid subgroup - // There is only one r-order subgroup, as r does not divide the cofactor. - { - let p = G1Affine { - x: Fq::from_repr(FqRepr([ - 0x76e1c971c6db8fe8, - 0xe37e1a610eff2f79, - 0x88ae9c499f46f0c0, - 0xf35de9ce0d6b4e84, - 0x265bddd23d1dec54, - 0x12a8778088458308, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x8a22defa0d526256, - 0xc57ca55456fcb9ae, - 0x1ba194e89bab2610, - 0x921beef89d4f29df, - 0x5b6fda44ad85fa78, - 0xed74ab9f302cbe0, - ])) - .unwrap(), - infinity: false, - }; - assert!(p.is_on_curve()); - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - } - } - - #[test] - fn test_g1_addition_correctness() { - let mut p = G1 { - x: Fq::from_repr(FqRepr([ - 0x47fd1f891d6e8bbf, - 0x79a3b0448f31a2aa, - 0x81f3339e5f9968f, - 0x485e77d50a5df10d, - 0x4c6fcac4b55fd479, - 0x86ed4d9906fb064, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0xd25ee6461538c65, - 0x9f3bbb2ecd3719b9, - 0xa06fd3f1e540910d, - 0xcefca68333c35288, - 0x570c8005f8573fa6, - 0x152ca696fe034442, - ])) - .unwrap(), - z: Fq::one(), - }; - - p.add_assign(&G1 { - x: Fq::from_repr(FqRepr([ - 0xeec78f3096213cbf, - 0xa12beb1fea1056e6, - 0xc286c0211c40dd54, - 0x5f44314ec5e3fb03, - 0x24e8538737c6e675, - 0x8abd623a594fba8, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x6b0528f088bb7044, - 0x2fdeb5c82917ff9e, - 0x9a5181f2fac226ad, - 0xd65104c6f95a872a, - 0x1f2998a5a9c61253, - 0xe74846154a9e44, - ])) - .unwrap(), - z: Fq::one(), - }); - - let p = G1Affine::from(p); - - assert_eq!( - p, - G1Affine { - x: Fq::from_repr(FqRepr([ - 0x6dd3098f22235df, - 0xe865d221c8090260, - 0xeb96bb99fa50779f, - 0xc4f9a52a428e23bb, - 0xd178b28dd4f407ef, - 0x17fb8905e9183c69 - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0xd0de9d65292b7710, - 0xf6a05f2bcf1d9ca7, - 0x1040e27012f20b64, - 0xeec8d1a5b7466c58, - 0x4bc362649dce6376, - 0x430cbdc5455b00a - ])) - .unwrap(), - infinity: false, - } - ); - } - - #[test] - fn test_g1_doubling_correctness() { - let mut p = G1 { - x: Fq::from_repr(FqRepr([ - 0x47fd1f891d6e8bbf, - 0x79a3b0448f31a2aa, - 0x81f3339e5f9968f, - 0x485e77d50a5df10d, - 0x4c6fcac4b55fd479, - 0x86ed4d9906fb064, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0xd25ee6461538c65, - 0x9f3bbb2ecd3719b9, - 0xa06fd3f1e540910d, - 0xcefca68333c35288, - 0x570c8005f8573fa6, - 0x152ca696fe034442, - ])) - .unwrap(), - z: Fq::one(), - }; - - p.double(); - - let p = G1Affine::from(p); - - assert_eq!( - p, - G1Affine { - x: Fq::from_repr(FqRepr([ - 0xf939ddfe0ead7018, - 0x3b03942e732aecb, - 0xce0e9c38fdb11851, - 0x4b914c16687dcde0, - 0x66c8baf177d20533, - 0xaf960cff3d83833 - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x3f0675695f5177a8, - 0x2b6d82ae178a1ba0, - 0x9096380dd8e51b11, - 0x1771a65b60572f4e, - 0x8b547c1313b27555, - 0x135075589a687b1e - ])) - .unwrap(), - infinity: false, - } - ); - } - - #[test] - fn test_g1_same_y() { - // Test the addition of two points with different x coordinates - // but the same y coordinate. - - // x1 = 128100205326445210408953809171070606737678357140298133325128175840781723996595026100005714405541449960643523234125 - // x2 = 3821408151224848222394078037104966877485040835569514006839342061575586899845797797516352881516922679872117658572470 - // y = 2291134451313223670499022936083127939567618746216464377735567679979105510603740918204953301371880765657042046687078 - - let a = G1Affine { - x: Fq::from_repr(FqRepr([ - 0xea431f2cc38fc94d, - 0x3ad2354a07f5472b, - 0xfe669f133f16c26a, - 0x71ffa8021531705, - 0x7418d484386d267, - 0xd5108d8ff1fbd6, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0xa776ccbfe9981766, - 0x255632964ff40f4a, - 0xc09744e650b00499, - 0x520f74773e74c8c3, - 0x484c8fc982008f0, - 0xee2c3d922008cc6, - ])) - .unwrap(), - infinity: false, - }; - - let b = G1Affine { - x: Fq::from_repr(FqRepr([ - 0xe06cdb156b6356b6, - 0xd9040b2d75448ad9, - 0xe702f14bb0e2aca5, - 0xc6e05201e5f83991, - 0xf7c75910816f207c, - 0x18d4043e78103106, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0xa776ccbfe9981766, - 0x255632964ff40f4a, - 0xc09744e650b00499, - 0x520f74773e74c8c3, - 0x484c8fc982008f0, - 0xee2c3d922008cc6, - ])) - .unwrap(), - infinity: false, - }; - - // Expected - // x = 52901198670373960614757979459866672334163627229195745167587898707663026648445040826329033206551534205133090753192 - // y = 1711275103908443722918766889652776216989264073722543507596490456144926139887096946237734327757134898380852225872709 - let c = G1Affine { - x: Fq::from_repr(FqRepr([ - 0xef4f05bdd10c8aa8, - 0xad5bf87341a2df9, - 0x81c7424206b78714, - 0x9676ff02ec39c227, - 0x4c12c15d7e55b9f3, - 0x57fd1e317db9bd, - ])) - .unwrap(), - y: Fq::from_repr(FqRepr([ - 0x1288334016679345, - 0xf955cd68615ff0b5, - 0xa6998dbaa600f18a, - 0x1267d70db51049fb, - 0x4696deb9ab2ba3e7, - 0xb1e4e11177f59d4, - ])) - .unwrap(), - infinity: false, - }; - - assert!(a.is_on_curve() && a.is_in_correct_subgroup_assuming_on_curve()); - assert!(b.is_on_curve() && b.is_in_correct_subgroup_assuming_on_curve()); - assert!(c.is_on_curve() && c.is_in_correct_subgroup_assuming_on_curve()); - - let mut tmp1 = a.into_projective(); - tmp1.add_assign(&b.into_projective()); - assert_eq!(tmp1.into_affine(), c); - assert_eq!(tmp1, c.into_projective()); - - let mut tmp2 = a.into_projective(); - tmp2.add_assign(&b); - assert_eq!(tmp2.into_affine(), c); - assert_eq!(tmp2, c.into_projective()); - } - - #[test] - fn g1_curve_tests() { - use group::tests::curve_tests; - curve_tests::(); - } -} - -pub mod g2 { - use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr}; - use super::g1::G1Affine; - use crate::{Engine, PairingCurveAffine}; - use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; - use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; - use rand_core::RngCore; - use std::fmt; - use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - use subtle::CtOption; - - curve_impl!( - "G2", - G2, - G2Affine, - G2Prepared, - Fq2, - Fr, - G2Uncompressed, - G2Compressed, - G1Affine - ); - - #[derive(Copy, Clone)] - pub struct G2Uncompressed([u8; 192]); - - impl AsRef<[u8]> for G2Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G2Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - impl fmt::Debug for G2Uncompressed { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0[..].fmt(formatter) - } - } - - impl EncodedPoint for G2Uncompressed { - type Affine = G2Affine; - - fn empty() -> Self { - G2Uncompressed([0; 192]) - } - fn size() -> usize { - 192 - } - fn into_affine(&self) -> Result { - let affine = self.into_affine_unchecked()?; - - if !affine.is_on_curve() { - Err(GroupDecodingError::NotOnCurve) - } else if !affine.is_in_correct_subgroup_assuming_on_curve() { - Err(GroupDecodingError::NotInSubgroup) - } else { - Ok(affine) - } - } - fn into_affine_unchecked(&self) -> Result { - // Create a copy of this representation. - let mut copy = self.0; - - if copy[0] & (1 << 7) != 0 { - // Distinguisher bit is set, but this should be uncompressed! - return Err(GroupDecodingError::UnexpectedCompressionMode); - } - - if copy[0] & (1 << 6) != 0 { - // This is the point at infinity, which means that if we mask away - // the first two bits, the entire representation should consist - // of zeroes. - copy[0] &= 0x3f; - - if copy.iter().all(|b| *b == 0) { - Ok(G2Affine::zero()) - } else { - Err(GroupDecodingError::UnexpectedInformation) - } - } else { - if copy[0] & (1 << 5) != 0 { - // The bit indicating the y-coordinate should be lexicographically - // largest is set, but this is an uncompressed element. - return Err(GroupDecodingError::UnexpectedInformation); - } - - // Unset the three most significant bits. - copy[0] &= 0x1f; - - let mut x_c0 = FqRepr([0; 6]); - let mut x_c1 = FqRepr([0; 6]); - let mut y_c0 = FqRepr([0; 6]); - let mut y_c1 = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x_c1.read_be(&mut reader).unwrap(); - x_c0.read_be(&mut reader).unwrap(); - y_c1.read_be(&mut reader).unwrap(); - y_c0.read_be(&mut reader).unwrap(); - } - - Ok(G2Affine { - x: Fq2 { - c0: Fq::from_repr(x_c0).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) - })?, - c1: Fq::from_repr(x_c1).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) - })?, - }, - y: Fq2 { - c0: Fq::from_repr(y_c0).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("y coordinate (c0)", e) - })?, - c1: Fq::from_repr(y_c1).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("y coordinate (c1)", e) - })?, - }, - infinity: false, - }) - } - } - fn from_affine(affine: G2Affine) -> Self { - let mut res = Self::empty(); - - if affine.is_zero() { - // Set the second-most significant bit to indicate this point - // is at infinity. - res.0[0] |= 1 << 6; - } else { - let mut writer = &mut res.0[..]; - - affine.x.c1.into_repr().write_be(&mut writer).unwrap(); - affine.x.c0.into_repr().write_be(&mut writer).unwrap(); - affine.y.c1.into_repr().write_be(&mut writer).unwrap(); - affine.y.c0.into_repr().write_be(&mut writer).unwrap(); - } - - res - } - } - - #[derive(Copy, Clone)] - pub struct G2Compressed([u8; 96]); - - impl AsRef<[u8]> for G2Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for G2Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - impl fmt::Debug for G2Compressed { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - self.0[..].fmt(formatter) - } - } - - impl EncodedPoint for G2Compressed { - type Affine = G2Affine; - - fn empty() -> Self { - G2Compressed([0; 96]) - } - fn size() -> usize { - 96 - } - fn into_affine(&self) -> Result { - let affine = self.into_affine_unchecked()?; - - // NB: Decompression guarantees that it is on the curve already. - - if !affine.is_in_correct_subgroup_assuming_on_curve() { - Err(GroupDecodingError::NotInSubgroup) - } else { - Ok(affine) - } - } - fn into_affine_unchecked(&self) -> Result { - // Create a copy of this representation. - let mut copy = self.0; - - if copy[0] & (1 << 7) == 0 { - // Distinguisher bit isn't set. - return Err(GroupDecodingError::UnexpectedCompressionMode); - } - - if copy[0] & (1 << 6) != 0 { - // This is the point at infinity, which means that if we mask away - // the first two bits, the entire representation should consist - // of zeroes. - copy[0] &= 0x3f; - - if copy.iter().all(|b| *b == 0) { - Ok(G2Affine::zero()) - } else { - Err(GroupDecodingError::UnexpectedInformation) - } - } else { - // Determine if the intended y coordinate must be greater - // lexicographically. - let greatest = copy[0] & (1 << 5) != 0; - - // Unset the three most significant bits. - copy[0] &= 0x1f; - - let mut x_c1 = FqRepr([0; 6]); - let mut x_c0 = FqRepr([0; 6]); - - { - let mut reader = ©[..]; - - x_c1.read_be(&mut reader).unwrap(); - x_c0.read_be(&mut reader).unwrap(); - } - - // Interpret as Fq element. - let x = Fq2 { - c0: Fq::from_repr(x_c0).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) - })?, - c1: Fq::from_repr(x_c1).map_err(|e| { - GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) - })?, - }; - - let ret = G2Affine::get_point_from_x(x, greatest); - if ret.is_some().into() { - Ok(ret.unwrap()) - } else { - Err(GroupDecodingError::NotOnCurve) - } - } - } - fn from_affine(affine: G2Affine) -> Self { - let mut res = Self::empty(); - - if affine.is_zero() { - // Set the second-most significant bit to indicate this point - // is at infinity. - res.0[0] |= 1 << 6; - } else { - { - let mut writer = &mut res.0[..]; - - affine.x.c1.into_repr().write_be(&mut writer).unwrap(); - affine.x.c0.into_repr().write_be(&mut writer).unwrap(); - } - - let negy = affine.y.neg(); - - // Set the third most significant bit if the correct y-coordinate - // is lexicographically largest. - if affine.y > negy { - res.0[0] |= 1 << 5; - } - } - - // Set highest bit to distinguish this as a compressed element. - res.0[0] |= 1 << 7; - - res - } - } - - impl G2Affine { - fn get_generator() -> Self { - G2Affine { - x: Fq2 { - c0: super::super::fq::G2_GENERATOR_X_C0, - c1: super::super::fq::G2_GENERATOR_X_C1, - }, - y: Fq2 { - c0: super::super::fq::G2_GENERATOR_Y_C0, - c1: super::super::fq::G2_GENERATOR_Y_C1, - }, - infinity: false, - } - } - - fn get_coeff_b() -> Fq2 { - Fq2 { - c0: super::super::fq::B_COEFF, - c1: super::super::fq::B_COEFF, - } - } - - fn scale_by_cofactor(&self) -> G2 { - // G2 cofactor = (x^8 - 4 x^7 + 5 x^6) - (4 x^4 + 6 x^3 - 4 x^2 - 4 x + 13) // 9 - // 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5 - let cofactor = BitIterator::new([ - 0xcf1c38e31c7238e5, - 0x1616ec6e786f0c70, - 0x21537e293a6691ae, - 0xa628f1cb4d9e82ef, - 0xa68a205b2e5a7ddf, - 0xcd91de4547085aba, - 0x91d50792876a202, - 0x5d543a95414e7f1, - ]); - self.mul_bits(cofactor) - } - - fn perform_pairing(&self, other: &G1Affine) -> Fq12 { - super::super::Bls12::pairing(*other, *self) - } - } - - impl G2 { - fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { - let num_bits = scalar.num_bits() as usize; - - if num_bits >= 103 { - 4 - } else if num_bits >= 37 { - 3 - } else { - 2 - } - } - - fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - const RECOMMENDATIONS: [usize; 11] = - [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } - } - - #[derive(Clone, Debug)] - pub struct G2Prepared { - pub(crate) coeffs: Vec<(Fq2, Fq2, Fq2)>, - pub(crate) infinity: bool, - } - - #[test] - fn g2_generator() { - use crate::SqrtField; - - let mut x = Fq2::zero(); - let mut i = 0; - loop { - // y^2 = x^3 + b - let mut rhs = x.square(); - rhs.mul_assign(&x); - rhs.add_assign(&G2Affine::get_coeff_b()); - - let y = rhs.sqrt(); - if y.is_some().into() { - let y = y.unwrap(); - let negy = y.neg(); - - let p = G2Affine { - x, - y: if y < negy { y } else { negy }, - infinity: false, - }; - - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - - let g2 = p.scale_by_cofactor(); - if !g2.is_zero() { - assert_eq!(i, 2); - let g2 = G2Affine::from(g2); - - assert!(g2.is_in_correct_subgroup_assuming_on_curve()); - assert_eq!(g2, G2Affine::one()); - break; - } - } - - i += 1; - x.add_assign(&Fq2::one()); - } - } - - #[test] - fn g2_test_is_valid() { - // Reject point on isomorphic twist (b = 3 * (u + 1)) - { - let p = G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0xa757072d9fa35ba9, - 0xae3fb2fb418f6e8a, - 0xc1598ec46faa0c7c, - 0x7a17a004747e3dbe, - 0xcc65406a7c2e5a73, - 0x10b8c03d64db4d0c, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0xd30e70fe2f029778, - 0xda30772df0f5212e, - 0x5b47a9ff9a233a50, - 0xfb777e5b9b568608, - 0x789bac1fec71a2b9, - 0x1342f02e2da54405, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0xfe0812043de54dca, - 0xe455171a3d47a646, - 0xa493f36bc20be98a, - 0x663015d9410eb608, - 0x78e82a79d829a544, - 0x40a00545bb3c1e, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x4709802348e79377, - 0xb5ac4dc9204bcfbd, - 0xda361c97d02f42b2, - 0x15008b1dc399e8df, - 0x68128fd0548a3829, - 0x16a613db5c873aaa, - ])) - .unwrap(), - }, - infinity: false, - }; - assert!(!p.is_on_curve()); - assert!(p.is_in_correct_subgroup_assuming_on_curve()); - } - - // Reject point on a twist (b = 2 * (u + 1)) - { - let p = G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0xf4fdfe95a705f917, - 0xc2914df688233238, - 0x37c6b12cca35a34b, - 0x41abba710d6c692c, - 0xffcc4b2b62ce8484, - 0x6993ec01b8934ed, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0xb94e92d5f874e26, - 0x44516408bc115d95, - 0xe93946b290caa591, - 0xa5a0c2b7131f3555, - 0x83800965822367e7, - 0x10cf1d3ad8d90bfa, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0xbf00334c79701d97, - 0x4fe714f9ff204f9a, - 0xab70b28002f3d825, - 0x5a9171720e73eb51, - 0x38eb4fd8d658adb7, - 0xb649051bbc1164d, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x9225814253d7df75, - 0xc196c2513477f887, - 0xe05e2fbd15a804e0, - 0x55f2b8efad953e04, - 0x7379345eda55265e, - 0x377f2e6208fd4cb, - ])) - .unwrap(), - }, - infinity: false, - }; - assert!(!p.is_on_curve()); - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - } - - // Reject point in an invalid subgroup - // There is only one r-order subgroup, as r does not divide the cofactor. - { - let p = G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x262cea73ea1906c, - 0x2f08540770fabd6, - 0x4ceb92d0a76057be, - 0x2199bc19c48c393d, - 0x4a151b732a6075bf, - 0x17762a3b9108c4a7, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x26f461e944bbd3d1, - 0x298f3189a9cf6ed6, - 0x74328ad8bc2aa150, - 0x7e147f3f9e6e241, - 0x72a9b63583963fff, - 0x158b0083c000462, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x91fb0b225ecf103b, - 0x55d42edc1dc46ba0, - 0x43939b11997b1943, - 0x68cad19430706b4d, - 0x3ccfb97b924dcea8, - 0x1660f93434588f8d, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0xaaed3985b6dcb9c7, - 0xc1e985d6d898d9f4, - 0x618bd2ac3271ac42, - 0x3940a2dbb914b529, - 0xbeb88137cf34f3e7, - 0x1699ee577c61b694, - ])) - .unwrap(), - }, - infinity: false, - }; - assert!(p.is_on_curve()); - assert!(!p.is_in_correct_subgroup_assuming_on_curve()); - } - } - - #[test] - fn test_g2_addition_correctness() { - let mut p = G2 { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x6c994cc1e303094e, - 0xf034642d2c9e85bd, - 0x275094f1352123a9, - 0x72556c999f3707ac, - 0x4617f2e6774e9711, - 0x100b2fe5bffe030b, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x7a33555977ec608, - 0xe23039d1fe9c0881, - 0x19ce4678aed4fcb5, - 0x4637c4f417667e2e, - 0x93ebe7c3e41f6acc, - 0xde884f89a9a371b, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0xe073119472e1eb62, - 0x44fb3391fe3c9c30, - 0xaa9b066d74694006, - 0x25fd427b4122f231, - 0xd83112aace35cae, - 0x191b2432407cbb7f, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0xf68ae82fe97662f5, - 0xe986057068b50b7d, - 0x96c30f0411590b48, - 0x9eaa6d19de569196, - 0xf6a03d31e2ec2183, - 0x3bdafaf7ca9b39b, - ])) - .unwrap(), - }, - z: Fq2::one(), - }; - - p.add_assign(&G2 { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0xa8c763d25910bdd3, - 0x408777b30ca3add4, - 0x6115fcc12e2769e, - 0x8e73a96b329ad190, - 0x27c546f75ee1f3ab, - 0xa33d27add5e7e82, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x93b1ebcd54870dfe, - 0xf1578300e1342e11, - 0x8270dca3a912407b, - 0x2089faf462438296, - 0x828e5848cd48ea66, - 0x141ecbac1deb038b, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0xf5d2c28857229c3f, - 0x8c1574228757ca23, - 0xe8d8102175f5dc19, - 0x2767032fc37cc31d, - 0xd5ee2aba84fd10fe, - 0x16576ccd3dd0a4e8, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x4da9b6f6a96d1dd2, - 0x9657f7da77f1650e, - 0xbc150712f9ffe6da, - 0x31898db63f87363a, - 0xabab040ddbd097cc, - 0x11ad236b9ba02990, - ])) - .unwrap(), - }, - z: Fq2::one(), - }); - - let p = G2Affine::from(p); - - assert_eq!( - p, - G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0xcde7ee8a3f2ac8af, - 0xfc642eb35975b069, - 0xa7de72b7dd0e64b7, - 0xf1273e6406eef9cc, - 0xababd760ff05cb92, - 0xd7c20456617e89 - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0xd1a50b8572cbd2b8, - 0x238f0ac6119d07df, - 0x4dbe924fe5fd6ac2, - 0x8b203284c51edf6b, - 0xc8a0b730bbb21f5e, - 0x1a3b59d29a31274 - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x9e709e78a8eaa4c9, - 0xd30921c93ec342f4, - 0x6d1ef332486f5e34, - 0x64528ab3863633dc, - 0x159384333d7cba97, - 0x4cb84741f3cafe8 - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x242af0dc3640e1a4, - 0xe90a73ad65c66919, - 0x2bd7ca7f4346f9ec, - 0x38528f92b689644d, - 0xb6884deec59fb21f, - 0x3c075d3ec52ba90 - ])) - .unwrap(), - }, - infinity: false, - } - ); - } - - #[test] - fn test_g2_doubling_correctness() { - let mut p = G2 { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x6c994cc1e303094e, - 0xf034642d2c9e85bd, - 0x275094f1352123a9, - 0x72556c999f3707ac, - 0x4617f2e6774e9711, - 0x100b2fe5bffe030b, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x7a33555977ec608, - 0xe23039d1fe9c0881, - 0x19ce4678aed4fcb5, - 0x4637c4f417667e2e, - 0x93ebe7c3e41f6acc, - 0xde884f89a9a371b, - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0xe073119472e1eb62, - 0x44fb3391fe3c9c30, - 0xaa9b066d74694006, - 0x25fd427b4122f231, - 0xd83112aace35cae, - 0x191b2432407cbb7f, - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0xf68ae82fe97662f5, - 0xe986057068b50b7d, - 0x96c30f0411590b48, - 0x9eaa6d19de569196, - 0xf6a03d31e2ec2183, - 0x3bdafaf7ca9b39b, - ])) - .unwrap(), - }, - z: Fq2::one(), - }; - - p.double(); - - let p = G2Affine::from(p); - - assert_eq!( - p, - G2Affine { - x: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x91ccb1292727c404, - 0x91a6cb182438fad7, - 0x116aee59434de902, - 0xbcedcfce1e52d986, - 0x9755d4a3926e9862, - 0x18bab73760fd8024 - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x4e7c5e0a2ae5b99e, - 0x96e582a27f028961, - 0xc74d1cf4ef2d5926, - 0xeb0cf5e610ef4fe7, - 0x7b4c2bae8db6e70b, - 0xf136e43909fca0 - ])) - .unwrap(), - }, - y: Fq2 { - c0: Fq::from_repr(FqRepr([ - 0x954d4466ab13e58, - 0x3ee42eec614cf890, - 0x853bb1d28877577e, - 0xa5a2a51f7fde787b, - 0x8b92866bc6384188, - 0x81a53fe531d64ef - ])) - .unwrap(), - c1: Fq::from_repr(FqRepr([ - 0x4c5d607666239b34, - 0xeddb5f48304d14b3, - 0x337167ee6e8e3cb6, - 0xb271f52f12ead742, - 0x244e6c2015c83348, - 0x19e2deae6eb9b441 - ])) - .unwrap(), - }, - infinity: false, - } - ); - } - - #[test] - fn g2_curve_tests() { - use group::tests::curve_tests; - curve_tests::(); - } -} - -pub use self::g1::*; -pub use self::g2::*; diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs deleted file mode 100644 index 57d6532645..0000000000 --- a/pairing/src/bls12_381/fq.rs +++ /dev/null @@ -1,2235 +0,0 @@ -use super::fq2::Fq2; -use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -#[cfg(test)] -use std::ops::Neg; - -// B coefficient of BLS12-381 curve, 4. -pub const B_COEFF: Fq = Fq(FqRepr([ - 0xaa270000000cfff3, - 0x53cc0032fc34000a, - 0x478fe97a6b0a807f, - 0xb1d37ebee6ba24d7, - 0x8ec9733bbf78ab2f, - 0x9d645513d83de7e, -])); - -// The generators of G1/G2 are computed by finding the lexicographically smallest valid x coordinate, -// and its lexicographically smallest y coordinate and multiplying it by the cofactor such that the -// result is nonzero. - -// Generator of G1 -// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 -// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 -pub const G1_GENERATOR_X: Fq = Fq(FqRepr([ - 0x5cb38790fd530c16, - 0x7817fc679976fff5, - 0x154f95c7143ba1c1, - 0xf0ae6acdf3d0e747, - 0xedce6ecc21dbf440, - 0x120177419e0bfb75, -])); -pub const G1_GENERATOR_Y: Fq = Fq(FqRepr([ - 0xbaac93d50ce72271, - 0x8c22631a7918fd8e, - 0xdd595f13570725ce, - 0x51ac582950405194, - 0xe1c8c3fad0059c0, - 0xbbc3efc5008a26a, -])); - -// Generator of G2 -// x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 -// y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 -pub const G2_GENERATOR_X_C0: Fq = Fq(FqRepr([ - 0xf5f28fa202940a10, - 0xb3f5fb2687b4961a, - 0xa1a893b53e2ae580, - 0x9894999d1a3caee9, - 0x6f67b7631863366b, - 0x58191924350bcd7, -])); -pub const G2_GENERATOR_X_C1: Fq = Fq(FqRepr([ - 0xa5a9c0759e23f606, - 0xaaa0c59dbccd60c3, - 0x3bb17e18e2867806, - 0x1b1ab6cc8541b367, - 0xc2b6ed0ef2158547, - 0x11922a097360edf3, -])); -pub const G2_GENERATOR_Y_C0: Fq = Fq(FqRepr([ - 0x4c730af860494c4a, - 0x597cfa1f5e369c5a, - 0xe7e6856caa0a635a, - 0xbbefb5e96e0d495f, - 0x7d3a975f0ef25a2, - 0x83fd8e7e80dae5, -])); -pub const G2_GENERATOR_Y_C1: Fq = Fq(FqRepr([ - 0xadc0fc92df64b05d, - 0x18aa270a2b1461dc, - 0x86adac6a3be4eba0, - 0x79495c4ec93da33a, - 0xe7175850a43ccaed, - 0xb2bc2a163de1bf2, -])); - -// Coefficients for the Frobenius automorphism. -pub const FROBENIUS_COEFF_FQ2_C1: [Fq; 2] = [ - // Fq(-1)**(((q^0) - 1) / 2) - Fq(FqRepr([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ])), - // Fq(-1)**(((q^1) - 1) / 2) - Fq(FqRepr([ - 0x43f5fffffffcaaae, - 0x32b7fff2ed47fffd, - 0x7e83a49a2e99d69, - 0xeca8f3318332bb7a, - 0xef148d1ea0f4c069, - 0x40ab3263eff0206, - ])), -]; - -pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ - // Fq2(u + 1)**(((q^0) - 1) / 3) - Fq2 { - c0: Fq(FqRepr([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((q^1) - 1) / 3) - Fq2 { - c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - c1: Fq(FqRepr([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ])), - }, - // Fq2(u + 1)**(((q^2) - 1) / 3) - Fq2 { - c0: Fq(FqRepr([ - 0x30f1361b798a64e8, - 0xf3b8ddab7ece5a2a, - 0x16a8ca3ac61577f7, - 0xc26a2ff874fd029b, - 0x3636b76660701c6e, - 0x51ba4ab241b6160, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((q^3) - 1) / 3) - Fq2 { - c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - c1: Fq(FqRepr([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ])), - }, - // Fq2(u + 1)**(((q^4) - 1) / 3) - Fq2 { - c0: Fq(FqRepr([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((q^5) - 1) / 3) - Fq2 { - c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - c1: Fq(FqRepr([ - 0x30f1361b798a64e8, - 0xf3b8ddab7ece5a2a, - 0x16a8ca3ac61577f7, - 0xc26a2ff874fd029b, - 0x3636b76660701c6e, - 0x51ba4ab241b6160, - ])), - }, -]; - -pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ - // Fq2(u + 1)**(((2q^0) - 2) / 3) - Fq2 { - c0: Fq(FqRepr([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((2q^1) - 2) / 3) - Fq2 { - c0: Fq(FqRepr([ - 0x890dc9e4867545c3, - 0x2af322533285a5d5, - 0x50880866309b7e2c, - 0xa20d1b8c7e881024, - 0x14e4f04fe2db9068, - 0x14e56d3f1564853a, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((2q^2) - 2) / 3) - Fq2 { - c0: Fq(FqRepr([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((2q^3) - 2) / 3) - Fq2 { - c0: Fq(FqRepr([ - 0x43f5fffffffcaaae, - 0x32b7fff2ed47fffd, - 0x7e83a49a2e99d69, - 0xeca8f3318332bb7a, - 0xef148d1ea0f4c069, - 0x40ab3263eff0206, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((2q^4) - 2) / 3) - Fq2 { - c0: Fq(FqRepr([ - 0x30f1361b798a64e8, - 0xf3b8ddab7ece5a2a, - 0x16a8ca3ac61577f7, - 0xc26a2ff874fd029b, - 0x3636b76660701c6e, - 0x51ba4ab241b6160, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((2q^5) - 2) / 3) - Fq2 { - c0: Fq(FqRepr([ - 0xecfb361b798dba3a, - 0xc100ddb891865a2c, - 0xec08ff1232bda8e, - 0xd5c13cc6f1ca4721, - 0x47222a47bf7b5c04, - 0x110f184e51c5f59, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, -]; - -// non_residue^((modulus^i-1)/6) for i=0,...,11 -pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ - // Fq2(u + 1)**(((q^0) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0x760900000002fffd, - 0xebf4000bc40c0002, - 0x5f48985753c758ba, - 0x77ce585370525745, - 0x5c071a97a256ec6d, - 0x15f65ec3fa80e493, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((q^1) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0x7089552b319d465, - 0xc6695f92b50a8313, - 0x97e83cccd117228f, - 0xa35baecab2dc29ee, - 0x1ce393ea5daace4d, - 0x8f2220fb0fb66eb, - ])), - c1: Fq(FqRepr([ - 0xb2f66aad4ce5d646, - 0x5842a06bfc497cec, - 0xcf4895d42599d394, - 0xc11b9cba40a8e8d0, - 0x2e3813cbe5a0de89, - 0x110eefda88847faf, - ])), - }, - // Fq2(u + 1)**(((q^2) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0xecfb361b798dba3a, - 0xc100ddb891865a2c, - 0xec08ff1232bda8e, - 0xd5c13cc6f1ca4721, - 0x47222a47bf7b5c04, - 0x110f184e51c5f59, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((q^3) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0x3e2f585da55c9ad1, - 0x4294213d86c18183, - 0x382844c88b623732, - 0x92ad2afd19103e18, - 0x1d794e4fac7cf0b9, - 0xbd592fc7d825ec8, - ])), - c1: Fq(FqRepr([ - 0x7bcfa7a25aa30fda, - 0xdc17dec12a927e7c, - 0x2f088dd86b4ebef1, - 0xd1ca2087da74d4a7, - 0x2da2596696cebc1d, - 0xe2b7eedbbfd87d2, - ])), - }, - // Fq2(u + 1)**(((q^4) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0x30f1361b798a64e8, - 0xf3b8ddab7ece5a2a, - 0x16a8ca3ac61577f7, - 0xc26a2ff874fd029b, - 0x3636b76660701c6e, - 0x51ba4ab241b6160, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((q^5) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0x3726c30af242c66c, - 0x7c2ac1aad1b6fe70, - 0xa04007fbba4b14a2, - 0xef517c3266341429, - 0x95ba654ed2226b, - 0x2e370eccc86f7dd, - ])), - c1: Fq(FqRepr([ - 0x82d83cf50dbce43f, - 0xa2813e53df9d018f, - 0xc6f0caa53c65e181, - 0x7525cf528d50fe95, - 0x4a85ed50f4798a6b, - 0x171da0fd6cf8eebd, - ])), - }, - // Fq2(u + 1)**(((q^6) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0x43f5fffffffcaaae, - 0x32b7fff2ed47fffd, - 0x7e83a49a2e99d69, - 0xeca8f3318332bb7a, - 0xef148d1ea0f4c069, - 0x40ab3263eff0206, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((q^7) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0xb2f66aad4ce5d646, - 0x5842a06bfc497cec, - 0xcf4895d42599d394, - 0xc11b9cba40a8e8d0, - 0x2e3813cbe5a0de89, - 0x110eefda88847faf, - ])), - c1: Fq(FqRepr([ - 0x7089552b319d465, - 0xc6695f92b50a8313, - 0x97e83cccd117228f, - 0xa35baecab2dc29ee, - 0x1ce393ea5daace4d, - 0x8f2220fb0fb66eb, - ])), - }, - // Fq2(u + 1)**(((q^8) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x3f97d6e83d050d2, - 0x18f0206554638741, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((q^9) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0x7bcfa7a25aa30fda, - 0xdc17dec12a927e7c, - 0x2f088dd86b4ebef1, - 0xd1ca2087da74d4a7, - 0x2da2596696cebc1d, - 0xe2b7eedbbfd87d2, - ])), - c1: Fq(FqRepr([ - 0x3e2f585da55c9ad1, - 0x4294213d86c18183, - 0x382844c88b623732, - 0x92ad2afd19103e18, - 0x1d794e4fac7cf0b9, - 0xbd592fc7d825ec8, - ])), - }, - // Fq2(u + 1)**(((q^10) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0x890dc9e4867545c3, - 0x2af322533285a5d5, - 0x50880866309b7e2c, - 0xa20d1b8c7e881024, - 0x14e4f04fe2db9068, - 0x14e56d3f1564853a, - ])), - c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), - }, - // Fq2(u + 1)**(((q^11) - 1) / 6) - Fq2 { - c0: Fq(FqRepr([ - 0x82d83cf50dbce43f, - 0xa2813e53df9d018f, - 0xc6f0caa53c65e181, - 0x7525cf528d50fe95, - 0x4a85ed50f4798a6b, - 0x171da0fd6cf8eebd, - ])), - c1: Fq(FqRepr([ - 0x3726c30af242c66c, - 0x7c2ac1aad1b6fe70, - 0xa04007fbba4b14a2, - 0xef517c3266341429, - 0x95ba654ed2226b, - 0x2e370eccc86f7dd, - ])), - }, -]; - -// -((2**384) mod q) mod q -pub const NEGATIVE_ONE: Fq = Fq(FqRepr([ - 0x43f5fffffffcaaae, - 0x32b7fff2ed47fffd, - 0x7e83a49a2e99d69, - 0xeca8f3318332bb7a, - 0xef148d1ea0f4c069, - 0x40ab3263eff0206, -])); - -#[derive(PrimeField)] -#[PrimeFieldModulus = "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787"] -#[PrimeFieldGenerator = "2"] -pub struct Fq(FqRepr); - -#[test] -fn test_b_coeff() { - assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF); -} - -#[test] -#[allow(clippy::cognitive_complexity)] -fn test_frob_coeffs() { - let nqr = Fq::one().neg(); - - assert_eq!(FROBENIUS_COEFF_FQ2_C1[0], Fq::one()); - assert_eq!( - FROBENIUS_COEFF_FQ2_C1[1], - nqr.pow_vartime([ - 0xdcff7fffffffd555, - 0xf55ffff58a9ffff, - 0xb39869507b587b12, - 0xb23ba5c279c2895f, - 0x258dd3db21a5d66b, - 0xd0088f51cbff34d - ]) - ); - - let nqr = Fq2 { - c0: Fq::one(), - c1: Fq::one(), - }; - - assert_eq!(FROBENIUS_COEFF_FQ6_C1[0], Fq2::one()); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[1], - nqr.pow_vartime([ - 0x9354ffffffffe38e, - 0xa395554e5c6aaaa, - 0xcd104635a790520c, - 0xcc27c3d6fbd7063f, - 0x190937e76bc3e447, - 0x8ab05f8bdd54cde - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[2], - nqr.pow_vartime([ - 0xb78e0000097b2f68, - 0xd44f23b47cbd64e3, - 0x5cb9668120b069a9, - 0xccea85f9bf7b3d16, - 0xdba2c8d7adb356d, - 0x9cd75ded75d7429, - 0xfc65c31103284fab, - 0xc58cb9a9b249ee24, - 0xccf734c3118a2e9a, - 0xa0f4304c5a256ce6, - 0xc3f0d2f8e0ba61f8, - 0xe167e192ebca97 - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[3], - nqr.pow_vartime([ - 0xdbc6fcd6f35b9e06, - 0x997dead10becd6aa, - 0x9dbbd24c17206460, - 0x72b97acc6057c45e, - 0xf8e9a230bf0c628e, - 0x647ccb1885c63a7, - 0xce80264fc55bf6ee, - 0x94d8d716c3939fc4, - 0xad78f0eb77ee6ee1, - 0xd6fe49bfe57dc5f9, - 0x2656d6c15c63647, - 0xdf6282f111fa903, - 0x1bdba63e0632b4bb, - 0x6883597bcaa505eb, - 0xa56d4ec90c34a982, - 0x7e4c42823bbe90b2, - 0xf64728aa6dcb0f20, - 0x16e57e16ef152f - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[4], - nqr.pow_vartime([ - 0x4649add3c71c6d90, - 0x43caa6528972a865, - 0xcda8445bbaaa0fbb, - 0xc93dea665662aa66, - 0x2863bc891834481d, - 0x51a0c3f5d4ccbed8, - 0x9210e660f90ccae9, - 0xe2bd6836c546d65e, - 0xf223abbaa7cf778b, - 0xd4f10b222cf11680, - 0xd540f5eff4a1962e, - 0xa123a1f140b56526, - 0x31ace500636a59f6, - 0x3a82bc8c8dfa57a9, - 0x648c511e217fc1f8, - 0x36c17ffd53a4558f, - 0x881bef5fd684eefd, - 0x5d648dbdc5dbb522, - 0x8fd07bf06e5e59b8, - 0x8ddec8a9acaa4b51, - 0x4cc1f8688e2def26, - 0xa74e63cb492c03de, - 0x57c968173d1349bb, - 0x253674e02a866 - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C1[5], - nqr.pow_vartime([ - 0xf896f792732eb2be, - 0x49c86a6d1dc593a1, - 0xe5b31e94581f91c3, - 0xe3da5cc0a6b20d7f, - 0x822caef950e0bfed, - 0x317ed950b9ee67cd, - 0xffd664016ee3f6cd, - 0x77d991c88810b122, - 0x62e72e635e698264, - 0x905e1a1a2d22814a, - 0xf5b7ab3a3f33d981, - 0x175871b0bc0e25dd, - 0x1e2e9a63df5c3772, - 0xe888b1f7445b149d, - 0x9551c19e5e7e2c24, - 0xecf21939a3d2d6be, - 0xd830dbfdab72dbd4, - 0x7b34af8d622d40c0, - 0x3df6d20a45671242, - 0xaf86bee30e21d98, - 0x41064c1534e5df5d, - 0xf5f6cabd3164c609, - 0xa5d14bdf2b7ee65, - 0xa718c069defc9138, - 0xdb1447e770e3110e, - 0xc1b164a9e90af491, - 0x7180441f9d251602, - 0x1fd3a5e6a9a893e, - 0x1e17b779d54d5db, - 0x3c7afafe3174 - ]) - ); - - assert_eq!(FROBENIUS_COEFF_FQ6_C2[0], Fq2::one()); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[1], - nqr.pow_vartime([ - 0x26a9ffffffffc71c, - 0x1472aaa9cb8d5555, - 0x9a208c6b4f20a418, - 0x984f87adf7ae0c7f, - 0x32126fced787c88f, - 0x11560bf17baa99bc - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[2], - nqr.pow_vartime([ - 0x6f1c000012f65ed0, - 0xa89e4768f97ac9c7, - 0xb972cd024160d353, - 0x99d50bf37ef67a2c, - 0x1b74591af5b66adb, - 0x139aebbdaebae852, - 0xf8cb862206509f56, - 0x8b1973536493dc49, - 0x99ee698623145d35, - 0x41e86098b44ad9cd, - 0x87e1a5f1c174c3f1, - 0x1c2cfc325d7952f - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[3], - nqr.pow_vartime([ - 0xb78df9ade6b73c0c, - 0x32fbd5a217d9ad55, - 0x3b77a4982e40c8c1, - 0xe572f598c0af88bd, - 0xf1d344617e18c51c, - 0xc8f996310b8c74f, - 0x9d004c9f8ab7eddc, - 0x29b1ae2d87273f89, - 0x5af1e1d6efdcddc3, - 0xadfc937fcafb8bf3, - 0x4cadad82b8c6c8f, - 0x1bec505e223f5206, - 0x37b74c7c0c656976, - 0xd106b2f7954a0bd6, - 0x4ada9d9218695304, - 0xfc988504777d2165, - 0xec8e5154db961e40, - 0x2dcafc2dde2a5f - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[4], - nqr.pow_vartime([ - 0x8c935ba78e38db20, - 0x87954ca512e550ca, - 0x9b5088b775541f76, - 0x927bd4ccacc554cd, - 0x50c779123068903b, - 0xa34187eba9997db0, - 0x2421ccc1f21995d2, - 0xc57ad06d8a8dacbd, - 0xe44757754f9eef17, - 0xa9e2164459e22d01, - 0xaa81ebdfe9432c5d, - 0x424743e2816aca4d, - 0x6359ca00c6d4b3ed, - 0x750579191bf4af52, - 0xc918a23c42ff83f0, - 0x6d82fffaa748ab1e, - 0x1037debfad09ddfa, - 0xbac91b7b8bb76a45, - 0x1fa0f7e0dcbcb370, - 0x1bbd9153595496a3, - 0x9983f0d11c5bde4d, - 0x4e9cc796925807bc, - 0xaf92d02e7a269377, - 0x4a6ce9c0550cc - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ6_C2[5], - nqr.pow_vartime([ - 0xf12def24e65d657c, - 0x9390d4da3b8b2743, - 0xcb663d28b03f2386, - 0xc7b4b9814d641aff, - 0x4595df2a1c17fdb, - 0x62fdb2a173dccf9b, - 0xffacc802ddc7ed9a, - 0xefb3239110216245, - 0xc5ce5cc6bcd304c8, - 0x20bc34345a450294, - 0xeb6f56747e67b303, - 0x2eb0e361781c4bbb, - 0x3c5d34c7beb86ee4, - 0xd11163ee88b6293a, - 0x2aa3833cbcfc5849, - 0xd9e4327347a5ad7d, - 0xb061b7fb56e5b7a9, - 0xf6695f1ac45a8181, - 0x7beda4148ace2484, - 0x15f0d7dc61c43b30, - 0x820c982a69cbbeba, - 0xebed957a62c98c12, - 0x14ba297be56fdccb, - 0x4e3180d3bdf92270, - 0xb6288fcee1c6221d, - 0x8362c953d215e923, - 0xe300883f3a4a2c05, - 0x3fa74bcd535127c, - 0x3c2f6ef3aa9abb6, - 0x78f5f5fc62e8 - ]) - ); - - assert_eq!(FROBENIUS_COEFF_FQ12_C1[0], Fq2::one()); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[1], - nqr.pow_vartime([ - 0x49aa7ffffffff1c7, - 0x51caaaa72e35555, - 0xe688231ad3c82906, - 0xe613e1eb7deb831f, - 0xc849bf3b5e1f223, - 0x45582fc5eeaa66f - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[2], - nqr.pow_vartime([ - 0xdbc7000004bd97b4, - 0xea2791da3e5eb271, - 0x2e5cb340905834d4, - 0xe67542fcdfbd9e8b, - 0x86dd1646bd6d9ab6, - 0x84e6baef6baeba14, - 0x7e32e188819427d5, - 0x62c65cd4d924f712, - 0x667b9a6188c5174d, - 0x507a18262d12b673, - 0xe1f8697c705d30fc, - 0x70b3f0c975e54b - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[3], - nqr.pow_vartime(vec![ - 0x6de37e6b79adcf03, - 0x4cbef56885f66b55, - 0x4edde9260b903230, - 0x395cbd66302be22f, - 0xfc74d1185f863147, - 0x323e658c42e31d3, - 0x67401327e2adfb77, - 0xca6c6b8b61c9cfe2, - 0xd6bc7875bbf73770, - 0xeb7f24dff2bee2fc, - 0x8132b6b60ae31b23, - 0x86fb1417888fd481, - 0x8dedd31f03195a5d, - 0x3441acbde55282f5, - 0x52b6a764861a54c1, - 0x3f2621411ddf4859, - 0xfb23945536e58790, - 0xb72bf0b778a97, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[4], - nqr.pow_vartime(vec![ - 0xa324d6e9e38e36c8, - 0xa1e5532944b95432, - 0x66d4222ddd5507dd, - 0xe49ef5332b315533, - 0x1431de448c1a240e, - 0xa8d061faea665f6c, - 0x490873307c866574, - 0xf15eb41b62a36b2f, - 0x7911d5dd53e7bbc5, - 0x6a78859116788b40, - 0x6aa07af7fa50cb17, - 0x5091d0f8a05ab293, - 0x98d6728031b52cfb, - 0x1d415e4646fd2bd4, - 0xb246288f10bfe0fc, - 0x9b60bffea9d22ac7, - 0x440df7afeb42777e, - 0x2eb246dee2edda91, - 0xc7e83df8372f2cdc, - 0x46ef6454d65525a8, - 0x2660fc344716f793, - 0xd3a731e5a49601ef, - 0x2be4b40b9e89a4dd, - 0x129b3a7015433, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[5], - nqr.pow_vartime(vec![ - 0xfc4b7bc93997595f, - 0xa4e435368ee2c9d0, - 0xf2d98f4a2c0fc8e1, - 0xf1ed2e60535906bf, - 0xc116577ca8705ff6, - 0x98bf6ca85cf733e6, - 0x7feb3200b771fb66, - 0x3becc8e444085891, - 0x31739731af34c132, - 0xc82f0d0d169140a5, - 0xfadbd59d1f99ecc0, - 0xbac38d85e0712ee, - 0x8f174d31efae1bb9, - 0x744458fba22d8a4e, - 0x4aa8e0cf2f3f1612, - 0x76790c9cd1e96b5f, - 0x6c186dfed5b96dea, - 0x3d9a57c6b116a060, - 0x1efb690522b38921, - 0x857c35f718710ecc, - 0xa083260a9a72efae, - 0xfafb655e98b26304, - 0x52e8a5ef95bf732, - 0x538c6034ef7e489c, - 0xed8a23f3b8718887, - 0x60d8b254f4857a48, - 0x38c0220fce928b01, - 0x80fe9d2f354d449f, - 0xf0bdbbceaa6aed, - 0x1e3d7d7f18ba, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[6], - nqr.pow_vartime(vec![ - 0x21219610a012ba3c, - 0xa5c19ad35375325, - 0x4e9df1e497674396, - 0xfb05b717c991c6ef, - 0x4a1265bca93a32f2, - 0xd875ff2a7bdc1f66, - 0xc6d8754736c771b2, - 0x2d80c759ba5a2ae7, - 0x138a20df4b03cc1a, - 0xc22d07fe68e93024, - 0xd1dc474d3b433133, - 0xc22aa5e75044e5c, - 0xf657c6fbf9c17ebf, - 0xc591a794a58660d, - 0x2261850ee1453281, - 0xd17d3bd3b7f5efb4, - 0xf00cec8ec507d01, - 0x2a6a775657a00ae6, - 0x5f098a12ff470719, - 0x409d194e7b5c5afa, - 0x1d66478e982af5b, - 0xda425a5b5e01ca3f, - 0xf77e4f78747e903c, - 0x177d49f73732c6fc, - 0xa9618fecabe0e1f4, - 0xba5337eac90bd080, - 0x66fececdbc35d4e7, - 0xa4cd583203d9206f, - 0x98391632ceeca596, - 0x4946b76e1236ad3f, - 0xa0dec64e60e711a1, - 0xfcb41ed3605013, - 0x8ca8f9692ae1e3a9, - 0xd3078bfc28cc1baf, - 0xf0536f764e982f82, - 0x3125f1a2656, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[7], - nqr.pow_vartime(vec![ - 0x742754a1f22fdb, - 0x2a1955c2dec3a702, - 0x9747b28c796d134e, - 0xc113a0411f59db79, - 0x3bb0fa929853bfc1, - 0x28c3c25f8f6fb487, - 0xbc2b6c99d3045b34, - 0x98fb67d6badde1fd, - 0x48841d76a24d2073, - 0xd49891145fe93ae6, - 0xc772b9c8e74d4099, - 0xccf4e7b9907755bb, - 0x9cf47b25d42fd908, - 0x5616a0c347fc445d, - 0xff93b7a7ad1b8a6d, - 0xac2099256b78a77a, - 0x7804a95b02892e1c, - 0x5cf59ca7bfd69776, - 0xa7023502acd3c866, - 0xc76f4982fcf8f37, - 0x51862a5a57ac986e, - 0x38b80ed72b1b1023, - 0x4a291812066a61e1, - 0xcd8a685eff45631, - 0x3f40f708764e4fa5, - 0x8aa0441891285092, - 0x9eff60d71cdf0a9, - 0x4fdd9d56517e2bfa, - 0x1f3c80d74a28bc85, - 0x24617417c064b648, - 0x7ddda1e4385d5088, - 0xf9e132b11dd32a16, - 0xcc957cb8ef66ab99, - 0xd4f206d37cb752c5, - 0x40de343f28ad616b, - 0x8d1f24379068f0e3, - 0x6f31d7947ea21137, - 0x27311f9c32184061, - 0x9eea0664cc78ce5f, - 0x7d4151f6fea9a0da, - 0x454096fa75bd571a, - 0x4fe0f20ecb, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[8], - nqr.pow_vartime(vec![ - 0x802f5720d0b25710, - 0x6714f0a258b85c7c, - 0x31394c90afdf16e, - 0xe9d2b0c64f957b19, - 0xe67c0d9c5e7903ee, - 0x3156fdc5443ea8ef, - 0x7c4c50524d88c892, - 0xc99dc8990c0ad244, - 0xd37ababf3649a896, - 0x76fe4b838ff7a20c, - 0xcf69ee2cec728db3, - 0xb83535548e5f41, - 0x371147684ccb0c23, - 0x194f6f4fa500db52, - 0xc4571dc78a4c5374, - 0xe4d46d479999ca97, - 0x76b6785a615a151c, - 0xcceb8bcea7eaf8c1, - 0x80d87a6fbe5ae687, - 0x6a97ddddb85ce85, - 0xd783958f26034204, - 0x7144506f2e2e8590, - 0x948693d377aef166, - 0x8364621ed6f96056, - 0xf021777c4c09ee2d, - 0xc6cf5e746ecd50b, - 0xa2337b7aa22743df, - 0xae753f8bbacab39c, - 0xfc782a9e34d3c1cc, - 0x21b827324fe494d9, - 0x5692ce350ed03b38, - 0xf323a2b3cd0481b0, - 0xe859c97a4ccad2e3, - 0x48434b70381e4503, - 0x46042d62e4132ed8, - 0x48c4d6f56122e2f2, - 0xf87711ab9f5c1af7, - 0xb14b7a054759b469, - 0x8eb0a96993ffa9aa, - 0x9b21fb6fc58b760c, - 0xf3abdd115d2e7d25, - 0xf7beac3d4d12409c, - 0x40a5585cce69bf03, - 0x697881e1ba22d5a8, - 0x3d6c04e6ad373fd9, - 0x849871bf627be886, - 0x550f4b9b71b28ef9, - 0x81d2e0d78, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[9], - nqr.pow_vartime(vec![ - 0x4af4accf7de0b977, - 0x742485e21805b4ee, - 0xee388fbc4ac36dec, - 0x1e199da57ad178a, - 0xc27c12b292c6726a, - 0x162e6ed84505b5e8, - 0xe191683f336e09df, - 0x17deb7e8d1e0fce6, - 0xd944f19ad06f5836, - 0x4c5f5e59f6276026, - 0xf1ba9c7c148a38a8, - 0xd205fe2dba72b326, - 0x9a2cf2a4c289824e, - 0x4f47ad512c39e24d, - 0xc5894d984000ea09, - 0x2974c03ff7cf01fa, - 0xfcd243b48cb99a22, - 0x2b5150c9313ac1e8, - 0x9089f37c7fc80eda, - 0x989540cc9a7aea56, - 0x1ab1d4e337e63018, - 0x42b546c30d357e43, - 0x1c6abc04f76233d9, - 0x78b3b8d88bf73e47, - 0x151c4e4c45dc68e6, - 0x519a79c4f54397ed, - 0x93f5b51535a127c5, - 0x5fc51b6f52fa153e, - 0x2e0504f2d4a965c3, - 0xc85bd3a3da52bffe, - 0x98c60957a46a89ef, - 0x48c03b5976b91cae, - 0xc6598040a0a61438, - 0xbf0b49dc255953af, - 0xb78dff905b628ab4, - 0x68140b797ba74ab8, - 0x116cf037991d1143, - 0x2f7fe82e58acb0b8, - 0xc20bf7a8f7be5d45, - 0x86c2905c338d5709, - 0xff13a3ae6c8ace3d, - 0xb6f95e2282d08337, - 0xd49f7b313e9cbf29, - 0xf794517193a1ce8c, - 0x39641fecb596a874, - 0x411c4c4edf462fb3, - 0x3f8cd55c10cf25b4, - 0x2bdd7ea165e860b6, - 0xacd7d2cef4caa193, - 0x6558a1d09a05f96, - 0x1f52b5f5b546fc20, - 0x4ee22a5a8c250c12, - 0xd3a63a54a205b6b3, - 0xd2ff5be8, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[10], - nqr.pow_vartime(vec![ - 0xe5953a4f96cdda44, - 0x336b2d734cbc32bb, - 0x3f79bfe3cd7410e, - 0x267ae19aaa0f0332, - 0x85a9c4db78d5c749, - 0x90996b046b5dc7d8, - 0x8945eae9820afc6a, - 0x2644ddea2b036bd, - 0x39898e35ac2e3819, - 0x2574eab095659ab9, - 0x65953d51ac5ea798, - 0xc6b8c7afe6752466, - 0x40e9e993e9286544, - 0x7e0ad34ad9700ea0, - 0xac1015eba2c69222, - 0x24f057a19239b5d8, - 0x2043b48c8a3767eb, - 0x1117c124a75d7ff4, - 0x433cfd1a09fb3ce7, - 0x25b087ce4bcf7fb, - 0xbcee0dc53a3e5bdb, - 0xbffda040cf028735, - 0xf7cf103a25512acc, - 0x31d4ecda673130b9, - 0xea0906dab18461e6, - 0x5a40585a5ac3050d, - 0x803358fc14fd0eda, - 0x3678ca654eada770, - 0x7b91a1293a45e33e, - 0xcd5e5b8ea8530e43, - 0x21ae563ab34da266, - 0xecb00dad60df8894, - 0x77fe53e652facfef, - 0x9b7d1ad0b00244ec, - 0xe695df5ca73f801, - 0x23cdb21feeab0149, - 0x14de113e7ea810d9, - 0x52600cd958dac7e7, - 0xc83392c14667e488, - 0x9f808444bc1717fc, - 0x56facb4bcf7c788f, - 0x8bcad53245fc3ca0, - 0xdef661e83f27d81c, - 0x37d4ebcac9ad87e5, - 0x6fe8b24f5cdb9324, - 0xee08a26c1197654c, - 0xc98b22f65f237e9a, - 0xf54873a908ed3401, - 0x6e1cb951d41f3f3, - 0x290b2250a54e8df6, - 0x7f36d51eb1db669e, - 0xb08c7ed81a6ee43e, - 0x95e1c90fb092f680, - 0x429e4afd0e8b820, - 0x2c14a83ee87d715c, - 0xf37267575cfc8af5, - 0xb99e9afeda3c2c30, - 0x8f0f69da75792d5a, - 0x35074a85a533c73, - 0x156ed119, - ]) - ); - assert_eq!( - FROBENIUS_COEFF_FQ12_C1[11], - nqr.pow_vartime(vec![ - 0x107db680942de533, - 0x6262b24d2052393b, - 0x6136df824159ebc, - 0xedb052c9970c5deb, - 0xca813aea916c3777, - 0xf49dacb9d76c1788, - 0x624941bd372933bb, - 0xa5e60c2520638331, - 0xb38b661683411074, - 0x1d2c9af4c43d962b, - 0x17d807a0f14aa830, - 0x6e6581a51012c108, - 0x668a537e5b35e6f5, - 0x6c396cf3782dca5d, - 0x33b679d1bff536ed, - 0x736cce41805d90aa, - 0x8a562f369eb680bf, - 0x9f61aa208a11ded8, - 0x43dd89dd94d20f35, - 0xcf84c6610575c10a, - 0x9f318d49cf2fe8e6, - 0xbbc6e5f25a6e434e, - 0x6528c433d11d987b, - 0xffced71cc48c0e8a, - 0x4cbb1474f4cb2a26, - 0x66a035c0b28b7231, - 0xa6f2875faa1a82ae, - 0xdd1ea3deff818b02, - 0xe0cfdf0dcdecf701, - 0x9aefa231f2f6d23, - 0xfb251297efa06746, - 0x5a40d367df985538, - 0x1ea31d69ab506fed, - 0xc64ea8280e89a73f, - 0x969acf9f2d4496f4, - 0xe84c9181ee60c52c, - 0xc60f27fc19fc6ca4, - 0x760b33d850154048, - 0x84f69080f66c8457, - 0xc0192ba0fabf640e, - 0xd2c338765c23a3a8, - 0xa7838c20f02cec6c, - 0xb7cf01d020572877, - 0xd63ffaeba0be200a, - 0xf7492baeb5f041ac, - 0x8602c5212170d117, - 0xad9b2e83a5a42068, - 0x2461829b3ba1083e, - 0x7c34650da5295273, - 0xdc824ba800a8265a, - 0xd18d9b47836af7b2, - 0x3af78945c58cbf4d, - 0x7ed9575b8596906c, - 0x6d0c133895009a66, - 0x53bc1247ea349fe1, - 0x6b3063078d41aa7a, - 0x6184acd8cd880b33, - 0x76f4d15503fd1b96, - 0x7a9afd61eef25746, - 0xce974aadece60609, - 0x88ca59546a8ceafd, - 0x6d29391c41a0ac07, - 0x443843a60e0f46a6, - 0xa1590f62fd2602c7, - 0x536d5b15b514373f, - 0x22d582b, - ]) - ); -} - -#[test] -fn test_neg_one() { - let o = Fq::one().neg(); - - assert_eq!(NEGATIVE_ONE, o); -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fq_repr_ordering() { - use std::cmp::Ordering; - - fn assert_equality(a: FqRepr, b: FqRepr) { - assert_eq!(a, b); - assert!(a.cmp(&b) == Ordering::Equal); - } - - fn assert_lt(a: FqRepr, b: FqRepr) { - assert!(a < b); - assert!(b > a); - } - - assert_equality( - FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), - FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), - ); - assert_equality( - FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), - FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), - ); - assert_equality( - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); - assert_lt( - FqRepr([9999, 9999, 9999, 9997, 9999, 9998]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); - assert_lt( - FqRepr([9999, 9999, 9999, 9997, 9998, 9999]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); - assert_lt( - FqRepr([9, 9999, 9999, 9997, 9998, 9999]), - FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), - ); -} - -#[test] -fn test_fq_repr_from() { - assert_eq!(FqRepr::from(100), FqRepr([100, 0, 0, 0, 0, 0])); -} - -#[test] -fn test_fq_repr_is_odd() { - assert!(!FqRepr::from(0).is_odd()); - assert!(FqRepr::from(0).is_even()); - assert!(FqRepr::from(1).is_odd()); - assert!(!FqRepr::from(1).is_even()); - assert!(!FqRepr::from(324834872).is_odd()); - assert!(FqRepr::from(324834872).is_even()); - assert!(FqRepr::from(324834873).is_odd()); - assert!(!FqRepr::from(324834873).is_even()); -} - -#[test] -fn test_fq_repr_is_zero() { - assert!(FqRepr::from(0).is_zero()); - assert!(!FqRepr::from(1).is_zero()); - assert!(!FqRepr([0, 0, 0, 0, 1, 0]).is_zero()); -} - -#[test] -fn test_fq_repr_div2() { - let mut a = FqRepr([ - 0x8b0ad39f8dd7482a, - 0x147221c9a7178b69, - 0x54764cb08d8a6aa0, - 0x8519d708e1d83041, - 0x41f82777bd13fdb, - 0xf43944578f9b771b, - ]); - a.div2(); - assert_eq!( - a, - FqRepr([ - 0xc58569cfc6eba415, - 0xa3910e4d38bc5b4, - 0xaa3b265846c53550, - 0xc28ceb8470ec1820, - 0x820fc13bbde89fed, - 0x7a1ca22bc7cdbb8d - ]) - ); - for _ in 0..10 { - a.div2(); - } - assert_eq!( - a, - FqRepr([ - 0x6d31615a73f1bae9, - 0x54028e443934e2f1, - 0x82a8ec99611b14d, - 0xfb70a33ae11c3b06, - 0xe36083f04eef7a27, - 0x1e87288af1f36e - ]) - ); - for _ in 0..300 { - a.div2(); - } - assert_eq!(a, FqRepr([0x7288af1f36ee3608, 0x1e8, 0x0, 0x0, 0x0, 0x0])); - for _ in 0..50 { - a.div2(); - } - assert_eq!(a, FqRepr([0x7a1ca2, 0x0, 0x0, 0x0, 0x0, 0x0])); - for _ in 0..22 { - a.div2(); - } - assert_eq!(a, FqRepr([0x1, 0x0, 0x0, 0x0, 0x0, 0x0])); - a.div2(); - assert!(a.is_zero()); -} - -#[test] -fn test_fq_repr_shr() { - let mut a = FqRepr([ - 0xaa5cdd6172847ffd, - 0x43242c06aed55287, - 0x9ddd5b312f3dd104, - 0xc5541fd48046b7e7, - 0x16080cf4071e0b05, - 0x1225f2901aea514e, - ]); - a.shr(0); - assert_eq!( - a, - FqRepr([ - 0xaa5cdd6172847ffd, - 0x43242c06aed55287, - 0x9ddd5b312f3dd104, - 0xc5541fd48046b7e7, - 0x16080cf4071e0b05, - 0x1225f2901aea514e - ]) - ); - a.shr(1); - assert_eq!( - a, - FqRepr([ - 0xd52e6eb0b9423ffe, - 0x21921603576aa943, - 0xceeead98979ee882, - 0xe2aa0fea40235bf3, - 0xb04067a038f0582, - 0x912f9480d7528a7 - ]) - ); - a.shr(50); - assert_eq!( - a, - FqRepr([ - 0x8580d5daaa50f54b, - 0xab6625e7ba208864, - 0x83fa9008d6fcf3bb, - 0x19e80e3c160b8aa, - 0xbe52035d4a29c2c1, - 0x244 - ]) - ); - a.shr(130); - assert_eq!( - a, - FqRepr([ - 0xa0fea40235bf3cee, - 0x4067a038f0582e2a, - 0x2f9480d7528a70b0, - 0x91, - 0x0, - 0x0 - ]) - ); - a.shr(64); - assert_eq!( - a, - FqRepr([0x4067a038f0582e2a, 0x2f9480d7528a70b0, 0x91, 0x0, 0x0, 0x0]) - ); -} - -#[test] -fn test_fq_repr_mul2() { - let mut a = FqRepr::from(23712937547); - a.mul2(); - assert_eq!(a, FqRepr([0xb0acd6c96, 0x0, 0x0, 0x0, 0x0, 0x0])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!( - a, - FqRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0, 0x0, 0x0]) - ); - for _ in 0..300 { - a.mul2(); - } - assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0xcd6c960000000000])); - for _ in 0..17 { - a.mul2(); - } - assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x2c00000000000000])); - for _ in 0..6 { - a.mul2(); - } - assert!(a.is_zero()); -} - -#[test] -fn test_fq_repr_num_bits() { - let mut a = FqRepr::from(0); - assert_eq!(0, a.num_bits()); - a = FqRepr::from(1); - for i in 1..385 { - assert_eq!(i, a.num_bits()); - a.mul2(); - } - assert_eq!(0, a.num_bits()); -} - -#[test] -fn test_fq_repr_sub_noborrow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FqRepr([ - 0x827a4a08041ebd9, - 0x3c239f3dcc8f0d6b, - 0x9ab46a912d555364, - 0x196936b17b43910b, - 0xad0eb3948a5c34fd, - 0xd56f7b5ab8b5ce8, - ]); - t.sub_noborrow(&FqRepr([ - 0xc7867917187ca02b, - 0x5d75679d4911ffef, - 0x8c5b3e48b1a71c15, - 0x6a427ae846fd66aa, - 0x7a37e7265ee1eaf9, - 0x7c0577a26f59d5, - ])); - assert!( - t == FqRepr([ - 0x40a12b8967c54bae, - 0xdeae37a0837d0d7b, - 0xe592c487bae374e, - 0xaf26bbc934462a61, - 0x32d6cc6e2b7a4a03, - 0xcdaf23e091c0313 - ]) - ); - - for _ in 0..1000 { - let mut a = Fq::random(&mut rng).into_repr(); - a.0[5] >>= 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 q+1 from q should produce -1 (mod 2**384) - let mut qplusone = FqRepr([ - 0xb9feffffffffaaab, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - ]); - qplusone.sub_noborrow(&FqRepr([ - 0xb9feffffffffaaac, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - ])); - assert_eq!( - qplusone, - FqRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ); -} - -#[test] -fn test_fq_repr_add_nocarry() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FqRepr([ - 0x827a4a08041ebd9, - 0x3c239f3dcc8f0d6b, - 0x9ab46a912d555364, - 0x196936b17b43910b, - 0xad0eb3948a5c34fd, - 0xd56f7b5ab8b5ce8, - ]); - t.add_nocarry(&FqRepr([ - 0xc7867917187ca02b, - 0x5d75679d4911ffef, - 0x8c5b3e48b1a71c15, - 0x6a427ae846fd66aa, - 0x7a37e7265ee1eaf9, - 0x7c0577a26f59d5, - ])); - assert!( - t == FqRepr([ - 0xcfae1db798be8c04, - 0x999906db15a10d5a, - 0x270fa8d9defc6f79, - 0x83abb199c240f7b6, - 0x27469abae93e1ff6, - 0xdd2fd2d4dfab6be - ]) - ); - - // Test for the associativity of addition. - for _ in 0..1000 { - let mut a = Fq::random(&mut rng).into_repr(); - let mut b = Fq::random(&mut rng).into_repr(); - let mut c = Fq::random(&mut rng).into_repr(); - - // Unset the first few bits, so that overflow won't occur. - a.0[5] >>= 3; - b.0[5] >>= 3; - c.0[5] >>= 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^384 - 1) should produce zero - let mut x = FqRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - ]); - x.add_nocarry(&FqRepr::from(1)); - assert!(x.is_zero()); -} - -#[test] -fn test_fq_is_valid() { - let mut a = Fq(MODULUS); - assert!(!a.is_valid()); - a.0.sub_noborrow(&FqRepr::from(1)); - assert!(a.is_valid()); - assert!(Fq(FqRepr::from(0)).is_valid()); - assert!(Fq(FqRepr([ - 0xdf4671abd14dab3e, - 0xe2dc0c9f534fbd33, - 0x31ca6c880cc444a6, - 0x257a67e70ef33359, - 0xf9b29e493f899b36, - 0x17c8be1800b9f059 - ])) - .is_valid()); - assert!(!Fq(FqRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ])) - .is_valid()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = Fq::random(&mut rng); - assert!(a.is_valid()); - } -} - -#[test] -fn test_fq_add_assign() { - { - // Random number - let mut tmp = Fq(FqRepr([ - 0x624434821df92b69, - 0x503260c04fd2e2ea, - 0xd9df726e0d16e8ce, - 0xfbcb39adfd5dfaeb, - 0x86b8a22b0c88b112, - 0x165a2ed809e4201b, - ])); - assert!(tmp.is_valid()); - // Test that adding zero has no effect. - tmp.add_assign(&Fq(FqRepr::from(0))); - assert_eq!( - tmp, - Fq(FqRepr([ - 0x624434821df92b69, - 0x503260c04fd2e2ea, - 0xd9df726e0d16e8ce, - 0xfbcb39adfd5dfaeb, - 0x86b8a22b0c88b112, - 0x165a2ed809e4201b - ])) - ); - // Add one and test for the result. - tmp.add_assign(&Fq(FqRepr::from(1))); - assert_eq!( - tmp, - Fq(FqRepr([ - 0x624434821df92b6a, - 0x503260c04fd2e2ea, - 0xd9df726e0d16e8ce, - 0xfbcb39adfd5dfaeb, - 0x86b8a22b0c88b112, - 0x165a2ed809e4201b - ])) - ); - // Add another random number that exercises the reduction. - tmp.add_assign(&Fq(FqRepr([ - 0x374d8f8ea7a648d8, - 0xe318bb0ebb8bfa9b, - 0x613d996f0a95b400, - 0x9fac233cb7e4fef1, - 0x67e47552d253c52, - 0x5c31b227edf25da, - ]))); - assert_eq!( - tmp, - Fq(FqRepr([ - 0xdf92c410c59fc997, - 0x149f1bd05a0add85, - 0xd3ec393c20fba6ab, - 0x37001165c1bde71d, - 0x421b41c9f662408e, - 0x21c38104f435f5b - ])) - ); - // Add one to (q - 1) and test for the result. - tmp = Fq(FqRepr([ - 0xb9feffffffffaaaa, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - ])); - tmp.add_assign(&Fq(FqRepr::from(1))); - assert!(tmp.0.is_zero()); - // Add a random number to another one such that the result is q - 1 - tmp = Fq(FqRepr([ - 0x531221a410efc95b, - 0x72819306027e9717, - 0x5ecefb937068b746, - 0x97de59cd6feaefd7, - 0xdc35c51158644588, - 0xb2d176c04f2100, - ])); - tmp.add_assign(&Fq(FqRepr([ - 0x66ecde5bef0fe14f, - 0xac2a6cf8aed568e8, - 0x861d70d86483edd, - 0xcc98f1b7839a22e8, - 0x6ee5e2a4eae7674e, - 0x194e40737930c599, - ]))); - assert_eq!( - tmp, - Fq(FqRepr([ - 0xb9feffffffffaaaa, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a - ])) - ); - // Add one to the result and test for it. - tmp.add_assign(&Fq(FqRepr::from(1))); - assert!(tmp.0.is_zero()); - } - - // Test associativity - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Generate a, b, c and ensure (a + b) + c == a + (b + c). - let a = Fq::random(&mut rng); - let b = Fq::random(&mut rng); - let c = Fq::random(&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_fq_sub_assign() { - { - // Test arbitrary subtraction that tests reduction. - let mut tmp = Fq(FqRepr([ - 0x531221a410efc95b, - 0x72819306027e9717, - 0x5ecefb937068b746, - 0x97de59cd6feaefd7, - 0xdc35c51158644588, - 0xb2d176c04f2100, - ])); - tmp.sub_assign(&Fq(FqRepr([ - 0x98910d20877e4ada, - 0x940c983013f4b8ba, - 0xf677dc9b8345ba33, - 0xbef2ce6b7f577eba, - 0xe1ae288ac3222c44, - 0x5968bb602790806, - ]))); - assert_eq!( - tmp, - Fq(FqRepr([ - 0x748014838971292c, - 0xfd20fad49fddde5c, - 0xcf87f198e3d3f336, - 0x3d62d6e6e41883db, - 0x45a3443cd88dc61b, - 0x151d57aaf755ff94 - ])) - ); - - // Test the opposite subtraction which doesn't test reduction. - tmp = Fq(FqRepr([ - 0x98910d20877e4ada, - 0x940c983013f4b8ba, - 0xf677dc9b8345ba33, - 0xbef2ce6b7f577eba, - 0xe1ae288ac3222c44, - 0x5968bb602790806, - ])); - tmp.sub_assign(&Fq(FqRepr([ - 0x531221a410efc95b, - 0x72819306027e9717, - 0x5ecefb937068b746, - 0x97de59cd6feaefd7, - 0xdc35c51158644588, - 0xb2d176c04f2100, - ]))); - assert_eq!( - tmp, - Fq(FqRepr([ - 0x457eeb7c768e817f, - 0x218b052a117621a3, - 0x97a8e10812dd02ed, - 0x2714749e0f6c8ee3, - 0x57863796abde6bc, - 0x4e3ba3f4229e706 - ])) - ); - - // Test for sensible results with zero - tmp = Fq(FqRepr::from(0)); - tmp.sub_assign(&Fq(FqRepr::from(0))); - assert!(tmp.is_zero()); - - tmp = Fq(FqRepr([ - 0x98910d20877e4ada, - 0x940c983013f4b8ba, - 0xf677dc9b8345ba33, - 0xbef2ce6b7f577eba, - 0xe1ae288ac3222c44, - 0x5968bb602790806, - ])); - tmp.sub_assign(&Fq(FqRepr::from(0))); - assert_eq!( - tmp, - Fq(FqRepr([ - 0x98910d20877e4ada, - 0x940c983013f4b8ba, - 0xf677dc9b8345ba33, - 0xbef2ce6b7f577eba, - 0xe1ae288ac3222c44, - 0x5968bb602790806 - ])) - ); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure that (a - b) + (b - a) = 0. - let a = Fq::random(&mut rng); - let b = Fq::random(&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_fq_mul_assign() { - let mut tmp = Fq(FqRepr([ - 0xcc6200000020aa8a, - 0x422800801dd8001a, - 0x7f4f5e619041c62c, - 0x8a55171ac70ed2ba, - 0x3f69cc3a3d07d58b, - 0xb972455fd09b8ef, - ])); - tmp.mul_assign(&Fq(FqRepr([ - 0x329300000030ffcf, - 0x633c00c02cc40028, - 0xbef70d925862a942, - 0x4f7fa2a82a963c17, - 0xdf1eb2575b8bc051, - 0x1162b680fb8e9566, - ]))); - assert!( - tmp == Fq(FqRepr([ - 0x9dc4000001ebfe14, - 0x2850078997b00193, - 0xa8197f1abb4d7bf, - 0xc0309573f4bfe871, - 0xf48d0923ffaf7620, - 0x11d4b58c7a926e66 - ])) - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * b) * c = a * (b * c) - let a = Fq::random(&mut rng); - let b = Fq::random(&mut rng); - let c = Fq::random(&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 = Fq::random(&mut rng); - let mut a = Fq::random(&mut rng); - let mut b = Fq::random(&mut rng); - let mut c = Fq::random(&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_fq_squaring() { - let a = Fq(FqRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0x19ffffffffffffff, - ])); - assert!(a.is_valid()); - assert_eq!( - a.square(), - Fq::from_repr(FqRepr([ - 0x1cfb28fe7dfbbb86, - 0x24cbe1731577a59, - 0xcce1d4edc120e66e, - 0xdc05c659b4e15b27, - 0x79361e5a802c6a23, - 0x24bcbe5d51b9a6f - ])) - .unwrap() - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * a) = a^2 - let a = Fq::random(&mut rng); - assert_eq!(a.square(), a * a); - } -} - -#[test] -fn test_fq_invert() { - assert!(bool::from(Fq::zero().invert().is_none())); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let one = Fq::one(); - - for _ in 0..1000 { - // Ensure that a * a^-1 = 1 - let mut a = Fq::random(&mut rng); - let ainv = a.invert().unwrap(); - a.mul_assign(&ainv); - assert_eq!(a, one); - } -} - -#[test] -fn test_fq_double() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure doubling a is equivalent to adding a to itself. - let a = Fq::random(&mut rng); - assert_eq!(a.double(), a + a); - } -} - -#[test] -fn test_fq_neg() { - { - let a = Fq::zero().neg(); - - assert!(a.is_zero()); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure (a - (-a)) = 0. - let mut a = Fq::random(&mut rng); - let b = a.neg(); - a.add_assign(&b); - - assert!(a.is_zero()); - } -} - -#[test] -fn test_fq_pow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for i in 0..1000 { - // Exponentiate by various small numbers and ensure it consists with repeated - // multiplication. - let a = Fq::random(&mut rng); - let target = a.pow_vartime(&[i]); - let mut c = Fq::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 = Fq::random(&mut rng); - - assert_eq!(a, a.pow_vartime(Fq::char())); - } -} - -#[test] -fn test_fq_sqrt() { - use ff::SqrtField; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero()); - - for _ in 0..1000 { - // Ensure sqrt(a^2) = a or -a - let a = Fq::random(&mut rng); - let nega = a.neg(); - let b = a.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 = Fq::random(&mut rng); - - let tmp = a.sqrt(); - if tmp.is_some().into() { - assert_eq!(a, tmp.unwrap().square()); - } - } -} - -#[test] -fn test_fq_from_into_repr() { - // q + 1 should not be in the field - assert!(Fq::from_repr(FqRepr([ - 0xb9feffffffffaaac, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a - ])) - .is_err()); - - // q should not be in the field - assert!(Fq::from_repr(Fq::char()).is_err()); - - // Multiply some arbitrary representations to see if the result is as expected. - let a = FqRepr([ - 0x4a49dad4ff6cde2d, - 0xac62a82a8f51cd50, - 0x2b1f41ab9f36d640, - 0x908a387f480735f1, - 0xae30740c08a875d7, - 0x6c80918a365ef78, - ]); - let mut a_fq = Fq::from_repr(a).unwrap(); - let b = FqRepr([ - 0xbba57917c32f0cf0, - 0xe7f878cf87f05e5d, - 0x9498b4292fd27459, - 0xd59fd94ee4572cfa, - 0x1f607186d5bb0059, - 0xb13955f5ac7f6a3, - ]); - let b_fq = Fq::from_repr(b).unwrap(); - let c = FqRepr([ - 0xf5f70713b717914c, - 0x355ea5ac64cbbab1, - 0xce60dd43417ec960, - 0xf16b9d77b0ad7d10, - 0xa44c204c1de7cdb7, - 0x1684487772bc9a5a, - ]); - a_fq.mul_assign(&b_fq); - assert_eq!(a_fq.into_repr(), c); - - // Zero should be in the field. - assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Try to turn Fq elements into representations and back again, and compare. - let a = Fq::random(&mut rng); - let a_repr = a.into_repr(); - let b_repr = FqRepr::from(a); - assert_eq!(a_repr, b_repr); - let a_again = Fq::from_repr(a_repr).unwrap(); - - assert_eq!(a, a_again); - } -} - -#[test] -fn test_fq_repr_display() { - assert_eq!( - format!("{}", FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])), - "0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24".to_string() - ); - assert_eq!( - format!("{}", FqRepr([0xb4171485fd8622dd, 0x864229a6edec7ec5, 0xc57f7bdcf8dfb707, 0x6db7ff0ecea4584a, 0xf8d8578c4a57132d, 0x6eb66d42d9fcaaa])), - "0x06eb66d42d9fcaaaf8d8578c4a57132d6db7ff0ecea4584ac57f7bdcf8dfb707864229a6edec7ec5b4171485fd8622dd".to_string() - ); - assert_eq!( - format!("{}", FqRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() - ); - assert_eq!( - format!("{}", FqRepr([0, 0, 0, 0, 0, 0])), - "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string() - ); -} - -#[test] -fn test_fq_display() { - assert_eq!( - format!("{}", Fq::from_repr(FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])).unwrap()), - "Fq(0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24)".to_string() - ); - assert_eq!( - format!("{}", Fq::from_repr(FqRepr([0xe28e79396ac2bbf8, 0x413f6f7f06ea87eb, 0xa4b62af4a792a689, 0xb7f89f88f59c1dc5, 0x9a551859b1e43a9a, 0x6c9f5a1060de974])).unwrap()), - "Fq(0x06c9f5a1060de9749a551859b1e43a9ab7f89f88f59c1dc5a4b62af4a792a689413f6f7f06ea87ebe28e79396ac2bbf8)".to_string() - ); -} - -#[test] -fn test_fq_num_bits() { - assert_eq!(Fq::NUM_BITS, 381); - assert_eq!(Fq::CAPACITY, 380); -} - -#[test] -fn test_fq_root_of_unity() { - use ff::SqrtField; - - assert_eq!(Fq::S, 1); - assert_eq!( - Fq::multiplicative_generator(), - Fq::from_repr(FqRepr::from(2)).unwrap() - ); - assert_eq!( - Fq::multiplicative_generator().pow_vartime([ - 0xdcff7fffffffd555, - 0xf55ffff58a9ffff, - 0xb39869507b587b12, - 0xb23ba5c279c2895f, - 0x258dd3db21a5d66b, - 0xd0088f51cbff34d - ]), - Fq::root_of_unity() - ); - assert_eq!(Fq::root_of_unity().pow_vartime([1 << Fq::S]), Fq::one()); - assert!(bool::from(Fq::multiplicative_generator().sqrt().is_none())); -} - -#[test] -fn fq_field_tests() { - crate::tests::field::random_field_tests::(); - crate::tests::field::random_sqrt_tests::(); - crate::tests::field::random_frobenius_tests::(Fq::char(), 13); - crate::tests::field::from_str_tests::(); -} - -#[test] -fn test_fq_ordering() { - // FqRepr's ordering is well-tested, but we still need to make sure the Fq - // elements aren't being compared in Montgomery form. - for i in 0..100 { - assert!( - Fq::from_repr(FqRepr::from(i + 1)).unwrap() > Fq::from_repr(FqRepr::from(i)).unwrap() - ); - } -} - -#[test] -fn fq_repr_tests() { - crate::tests::repr::random_repr_tests::(); -} diff --git a/pairing/src/bls12_381/fq12.rs b/pairing/src/bls12_381/fq12.rs deleted file mode 100644 index f8b4853601..0000000000 --- a/pairing/src/bls12_381/fq12.rs +++ /dev/null @@ -1,285 +0,0 @@ -use super::fq::FROBENIUS_COEFF_FQ12_C1; -use super::fq2::Fq2; -use super::fq6::Fq6; -use ff::Field; -use rand_core::RngCore; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, CtOption}; - -/// An element of Fq12, represented by c0 + c1 * w. -#[derive(Copy, Clone, Debug, Default, 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 Fq12 { - pub fn conjugate(&mut self) { - self.c1 = self.c1.neg(); - } - - 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 ConditionallySelectable for Fq12 { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fq12 { - c0: Fq6::conditional_select(&a.c0, &b.c0, choice), - c1: Fq6::conditional_select(&a.c1, &b.c1, choice), - } - } -} - -impl Neg for Fq12 { - type Output = Self; - - fn neg(self) -> Self { - Fq12 { - c0: self.c0.neg(), - c1: self.c1.neg(), - } - } -} - -impl<'r> Add<&'r Fq12> for Fq12 { - type Output = Self; - - fn add(self, other: &Self) -> Self { - Fq12 { - c0: self.c0 + other.c0, - c1: self.c1 + other.c1, - } - } -} - -impl Add for Fq12 { - type Output = Self; - - fn add(self, other: Self) -> Self { - self.add(&other) - } -} - -impl<'r> AddAssign<&'r Fq12> for Fq12 { - fn add_assign(&mut self, other: &'r Self) { - self.c0.add_assign(&other.c0); - self.c1.add_assign(&other.c1); - } -} - -impl AddAssign for Fq12 { - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } -} - -impl<'r> Sub<&'r Fq12> for Fq12 { - type Output = Self; - - fn sub(self, other: &Self) -> Self { - Fq12 { - c0: self.c0 - other.c0, - c1: self.c1 - other.c1, - } - } -} - -impl Sub for Fq12 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - self.sub(&other) - } -} - -impl<'r> SubAssign<&'r Fq12> for Fq12 { - fn sub_assign(&mut self, other: &'r Self) { - self.c0.sub_assign(&other.c0); - self.c1.sub_assign(&other.c1); - } -} - -impl SubAssign for Fq12 { - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } -} - -impl<'r> Mul<&'r Fq12> for Fq12 { - type Output = Self; - - fn mul(self, other: &Self) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } -} - -impl Mul for Fq12 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - self.mul(&other) - } -} - -impl<'r> MulAssign<&'r Fq12> for Fq12 { - 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); - } -} - -impl MulAssign for Fq12 { - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Field for Fq12 { - fn random(rng: &mut R) -> Self { - Fq12 { - c0: Fq6::random(rng), - c1: Fq6::random(rng), - } - } - - 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(&self) -> Self { - Fq12 { - c0: self.c0.double(), - c1: self.c1.double(), - } - } - - 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(&self) -> 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); - let mut c1 = ab; - c1.add_assign(&ab); - ab.mul_by_nonresidue(); - c0.sub_assign(&ab); - Fq12 { c0, c1 } - } - - fn invert(&self) -> CtOption { - let mut c0s = self.c0.square(); - let mut c1s = self.c1.square(); - c1s.mul_by_nonresidue(); - c0s.sub_assign(&c1s); - - c0s.invert().map(|t| Fq12 { - c0: t.mul(&self.c0), - c1: t.mul(&self.c1).neg(), - }) - } -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fq12_mul_by_014() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let c0 = Fq2::random(&mut rng); - let c1 = Fq2::random(&mut rng); - let c5 = Fq2::random(&mut rng); - let mut a = Fq12::random(&mut rng); - let mut b = a; - - a.mul_by_014(&c0, &c1, &c5); - b.mul_assign(&Fq12 { - c0: Fq6 { - c0, - 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; - - crate::tests::field::random_field_tests::(); - crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); -} diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs deleted file mode 100644 index 0cd88e7d7a..0000000000 --- a/pairing/src/bls12_381/fq2.rs +++ /dev/null @@ -1,1028 +0,0 @@ -use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; -use ff::{Field, SqrtField}; -use rand_core::RngCore; -use std::cmp::Ordering; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, CtOption}; - -/// An element of Fq2, represented by c0 + c1 * u. -#[derive(Copy, Clone, Debug, Default, 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 { - 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 t0 = self.c0.square(); - let mut t1 = self.c1.square(); - t1.add_assign(&t0); - - t1 - } -} - -impl ConditionallySelectable for Fq2 { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fq2 { - c0: Fq::conditional_select(&a.c0, &b.c0, choice), - c1: Fq::conditional_select(&a.c1, &b.c1, choice), - } - } -} - -impl Neg for Fq2 { - type Output = Self; - - fn neg(self) -> Self { - Fq2 { - c0: self.c0.neg(), - c1: self.c1.neg(), - } - } -} - -impl<'r> Add<&'r Fq2> for Fq2 { - type Output = Self; - - fn add(self, other: &Self) -> Self { - Fq2 { - c0: self.c0 + other.c0, - c1: self.c1 + other.c1, - } - } -} - -impl Add for Fq2 { - type Output = Self; - - fn add(self, other: Self) -> Self { - self.add(&other) - } -} - -impl<'r> AddAssign<&'r Fq2> for Fq2 { - fn add_assign(&mut self, other: &'r Self) { - self.c0.add_assign(&other.c0); - self.c1.add_assign(&other.c1); - } -} - -impl AddAssign for Fq2 { - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } -} - -impl<'r> Sub<&'r Fq2> for Fq2 { - type Output = Self; - - fn sub(self, other: &Self) -> Self { - Fq2 { - c0: self.c0 - other.c0, - c1: self.c1 - other.c1, - } - } -} - -impl Sub for Fq2 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - self.sub(&other) - } -} - -impl<'r> SubAssign<&'r Fq2> for Fq2 { - fn sub_assign(&mut self, other: &'r Self) { - self.c0.sub_assign(&other.c0); - self.c1.sub_assign(&other.c1); - } -} - -impl SubAssign for Fq2 { - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } -} - -impl<'r> Mul<&'r Fq2> for Fq2 { - type Output = Self; - - fn mul(self, other: &Self) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } -} - -impl Mul for Fq2 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - self.mul(&other) - } -} - -impl<'r> MulAssign<&'r Fq2> for Fq2 { - 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); - } -} - -impl MulAssign for Fq2 { - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Field for Fq2 { - fn random(rng: &mut R) -> Self { - Fq2 { - c0: Fq::random(rng), - c1: Fq::random(rng), - } - } - - 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(&self) -> 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.neg(); - c0.add_assign(&self.c0); - c0.mul_assign(&c0c1); - c0.sub_assign(&ab); - let mut c1 = ab; - c1.add_assign(&ab); - c0.add_assign(&ab); - Fq2 { c0, c1 } - } - - fn double(&self) -> Self { - Fq2 { - c0: self.c0.double(), - c1: self.c1.double(), - } - } - - fn invert(&self) -> CtOption { - let t1 = self.c1.square(); - let mut t0 = self.c0.square(); - t0.add_assign(&t1); - t0.invert().map(|t| Fq2 { - c0: self.c0.mul(&t), - c1: self.c1.mul(&t).neg(), - }) - } - - fn frobenius_map(&mut self, power: usize) { - self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]); - } -} - -impl SqrtField for Fq2 { - /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! - /// THIS WILL BE REPLACED BY THE bls12_381 CRATE, WHICH IS CONSTANT TIME! - fn sqrt(&self) -> CtOption { - // Algorithm 9, https://eprint.iacr.org/2012/685.pdf - - if self.is_zero() { - CtOption::new(Self::zero(), Choice::from(1)) - } else { - // a1 = self^((q - 3) / 4) - let mut a1 = self.pow_vartime([ - 0xee7fbfffffffeaaa, - 0x7aaffffac54ffff, - 0xd9cc34a83dac3d89, - 0xd91dd2e13ce144af, - 0x92c6e9ed90d2eb35, - 0x680447a8e5ff9a6, - ]); - let mut alpha = a1.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 { - CtOption::new(Self::zero(), Choice::from(0)) - } 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_vartime([ - 0xdcff7fffffffd555, - 0xf55ffff58a9ffff, - 0xb39869507b587b12, - 0xb23ba5c279c2895f, - 0x258dd3db21a5d66b, - 0xd0088f51cbff34d, - ]); - a1.mul_assign(&alpha); - } - - CtOption::new(a1, Choice::from(1)) - } - } - } -} - -#[test] -fn test_fq2_ordering() { - let mut a = Fq2 { - c0: Fq::zero(), - c1: Fq::zero(), - }; - - let mut b = a; - - 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 a = Fq2 { - c0: Fq::one(), - c1: Fq::one(), - }; // u + 1 - assert_eq!( - a.square(), - Fq2 { - c0: Fq::zero(), - c1: Fq::from_repr(FqRepr::from(2)).unwrap(), - } - ); // 2u - - let a = Fq2 { - c0: Fq::zero(), - c1: Fq::one(), - }; // u - assert_eq!(a.square(), { - Fq2 { - c0: Fq::one().neg(), - c1: Fq::zero(), - } - }); // -1 - - let 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(), - }; - assert_eq!( - a.square(), - 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_invert() { - use super::fq::FqRepr; - use ff::PrimeField; - - assert!(bool::from(Fq2::zero().invert().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.invert().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 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(), - } - .neg(); - 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 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(), - }; - assert_eq!( - a.double(), - 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(), - } - ); -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fq2_mul_nonresidue() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let nqr = Fq2 { - c0: Fq::one(), - c1: Fq::one(), - }; - - for _ in 0..1000 { - let mut a = Fq2::random(&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; - - crate::tests::field::random_field_tests::(); - crate::tests::field::random_sqrt_tests::(); - crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); -} diff --git a/pairing/src/bls12_381/fq6.rs b/pairing/src/bls12_381/fq6.rs deleted file mode 100644 index b8ac627df7..0000000000 --- a/pairing/src/bls12_381/fq6.rs +++ /dev/null @@ -1,477 +0,0 @@ -use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; -use super::fq2::Fq2; -use ff::Field; -use rand_core::RngCore; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, CtOption}; - -/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). -#[derive(Copy, Clone, Debug, Default, 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 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 ConditionallySelectable for Fq6 { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fq6 { - c0: Fq2::conditional_select(&a.c0, &b.c0, choice), - c1: Fq2::conditional_select(&a.c1, &b.c1, choice), - c2: Fq2::conditional_select(&a.c2, &b.c2, choice), - } - } -} - -impl Neg for Fq6 { - type Output = Self; - - fn neg(self) -> Self { - Fq6 { - c0: self.c0.neg(), - c1: self.c1.neg(), - c2: self.c2.neg(), - } - } -} - -impl<'r> Add<&'r Fq6> for Fq6 { - type Output = Self; - - fn add(self, other: &Self) -> Self { - Fq6 { - c0: self.c0 + other.c0, - c1: self.c1 + other.c1, - c2: self.c2 + other.c2, - } - } -} - -impl Add for Fq6 { - type Output = Self; - - fn add(self, other: Self) -> Self { - self.add(&other) - } -} - -impl<'r> AddAssign<&'r Fq6> for Fq6 { - fn add_assign(&mut self, other: &'r Self) { - self.c0.add_assign(&other.c0); - self.c1.add_assign(&other.c1); - self.c2.add_assign(&other.c2); - } -} - -impl AddAssign for Fq6 { - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } -} - -impl<'r> Sub<&'r Fq6> for Fq6 { - type Output = Self; - - fn sub(self, other: &Self) -> Self { - Fq6 { - c0: self.c0 - other.c0, - c1: self.c1 - other.c1, - c2: self.c2 - other.c2, - } - } -} - -impl Sub for Fq6 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - self.sub(&other) - } -} - -impl<'r> SubAssign<&'r Fq6> for Fq6 { - fn sub_assign(&mut self, other: &'r Self) { - self.c0.sub_assign(&other.c0); - self.c1.sub_assign(&other.c1); - self.c2.sub_assign(&other.c2); - } -} - -impl SubAssign for Fq6 { - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } -} - -impl<'r> Mul<&'r Fq6> for Fq6 { - type Output = Self; - - fn mul(self, other: &Self) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } -} - -impl Mul for Fq6 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - self.mul(&other) - } -} - -impl<'r> MulAssign<&'r Fq6> for Fq6 { - 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; - } -} - -impl MulAssign for Fq6 { - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Field for Fq6 { - fn random(rng: &mut R) -> Self { - Fq6 { - c0: Fq2::random(rng), - c1: Fq2::random(rng), - c2: Fq2::random(rng), - } - } - - 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(&self) -> Self { - Fq6 { - c0: self.c0.double(), - c1: self.c1.double(), - c2: self.c2.double(), - } - } - - 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(&self) -> Self { - let s0 = self.c0.square(); - let mut ab = self.c0; - ab.mul_assign(&self.c1); - let s1 = ab.double(); - let mut s2 = self.c0; - s2.sub_assign(&self.c1); - s2.add_assign(&self.c2); - s2 = s2.square(); - let mut bc = self.c1; - bc.mul_assign(&self.c2); - let s3 = bc.double(); - let s4 = self.c2.square(); - - let mut c0 = s3; - c0.mul_by_nonresidue(); - c0.add_assign(&s0); - - let mut c1 = s4; - c1.mul_by_nonresidue(); - c1.add_assign(&s1); - - let mut c2 = s1; - c2.add_assign(&s2); - c2.add_assign(&s3); - c2.sub_assign(&s0); - c2.sub_assign(&s4); - - Fq6 { c0, c1, c2 } - } - - fn invert(&self) -> CtOption { - let mut c0 = self.c2; - c0.mul_by_nonresidue(); - c0.mul_assign(&self.c1); - c0 = c0.neg(); - { - let c0s = self.c0.square(); - c0.add_assign(&c0s); - } - let mut c1 = self.c2.square(); - c1.mul_by_nonresidue(); - { - let mut c01 = self.c0; - c01.mul_assign(&self.c1); - c1.sub_assign(&c01); - } - let mut c2 = self.c1.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); - - tmp1.invert().map(|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); - - tmp - }) - } -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fq6_mul_nonresidue() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let nqr = Fq6 { - c0: Fq2::zero(), - c1: Fq2::one(), - c2: Fq2::zero(), - }; - - for _ in 0..1000 { - let mut a = Fq6::random(&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([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let c1 = Fq2::random(&mut rng); - let mut a = Fq6::random(&mut rng); - let mut b = a; - - a.mul_by_1(&c1); - b.mul_assign(&Fq6 { - c0: Fq2::zero(), - c1, - c2: Fq2::zero(), - }); - - assert_eq!(a, b); - } -} - -#[test] -fn test_fq6_mul_by_01() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let c0 = Fq2::random(&mut rng); - let c1 = Fq2::random(&mut rng); - let mut a = Fq6::random(&mut rng); - let mut b = a; - - a.mul_by_01(&c0, &c1); - b.mul_assign(&Fq6 { - c0, - c1, - c2: Fq2::zero(), - }); - - assert_eq!(a, b); - } -} - -#[test] -fn fq6_field_tests() { - use ff::PrimeField; - - crate::tests::field::random_field_tests::(); - crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); -} diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs deleted file mode 100644 index 4c30e49cb0..0000000000 --- a/pairing/src/bls12_381/fr.rs +++ /dev/null @@ -1,990 +0,0 @@ -use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr}; -use std::ops::{AddAssign, MulAssign, SubAssign}; - -#[derive(PrimeField)] -#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"] -#[PrimeFieldGenerator = "7"] -pub struct Fr(FrRepr); - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; -#[cfg(test)] -use std::ops::Neg; - -#[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([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - 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 = Fr::random(&mut rng).into_repr(); - 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_repr_add_nocarry() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FrRepr([ - 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 = Fr::random(&mut rng).into_repr(); - let mut b = Fr::random(&mut rng).into_repr(); - let mut c = Fr::random(&mut rng).into_repr(); - - // 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([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = Fr::random(&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([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Generate a, b, c and ensure (a + b) + c == a + (b + c). - let a = Fr::random(&mut rng); - let b = Fr::random(&mut rng); - let c = Fr::random(&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([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure that (a - b) + (b - a) = 0. - let a = Fr::random(&mut rng); - let b = Fr::random(&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([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * b) * c = a * (b * c) - let a = Fr::random(&mut rng); - let b = Fr::random(&mut rng); - let c = Fr::random(&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::random(&mut rng); - let mut a = Fr::random(&mut rng); - let mut b = Fr::random(&mut rng); - let mut c = Fr::random(&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 a = Fr(FrRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0x73eda753299d7d47, - ])); - assert!(a.is_valid()); - assert_eq!( - a.square(), - Fr::from_repr(FrRepr([ - 0xc0d698e7bde077b8, - 0xb79a310579e76ec2, - 0xac1da8d0a9af4e5f, - 0x13f629c49bf23e97 - ])) - .unwrap() - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * a) = a^2 - let a = Fr::random(&mut rng); - assert_eq!(a.square(), a * a); - } -} - -#[test] -fn test_fr_invert() { - assert!(bool::from(Fr::zero().invert().is_none())); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let one = Fr::one(); - - for _ in 0..1000 { - // Ensure that a * a^-1 = 1 - let mut a = Fr::random(&mut rng); - let ainv = a.invert().unwrap(); - a.mul_assign(&ainv); - assert_eq!(a, one); - } -} - -#[test] -fn test_fr_double() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure doubling a is equivalent to adding a to itself. - let a = Fr::random(&mut rng); - assert_eq!(a.double(), a + a); - } -} - -#[test] -fn test_fr_neg() { - { - let a = Fr::zero().neg(); - - assert!(a.is_zero()); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure (a - (-a)) = 0. - let mut a = Fr::random(&mut rng); - let b = a.neg(); - a.add_assign(&b); - - assert!(a.is_zero()); - } -} - -#[test] -fn test_fr_pow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for i in 0..1000 { - // Exponentiate by various small numbers and ensure it consists with repeated - // multiplication. - let a = Fr::random(&mut rng); - let target = a.pow_vartime(&[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::random(&mut rng); - - assert_eq!(a, a.pow_vartime(Fr::char())); - } -} - -#[test] -fn test_fr_sqrt() { - use ff::SqrtField; - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero()); - - for _ in 0..1000 { - // Ensure sqrt(a^2) = a or -a - let a = Fr::random(&mut rng); - let nega = a.neg(); - let b = a.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::random(&mut rng); - - let tmp = a.sqrt(); - if tmp.is_some().into() { - assert_eq!(a, tmp.unwrap().square()); - } - } -} - -#[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([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Try to turn Fr elements into representations and back again, and compare. - let a = Fr::random(&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_vartime([ - 0xfffe5bfeffffffff, - 0x9a1d80553bda402, - 0x299d7d483339d808, - 0x73eda753 - ]), - Fr::root_of_unity() - ); - assert_eq!(Fr::root_of_unity().pow_vartime([1 << Fr::S]), Fr::one()); - assert!(bool::from(Fr::multiplicative_generator().sqrt().is_none())); -} - -#[test] -fn fr_field_tests() { - crate::tests::field::random_field_tests::(); - crate::tests::field::random_sqrt_tests::(); - crate::tests::field::random_frobenius_tests::(Fr::char(), 13); - crate::tests::field::from_str_tests::(); -} - -#[test] -fn fr_repr_tests() { - crate::tests::repr::random_repr_tests::(); -} diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs deleted file mode 100644 index 80848e12dc..0000000000 --- a/pairing/src/bls12_381/mod.rs +++ /dev/null @@ -1,352 +0,0 @@ -//! An implementation of the BLS12-381 pairing-friendly elliptic curve -//! construction. - -mod ec; -mod fq; -mod fq12; -mod fq2; -mod fq6; -mod fr; - -#[cfg(test)] -mod tests; - -pub use self::ec::{ - G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared, - G2Uncompressed, G1, G2, -}; -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; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use subtle::CtOption; - -// 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 ::Prepared, - &'a ::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 = 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) -> CtOption { - let mut f1 = *r; - f1.conjugate(); - - r.invert().map(|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_vartime(&[x]); - if BLS_X_IS_NEGATIVE { - f.conjugate(); - } - } - - let mut x = BLS_X; - let y0 = r.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); - - y1 - }) - } -} - -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.square(); - - let mut tmp1 = r.y.square(); - - let mut tmp2 = tmp1.square(); - - let mut tmp3 = tmp1; - tmp3.add_assign(&r.x); - tmp3 = tmp3.square(); - tmp3.sub_assign(&tmp0); - tmp3.sub_assign(&tmp2); - tmp3 = tmp3.double(); - - let mut tmp4 = tmp0.double(); - tmp4.add_assign(&tmp0); - - let mut tmp6 = r.x; - tmp6.add_assign(&tmp4); - - let tmp5 = tmp4.square(); - - let zsquared = r.z.square(); - - r.x = tmp5; - r.x.sub_assign(&tmp3); - r.x.sub_assign(&tmp3); - - r.z.add_assign(&r.y); - r.z = 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 = tmp2.double().double().double(); - - r.y.sub_assign(&tmp2); - - tmp3 = tmp4; - tmp3.mul_assign(&zsquared); - tmp3 = tmp3.double().neg(); - - tmp6 = tmp6.square(); - tmp6.sub_assign(&tmp0); - tmp6.sub_assign(&tmp5); - - tmp1 = tmp1.double().double(); - - tmp6.sub_assign(&tmp1); - - tmp0 = r.z; - tmp0.mul_assign(&zsquared); - tmp0 = 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 zsquared = r.z.square(); - - let ysquared = q.y.square(); - - let mut t0 = zsquared; - t0.mul_assign(&q.x); - - let mut t1 = q.y; - t1.add_assign(&r.z); - t1 = t1.square(); - t1.sub_assign(&ysquared); - t1.sub_assign(&zsquared); - t1.mul_assign(&zsquared); - - let mut t2 = t0; - t2.sub_assign(&r.x); - - let t3 = t2.square(); - - let t4 = t3.double().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.square(); - r.x.sub_assign(&t5); - r.x.sub_assign(&t7); - r.x.sub_assign(&t7); - - r.z.add_assign(&t2); - r.z = 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 = t0.double(); - - r.y = t8; - r.y.sub_assign(&t0); - - t10 = t10.square(); - t10.sub_assign(&ysquared); - - let ztsquared = r.z.square(); - - t10.sub_assign(&ztsquared); - - t9 = t9.double(); - t9.sub_assign(&t10); - - t10 = r.z.double(); - - t6 = t6.neg(); - - t1 = t6.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() { - crate::tests::engine::engine_tests::(); -} diff --git a/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat deleted file mode 100644 index ea8cd67652..0000000000 Binary files a/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat and /dev/null differ diff --git a/pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat b/pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 86abfba945..0000000000 Binary files a/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat and /dev/null differ diff --git a/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat deleted file mode 100644 index a40bbe251d..0000000000 Binary files a/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat and /dev/null differ diff --git a/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat b/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 92e4bc528e..0000000000 Binary files a/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat and /dev/null differ diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs deleted file mode 100644 index 9c5b2c93ff..0000000000 --- a/pairing/src/bls12_381/tests/mod.rs +++ /dev/null @@ -1,614 +0,0 @@ -use ff::PrimeFieldRepr; -use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; - -use super::*; -use crate::*; - -#[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>(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::(include_bytes!("g1_uncompressed_valid_test_vectors.dat")); -} - -#[test] -fn test_g1_compressed_valid_vectors() { - test_vectors::(include_bytes!("g1_compressed_valid_test_vectors.dat")); -} - -#[test] -fn test_g2_uncompressed_valid_vectors() { - test_vectors::(include_bytes!("g2_uncompressed_valid_test_vectors.dat")); -} - -#[test] -fn test_g2_compressed_valid_vectors() { - test_vectors::(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.square(); - x3b.mul_assign(&x); - x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? - - let y = x3b.sqrt(); - if y.is_some().into() { - let y = y.unwrap(); - - // 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.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? - - let y = x3b.sqrt(); - if y.is_some().into() { - let y = y.unwrap(); - - // 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.square(); - x3b.mul_assign(&x); - x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? - - if x3b.sqrt().is_some().into() { - 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.square(); - x3b.mul_assign(&x); - x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? - - if x3b.sqrt().is_some().into() { - // 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.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 x3b.sqrt().is_some().into() { - 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.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 x3b.sqrt().is_some().into() { - // 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()); - } - } - } -} diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs deleted file mode 100644 index 0081d811e6..0000000000 --- a/pairing/src/lib.rs +++ /dev/null @@ -1,110 +0,0 @@ -//! A library for working with pairing-friendly curves. - -// `clippy` is a code linting tool for improving code quality by catching -// common mistakes or strange code patterns. If the `cargo-clippy` feature -// is provided, all compiler warnings are prohibited. -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))] -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] -// Force public structures to implement Debug -#![deny(missing_debug_implementations)] - -#[cfg(test)] -pub mod tests; - -pub mod bls12_381; - -use ff::{Field, PrimeField, ScalarEngine, SqrtField}; -use group::{CurveAffine, CurveOps, CurveOpsOwned, CurveProjective}; -use subtle::CtOption; - -/// 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 - + From - + CurveOps - + CurveOpsOwned; - - /// 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; - - /// The projective representation of an element in G2. - type G2: CurveProjective - + From - + CurveOps - + CurveOpsOwned; - - /// 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; - - /// 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 ::Prepared, - &'a ::Prepared, - ), - >; - - /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(_: &Self::Fqk) -> CtOption; - - /// Performs a complete pairing operation `(p, q)`. - fn pairing(p: G1, q: G2) -> Self::Fqk - where - G1: Into, - G2: Into, - { - Self::final_exponentiation(&Self::miller_loop( - [(&(p.into().prepare()), &(q.into().prepare()))].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; - 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; -} diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs deleted file mode 100644 index 0776e5d4b1..0000000000 --- a/pairing/src/tests/engine.rs +++ /dev/null @@ -1,138 +0,0 @@ -use group::{CurveAffine, CurveProjective}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::MulAssign; - -use crate::{Engine, Field, PairingCurveAffine, PrimeField}; - -pub fn engine_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..10 { - let a = E::G1::random(&mut rng).into_affine(); - let b = E::G2::random(&mut rng).into_affine(); - - assert!(a.pairing_with(&b) == b.pairing_with(&a)); - assert!(a.pairing_with(&b) == E::pairing(a, b)); - } - - for _ in 0..1000 { - let z1 = E::G1Affine::zero().prepare(); - let z2 = E::G2Affine::zero().prepare(); - - let a = E::G1::random(&mut rng).into_affine().prepare(); - let b = E::G2::random(&mut rng).into_affine().prepare(); - let c = E::G1::random(&mut rng).into_affine().prepare(); - let d = E::G2::random(&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::(); - random_miller_loop_tests::(); -} - -fn random_miller_loop_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - // Exercise the miller loop for a reduced pairing - for _ in 0..1000 { - let a = E::G1::random(&mut rng); - let b = E::G2::random(&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::random(&mut rng); - let b = E::G2::random(&mut rng); - let c = E::G1::random(&mut rng); - let d = E::G2::random(&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() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = E::G1::random(&mut rng); - let b = E::G2::random(&mut rng); - - let c = E::Fr::random(&mut rng); - let d = E::Fr::random(&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_vartime(cd.into_repr()); - - assert_eq!(acbd, adbc); - assert_eq!(acbd, abcd); - } -} diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs deleted file mode 100644 index 7ddb365347..0000000000 --- a/pairing/src/tests/field.rs +++ /dev/null @@ -1,264 +0,0 @@ -use ff::{Field, PrimeField, SqrtField}; -use rand_core::{RngCore, SeedableRng}; -use rand_xorshift::XorShiftRng; - -pub fn random_frobenius_tests>(characteristic: C, maxpower: usize) { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - for i in 0..=maxpower { - let mut a = F::random(&mut rng); - let mut b = a; - - for _ in 0..i { - a = a.pow_vartime(&characteristic); - } - b.frobenius_map(i); - - assert_eq!(a, b); - } - } -} - -pub fn random_sqrt_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..10000 { - let a = F::random(&mut rng); - let b = a.square(); - - let b = b.sqrt().unwrap(); - let negb = b.neg(); - - assert!(a == b || a == negb); - } - - let mut c = F::one(); - for _ in 0..10000 { - let mut b = c.square(); - - b = b.sqrt().unwrap(); - - if b != c { - b = b.neg(); - } - - assert_eq!(b, c); - - c.add_assign(&F::one()); - } -} - -pub fn random_field_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - random_multiplication_tests::(&mut rng); - random_addition_tests::(&mut rng); - random_subtraction_tests::(&mut rng); - random_negation_tests::(&mut rng); - random_doubling_tests::(&mut rng); - random_squaring_tests::(&mut rng); - random_inversion_tests::(&mut rng); - random_expansion_tests::(&mut rng); - - assert!(F::zero().is_zero()); - { - let z = F::zero().neg(); - assert!(z.is_zero()); - } - - assert!(bool::from(F::zero().invert().is_none())); - - // Multiplication by zero - { - let mut a = F::random(&mut rng); - a.mul_assign(&F::zero()); - assert!(a.is_zero()); - } - - // Addition by zero - { - let mut a = F::random(&mut rng); - let copy = a; - a.add_assign(&F::zero()); - assert_eq!(a, copy); - } -} - -pub fn from_str_tests() { - { - 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([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let n = rng.next_u64(); - - 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(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - let b = F::random(rng); - let c = F::random(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(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - let b = F::random(rng); - let c = F::random(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(rng: &mut R) { - for _ in 0..10000 { - let b = F::random(rng); - let a = F::random(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(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - let mut b = a.neg(); - b.add_assign(&a); - - assert!(b.is_zero()); - } -} - -fn random_doubling_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - assert_eq!(a + a, a.double()); - } -} - -fn random_squaring_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - assert_eq!(a * a, a.square()); - } -} - -fn random_inversion_tests(rng: &mut R) { - assert!(bool::from(F::zero().invert().is_none())); - - for _ in 0..10000 { - let mut a = F::random(rng); - let b = a.invert().unwrap(); // probablistically nonzero - a.mul_assign(&b); - - assert_eq!(a, F::one()); - } -} - -fn random_expansion_tests(rng: &mut R) { - for _ in 0..10000 { - // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) - - let a = F::random(rng); - let b = F::random(rng); - let c = F::random(rng); - let d = F::random(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); - } -} diff --git a/pairing/src/tests/mod.rs b/pairing/src/tests/mod.rs deleted file mode 100644 index d6ad6a12f1..0000000000 --- a/pairing/src/tests/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod engine; -pub mod field; -pub mod repr; diff --git a/pairing/src/tests/repr.rs b/pairing/src/tests/repr.rs deleted file mode 100644 index cde3ab3bc0..0000000000 --- a/pairing/src/tests/repr.rs +++ /dev/null @@ -1,108 +0,0 @@ -use ff::{PrimeField, PrimeFieldRepr}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; - -pub fn random_repr_tests() { - random_encoding_tests::

(); - random_shl_tests::

(); - random_shr_tests::

(); -} - -fn random_encoding_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let r = P::random(&mut rng).into_repr(); - - // Big endian - { - let mut rdecoded =

::Repr::default(); - - let mut v: Vec = vec![]; - r.write_be(&mut v).unwrap(); - rdecoded.read_be(&v[0..]).unwrap(); - - assert_eq!(r, rdecoded); - } - - // Little endian - { - let mut rdecoded =

::Repr::default(); - - let mut v: Vec = vec![]; - r.write_le(&mut v).unwrap(); - rdecoded.read_le(&v[0..]).unwrap(); - - assert_eq!(r, rdecoded); - } - - { - let mut rdecoded_le =

::Repr::default(); - let mut rdecoded_be_flip =

::Repr::default(); - - let mut v: Vec = 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 = v.into_iter().rev().collect(); - rdecoded_be_flip.read_be(&v[..]).unwrap(); - - assert_eq!(rdecoded_le, rdecoded_be_flip); - } - } -} - -fn random_shl_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - let r = P::random(&mut rng).into_repr(); - - for shift in 0..=r.num_bits() { - 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() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - let r = P::random(&mut rng).into_repr(); - - for shift in 0..=r.num_bits() { - let mut r1 = r; - let mut r2 = r; - - for _ in 0..shift { - r1.div2(); - } - - r2.shr(shift); - - assert_eq!(r1, r2); - } - } -} diff --git a/rust-toolchain b/rust-toolchain index 5edffce6d5..32b7211cb6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.39.0 +1.40.0 diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 562d1a50e5..f2ffcbc367 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash_client_backend" description = "APIs for creating shielded Zcash light clients" -version = "0.2.0" +version = "0.3.0" authors = [ "Jack Grigg ", ] @@ -13,15 +13,18 @@ edition = "2018" [dependencies] bech32 = "0.7" -ff = { version = "0.6", path = "../ff" } -hex = "0.3" -pairing = { version = "0.16", path = "../pairing" } -protobuf = "2" -subtle = "2" -zcash_primitives = { version = "0.2", path = "../zcash_primitives" } +bls12_381 = "0.2" +bs58 = { version = "0.3", features = ["check"] } +ff = "0.7" +group = "0.7" +hex = "0.4" +jubjub = "0.4" +protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 +subtle = "2.2.3" +zcash_primitives = { version = "0.3", path = "../zcash_primitives" } [build-dependencies] -protobuf-codegen-pure = "2" +protobuf-codegen-pure = "2.14" [dev-dependencies] rand_core = "0.5" diff --git a/zcash_client_backend/build.rs b/zcash_client_backend/build.rs index 41e021444f..d1014dbedb 100644 --- a/zcash_client_backend/build.rs +++ b/zcash_client_backend/build.rs @@ -1,11 +1,10 @@ use protobuf_codegen_pure; fn main() { - protobuf_codegen_pure::run(protobuf_codegen_pure::Args { - out_dir: "src/proto", - input: &["proto/compact_formats.proto"], - includes: &["proto"], - customize: Default::default(), - }) - .expect("protoc"); + protobuf_codegen_pure::Codegen::new() + .out_dir("src/proto") + .inputs(&["proto/compact_formats.proto"]) + .includes(&["proto"]) + .run() + .expect("Protobuf codegen failed"); } diff --git a/zcash_client_backend/src/constants/mainnet.rs b/zcash_client_backend/src/constants/mainnet.rs index f3f020dc58..db932fa568 100644 --- a/zcash_client_backend/src/constants/mainnet.rs +++ b/zcash_client_backend/src/constants/mainnet.rs @@ -28,3 +28,13 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews"; /// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs"; + +/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::PublicKey`]. +/// +/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey +pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xb8]; + +/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::Script`]. +/// +/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script +pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xbd]; diff --git a/zcash_client_backend/src/constants/testnet.rs b/zcash_client_backend/src/constants/testnet.rs index 5379d5abd9..d39a11d535 100644 --- a/zcash_client_backend/src/constants/testnet.rs +++ b/zcash_client_backend/src/constants/testnet.rs @@ -28,3 +28,13 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling"; /// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling"; + +/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::PublicKey`]. +/// +/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey +pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25]; + +/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::Script`]. +/// +/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script +pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xba]; diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index f9830f61be..c20626fca8 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -1,10 +1,10 @@ -use pairing::bls12_381::Bls12; +use group::cofactor::CofactorGroup; use zcash_primitives::{ + consensus, note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo}, primitives::{Note, PaymentAddress}, transaction::Transaction, zip32::ExtendedFullViewingKey, - JUBJUB, }; /// A decrypted shielded output. @@ -14,9 +14,11 @@ pub struct DecryptedOutput { /// [`shielded_outputs`]: zcash_primitives::transaction::TransactionData pub index: usize, /// The note within the output. - pub note: Note, + pub note: Note, + /// The account that decrypted the note. + pub account: usize, /// The address the note was sent to. - pub to: PaymentAddress, + pub to: PaymentAddress, /// The memo included with the note. pub memo: Memo, /// True if this output was recovered using an [`OutgoingViewingKey`], meaning that @@ -28,7 +30,8 @@ pub struct DecryptedOutput { /// Scans a [`Transaction`] for any information that can be decrypted by the set of /// [`ExtendedFullViewingKey`]s. -pub fn decrypt_transaction( +pub fn decrypt_transaction( + height: u32, tx: &Transaction, extfvks: &[ExtendedFullViewingKey], ) -> Vec { @@ -41,30 +44,38 @@ pub fn decrypt_transaction( .collect(); for (index, output) in tx.shielded_outputs.iter().enumerate() { - let epk = match output.ephemeral_key.as_prime_order(&JUBJUB) { - Some(p) => p, - None => continue, - }; + let epk = output.ephemeral_key.into_subgroup(); + if epk.is_none().into() { + continue; + } + let epk = epk.unwrap(); - for (ivk, ovk) in &vks { - let ((note, to, memo), outgoing) = - match try_sapling_note_decryption(ivk, &epk, &output.cmu, &output.enc_ciphertext) { - Some(ret) => (ret, false), - None => match try_sapling_output_recovery( - ovk, - &output.cv, - &output.cmu, - &epk, - &output.enc_ciphertext, - &output.out_ciphertext, - ) { - Some(ret) => (ret, true), - None => continue, - }, - }; + for (account, (ivk, ovk)) in vks.iter().enumerate() { + let ((note, to, memo), outgoing) = match try_sapling_note_decryption::

( + height, + ivk, + &epk, + &output.cmu, + &output.enc_ciphertext, + ) { + Some(ret) => (ret, false), + None => match try_sapling_output_recovery::

( + height, + ovk, + &output.cv, + &output.cmu, + &epk, + &output.enc_ciphertext, + &output.out_ciphertext, + ) { + Some(ret) => (ret, true), + None => continue, + }, + }; decrypted.push(DecryptedOutput { index, note, + account, to, memo, outgoing, diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index ba999126c6..9368132e3e 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -6,12 +6,13 @@ //! [`constants`]: crate::constants use bech32::{self, Error, FromBase32, ToBase32}; -use pairing::bls12_381::Bls12; +use bs58::{self, decode::Error as Bs58Error}; +use std::convert::TryInto; use std::io::{self, Write}; use zcash_primitives::{ + legacy::TransparentAddress, primitives::PaymentAddress, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, }; fn bech32_encode(hrp: &str, write: F) -> String @@ -94,18 +95,15 @@ pub fn decode_extended_full_viewing_key( /// # Examples /// /// ``` -/// use pairing::bls12_381::Bls12; +/// use group::Group; +/// use jubjub::SubgroupPoint; /// use rand_core::SeedableRng; /// use rand_xorshift::XorShiftRng; /// use zcash_client_backend::{ /// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, /// encoding::encode_payment_address, /// }; -/// use zcash_primitives::{ -/// jubjub::edwards, -/// primitives::{Diversifier, PaymentAddress}, -/// JUBJUB, -/// }; +/// use zcash_primitives::primitives::{Diversifier, PaymentAddress}; /// /// let rng = &mut XorShiftRng::from_seed([ /// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, @@ -114,16 +112,16 @@ pub fn decode_extended_full_viewing_key( /// /// let pa = PaymentAddress::from_parts( /// Diversifier([0u8; 11]), -/// edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), +/// SubgroupPoint::random(rng), /// ) /// .unwrap(); /// /// assert_eq!( /// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa), -/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe", +/// "ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk", /// ); /// ``` -pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String { +pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String { bech32_encode(hrp, |w| w.write_all(&addr.to_bytes())) } @@ -132,18 +130,15 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String /// # Examples /// /// ``` -/// use pairing::bls12_381::Bls12; +/// use group::Group; +/// use jubjub::SubgroupPoint; /// use rand_core::SeedableRng; /// use rand_xorshift::XorShiftRng; /// use zcash_client_backend::{ /// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, /// encoding::decode_payment_address, /// }; -/// use zcash_primitives::{ -/// jubjub::edwards, -/// primitives::{Diversifier, PaymentAddress}, -/// JUBJUB, -/// }; +/// use zcash_primitives::primitives::{Diversifier, PaymentAddress}; /// /// let rng = &mut XorShiftRng::from_seed([ /// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, @@ -152,19 +147,19 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String /// /// let pa = PaymentAddress::from_parts( /// Diversifier([0u8; 11]), -/// edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), +/// SubgroupPoint::random(rng), /// ) /// .unwrap(); /// /// assert_eq!( /// decode_payment_address( /// HRP_SAPLING_PAYMENT_ADDRESS, -/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe", +/// "ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk", /// ), /// Ok(Some(pa)), /// ); /// ``` -pub fn decode_payment_address(hrp: &str, s: &str) -> Result>, Error> { +pub fn decode_payment_address(hrp: &str, s: &str) -> Result, Error> { bech32_decode(hrp, s, |data| { if data.len() != 43 { return None; @@ -172,18 +167,118 @@ pub fn decode_payment_address(hrp: &str, s: &str) -> Result::from_bytes(&bytes, &JUBJUB) + PaymentAddress::from_bytes(&bytes) + }) +} + +/// Writes a [`TransparentAddress`] as a Base58Check-encoded string. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_backend::{ +/// constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX}, +/// encoding::encode_transparent_address, +/// }; +/// use zcash_primitives::legacy::TransparentAddress; +/// +/// assert_eq!( +/// encode_transparent_address( +/// &B58_PUBKEY_ADDRESS_PREFIX, +/// &B58_SCRIPT_ADDRESS_PREFIX, +/// &TransparentAddress::PublicKey([0; 20]), +/// ), +/// "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma", +/// ); +/// +/// assert_eq!( +/// encode_transparent_address( +/// &B58_PUBKEY_ADDRESS_PREFIX, +/// &B58_SCRIPT_ADDRESS_PREFIX, +/// &TransparentAddress::Script([0; 20]), +/// ), +/// "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2", +/// ); +/// ``` +pub fn encode_transparent_address( + pubkey_version: &[u8], + script_version: &[u8], + addr: &TransparentAddress, +) -> String { + let decoded = match addr { + TransparentAddress::PublicKey(key_id) => { + let mut decoded = vec![0; pubkey_version.len() + 20]; + decoded[..pubkey_version.len()].copy_from_slice(pubkey_version); + decoded[pubkey_version.len()..].copy_from_slice(key_id); + decoded + } + TransparentAddress::Script(script_id) => { + let mut decoded = vec![0; script_version.len() + 20]; + decoded[..script_version.len()].copy_from_slice(script_version); + decoded[script_version.len()..].copy_from_slice(script_id); + decoded + } + }; + bs58::encode(decoded).with_check().into_string() +} + +/// Decodes a [`TransparentAddress`] from a Base58Check-encoded string. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_backend::{ +/// constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX}, +/// encoding::decode_transparent_address, +/// }; +/// use zcash_primitives::legacy::TransparentAddress; +/// +/// assert_eq!( +/// decode_transparent_address( +/// &B58_PUBKEY_ADDRESS_PREFIX, +/// &B58_SCRIPT_ADDRESS_PREFIX, +/// "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma", +/// ), +/// Ok(Some(TransparentAddress::PublicKey([0; 20]))), +/// ); +/// +/// assert_eq!( +/// decode_transparent_address( +/// &B58_PUBKEY_ADDRESS_PREFIX, +/// &B58_SCRIPT_ADDRESS_PREFIX, +/// "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2", +/// ), +/// Ok(Some(TransparentAddress::Script([0; 20]))), +/// ); +/// ``` +pub fn decode_transparent_address( + pubkey_version: &[u8], + script_version: &[u8], + s: &str, +) -> Result, Bs58Error> { + bs58::decode(s).with_check(None).into_vec().map(|decoded| { + if decoded.starts_with(pubkey_version) { + decoded[pubkey_version.len()..] + .try_into() + .ok() + .map(TransparentAddress::PublicKey) + } else if decoded.starts_with(script_version) { + decoded[script_version.len()..] + .try_into() + .ok() + .map(TransparentAddress::Script) + } else { + None + } }) } #[cfg(test)] mod tests { - use pairing::bls12_381::Bls12; + use group::Group; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; - use zcash_primitives::JUBJUB; use zcash_primitives::{ - jubjub::edwards, primitives::{Diversifier, PaymentAddress}, zip32::ExtendedSpendingKey, }; @@ -281,16 +376,14 @@ mod tests { 0xbc, 0xe5, ]); - let addr = PaymentAddress::from_parts( - Diversifier([0u8; 11]), - edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - ) - .unwrap(); + let addr = + PaymentAddress::from_parts(Diversifier([0u8; 11]), jubjub::SubgroupPoint::random(rng)) + .unwrap(); let encoded_main = - "zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd"; + "zs1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75c8v35z"; let encoded_test = - "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe"; + "ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk"; assert_eq!( encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr), @@ -326,11 +419,9 @@ mod tests { 0xbc, 0xe5, ]); - let addr = PaymentAddress::from_parts( - Diversifier([1u8; 11]), - edwards::Point::::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - ) - .unwrap(); + let addr = + PaymentAddress::from_parts(Diversifier([1u8; 11]), jubjub::SubgroupPoint::random(rng)) + .unwrap(); let encoded_main = encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr); diff --git a/zcash_client_backend/src/proto/mod.rs b/zcash_client_backend/src/proto/mod.rs index 0ab1b6d51c..ee3b8bcdc4 100644 --- a/zcash_client_backend/src/proto/mod.rs +++ b/zcash_client_backend/src/proto/mod.rs @@ -1,12 +1,9 @@ //! Generated code for handling light client protobuf structs. -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::{Bls12, Fr, FrRepr}; -use zcash_primitives::{ - block::{BlockHash, BlockHeader}, - jubjub::{edwards, PrimeOrder}, - JUBJUB, -}; +use ff::PrimeField; +use group::GroupEncoding; +use std::convert::TryInto; +use zcash_primitives::block::{BlockHash, BlockHeader}; pub mod compact_formats; @@ -65,10 +62,10 @@ impl compact_formats::CompactOutput { /// A convenience method that parses [`CompactOutput.cmu`]. /// /// [`CompactOutput.cmu`]: #structfield.cmu - pub fn cmu(&self) -> Result { - let mut repr = FrRepr::default(); - repr.read_le(&self.cmu[..]).map_err(|_| ())?; - Fr::from_repr(repr).map_err(|_| ()) + pub fn cmu(&self) -> Result { + let mut repr = [0; 32]; + repr.as_mut().copy_from_slice(&self.cmu[..]); + bls12_381::Scalar::from_repr(repr).ok_or(()) } /// Returns the ephemeral public key for this output. @@ -76,8 +73,12 @@ impl compact_formats::CompactOutput { /// A convenience method that parses [`CompactOutput.epk`]. /// /// [`CompactOutput.epk`]: #structfield.epk - pub fn epk(&self) -> Result, ()> { - let p = edwards::Point::::read(&self.epk[..], &JUBJUB).map_err(|_| ())?; - p.as_prime_order(&JUBJUB).ok_or(()) + pub fn epk(&self) -> Result { + let p = jubjub::SubgroupPoint::from_bytes(&self.epk[..].try_into().map_err(|_| ())?); + if p.is_some().into() { + Ok(p.unwrap()) + } else { + Err(()) + } } } diff --git a/zcash_client_backend/src/wallet.rs b/zcash_client_backend/src/wallet.rs index dc46a86c7f..f5b17ca864 100644 --- a/zcash_client_backend/src/wallet.rs +++ b/zcash_client_backend/src/wallet.rs @@ -1,9 +1,7 @@ //! Structs representing transaction data scanned from the block chain by a wallet or //! light client. -use pairing::bls12_381::{Bls12, Fr}; use zcash_primitives::{ - jubjub::{edwards, PrimeOrder}, merkle_tree::IncrementalWitness, primitives::{Note, PaymentAddress}, sapling::Node, @@ -36,11 +34,11 @@ pub struct WalletShieldedSpend { /// [`OutputDescription`]: zcash_primitives::transaction::components::OutputDescription pub struct WalletShieldedOutput { pub index: usize, - pub cmu: Fr, - pub epk: edwards::Point, + pub cmu: bls12_381::Scalar, + pub epk: jubjub::SubgroupPoint, pub account: usize, - pub note: Note, - pub to: PaymentAddress, + pub note: Note, + pub to: PaymentAddress, pub is_change: bool, pub witness: IncrementalWitness, } diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index 14939d763d..c654d31ad5 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -4,7 +4,7 @@ use ff::PrimeField; use std::collections::HashSet; use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption}; use zcash_primitives::{ - jubjub::fs::Fs, + consensus, merkle_tree::{CommitmentTree, IncrementalWitness}, note_encryption::try_sapling_compact_note_decryption, sapling::Node, @@ -22,9 +22,10 @@ use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx}; /// /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are incremented /// with this output's commitment. -fn scan_output( +fn scan_output( + height: u32, (index, output): (usize, CompactOutput), - ivks: &[Fs], + ivks: &[jubjub::Fr], spent_from_accounts: &HashSet, tree: &mut CommitmentTree, existing_witnesses: &mut [&mut IncrementalWitness], @@ -36,7 +37,7 @@ fn scan_output( let ct = output.ciphertext; // Increment tree and witnesses - let node = Node::new(cmu.into_repr()); + let node = Node::new(cmu.to_repr()); for witness in existing_witnesses { witness.append(node).unwrap(); } @@ -49,10 +50,11 @@ fn scan_output( tree.append(node).unwrap(); for (account, ivk) in ivks.iter().enumerate() { - let (note, to) = match try_sapling_compact_note_decryption(ivk, &epk, &cmu, &ct) { - Some(ret) => ret, - None => continue, - }; + let (note, to) = + match try_sapling_compact_note_decryption::

(height, ivk, &epk, &cmu, &ct) { + Some(ret) => ret, + None => continue, + }; // A note is marked as "change" if the account that received it // also spent notes in the same transaction. This will catch, @@ -83,7 +85,7 @@ fn scan_output( /// /// The given [`CommitmentTree`] and existing [`IncrementalWitness`]es are /// incremented appropriately. -pub fn scan_block( +pub fn scan_block( block: CompactBlock, extfvks: &[ExtendedFullViewingKey], nullifiers: &[(&[u8], usize)], @@ -150,7 +152,8 @@ pub fn scan_block( .map(|output| &mut output.witness) .collect(); - if let Some(output) = scan_output( + if let Some(output) = scan_output::

( + block.height as u32, to_scan, &ivks, &spent_from_accounts, @@ -183,17 +186,18 @@ pub fn scan_block( #[cfg(test)] mod tests { - use ff::{Field, PrimeField, PrimeFieldRepr}; - use pairing::bls12_381::{Bls12, Fr}; + use ff::{Field, PrimeField}; + use group::GroupEncoding; use rand_core::{OsRng, RngCore}; use zcash_primitives::{ - jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform}, + consensus::TestNetwork, + constants::SPENDING_KEY_GENERATOR, merkle_tree::CommitmentTree, note_encryption::{Memo, SaplingNoteEncryption}, primitives::Note, transaction::components::Amount, + util::generate_random_rseed, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, }; use super::scan_block; @@ -206,21 +210,15 @@ mod tests { nf }; let fake_cmu = { - let fake_cmu = Fr::random(rng); - let mut bytes = vec![]; - fake_cmu.into_repr().write_le(&mut bytes).unwrap(); - bytes + let fake_cmu = bls12_381::Scalar::random(rng); + fake_cmu.to_repr().as_ref().to_owned() }; let fake_epk = { - let mut buffer = vec![0; 64]; + let mut buffer = [0; 64]; rng.fill_bytes(&mut buffer); - let fake_esk = Fs::to_uniform(&buffer[..]); - let fake_epk = JUBJUB - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(fake_esk, &JUBJUB); - let mut bytes = vec![]; - fake_epk.write(&mut bytes).unwrap(); - bytes + let fake_esk = jubjub::Fr::from_bytes_wide(&buffer); + let fake_epk = SPENDING_KEY_GENERATOR * fake_esk; + fake_epk.to_bytes().to_vec() }; let mut cspend = CompactSpend::new(); cspend.set_nf(fake_nf); @@ -251,11 +249,12 @@ mod tests { // Create a fake Note for the account let mut rng = OsRng; + let rseed = generate_random_rseed::(height as u32, &mut rng); let note = Note { - g_d: to.diversifier().g_d::(&JUBJUB).unwrap(), + g_d: to.diversifier().g_d().unwrap(), pk_d: to.pk_d().clone(), value: value.into(), - r: Fs::random(&mut rng), + rseed, }; let encryptor = SaplingNoteEncryption::new( extfvk.fvk.ovk, @@ -264,10 +263,8 @@ mod tests { Memo::default(), &mut rng, ); - let mut cmu = vec![]; - note.cm(&JUBJUB).into_repr().write_le(&mut cmu).unwrap(); - let mut epk = vec![]; - encryptor.epk().write(&mut epk).unwrap(); + let cmu = note.cmu().to_repr().as_ref().to_owned(); + let epk = encryptor.epk().to_bytes().to_vec(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); // Create a fake CompactBlock containing the note @@ -321,7 +318,7 @@ mod tests { assert_eq!(cb.vtx.len(), 2); let mut tree = CommitmentTree::new(); - let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []); + let txs = scan_block::(cb, &[extfvk], &[], &mut tree, &mut []); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -353,7 +350,7 @@ mod tests { assert_eq!(cb.vtx.len(), 3); let mut tree = CommitmentTree::new(); - let txs = scan_block(cb, &[extfvk], &[], &mut tree, &mut []); + let txs = scan_block::(cb, &[extfvk], &[], &mut tree, &mut []); assert_eq!(txs.len(), 1); let tx = &txs[0]; @@ -381,7 +378,7 @@ mod tests { assert_eq!(cb.vtx.len(), 2); let mut tree = CommitmentTree::new(); - let txs = scan_block(cb, &[], &[(&nf, account)], &mut tree, &mut []); + let txs = scan_block::(cb, &[], &[(&nf, account)], &mut tree, &mut []); assert_eq!(txs.len(), 1); let tx = &txs[0]; diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml new file mode 100644 index 0000000000..d0979aa79a --- /dev/null +++ b/zcash_client_sqlite/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "zcash_client_sqlite" +description = "An SQLite-based Zcash light client" +version = "0.1.0" +authors = [ + "Jack Grigg ", +] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +readme = "README.md" +license = "MIT OR Apache-2.0" +edition = "2018" + +[dependencies] +bech32 = "0.7" +bs58 = { version = "0.3", features = ["check"] } +ff = "0.7" +group = "0.7" +jubjub = "0.4" +protobuf = "2" +rand_core = "0.5.1" +rusqlite = { version = "0.23", features = ["bundled"] } +time = "0.1" +zcash_client_backend = { version = "0.3", path = "../zcash_client_backend" } +zcash_primitives = { version = "0.3", path = "../zcash_primitives" } + +[dev-dependencies] +rand_core = "0.5.1" +tempfile = "3" +zcash_proofs = { version = "0.3", path = "../zcash_proofs" } + +[features] +mainnet = [] diff --git a/zcash_client_sqlite/README.md b/zcash_client_sqlite/README.md new file mode 100644 index 0000000000..af077e8d59 --- /dev/null +++ b/zcash_client_sqlite/README.md @@ -0,0 +1,33 @@ +# Security Disclaimer + +This is a beta build, and is currently under active development. Please be advised +of the following: + +* This code currently is not audited by an external security auditor, use it at + your own risk. +* The code **has not been subjected to thorough review** by engineers at the Electric Coin Company. +* We **are actively changing** the codebase and adding features where/when needed. + +---- + +# zcash_client_sqlite + +This library contains APIs that collectively implement a Zcash light client in +an SQLite database. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. + diff --git a/zcash_client_sqlite/src/address.rs b/zcash_client_sqlite/src/address.rs new file mode 100644 index 0000000000..bcd5a7e32d --- /dev/null +++ b/zcash_client_sqlite/src/address.rs @@ -0,0 +1,62 @@ +//! Structs for handling supported address types. + +use zcash_client_backend::encoding::{ + decode_payment_address, decode_transparent_address, encode_payment_address, + encode_transparent_address, +}; +use zcash_primitives::{legacy::TransparentAddress, primitives::PaymentAddress}; + +#[cfg(feature = "mainnet")] +use zcash_client_backend::constants::mainnet::{ + B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, +}; + +#[cfg(not(feature = "mainnet"))] +use zcash_client_backend::constants::testnet::{ + B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, +}; + +/// An address that funds can be sent to. +pub enum RecipientAddress { + Shielded(PaymentAddress), + Transparent(TransparentAddress), +} + +impl From for RecipientAddress { + fn from(addr: PaymentAddress) -> Self { + RecipientAddress::Shielded(addr) + } +} + +impl From for RecipientAddress { + fn from(addr: TransparentAddress) -> Self { + RecipientAddress::Transparent(addr) + } +} + +impl RecipientAddress { + pub fn from_str(s: &str) -> Option { + if let Ok(Some(pa)) = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, s) { + Some(pa.into()) + } else if let Ok(Some(addr)) = + decode_transparent_address(&B58_PUBKEY_ADDRESS_PREFIX, &B58_SCRIPT_ADDRESS_PREFIX, s) + { + Some(addr.into()) + } else { + None + } + } + + pub fn to_string(&self) -> String { + match self { + RecipientAddress::Shielded(pa) => { + encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, pa) + } + RecipientAddress::Transparent(addr) => encode_transparent_address( + &B58_PUBKEY_ADDRESS_PREFIX, + &B58_SCRIPT_ADDRESS_PREFIX, + addr, + ), + } + } +} diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs new file mode 100644 index 0000000000..dfcaf265e0 --- /dev/null +++ b/zcash_client_sqlite/src/chain.rs @@ -0,0 +1,480 @@ +//! Functions for enforcing chain validity and handling chain reorgs. +//! +//! # Examples +//! +//! ``` +//! use zcash_client_sqlite::{ +//! chain::{rewind_to_height, validate_combined_chain}, +//! error::ErrorKind, +//! scan::scan_cached_blocks, +//! }; +//! +//! let db_cache = "/path/to/cache.db"; +//! let db_data = "/path/to/data.db"; +//! +//! // 1) Download new CompactBlocks into db_cache. +//! +//! // 2) Run the chain validator on the received blocks. +//! // +//! // Given that we assume the server always gives us correct-at-the-time blocks, any +//! // errors are in the blocks we have previously cached or scanned. +//! if let Err(e) = validate_combined_chain(&db_cache, &db_data) { +//! match e.kind() { +//! ErrorKind::InvalidChain(upper_bound, _) => { +//! // a) Pick a height to rewind to. +//! // +//! // This might be informed by some external chain reorg information, or +//! // heuristics such as the platform, available bandwidth, size of recent +//! // CompactBlocks, etc. +//! let rewind_height = upper_bound - 10; +//! +//! // b) Rewind scanned block information. +//! rewind_to_height(&db_data, rewind_height); +//! +//! // c) Delete cached blocks from rewind_height onwards. +//! // +//! // This does imply that assumed-valid blocks will be re-downloaded, but it +//! // is also possible that in the intervening time, a chain reorg has +//! // occurred that orphaned some of those blocks. +//! +//! // d) If there is some separate thread or service downloading +//! // CompactBlocks, tell it to go back and download from rewind_height +//! // onwards. +//! } +//! _ => { +//! // Handle other errors. +//! } +//! } +//! } +//! +//! // 3) Scan (any remaining) cached blocks. +//! // +//! // At this point, the cache and scanned data are locally consistent (though not +//! // necessarily consistent with the latest chain tip - this would be discovered the +//! // next time this codepath is executed after new blocks are received). +//! scan_cached_blocks(&db_cache, &db_data, None); +//! ``` + +use protobuf::parse_from_bytes; +use rusqlite::{Connection, NO_PARAMS}; +use std::path::Path; +use zcash_client_backend::proto::compact_formats::CompactBlock; + +use crate::{ + error::{Error, ErrorKind}, + SAPLING_ACTIVATION_HEIGHT, +}; + +#[derive(Debug)] +pub enum ChainInvalidCause { + PrevHashMismatch, + /// (expected_height, actual_height) + HeightMismatch(i32, i32), +} + +struct CompactBlockRow { + height: i32, + data: Vec, +} + +/// Checks that the scanned blocks in the data database, when combined with the recent +/// `CompactBlock`s in the cache database, form a valid chain. +/// +/// This function is built on the core assumption that the information provided in the +/// cache database is more likely to be accurate than the previously-scanned information. +/// This follows from the design (and trust) assumption that the `lightwalletd` server +/// provides accurate block information as of the time it was requested. +/// +/// Returns: +/// - `Ok(())` if the combined chain is valid. +/// - `Err(ErrorKind::InvalidChain(upper_bound, cause))` if the combined chain is invalid. +/// `upper_bound` is the height of the highest invalid block (on the assumption that the +/// highest block in the cache database is correct). +/// - `Err(e)` if there was an error during validation unrelated to chain validity. +/// +/// This function does not mutate either of the databases. +pub fn validate_combined_chain, Q: AsRef>( + db_cache: P, + db_data: Q, +) -> Result<(), Error> { + let cache = Connection::open(db_cache)?; + let data = Connection::open(db_data)?; + + // Recall where we synced up to previously. + // If we have never synced, use Sapling activation height to select all cached CompactBlocks. + let (have_scanned, last_scanned_height) = + data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0) + .map(|h| (true, h)) + .or(Ok((false, SAPLING_ACTIVATION_HEIGHT - 1))) + })?; + + // Fetch the CompactBlocks we need to validate + let mut stmt_blocks = cache + .prepare("SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height DESC")?; + let mut rows = stmt_blocks.query_map(&[last_scanned_height], |row| { + Ok(CompactBlockRow { + height: row.get(0)?, + data: row.get(1)?, + }) + })?; + + // Take the highest cached block as accurate. + let (mut last_height, mut last_prev_hash) = { + let assumed_correct = match rows.next() { + Some(row) => row?, + None => { + // No cached blocks, and we've already validated the blocks we've scanned, + // so there's nothing to validate. + // TODO: Maybe we still want to check if there are cached blocks that are + // at heights we previously scanned? Check scanning flow again. + return Ok(()); + } + }; + let block: CompactBlock = parse_from_bytes(&assumed_correct.data)?; + (block.height as i32, block.prev_hash()) + }; + + for row in rows { + let row = row?; + + // Scanned blocks MUST be height-sequential. + if row.height != (last_height - 1) { + return Err(Error(ErrorKind::InvalidChain( + last_height - 1, + ChainInvalidCause::HeightMismatch(last_height - 1, row.height), + ))); + } + last_height = row.height; + + let block: CompactBlock = parse_from_bytes(&row.data)?; + + // Cached blocks MUST be hash-chained. + if block.hash() != last_prev_hash { + return Err(Error(ErrorKind::InvalidChain( + last_height, + ChainInvalidCause::PrevHashMismatch, + ))); + } + last_prev_hash = block.prev_hash(); + } + + if have_scanned { + // Cached blocks MUST hash-chain to the last scanned block. + let last_scanned_hash = data.query_row( + "SELECT hash FROM blocks WHERE height = ?", + &[last_scanned_height], + |row| row.get::<_, Vec<_>>(0), + )?; + if &last_scanned_hash[..] != &last_prev_hash.0[..] { + return Err(Error(ErrorKind::InvalidChain( + last_scanned_height, + ChainInvalidCause::PrevHashMismatch, + ))); + } + } + + // All good! + Ok(()) +} + +/// Rewinds the data database to the given height. +/// +/// If the requested height is greater than or equal to the height of the last scanned +/// block, this function does nothing. +pub fn rewind_to_height>(db_data: P, height: i32) -> Result<(), Error> { + let data = Connection::open(db_data)?; + + // Recall where we synced up to previously. + // If we have never synced, use Sapling activation height. + let last_scanned_height = + data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) + })?; + + if height >= last_scanned_height { + // Nothing to do. + return Ok(()); + } + + // Start an SQL transaction for rewinding. + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + + // Decrement witnesses. + data.execute("DELETE FROM sapling_witnesses WHERE block > ?", &[height])?; + + // Un-mine transactions. + data.execute( + "UPDATE transactions SET block = NULL, tx_index = NULL WHERE block > ?", + &[height], + )?; + + // Now that they aren't depended on, delete scanned blocks. + data.execute("DELETE FROM blocks WHERE height > ?", &[height])?; + + // Commit the SQL transaction, rewinding atomically. + data.execute("COMMIT", NO_PARAMS)?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use zcash_primitives::{ + block::BlockHash, + transaction::components::Amount, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + + use super::{rewind_to_height, validate_combined_chain}; + use crate::{ + error::ErrorKind, + init::{init_accounts_table, init_cache_database, init_data_database}, + query::get_balance, + scan::scan_cached_blocks, + tests::{fake_compact_block, insert_into_cache}, + SAPLING_ACTIVATION_HEIGHT, + }; + + #[test] + fn valid_chain_states() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Empty chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Create a fake CompactBlock sending value to the address + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + Amount::from_u64(5).unwrap(), + ); + insert_into_cache(db_cache, &cb); + + // Cache-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Scan the cache + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Data-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Create a second fake CompactBlock sending more value to the address + let (cb2, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + extfvk, + Amount::from_u64(7).unwrap(), + ); + insert_into_cache(db_cache, &cb2); + + // Data+cache chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Scan the cache again + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Data-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + } + + #[test] + fn invalid_chain_cache_disconnected() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Create some fake CompactBlocks + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + Amount::from_u64(5).unwrap(), + ); + let (cb2, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + extfvk.clone(), + Amount::from_u64(7).unwrap(), + ); + insert_into_cache(db_cache, &cb); + insert_into_cache(db_cache, &cb2); + + // Scan the cache + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Data-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Create more fake CompactBlocks that don't connect to the scanned ones + let (cb3, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 2, + BlockHash([1; 32]), + extfvk.clone(), + Amount::from_u64(8).unwrap(), + ); + let (cb4, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 3, + cb3.hash(), + extfvk.clone(), + Amount::from_u64(3).unwrap(), + ); + insert_into_cache(db_cache, &cb3); + insert_into_cache(db_cache, &cb4); + + // Data+cache chain should be invalid at the data/cache boundary + match validate_combined_chain(db_cache, db_data) { + Err(e) => match e.kind() { + ErrorKind::InvalidChain(upper_bound, _) => { + assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 1) + } + _ => panic!(), + }, + _ => panic!(), + } + } + + #[test] + fn invalid_chain_cache_reorg() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Create some fake CompactBlocks + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + Amount::from_u64(5).unwrap(), + ); + let (cb2, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + extfvk.clone(), + Amount::from_u64(7).unwrap(), + ); + insert_into_cache(db_cache, &cb); + insert_into_cache(db_cache, &cb2); + + // Scan the cache + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Data-only chain should be valid + validate_combined_chain(db_cache, db_data).unwrap(); + + // Create more fake CompactBlocks that contain a reorg + let (cb3, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 2, + cb2.hash(), + extfvk.clone(), + Amount::from_u64(8).unwrap(), + ); + let (cb4, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 3, + BlockHash([1; 32]), + extfvk.clone(), + Amount::from_u64(3).unwrap(), + ); + insert_into_cache(db_cache, &cb3); + insert_into_cache(db_cache, &cb4); + + // Data+cache chain should be invalid inside the cache + match validate_combined_chain(db_cache, db_data) { + Err(e) => match e.kind() { + ErrorKind::InvalidChain(upper_bound, _) => { + assert_eq!(*upper_bound, SAPLING_ACTIVATION_HEIGHT + 2) + } + _ => panic!(), + }, + _ => panic!(), + } + } + + #[test] + fn data_db_rewinding() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Account balance should be zero + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // Create fake CompactBlocks sending value to the address + let value = Amount::from_u64(5).unwrap(); + let value2 = Amount::from_u64(7).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2); + insert_into_cache(db_cache, &cb); + insert_into_cache(db_cache, &cb2); + + // Scan the cache + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Account balance should reflect both received notes + assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); + + // "Rewind" to height of last scanned block + rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT + 1).unwrap(); + + // Account balance should be unaltered + assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); + + // Rewind so that one block is dropped + rewind_to_height(db_data, SAPLING_ACTIVATION_HEIGHT).unwrap(); + + // Account balance should only contain the first received note + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // Scan the cache again + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Account balance should again reflect both received notes + assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); + } +} diff --git a/zcash_client_sqlite/src/error.rs b/zcash_client_sqlite/src/error.rs new file mode 100644 index 0000000000..ed33922b7b --- /dev/null +++ b/zcash_client_sqlite/src/error.rs @@ -0,0 +1,132 @@ +use std::error; +use std::fmt; +use zcash_primitives::{ + sapling::Node, + transaction::{builder, TxId}, +}; + +#[derive(Debug)] +pub enum ErrorKind { + CorruptedData(&'static str), + IncorrectHRPExtFVK, + InsufficientBalance(u64, u64), + InvalidChain(i32, crate::chain::ChainInvalidCause), + InvalidExtSK(u32), + InvalidHeight(i32, i32), + InvalidMemo(std::str::Utf8Error), + InvalidNewWitnessAnchor(usize, TxId, i32, Node), + InvalidNote, + InvalidWitnessAnchor(i64, i32), + ScanRequired, + TableNotEmpty, + Bech32(bech32::Error), + Base58(bs58::decode::Error), + Builder(builder::Error), + Database(rusqlite::Error), + Io(std::io::Error), + Protobuf(protobuf::ProtobufError), +} + +#[derive(Debug)] +pub struct Error(pub(crate) ErrorKind); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self.0 { + ErrorKind::CorruptedData(reason) => write!(f, "Data DB is corrupted: {}", reason), + ErrorKind::IncorrectHRPExtFVK => write!(f, "Incorrect HRP for extfvk"), + ErrorKind::InsufficientBalance(have, need) => write!( + f, + "Insufficient balance (have {}, need {} including fee)", + have, need + ), + ErrorKind::InvalidChain(upper_bound, cause) => { + write!(f, "Invalid chain (upper bound: {}): {:?}", upper_bound, cause) + } + ErrorKind::InvalidExtSK(account) => { + write!(f, "Incorrect ExtendedSpendingKey for account {}", account) + } + ErrorKind::InvalidHeight(expected, actual) => write!( + f, + "Expected height of next CompactBlock to be {}, but was {}", + expected, actual + ), + ErrorKind::InvalidMemo(e) => write!(f, "{}", e), + ErrorKind::InvalidNewWitnessAnchor(output, txid, last_height, anchor) => write!( + f, + "New witness for output {} in tx {} has incorrect anchor after scanning block {}: {:?}", + output, txid, last_height, anchor, + ), + ErrorKind::InvalidNote => write!(f, "Invalid note"), + ErrorKind::InvalidWitnessAnchor(id_note, last_height) => write!( + f, + "Witness for note {} has incorrect anchor after scanning block {}", + id_note, last_height + ), + ErrorKind::ScanRequired => write!(f, "Must scan blocks first"), + ErrorKind::TableNotEmpty => write!(f, "Table is not empty"), + ErrorKind::Bech32(e) => write!(f, "{}", e), + ErrorKind::Base58(e) => write!(f, "{}", e), + ErrorKind::Builder(e) => write!(f, "{:?}", e), + ErrorKind::Database(e) => write!(f, "{}", e), + ErrorKind::Io(e) => write!(f, "{}", e), + ErrorKind::Protobuf(e) => write!(f, "{}", e), + } + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match &self.0 { + ErrorKind::InvalidMemo(e) => Some(e), + ErrorKind::Bech32(e) => Some(e), + ErrorKind::Builder(e) => Some(e), + ErrorKind::Database(e) => Some(e), + ErrorKind::Io(e) => Some(e), + ErrorKind::Protobuf(e) => Some(e), + _ => None, + } + } +} + +impl From for Error { + fn from(e: bech32::Error) -> Self { + Error(ErrorKind::Bech32(e)) + } +} + +impl From for Error { + fn from(e: bs58::decode::Error) -> Self { + Error(ErrorKind::Base58(e)) + } +} + +impl From for Error { + fn from(e: builder::Error) -> Self { + Error(ErrorKind::Builder(e)) + } +} + +impl From for Error { + fn from(e: rusqlite::Error) -> Self { + Error(ErrorKind::Database(e)) + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error(ErrorKind::Io(e)) + } +} + +impl From for Error { + fn from(e: protobuf::ProtobufError) -> Self { + Error(ErrorKind::Protobuf(e)) + } +} + +impl Error { + pub fn kind(&self) -> &ErrorKind { + &self.0 + } +} diff --git a/zcash_client_sqlite/src/init.rs b/zcash_client_sqlite/src/init.rs new file mode 100644 index 0000000000..cf6d3f2835 --- /dev/null +++ b/zcash_client_sqlite/src/init.rs @@ -0,0 +1,303 @@ +//! Functions for initializing the various databases. + +use rusqlite::{types::ToSql, Connection, NO_PARAMS}; +use std::path::Path; +use zcash_client_backend::encoding::encode_extended_full_viewing_key; +use zcash_primitives::{block::BlockHash, zip32::ExtendedFullViewingKey}; + +use crate::{ + address_from_extfvk, + error::{Error, ErrorKind}, + HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, +}; + +/// Sets up the internal structure of the cache database. +/// +/// # Examples +/// +/// ``` +/// use tempfile::NamedTempFile; +/// use zcash_client_sqlite::init::init_cache_database; +/// +/// let data_file = NamedTempFile::new().unwrap(); +/// let db_cache = data_file.path(); +/// init_cache_database(&db_cache).unwrap(); +/// ``` +pub fn init_cache_database>(db_cache: P) -> Result<(), Error> { + let cache = Connection::open(db_cache)?; + cache.execute( + "CREATE TABLE IF NOT EXISTS compactblocks ( + height INTEGER PRIMARY KEY, + data BLOB NOT NULL + )", + NO_PARAMS, + )?; + Ok(()) +} + +/// Sets up the internal structure of the data database. +/// +/// # Examples +/// +/// ``` +/// use tempfile::NamedTempFile; +/// use zcash_client_sqlite::init::init_data_database; +/// +/// let data_file = NamedTempFile::new().unwrap(); +/// let db_data = data_file.path(); +/// init_data_database(&db_data).unwrap(); +/// ``` +pub fn init_data_database>(db_data: P) -> Result<(), Error> { + let data = Connection::open(db_data)?; + data.execute( + "CREATE TABLE IF NOT EXISTS accounts ( + account INTEGER PRIMARY KEY, + extfvk TEXT NOT NULL, + address TEXT NOT NULL + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS blocks ( + height INTEGER PRIMARY KEY, + hash BLOB NOT NULL, + time INTEGER NOT NULL, + sapling_tree BLOB NOT NULL + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS transactions ( + id_tx INTEGER PRIMARY KEY, + txid BLOB NOT NULL UNIQUE, + created TEXT, + block INTEGER, + tx_index INTEGER, + expiry_height INTEGER, + raw BLOB, + FOREIGN KEY (block) REFERENCES blocks(height) + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS received_notes ( + id_note INTEGER PRIMARY KEY, + tx INTEGER NOT NULL, + output_index INTEGER NOT NULL, + account INTEGER NOT NULL, + diversifier BLOB NOT NULL, + value INTEGER NOT NULL, + rcm BLOB NOT NULL, + nf BLOB NOT NULL UNIQUE, + is_change INTEGER NOT NULL, + memo BLOB, + spent INTEGER, + FOREIGN KEY (tx) REFERENCES transactions(id_tx), + FOREIGN KEY (account) REFERENCES accounts(account), + FOREIGN KEY (spent) REFERENCES transactions(id_tx), + CONSTRAINT tx_output UNIQUE (tx, output_index) + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS sapling_witnesses ( + id_witness INTEGER PRIMARY KEY, + note INTEGER NOT NULL, + block INTEGER NOT NULL, + witness BLOB NOT NULL, + FOREIGN KEY (note) REFERENCES received_notes(id_note), + FOREIGN KEY (block) REFERENCES blocks(height), + CONSTRAINT witness_height UNIQUE (note, block) + )", + NO_PARAMS, + )?; + data.execute( + "CREATE TABLE IF NOT EXISTS sent_notes ( + id_note INTEGER PRIMARY KEY, + tx INTEGER NOT NULL, + output_index INTEGER NOT NULL, + from_account INTEGER NOT NULL, + address TEXT NOT NULL, + value INTEGER NOT NULL, + memo BLOB, + FOREIGN KEY (tx) REFERENCES transactions(id_tx), + FOREIGN KEY (from_account) REFERENCES accounts(account), + CONSTRAINT tx_output UNIQUE (tx, output_index) + )", + NO_PARAMS, + )?; + Ok(()) +} + +/// Initialises the data database with the given [`ExtendedFullViewingKey`]s. +/// +/// The [`ExtendedFullViewingKey`]s are stored internally and used by other APIs such as +/// [`get_address`], [`scan_cached_blocks`], and [`create_to_address`]. `extfvks` **MUST** +/// be arranged in account-order; that is, the [`ExtendedFullViewingKey`] for ZIP 32 +/// account `i` **MUST** be at `extfvks[i]`. +/// +/// # Examples +/// +/// ``` +/// use tempfile::NamedTempFile; +/// use zcash_client_sqlite::init::{init_accounts_table, init_data_database}; +/// use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; +/// +/// let data_file = NamedTempFile::new().unwrap(); +/// let db_data = data_file.path(); +/// init_data_database(&db_data).unwrap(); +/// +/// let extsk = ExtendedSpendingKey::master(&[]); +/// let extfvks = [ExtendedFullViewingKey::from(&extsk)]; +/// init_accounts_table(&db_data, &extfvks).unwrap(); +/// ``` +/// +/// [`get_address`]: crate::query::get_address +/// [`scan_cached_blocks`]: crate::scan::scan_cached_blocks +/// [`create_to_address`]: crate::transact::create_to_address +pub fn init_accounts_table>( + db_data: P, + extfvks: &[ExtendedFullViewingKey], +) -> Result<(), Error> { + let data = Connection::open(db_data)?; + + let mut empty_check = data.prepare("SELECT * FROM accounts LIMIT 1")?; + if empty_check.exists(NO_PARAMS)? { + return Err(Error(ErrorKind::TableNotEmpty)); + } + + // Insert accounts atomically + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + for (account, extfvk) in extfvks.iter().enumerate() { + let address = address_from_extfvk(extfvk); + let extfvk = + encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, extfvk); + data.execute( + "INSERT INTO accounts (account, extfvk, address) + VALUES (?, ?, ?)", + &[ + (account as u32).to_sql()?, + extfvk.to_sql()?, + address.to_sql()?, + ], + )?; + } + data.execute("COMMIT", NO_PARAMS)?; + + Ok(()) +} + +/// Initialises the data database with the given block. +/// +/// This enables a newly-created database to be immediately-usable, without needing to +/// synchronise historic blocks. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::init::init_blocks_table; +/// use zcash_primitives::block::BlockHash; +/// +/// // The block height. +/// let height = 500_000; +/// // The hash of the block header. +/// let hash = BlockHash([0; 32]); +/// // The nTime field from the block header. +/// let time = 12_3456_7890; +/// // The serialized Sapling commitment tree as of this block. +/// // Pre-compute and hard-code, or obtain from a service. +/// let sapling_tree = &[]; +/// +/// init_blocks_table("/path/to/data.db", height, hash, time, sapling_tree); +/// ``` +pub fn init_blocks_table>( + db_data: P, + height: i32, + hash: BlockHash, + time: u32, + sapling_tree: &[u8], +) -> Result<(), Error> { + let data = Connection::open(db_data)?; + + let mut empty_check = data.prepare("SELECT * FROM blocks LIMIT 1")?; + if empty_check.exists(NO_PARAMS)? { + return Err(Error(ErrorKind::TableNotEmpty)); + } + + data.execute( + "INSERT INTO blocks (height, hash, time, sapling_tree) + VALUES (?, ?, ?, ?)", + &[ + height.to_sql()?, + hash.0.to_sql()?, + time.to_sql()?, + sapling_tree.to_sql()?, + ], + )?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use zcash_client_backend::encoding::decode_payment_address; + use zcash_primitives::{ + block::BlockHash, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + + use super::{init_accounts_table, init_blocks_table, init_data_database}; + use crate::{query::get_address, HRP_SAPLING_PAYMENT_ADDRESS}; + + #[test] + fn init_accounts_table_only_works_once() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // We can call the function as many times as we want with no data + init_accounts_table(&db_data, &[]).unwrap(); + init_accounts_table(&db_data, &[]).unwrap(); + + // First call with data should initialise the accounts table + let extfvks = [ExtendedFullViewingKey::from(&ExtendedSpendingKey::master( + &[], + ))]; + init_accounts_table(&db_data, &extfvks).unwrap(); + + // Subsequent calls should return an error + init_accounts_table(&db_data, &[]).unwrap_err(); + init_accounts_table(&db_data, &extfvks).unwrap_err(); + } + + #[test] + fn init_blocks_table_only_works_once() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // First call with data should initialise the blocks table + init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap(); + + // Subsequent calls should return an error + init_blocks_table(&db_data, 2, BlockHash([2; 32]), 2, &[]).unwrap_err(); + } + + #[test] + fn init_accounts_table_stores_correct_address() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvks = [ExtendedFullViewingKey::from(&extsk)]; + init_accounts_table(&db_data, &extfvks).unwrap(); + + // The account's address should be in the data DB + let addr = get_address(&db_data, 0).unwrap(); + let pa = decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr).unwrap(); + assert_eq!(pa.unwrap(), extsk.default_address().unwrap().1); + } +} diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs new file mode 100644 index 0000000000..87327ee6a7 --- /dev/null +++ b/zcash_client_sqlite/src/lib.rs @@ -0,0 +1,263 @@ +//! *An SQLite-based Zcash light client.* +//! +//! `zcash_client_backend` contains a set of APIs that collectively implement an +//! SQLite-based light client for the Zcash network. +//! +//! # Design +//! +//! The light client is built around two SQLite databases: +//! +//! - A cache database, used to inform the light client about new [`CompactBlock`]s. It is +//! read-only within all light client APIs *except* for [`init_cache_database`] which +//! can be used to initialize the database. +//! +//! - A data database, where the light client's state is stored. It is read-write within +//! the light client APIs, and **assumed to be read-only outside these APIs**. Callers +//! **MUST NOT** write to the database without using these APIs. Callers **MAY** read +//! the database directly in order to extract information for display to users. +//! +//! # Features +//! +//! The `mainnet` feature configures the light client for use with the Zcash mainnet. By +//! default, the light client is configured for use with the Zcash testnet. +//! +//! [`CompactBlock`]: zcash_client_backend::proto::compact_formats::CompactBlock +//! [`init_cache_database`]: crate::init::init_cache_database + +use rusqlite::{Connection, NO_PARAMS}; +use std::cmp; +use zcash_client_backend::encoding::encode_payment_address; +use zcash_primitives::zip32::ExtendedFullViewingKey; + +#[cfg(feature = "mainnet")] +use zcash_client_backend::constants::mainnet::{ + HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, +}; +#[cfg(not(feature = "mainnet"))] +use zcash_client_backend::constants::testnet::{ + HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, HRP_SAPLING_PAYMENT_ADDRESS, +}; + +#[cfg(feature = "mainnet")] +pub use zcash_primitives::consensus::MainNetwork as Network; + +#[cfg(not(feature = "mainnet"))] +pub use zcash_primitives::consensus::TestNetwork as Network; + +pub mod address; +pub mod chain; +pub mod error; +pub mod init; +pub mod query; +pub mod scan; +pub mod transact; + +const ANCHOR_OFFSET: u32 = 10; + +#[cfg(feature = "mainnet")] +const SAPLING_ACTIVATION_HEIGHT: i32 = 419_200; + +#[cfg(not(feature = "mainnet"))] +const SAPLING_ACTIVATION_HEIGHT: i32 = 280_000; + +fn address_from_extfvk(extfvk: &ExtendedFullViewingKey) -> String { + let addr = extfvk.default_address().unwrap().1; + encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &addr) +} + +/// Determines the target height for a transaction, and the height from which to +/// select anchors, based on the current synchronised block chain. +fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error::Error> { + data.query_row_and_then( + "SELECT MIN(height), MAX(height) FROM blocks", + NO_PARAMS, + |row| match (row.get::<_, u32>(0), row.get::<_, u32>(1)) { + // If there are no blocks, the query returns NULL. + (Err(rusqlite::Error::InvalidColumnType(_, _, _)), _) + | (_, Err(rusqlite::Error::InvalidColumnType(_, _, _))) => { + Err(error::Error(error::ErrorKind::ScanRequired)) + } + (Err(e), _) | (_, Err(e)) => Err(e.into()), + (Ok(min_height), Ok(max_height)) => { + let target_height = max_height + 1; + + // Select an anchor ANCHOR_OFFSET back from the target block, + // unless that would be before the earliest block we have. + let anchor_height = + cmp::max(target_height.saturating_sub(ANCHOR_OFFSET), min_height); + + Ok((target_height, anchor_height)) + } + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::Network; + use ff::PrimeField; + use group::GroupEncoding; + use protobuf::Message; + use rand_core::{OsRng, RngCore}; + use rusqlite::{types::ToSql, Connection}; + use std::path::Path; + use zcash_client_backend::proto::compact_formats::{ + CompactBlock, CompactOutput, CompactSpend, CompactTx, + }; + use zcash_primitives::{ + block::BlockHash, + note_encryption::{Memo, SaplingNoteEncryption}, + primitives::{Note, PaymentAddress}, + transaction::components::Amount, + util::generate_random_rseed, + zip32::ExtendedFullViewingKey, + }; + + /// Create a fake CompactBlock at the given height, containing a single output paying + /// the given address. Returns the CompactBlock and the nullifier for the new note. + pub(crate) fn fake_compact_block( + height: i32, + prev_hash: BlockHash, + extfvk: ExtendedFullViewingKey, + value: Amount, + ) -> (CompactBlock, Vec) { + let to = extfvk.default_address().unwrap().1; + + // Create a fake Note for the account + let mut rng = OsRng; + let rseed = generate_random_rseed::(height as u32, &mut rng); + let note = Note { + g_d: to.diversifier().g_d().unwrap(), + pk_d: to.pk_d().clone(), + value: value.into(), + rseed, + }; + let encryptor = SaplingNoteEncryption::new( + extfvk.fvk.ovk, + note.clone(), + to.clone(), + Memo::default(), + &mut rng, + ); + let cmu = note.cmu().to_repr().as_ref().to_vec(); + let epk = encryptor.epk().to_bytes().to_vec(); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + // Create a fake CompactBlock containing the note + let mut cout = CompactOutput::new(); + cout.set_cmu(cmu); + cout.set_epk(epk); + cout.set_ciphertext(enc_ciphertext[..52].to_vec()); + let mut ctx = CompactTx::new(); + let mut txid = vec![0; 32]; + rng.fill_bytes(&mut txid); + ctx.set_hash(txid); + ctx.outputs.push(cout); + let mut cb = CompactBlock::new(); + cb.set_height(height as u64); + cb.hash.resize(32, 0); + rng.fill_bytes(&mut cb.hash); + cb.prevHash.extend_from_slice(&prev_hash.0); + cb.vtx.push(ctx); + (cb, note.nf(&extfvk.fvk.vk, 0)) + } + + /// Create a fake CompactBlock at the given height, spending a single note from the + /// given address. + pub(crate) fn fake_compact_block_spending( + height: i32, + prev_hash: BlockHash, + (nf, in_value): (Vec, Amount), + extfvk: ExtendedFullViewingKey, + to: PaymentAddress, + value: Amount, + ) -> CompactBlock { + let mut rng = OsRng; + let rseed = generate_random_rseed::(height as u32, &mut rng); + + // Create a fake CompactBlock containing the note + let mut cspend = CompactSpend::new(); + cspend.set_nf(nf); + let mut ctx = CompactTx::new(); + let mut txid = vec![0; 32]; + rng.fill_bytes(&mut txid); + ctx.set_hash(txid); + ctx.spends.push(cspend); + + // Create a fake Note for the payment + ctx.outputs.push({ + let note = Note { + g_d: to.diversifier().g_d().unwrap(), + pk_d: to.pk_d().clone(), + value: value.into(), + rseed, + }; + let encryptor = SaplingNoteEncryption::new( + extfvk.fvk.ovk, + note.clone(), + to, + Memo::default(), + &mut rng, + ); + let cmu = note.cmu().to_repr().as_ref().to_vec(); + let epk = encryptor.epk().to_bytes().to_vec(); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + let mut cout = CompactOutput::new(); + cout.set_cmu(cmu); + cout.set_epk(epk); + cout.set_ciphertext(enc_ciphertext[..52].to_vec()); + cout + }); + + // Create a fake Note for the change + ctx.outputs.push({ + let change_addr = extfvk.default_address().unwrap().1; + let rseed = generate_random_rseed::(height as u32, &mut rng); + let note = Note { + g_d: change_addr.diversifier().g_d().unwrap(), + pk_d: change_addr.pk_d().clone(), + value: (in_value - value).into(), + rseed, + }; + let encryptor = SaplingNoteEncryption::new( + extfvk.fvk.ovk, + note.clone(), + change_addr, + Memo::default(), + &mut rng, + ); + let cmu = note.cmu().to_repr().as_ref().to_vec(); + let epk = encryptor.epk().to_bytes().to_vec(); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + let mut cout = CompactOutput::new(); + cout.set_cmu(cmu); + cout.set_epk(epk); + cout.set_ciphertext(enc_ciphertext[..52].to_vec()); + cout + }); + + let mut cb = CompactBlock::new(); + cb.set_height(height as u64); + cb.hash.resize(32, 0); + rng.fill_bytes(&mut cb.hash); + cb.prevHash.extend_from_slice(&prev_hash.0); + cb.vtx.push(ctx); + cb + } + + /// Insert a fake CompactBlock into the cache DB. + pub(crate) fn insert_into_cache>(db_cache: P, cb: &CompactBlock) { + let cb_bytes = cb.write_to_bytes().unwrap(); + let cache = Connection::open(&db_cache).unwrap(); + cache + .prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)") + .unwrap() + .execute(&[ + (cb.height as i32).to_sql().unwrap(), + cb_bytes.to_sql().unwrap(), + ]) + .unwrap(); + } +} diff --git a/zcash_client_sqlite/src/query.rs b/zcash_client_sqlite/src/query.rs new file mode 100644 index 0000000000..c9bd5c4a92 --- /dev/null +++ b/zcash_client_sqlite/src/query.rs @@ -0,0 +1,206 @@ +//! Functions for querying information in the data database. + +use rusqlite::Connection; +use std::path::Path; +use zcash_primitives::{note_encryption::Memo, transaction::components::Amount}; + +use crate::{ + error::{Error, ErrorKind}, + get_target_and_anchor_heights, +}; + +/// Returns the address for the account. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::query::get_address; +/// +/// let addr = get_address("/path/to/data.db", 0); +/// ``` +pub fn get_address>(db_data: P, account: u32) -> Result { + let data = Connection::open(db_data)?; + + let addr = data.query_row( + "SELECT address FROM accounts + WHERE account = ?", + &[account], + |row| row.get(0), + )?; + + Ok(addr) +} + +/// Returns the balance for the account, including all mined unspent notes that we know +/// about. +/// +/// WARNING: This balance is potentially unreliable, as mined notes may become unmined due +/// to chain reorgs. You should generally not show this balance to users without some +/// caveat. Use [`get_verified_balance`] where you need a more reliable indication of the +/// wallet balance. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::query::get_balance; +/// +/// let addr = get_balance("/path/to/data.db", 0); +/// ``` +pub fn get_balance>(db_data: P, account: u32) -> Result { + let data = Connection::open(db_data)?; + + let balance = data.query_row( + "SELECT SUM(value) FROM received_notes + INNER JOIN transactions ON transactions.id_tx = received_notes.tx + WHERE account = ? AND spent IS NULL AND transactions.block IS NOT NULL", + &[account], + |row| row.get(0).or(Ok(0)), + )?; + + match Amount::from_i64(balance) { + Ok(amount) if !amount.is_negative() => Ok(amount), + _ => Err(Error(ErrorKind::CorruptedData( + "Sum of values in received_notes is out of range", + ))), + } +} + +/// Returns the verified balance for the account, which ignores notes that have been +/// received too recently and are not yet deemed spendable. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::query::get_verified_balance; +/// +/// let addr = get_verified_balance("/path/to/data.db", 0); +/// ``` +pub fn get_verified_balance>(db_data: P, account: u32) -> Result { + let data = Connection::open(db_data)?; + + let (_, anchor_height) = get_target_and_anchor_heights(&data)?; + + let balance = data.query_row( + "SELECT SUM(value) FROM received_notes + INNER JOIN transactions ON transactions.id_tx = received_notes.tx + WHERE account = ? AND spent IS NULL AND transactions.block <= ?", + &[account, anchor_height], + |row| row.get(0).or(Ok(0)), + )?; + + match Amount::from_i64(balance) { + Ok(amount) if !amount.is_negative() => Ok(amount), + _ => Err(Error(ErrorKind::CorruptedData( + "Sum of values in received_notes is out of range", + ))), + } +} + +/// Returns the memo for a received note, if it is known and a valid UTF-8 string. +/// +/// The note is identified by its row index in the `received_notes` table within the data +/// database. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::query::get_received_memo_as_utf8; +/// +/// let memo = get_received_memo_as_utf8("/path/to/data.db", 27); +pub fn get_received_memo_as_utf8>( + db_data: P, + id_note: i64, +) -> Result, Error> { + let data = Connection::open(db_data)?; + + let memo: Vec<_> = data.query_row( + "SELECT memo FROM received_notes + WHERE id_note = ?", + &[id_note], + |row| row.get(0), + )?; + + match Memo::from_bytes(&memo) { + Some(memo) => match memo.to_utf8() { + Some(Ok(res)) => Ok(Some(res)), + Some(Err(e)) => Err(Error(ErrorKind::InvalidMemo(e))), + None => Ok(None), + }, + None => Ok(None), + } +} + +/// Returns the memo for a sent note, if it is known and a valid UTF-8 string. +/// +/// The note is identified by its row index in the `sent_notes` table within the data +/// database. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::query::get_sent_memo_as_utf8; +/// +/// let memo = get_sent_memo_as_utf8("/path/to/data.db", 12); +pub fn get_sent_memo_as_utf8>( + db_data: P, + id_note: i64, +) -> Result, Error> { + let data = Connection::open(db_data)?; + + let memo: Vec<_> = data.query_row( + "SELECT memo FROM sent_notes + WHERE id_note = ?", + &[id_note], + |row| row.get(0), + )?; + + match Memo::from_bytes(&memo) { + Some(memo) => match memo.to_utf8() { + Some(Ok(res)) => Ok(Some(res)), + Some(Err(e)) => Err(Error(ErrorKind::InvalidMemo(e))), + None => Ok(None), + }, + None => Ok(None), + } +} + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use zcash_primitives::{ + transaction::components::Amount, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + + use super::{get_address, get_balance, get_verified_balance}; + use crate::{ + error::ErrorKind, + init::{init_accounts_table, init_data_database}, + }; + + #[test] + fn empty_database_has_no_balance() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvks = [ExtendedFullViewingKey::from(&extsk)]; + init_accounts_table(&db_data, &extfvks).unwrap(); + + // The account should be empty + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // The account should have no verified balance, as we haven't scanned any blocks + let e = get_verified_balance(db_data, 0).unwrap_err(); + match e.kind() { + ErrorKind::ScanRequired => (), + _ => panic!("Unexpected error: {:?}", e), + } + + // An invalid account has zero balance + assert!(get_address(db_data, 1).is_err()); + assert_eq!(get_balance(db_data, 1).unwrap(), Amount::zero()); + } +} diff --git a/zcash_client_sqlite/src/scan.rs b/zcash_client_sqlite/src/scan.rs new file mode 100644 index 0000000000..318b33b671 --- /dev/null +++ b/zcash_client_sqlite/src/scan.rs @@ -0,0 +1,687 @@ +//! Functions for scanning the chain and extracting relevant information. + +use ff::PrimeField; +use protobuf::parse_from_bytes; +use rusqlite::{types::ToSql, Connection, OptionalExtension, NO_PARAMS}; +use std::path::Path; +use zcash_client_backend::{ + decrypt_transaction, encoding::decode_extended_full_viewing_key, + proto::compact_formats::CompactBlock, welding_rig::scan_block, +}; +use zcash_primitives::{ + merkle_tree::{CommitmentTree, IncrementalWitness}, + sapling::Node, + transaction::Transaction, +}; + +use crate::{ + address::RecipientAddress, + error::{Error, ErrorKind}, + Network, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SAPLING_ACTIVATION_HEIGHT, +}; + +struct CompactBlockRow { + height: i32, + data: Vec, +} + +#[derive(Clone)] +struct WitnessRow { + id_note: i64, + witness: IncrementalWitness, +} + +/// Scans at most `limit` new blocks added to the cache for any transactions received by +/// the tracked accounts. +/// +/// This function will return without error after scanning at most `limit` new blocks, to +/// enable the caller to update their UI with scanning progress. Repeatedly calling this +/// function will process sequential ranges of blocks, and is equivalent to calling +/// `scan_cached_blocks` and passing `None` for the optional `limit` value. +/// +/// This function pays attention only to cached blocks with heights greater than the +/// highest scanned block in `db_data`. Cached blocks with lower heights are not verified +/// against previously-scanned blocks. In particular, this function **assumes** that the +/// caller is handling rollbacks. +/// +/// For brand-new light client databases, this function starts scanning from the Sapling +/// activation height. This height can be fast-forwarded to a more recent block by calling +/// [`init_blocks_table`] before this function. +/// +/// Scanned blocks are required to be height-sequential. If a block is missing from the +/// cache, an error will be returned with kind [`ErrorKind::InvalidHeight`]. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_sqlite::scan::scan_cached_blocks; +/// +/// scan_cached_blocks("/path/to/cache.db", "/path/to/data.db", None); +/// ``` +/// +/// [`init_blocks_table`]: crate::init::init_blocks_table +pub fn scan_cached_blocks, Q: AsRef>( + db_cache: P, + db_data: Q, + limit: Option, +) -> Result<(), Error> { + let cache = Connection::open(db_cache)?; + let data = Connection::open(db_data)?; + + // Recall where we synced up to previously. + // If we have never synced, use sapling activation height to select all cached CompactBlocks. + let mut last_height = data.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0).or(Ok(SAPLING_ACTIVATION_HEIGHT - 1)) + })?; + + // Fetch the CompactBlocks we need to scan + let mut stmt_blocks = cache.prepare( + "SELECT height, data FROM compactblocks WHERE height > ? ORDER BY height ASC LIMIT ?", + )?; + let rows = stmt_blocks.query_map(&[last_height, limit.unwrap_or(i32::max_value())], |row| { + Ok(CompactBlockRow { + height: row.get(0)?, + data: row.get(1)?, + }) + })?; + + // Fetch the ExtendedFullViewingKeys we are tracking + let mut stmt_fetch_accounts = + data.prepare("SELECT extfvk FROM accounts ORDER BY account ASC")?; + let extfvks = stmt_fetch_accounts.query_map(NO_PARAMS, |row| { + row.get(0).map(|extfvk: String| { + decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) + }) + })?; + // Raise SQL errors from the query, IO errors from parsing, and incorrect HRP errors. + let extfvks: Vec<_> = extfvks + .collect::, _>, _>>()?? + .ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?; + + // Get the most recent CommitmentTree + let mut stmt_fetch_tree = data.prepare("SELECT sapling_tree FROM blocks WHERE height = ?")?; + let mut tree = stmt_fetch_tree + .query_row(&[last_height], |row| { + row.get(0).map(|data: Vec<_>| { + CommitmentTree::read(&data[..]).unwrap_or_else(|_| CommitmentTree::new()) + }) + }) + .unwrap_or_else(|_| CommitmentTree::new()); + + // Get most recent incremental witnesses for the notes we are tracking + let mut stmt_fetch_witnesses = + data.prepare("SELECT note, witness FROM sapling_witnesses WHERE block = ?")?; + let witnesses = stmt_fetch_witnesses.query_map(&[last_height], |row| { + let id_note = row.get(0)?; + let data: Vec<_> = row.get(1)?; + Ok(IncrementalWitness::read(&data[..]).map(|witness| WitnessRow { id_note, witness })) + })?; + let mut witnesses: Vec<_> = witnesses.collect::, _>>()??; + + // Get the nullifiers for the notes we are tracking + let mut stmt_fetch_nullifiers = + data.prepare("SELECT id_note, nf, account FROM received_notes WHERE spent IS NULL")?; + let nullifiers = stmt_fetch_nullifiers.query_map(NO_PARAMS, |row| { + let nf: Vec<_> = row.get(1)?; + let account: i64 = row.get(2)?; + Ok((nf, account as usize)) + })?; + let mut nullifiers: Vec<_> = nullifiers.collect::>()?; + + // Prepare per-block SQL statements + let mut stmt_insert_block = data.prepare( + "INSERT INTO blocks (height, hash, time, sapling_tree) + VALUES (?, ?, ?, ?)", + )?; + let mut stmt_update_tx = data.prepare( + "UPDATE transactions + SET block = ?, tx_index = ? WHERE txid = ?", + )?; + let mut stmt_insert_tx = data.prepare( + "INSERT INTO transactions (txid, block, tx_index) + VALUES (?, ?, ?)", + )?; + let mut stmt_select_tx = data.prepare("SELECT id_tx FROM transactions WHERE txid = ?")?; + let mut stmt_mark_spent_note = + data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?; + let mut stmt_update_note = data.prepare( + "UPDATE received_notes + SET account = ?, diversifier = ?, value = ?, rcm = ?, nf = ?, is_change = ? + WHERE tx = ? AND output_index = ?", + )?; + let mut stmt_insert_note = data.prepare( + "INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + )?; + let mut stmt_select_note = + data.prepare("SELECT id_note FROM received_notes WHERE tx = ? AND output_index = ?")?; + let mut stmt_insert_witness = data.prepare( + "INSERT INTO sapling_witnesses (note, block, witness) + VALUES (?, ?, ?)", + )?; + let mut stmt_prune_witnesses = data.prepare("DELETE FROM sapling_witnesses WHERE block < ?")?; + let mut stmt_update_expired = data.prepare( + "UPDATE received_notes SET spent = NULL WHERE EXISTS ( + SELECT id_tx FROM transactions + WHERE id_tx = received_notes.spent AND block IS NULL AND expiry_height < ? + )", + )?; + + for row in rows { + let row = row?; + + // Start an SQL transaction for this block. + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + + // Scanned blocks MUST be height-sequential. + if row.height != (last_height + 1) { + return Err(Error(ErrorKind::InvalidHeight(last_height + 1, row.height))); + } + last_height = row.height; + + let block: CompactBlock = parse_from_bytes(&row.data)?; + let block_hash = block.hash.clone(); + let block_time = block.time; + + let txs = { + let nf_refs: Vec<_> = nullifiers.iter().map(|(nf, acc)| (&nf[..], *acc)).collect(); + let mut witness_refs: Vec<_> = witnesses.iter_mut().map(|w| &mut w.witness).collect(); + scan_block::( + block, + &extfvks[..], + &nf_refs, + &mut tree, + &mut witness_refs[..], + ) + }; + + // Enforce that all roots match. This is slow, so only include in debug builds. + #[cfg(debug_assertions)] + { + let cur_root = tree.root(); + for row in &witnesses { + if row.witness.root() != cur_root { + return Err(Error(ErrorKind::InvalidWitnessAnchor( + row.id_note, + last_height, + ))); + } + } + for tx in &txs { + for output in tx.shielded_outputs.iter() { + if output.witness.root() != cur_root { + return Err(Error(ErrorKind::InvalidNewWitnessAnchor( + output.index, + tx.txid, + last_height, + output.witness.root(), + ))); + } + } + } + } + + // Insert the block into the database. + let mut encoded_tree = Vec::new(); + tree.write(&mut encoded_tree) + .expect("Should be able to write to a Vec"); + stmt_insert_block.execute(&[ + row.height.to_sql()?, + block_hash.to_sql()?, + block_time.to_sql()?, + encoded_tree.to_sql()?, + ])?; + + for tx in txs { + // First try update an existing transaction in the database. + let txid = tx.txid.0.to_vec(); + let tx_row = if stmt_update_tx.execute(&[ + row.height.to_sql()?, + (tx.index as i64).to_sql()?, + txid.to_sql()?, + ])? == 0 + { + // It isn't there, so insert our transaction into the database. + stmt_insert_tx.execute(&[ + txid.to_sql()?, + row.height.to_sql()?, + (tx.index as i64).to_sql()?, + ])?; + data.last_insert_rowid() + } else { + // It was there, so grab its row number. + stmt_select_tx.query_row(&[txid], |row| row.get(0))? + }; + + // Mark notes as spent and remove them from the scanning cache + for spend in &tx.shielded_spends { + stmt_mark_spent_note.execute(&[tx_row.to_sql()?, spend.nf.to_sql()?])?; + } + nullifiers = nullifiers + .into_iter() + .filter(|(nf, _acc)| { + tx.shielded_spends + .iter() + .find(|spend| &spend.nf == nf) + .is_none() + }) + .collect(); + + for output in tx.shielded_outputs { + let rcm = output.note.rcm().to_repr(); + let nf = output.note.nf( + &extfvks[output.account].fvk.vk, + output.witness.position() as u64, + ); + + // Assumptions: + // - A transaction will not contain more than 2^63 shielded outputs. + // - A note value will never exceed 2^63 zatoshis. + + // First try updating an existing received note into the database. + let note_row = if stmt_update_note.execute(&[ + (output.account as i64).to_sql()?, + output.to.diversifier().0.to_sql()?, + (output.note.value as i64).to_sql()?, + rcm.as_ref().to_sql()?, + nf.to_sql()?, + output.is_change.to_sql()?, + tx_row.to_sql()?, + (output.index as i64).to_sql()?, + ])? == 0 + { + // It isn't there, so insert our note into the database. + stmt_insert_note.execute(&[ + tx_row.to_sql()?, + (output.index as i64).to_sql()?, + (output.account as i64).to_sql()?, + output.to.diversifier().0.to_sql()?, + (output.note.value as i64).to_sql()?, + rcm.as_ref().to_sql()?, + nf.to_sql()?, + output.is_change.to_sql()?, + ])?; + data.last_insert_rowid() + } else { + // It was there, so grab its row number. + stmt_select_note.query_row( + &[tx_row.to_sql()?, (output.index as i64).to_sql()?], + |row| row.get(0), + )? + }; + + // Save witness for note. + witnesses.push(WitnessRow { + id_note: note_row, + witness: output.witness, + }); + + // Cache nullifier for note (to detect subsequent spends in this scan). + nullifiers.push((nf, output.account)); + } + } + + // Insert current witnesses into the database. + let mut encoded = Vec::new(); + for witness_row in witnesses.iter() { + encoded.clear(); + witness_row + .witness + .write(&mut encoded) + .expect("Should be able to write to a Vec"); + stmt_insert_witness.execute(&[ + witness_row.id_note.to_sql()?, + last_height.to_sql()?, + encoded.to_sql()?, + ])?; + } + + // Prune the stored witnesses (we only expect rollbacks of at most 100 blocks). + stmt_prune_witnesses.execute(&[last_height - 100])?; + + // Update now-expired transactions that didn't get mined. + stmt_update_expired.execute(&[last_height])?; + + // Commit the SQL transaction, writing this block's data atomically. + data.execute("COMMIT", NO_PARAMS)?; + } + + Ok(()) +} + +/// Scans a [`Transaction`] for any information that can be decrypted by the accounts in +/// the wallet, and saves it to the wallet. +pub fn decrypt_and_store_transaction>( + db_data: P, + tx: &Transaction, +) -> Result<(), Error> { + let data = Connection::open(db_data)?; + + // Fetch the ExtendedFullViewingKeys we are tracking + let mut stmt_fetch_accounts = + data.prepare("SELECT extfvk FROM accounts ORDER BY account ASC")?; + let extfvks = stmt_fetch_accounts.query_map(NO_PARAMS, |row| { + row.get(0).map(|extfvk: String| { + decode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) + }) + })?; + // Raise SQL errors from the query, IO errors from parsing, and incorrect HRP errors. + let extfvks: Vec<_> = extfvks + .collect::, _>, _>>()?? + .ok_or(Error(ErrorKind::IncorrectHRPExtFVK))?; + + // Height is block height for mined transactions, and the "mempool height" (chain height + 1) for mempool transactions. + let mut stmt_select_block = data.prepare("SELECT block FROM transactions WHERE txid = ?")?; + let height = match stmt_select_block + .query_row(&[tx.txid().0.to_vec()], |row| row.get(0)) + .optional()? + { + Some(height) => height, + None => data + .query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| { + row.get(0) + }) + .optional()? + .map(|last_height: u32| last_height + 1) + .unwrap_or(SAPLING_ACTIVATION_HEIGHT as u32), + }; + + let outputs = decrypt_transaction::(height as u32, tx, &extfvks); + + if outputs.is_empty() { + // Nothing to see here + return Ok(()); + } + + let mut stmt_update_tx = data.prepare( + "UPDATE transactions + SET expiry_height = ?, raw = ? WHERE txid = ?", + )?; + let mut stmt_insert_tx = data.prepare( + "INSERT INTO transactions (txid, expiry_height, raw) + VALUES (?, ?, ?)", + )?; + let mut stmt_select_tx = data.prepare("SELECT id_tx FROM transactions WHERE txid = ?")?; + let mut stmt_update_sent_note = data.prepare( + "UPDATE sent_notes + SET from_account = ?, address = ?, value = ?, memo = ? + WHERE tx = ? AND output_index = ?", + )?; + let mut stmt_insert_sent_note = data.prepare( + "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) + VALUES (?, ?, ?, ?, ?, ?)", + )?; + let mut stmt_update_received_note = data.prepare( + "UPDATE received_notes + SET account = ?, diversifier = ?, value = ?, rcm = ?, memo = ? + WHERE tx = ? AND output_index = ?", + )?; + let mut stmt_insert_received_note = data.prepare( + "INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, memo) + VALUES (?, ?, ?, ?, ?, ?, ?)", + )?; + + // Update the database atomically, to ensure the result is internally consistent. + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + + // First try update an existing transaction in the database. + let txid = tx.txid().0.to_vec(); + let mut raw_tx = vec![]; + tx.write(&mut raw_tx)?; + let tx_row = if stmt_update_tx.execute(&[ + tx.expiry_height.to_sql()?, + raw_tx.to_sql()?, + txid.to_sql()?, + ])? == 0 + { + // It isn't there, so insert our transaction into the database. + stmt_insert_tx.execute(&[txid.to_sql()?, tx.expiry_height.to_sql()?, raw_tx.to_sql()?])?; + data.last_insert_rowid() + } else { + // It was there, so grab its row number. + stmt_select_tx.query_row(&[txid], |row| row.get(0))? + }; + + for output in outputs { + let output_index = output.index as i64; + let account = output.account as i64; + let value = output.note.value as i64; + + if output.outgoing { + let to_str = RecipientAddress::from(output.to).to_string(); + + // Try updating an existing sent note. + if stmt_update_sent_note.execute(&[ + account.to_sql()?, + to_str.to_sql()?, + value.to_sql()?, + output.memo.as_bytes().to_sql()?, + tx_row.to_sql()?, + output_index.to_sql()?, + ])? == 0 + { + // It isn't there, so insert. + stmt_insert_sent_note.execute(&[ + tx_row.to_sql()?, + output_index.to_sql()?, + account.to_sql()?, + to_str.to_sql()?, + value.to_sql()?, + output.memo.as_bytes().to_sql()?, + ])?; + } + } else { + let rcm = output.note.rcm().to_repr(); + + // Try updating an existing received note. + if stmt_update_received_note.execute(&[ + account.to_sql()?, + output.to.diversifier().0.to_sql()?, + value.to_sql()?, + rcm.as_ref().to_sql()?, + output.memo.as_bytes().to_sql()?, + tx_row.to_sql()?, + output_index.to_sql()?, + ])? == 0 + { + // It isn't there, so insert. + stmt_insert_received_note.execute(&[ + tx_row.to_sql()?, + output_index.to_sql()?, + account.to_sql()?, + output.to.diversifier().0.to_sql()?, + value.to_sql()?, + rcm.as_ref().to_sql()?, + output.memo.as_bytes().to_sql()?, + ])?; + } + } + } + + data.execute("COMMIT", NO_PARAMS)?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use zcash_primitives::{ + block::BlockHash, + transaction::components::Amount, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + + use super::scan_cached_blocks; + use crate::{ + init::{init_accounts_table, init_cache_database, init_data_database}, + query::get_balance, + tests::{fake_compact_block, fake_compact_block_spending, insert_into_cache}, + SAPLING_ACTIVATION_HEIGHT, + }; + + #[test] + fn scan_cached_blocks_requires_sequential_blocks() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Create a block with height SAPLING_ACTIVATION_HEIGHT + let value = Amount::from_u64(50000).unwrap(); + let (cb1, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb1); + scan_cached_blocks(db_cache, db_data, None).unwrap(); + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // We cannot scan a block of height SAPLING_ACTIVATION_HEIGHT + 2 next + let (cb2, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb1.hash(), + extfvk.clone(), + value, + ); + let (cb3, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 2, + cb2.hash(), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb3); + match scan_cached_blocks(db_cache, db_data, None) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + format!( + "Expected height of next CompactBlock to be {}, but was {}", + SAPLING_ACTIVATION_HEIGHT + 1, + SAPLING_ACTIVATION_HEIGHT + 2 + ) + ), + } + + // If we add a block of height SAPLING_ACTIVATION_HEIGHT + 1, we can now scan both + insert_into_cache(db_cache, &cb2); + scan_cached_blocks(db_cache, db_data, None).unwrap(); + assert_eq!( + get_balance(db_data, 0).unwrap(), + Amount::from_u64(150_000).unwrap() + ); + } + + #[test] + fn scan_cached_blocks_finds_received_notes() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Account balance should be zero + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // Create a fake CompactBlock sending value to the address + let value = Amount::from_u64(5).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + + // Scan the cache + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Account balance should reflect the received note + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // Create a second fake CompactBlock sending more value to the address + let value2 = Amount::from_u64(7).unwrap(); + let (cb2, _) = fake_compact_block(SAPLING_ACTIVATION_HEIGHT + 1, cb.hash(), extfvk, value2); + insert_into_cache(db_cache, &cb2); + + // Scan the cache again + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Account balance should reflect both received notes + assert_eq!(get_balance(db_data, 0).unwrap(), value + value2); + } + + #[test] + fn scan_cached_blocks_finds_change_notes() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Account balance should be zero + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // Create a fake CompactBlock sending value to the address + let value = Amount::from_u64(5).unwrap(); + let (cb, nf) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + + // Scan the cache + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Account balance should reflect the received note + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // Create a second fake CompactBlock spending value from the address + let extsk2 = ExtendedSpendingKey::master(&[0]); + let to2 = extsk2.default_address().unwrap().1; + let value2 = Amount::from_u64(2).unwrap(); + insert_into_cache( + db_cache, + &fake_compact_block_spending( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + (nf, value), + extfvk, + to2, + value2, + ), + ); + + // Scan the cache again + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Account balance should equal the change + assert_eq!(get_balance(db_data, 0).unwrap(), value - value2); + } +} diff --git a/zcash_client_sqlite/src/transact.rs b/zcash_client_sqlite/src/transact.rs new file mode 100644 index 0000000000..a7488eaa38 --- /dev/null +++ b/zcash_client_sqlite/src/transact.rs @@ -0,0 +1,850 @@ +//! Functions for creating transactions. + +use ff::PrimeField; +use rand_core::{OsRng, RngCore}; +use rusqlite::{types::ToSql, Connection, NO_PARAMS}; +use std::convert::TryInto; +use std::path::Path; +use zcash_client_backend::encoding::encode_extended_full_viewing_key; +use zcash_primitives::{ + consensus::{self, NetworkUpgrade, Parameters}, + keys::OutgoingViewingKey, + merkle_tree::{IncrementalWitness, MerklePath}, + note_encryption::Memo, + primitives::{Diversifier, Note, Rseed}, + prover::TxProver, + sapling::Node, + transaction::{ + builder::Builder, + components::{amount::DEFAULT_FEE, Amount}, + }, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, +}; + +use crate::{ + address::RecipientAddress, + error::{Error, ErrorKind}, + get_target_and_anchor_heights, Network, HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, +}; + +/// Describes a policy for which outgoing viewing key should be able to decrypt +/// transaction outputs. +/// +/// For details on what transaction information is visible to the holder of an outgoing +/// viewing key, refer to [ZIP 310]. +/// +/// [ZIP 310]: https://zips.z.cash/zip-0310 +pub enum OvkPolicy { + /// Use the outgoing viewing key from the sender's [`ExtendedFullViewingKey`]. + /// + /// Transaction outputs will be decryptable by the sender, in addition to the + /// recipients. + Sender, + + /// Use a custom outgoing viewing key. This might for instance be derived from a + /// separate seed than the wallet's spending keys. + /// + /// Transaction outputs will be decryptable by the recipients, and whoever controls + /// the provided outgoing viewing key. + Custom(OutgoingViewingKey), + + /// Use no outgoing viewing key. Transaction outputs will be decryptable by their + /// recipients, but not by the sender. + Discard, +} + +struct SelectedNoteRow { + diversifier: Diversifier, + note: Note, + merkle_path: MerklePath, +} + +/// Creates a transaction paying the specified address from the given account. +/// +/// Returns the row index of the newly-created transaction in the `transactions` table +/// within the data database. The caller can read the raw transaction bytes from the `raw` +/// column in order to broadcast the transaction to the network. +/// +/// Do not call this multiple times in parallel, or you will generate transactions that +/// double-spend the same notes. +/// +/// # Transaction privacy +/// +/// `ovk_policy` specifies the desired policy for which outgoing viewing key should be +/// able to decrypt the outputs of this transaction. This is primarily relevant to +/// wallet recovery from backup; in particular, [`OvkPolicy::Discard`] will prevent the +/// recipient's address, and the contents of `memo`, from ever being recovered from the +/// block chain. (The total value sent can always be inferred by the sender from the spent +/// notes and received change.) +/// +/// Regardless of the specified policy, `create_to_address` saves `to`, `value`, and +/// `memo` in `db_data`. This can be deleted independently of `ovk_policy`. +/// +/// For details on what transaction information is visible to the holder of a full or +/// outgoing viewing key, refer to [ZIP 310]. +/// +/// [ZIP 310]: https://zips.z.cash/zip-0310 +/// +/// # Examples +/// +/// ``` +/// use zcash_client_backend::{ +/// constants::testnet::COIN_TYPE, +/// keys::spending_key, +/// }; +/// use zcash_client_sqlite::transact::{create_to_address, OvkPolicy}; +/// use zcash_primitives::{consensus, transaction::components::Amount}; +/// use zcash_proofs::prover::LocalTxProver; +/// +/// let tx_prover = match LocalTxProver::with_default_location() { +/// Some(tx_prover) => tx_prover, +/// None => { +/// panic!("Cannot locate the Zcash parameters. Please run zcash-fetch-params or fetch-params.sh to download the parameters, and then re-run the tests."); +/// } +/// }; +/// +/// let account = 0; +/// let extsk = spending_key(&[0; 32][..], COIN_TYPE, account); +/// let to = extsk.default_address().unwrap().1.into(); +/// match create_to_address( +/// "/path/to/data.db", +/// consensus::BranchId::Sapling, +/// tx_prover, +/// (account, &extsk), +/// &to, +/// Amount::from_u64(1).unwrap(), +/// None, +/// OvkPolicy::Sender, +/// ) { +/// Ok(tx_row) => (), +/// Err(e) => (), +/// } +/// ``` +pub fn create_to_address>( + db_data: P, + consensus_branch_id: consensus::BranchId, + prover: impl TxProver, + (account, extsk): (u32, &ExtendedSpendingKey), + to: &RecipientAddress, + value: Amount, + memo: Option, + ovk_policy: OvkPolicy, +) -> Result { + let data = Connection::open(db_data)?; + + // Check that the ExtendedSpendingKey we have been given corresponds to the + // ExtendedFullViewingKey for the account we are spending from. + let extfvk = ExtendedFullViewingKey::from(extsk); + if !data + .prepare("SELECT * FROM accounts WHERE account = ? AND extfvk = ?")? + .exists(&[ + account.to_sql()?, + encode_extended_full_viewing_key(HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, &extfvk) + .to_sql()?, + ])? + { + return Err(Error(ErrorKind::InvalidExtSK(account))); + } + + // Apply the outgoing viewing key policy. + let ovk = match ovk_policy { + OvkPolicy::Sender => extfvk.fvk.ovk, + OvkPolicy::Custom(ovk) => ovk, + OvkPolicy::Discard => { + // Generate a random outgoing viewing key that the caller does not know. + // The probability of this colliding with a legitimate outgoing viewing + // key is negligible. + let mut ovk = [0; 32]; + OsRng.fill_bytes(&mut ovk); + OutgoingViewingKey(ovk) + } + }; + + // Target the next block, assuming we are up-to-date. + let (height, anchor_height) = { + let (target_height, anchor_height) = get_target_and_anchor_heights(&data)?; + (target_height, i64::from(anchor_height)) + }; + + // The goal of this SQL statement is to select the oldest notes until the required + // value has been reached, and then fetch the witnesses at the desired height for the + // selected notes. This is achieved in several steps: + // + // 1) Use a window function to create a view of all notes, ordered from oldest to + // newest, with an additional column containing a running sum: + // - Unspent notes accumulate the values of all unspent notes in that note's + // account, up to itself. + // - Spent notes accumulate the values of all notes in the transaction they were + // spent in, up to itself. + // + // 2) Select all unspent notes in the desired account, along with their running sum. + // + // 3) Select all notes for which the running sum was less than the required value, as + // well as a single note for which the sum was greater than or equal to the + // required value, bringing the sum of all selected notes across the threshold. + // + // 4) Match the selected notes against the witnesses at the desired height. + let target_value = i64::from(value + DEFAULT_FEE); + let mut stmt_select_notes = data.prepare( + "WITH selected AS ( + WITH eligible AS ( + SELECT id_note, diversifier, value, rcm, + SUM(value) OVER + (PARTITION BY account, spent ORDER BY id_note) AS so_far + FROM received_notes + INNER JOIN transactions ON transactions.id_tx = received_notes.tx + WHERE account = ? AND spent IS NULL AND transactions.block <= ? + ) + SELECT * FROM eligible WHERE so_far < ? + UNION + SELECT * FROM (SELECT * FROM eligible WHERE so_far >= ? LIMIT 1) + ), witnesses AS ( + SELECT note, witness FROM sapling_witnesses + WHERE block = ? + ) + SELECT selected.diversifier, selected.value, selected.rcm, witnesses.witness + FROM selected + INNER JOIN witnesses ON selected.id_note = witnesses.note", + )?; + + // Select notes + let notes = stmt_select_notes.query_and_then::<_, Error, _, _>( + &[ + i64::from(account), + anchor_height, + target_value, + target_value, + anchor_height, + ], + |row| { + let diversifier = { + let d: Vec<_> = row.get(0)?; + if d.len() != 11 { + return Err(Error(ErrorKind::CorruptedData( + "Invalid diversifier length", + ))); + } + let mut tmp = [0; 11]; + tmp.copy_from_slice(&d); + Diversifier(tmp) + }; + + let note_value: i64 = row.get(1)?; + + let rseed = { + let d: Vec<_> = row.get(2)?; + + if Network::is_nu_active(NetworkUpgrade::Canopy, height) { + let mut r = [0u8; 32]; + r.copy_from_slice(&d[..]); + Rseed::AfterZip212(r) + } else { + let r = jubjub::Fr::from_repr( + d[..] + .try_into() + .map_err(|_| Error(ErrorKind::InvalidNote))?, + ) + .ok_or(Error(ErrorKind::InvalidNote))?; + Rseed::BeforeZip212(r) + } + }; + + let from = extfvk.fvk.vk.to_payment_address(diversifier).unwrap(); + let note = from.create_note(note_value as u64, rseed).unwrap(); + + let merkle_path = { + let d: Vec<_> = row.get(3)?; + IncrementalWitness::read(&d[..])? + .path() + .expect("the tree is not empty") + }; + + Ok(SelectedNoteRow { + diversifier, + note, + merkle_path, + }) + }, + )?; + let notes: Vec = notes.collect::>()?; + + // Confirm we were able to select sufficient value + let selected_value = notes + .iter() + .fold(0, |acc, selected| acc + selected.note.value); + if selected_value < target_value as u64 { + return Err(Error(ErrorKind::InsufficientBalance( + selected_value, + target_value as u64, + ))); + } + + // Create the transaction + let mut builder = Builder::::new(height); + for selected in notes { + builder.add_sapling_spend( + extsk.clone(), + selected.diversifier, + selected.note, + selected.merkle_path, + )?; + } + match to { + RecipientAddress::Shielded(to) => { + builder.add_sapling_output(ovk, to.clone(), value, memo.clone()) + } + RecipientAddress::Transparent(to) => builder.add_transparent_output(&to, value), + }?; + let (tx, tx_metadata) = builder.build(consensus_branch_id, &prover)?; + // We only called add_sapling_output() once. + let output_index = match tx_metadata.output_index(0) { + Some(idx) => idx as i64, + None => panic!("Output 0 should exist in the transaction"), + }; + let created = time::get_time(); + + // Update the database atomically, to ensure the result is internally consistent. + data.execute("BEGIN IMMEDIATE", NO_PARAMS)?; + + // Save the transaction in the database. + let mut raw_tx = vec![]; + tx.write(&mut raw_tx)?; + let mut stmt_insert_tx = data.prepare( + "INSERT INTO transactions (txid, created, expiry_height, raw) + VALUES (?, ?, ?, ?)", + )?; + stmt_insert_tx.execute(&[ + tx.txid().0.to_sql()?, + created.to_sql()?, + tx.expiry_height.to_sql()?, + raw_tx.to_sql()?, + ])?; + let id_tx = data.last_insert_rowid(); + + // Mark notes as spent. + // + // This locks the notes so they aren't selected again by a subsequent call to + // create_to_address() before this transaction has been mined (at which point the notes + // get re-marked as spent). + // + // Assumes that create_to_address() will never be called in parallel, which is a + // reasonable assumption for a light client such as a mobile phone. + let mut stmt_mark_spent_note = + data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?; + for spend in &tx.shielded_spends { + stmt_mark_spent_note.execute(&[id_tx.to_sql()?, spend.nullifier.to_sql()?])?; + } + + // Save the sent note in the database. + // TODO: Decide how to save transparent output information. + let to_str = to.to_string(); + if let Some(memo) = memo { + let mut stmt_insert_sent_note = data.prepare( + "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) + VALUES (?, ?, ?, ?, ?, ?)", + )?; + stmt_insert_sent_note.execute(&[ + id_tx.to_sql()?, + output_index.to_sql()?, + account.to_sql()?, + to_str.to_sql()?, + i64::from(value).to_sql()?, + memo.as_bytes().to_sql()?, + ])?; + } else { + let mut stmt_insert_sent_note = data.prepare( + "INSERT INTO sent_notes (tx, output_index, from_account, address, value) + VALUES (?, ?, ?, ?, ?)", + )?; + stmt_insert_sent_note.execute(&[ + id_tx.to_sql()?, + output_index.to_sql()?, + account.to_sql()?, + to_str.to_sql()?, + i64::from(value).to_sql()?, + ])?; + } + + data.execute("COMMIT", NO_PARAMS)?; + + // Return the row number of the transaction, so the caller can fetch it for sending. + Ok(id_tx) +} + +#[cfg(test)] +mod tests { + use group::cofactor::CofactorGroup; + use rusqlite::Connection; + use tempfile::NamedTempFile; + use zcash_primitives::{ + block::BlockHash, + consensus, + note_encryption::try_sapling_output_recovery, + prover::TxProver, + transaction::{components::Amount, Transaction}, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + }; + use zcash_proofs::prover::LocalTxProver; + + use super::{create_to_address, OvkPolicy}; + use crate::{ + init::{init_accounts_table, init_blocks_table, init_cache_database, init_data_database}, + query::{get_balance, get_verified_balance}, + scan::scan_cached_blocks, + tests::{fake_compact_block, insert_into_cache}, + Network, SAPLING_ACTIVATION_HEIGHT, + }; + + fn test_prover() -> impl TxProver { + match LocalTxProver::with_default_location() { + Some(tx_prover) => tx_prover, + None => { + panic!("Cannot locate the Zcash parameters. Please run zcash-fetch-params or fetch-params.sh to download the parameters, and then re-run the tests."); + } + } + } + + #[test] + fn create_to_address_fails_on_incorrect_extsk() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add two accounts to the wallet + let extsk0 = ExtendedSpendingKey::master(&[]); + let extsk1 = ExtendedSpendingKey::master(&[0]); + let extfvks = [ + ExtendedFullViewingKey::from(&extsk0), + ExtendedFullViewingKey::from(&extsk1), + ]; + init_accounts_table(&db_data, &extfvks).unwrap(); + let to = extsk0.default_address().unwrap().1.into(); + + // Invalid extsk for the given account should cause an error + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk1), + &to, + Amount::from_u64(1).unwrap(), + None, + OvkPolicy::Sender, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 0"), + } + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (1, &extsk0), + &to, + Amount::from_u64(1).unwrap(), + None, + OvkPolicy::Sender, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!(e.to_string(), "Incorrect ExtendedSpendingKey for account 1"), + } + } + + #[test] + fn create_to_address_fails_with_no_blocks() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvks = [ExtendedFullViewingKey::from(&extsk)]; + init_accounts_table(&db_data, &extfvks).unwrap(); + let to = extsk.default_address().unwrap().1.into(); + + // We cannot do anything if we aren't synchronised + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(1).unwrap(), + None, + OvkPolicy::Sender, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!(e.to_string(), "Must scan blocks first"), + } + } + + #[test] + fn create_to_address_fails_on_insufficient_balance() { + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvks = [ExtendedFullViewingKey::from(&extsk)]; + init_accounts_table(&db_data, &extfvks).unwrap(); + let to = extsk.default_address().unwrap().1.into(); + + // Account balance should be zero + assert_eq!(get_balance(db_data, 0).unwrap(), Amount::zero()); + + // We cannot spend anything + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(1).unwrap(), + None, + OvkPolicy::Sender, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 0, need 10001 including fee)" + ), + } + } + + #[test] + fn create_to_address_fails_on_unverified_notes() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Add funds to the wallet in a single note + let value = Amount::from_u64(50000).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Verified balance matches total balance + assert_eq!(get_balance(db_data, 0).unwrap(), value); + assert_eq!(get_verified_balance(db_data, 0).unwrap(), value); + + // Add more funds to the wallet in a second note + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 1, + cb.hash(), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Verified balance does not include the second note + assert_eq!(get_balance(db_data, 0).unwrap(), value + value); + assert_eq!(get_verified_balance(db_data, 0).unwrap(), value); + + // Spend fails because there are insufficient verified notes + let extsk2 = ExtendedSpendingKey::master(&[]); + let to = extsk2.default_address().unwrap().1.into(); + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(70000).unwrap(), + None, + OvkPolicy::Sender, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 50000, need 80000 including fee)" + ), + } + + // Mine blocks SAPLING_ACTIVATION_HEIGHT + 2 to 9 until just before the second + // note is verified + for i in 2..10 { + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + i, + cb.hash(), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + } + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Second spend still fails + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(70000).unwrap(), + None, + OvkPolicy::Sender, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 50000, need 80000 including fee)" + ), + } + + // Mine block 11 so that the second note becomes verified + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 10, + cb.hash(), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Second spend should now succeed + create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(70000).unwrap(), + None, + OvkPolicy::Sender, + ) + .unwrap(); + } + + #[test] + fn create_to_address_fails_on_locked_notes() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Add funds to the wallet in a single note + let value = Amount::from_u64(50000).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data, None).unwrap(); + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + // Send some of the funds to another address + let extsk2 = ExtendedSpendingKey::master(&[]); + let to = extsk2.default_address().unwrap().1.into(); + create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(15000).unwrap(), + None, + OvkPolicy::Sender, + ) + .unwrap(); + + // A second spend fails because there are no usable notes + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(2000).unwrap(), + None, + OvkPolicy::Sender, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 0, need 12000 including fee)" + ), + } + + // Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 21 (that don't send us funds) + // until just before the first transaction expires + for i in 1..22 { + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + i, + cb.hash(), + ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])), + value, + ); + insert_into_cache(db_cache, &cb); + } + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Second spend still fails + match create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(2000).unwrap(), + None, + OvkPolicy::Sender, + ) { + Ok(_) => panic!("Should have failed"), + Err(e) => assert_eq!( + e.to_string(), + "Insufficient balance (have 0, need 12000 including fee)" + ), + } + + // Mine block SAPLING_ACTIVATION_HEIGHT + 22 so that the first transaction expires + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + 22, + cb.hash(), + ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[22])), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Second spend should now succeed + create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(2000).unwrap(), + None, + OvkPolicy::Sender, + ) + .unwrap(); + } + + #[test] + fn ovk_policy_prevents_recovery_from_chain() { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = cache_file.path(); + init_cache_database(&db_cache).unwrap(); + + let data_file = NamedTempFile::new().unwrap(); + let db_data = data_file.path(); + init_data_database(&db_data).unwrap(); + + // Add an account to the wallet + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + init_accounts_table(&db_data, &[extfvk.clone()]).unwrap(); + + // Add funds to the wallet in a single note + let value = Amount::from_u64(50000).unwrap(); + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT, + BlockHash([0; 32]), + extfvk.clone(), + value, + ); + insert_into_cache(db_cache, &cb); + scan_cached_blocks(db_cache, db_data, None).unwrap(); + assert_eq!(get_balance(db_data, 0).unwrap(), value); + + let extsk2 = ExtendedSpendingKey::master(&[]); + let addr2 = extsk2.default_address().unwrap().1; + let to = addr2.clone().into(); + + let send_and_recover_with_policy = |ovk_policy| { + let tx_row = create_to_address( + db_data, + consensus::BranchId::Blossom, + test_prover(), + (0, &extsk), + &to, + Amount::from_u64(15000).unwrap(), + None, + ovk_policy, + ) + .unwrap(); + + let data = Connection::open(db_data).unwrap(); + + // Fetch the transaction from the database + let raw_tx: Vec<_> = data + .query_row( + "SELECT raw FROM transactions + WHERE id_tx = ?", + &[tx_row], + |row| row.get(0), + ) + .unwrap(); + let tx = Transaction::read(&raw_tx[..]).unwrap(); + + // Fetch the output index from the database + let output_index: i64 = data + .query_row( + "SELECT output_index FROM sent_notes + WHERE tx = ?", + &[tx_row], + |row| row.get(0), + ) + .unwrap(); + let output = &tx.shielded_outputs[output_index as usize]; + + try_sapling_output_recovery::( + SAPLING_ACTIVATION_HEIGHT as u32, + &extfvk.fvk.ovk, + &output.cv, + &output.cmu, + &output.ephemeral_key.into_subgroup().unwrap(), + &output.enc_ciphertext, + &output.out_ciphertext, + ) + }; + + // Send some of the funds to another address, keeping history. + // The recipient output is decryptable by the sender. + let (_, recovered_to, _) = send_and_recover_with_policy(OvkPolicy::Sender).unwrap(); + assert_eq!(&recovered_to, &addr2); + + // Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 22 (that don't send us funds) + // so that the first transaction expires + for i in 1..=22 { + let (cb, _) = fake_compact_block( + SAPLING_ACTIVATION_HEIGHT + i, + cb.hash(), + ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])), + value, + ); + insert_into_cache(db_cache, &cb); + } + scan_cached_blocks(db_cache, db_data, None).unwrap(); + + // Send the funds again, discarding history. + // Neither transaction output is decryptable by the sender. + assert!(send_and_recover_with_policy(OvkPolicy::Discard).is_none()); + } +} diff --git a/zcash_extensions/Cargo.toml b/zcash_extensions/Cargo.toml new file mode 100644 index 0000000000..acdd782e06 --- /dev/null +++ b/zcash_extensions/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "zcash_extensions" +description = "TBD" +version = "0.0.0" +authors = ["Jack Grigg ", "Kris Nuttycombe "] +homepage = "https://github.com/zcash/librustzcash" +repository = "https://github.com/zcash/librustzcash" +license = "MIT OR Apache-2.0" +edition = "2018" + +[dependencies] +blake2b_simd = "0.5" +zcash_primitives = { version = "0.3.0", path = "../zcash_primitives" } + +[dev-dependencies] +ff = "0.7" +jubjub = "0.4" +rand_core = "0.5.1" +zcash_proofs = { path = "../zcash_proofs" } diff --git a/zcash_extensions/src/consensus.rs b/zcash_extensions/src/consensus.rs new file mode 100644 index 0000000000..7d540a8bac --- /dev/null +++ b/zcash_extensions/src/consensus.rs @@ -0,0 +1 @@ +pub mod transparent; diff --git a/zcash_extensions/src/consensus/transparent.rs b/zcash_extensions/src/consensus/transparent.rs new file mode 100644 index 0000000000..de06781ff3 --- /dev/null +++ b/zcash_extensions/src/consensus/transparent.rs @@ -0,0 +1,110 @@ +//! Consensus logic for Transparent Zcash Extensions. + +use std::convert::TryFrom; +use zcash_primitives::extensions::transparent::{Error, Extension, Precondition, Witness}; +use zcash_primitives::transaction::components::TzeOut; +use zcash_primitives::transaction::Transaction; + +use crate::transparent::demo; + +/// The set of programs that have assigned type IDs within the Zcash consensus rules. +#[derive(Debug, Clone, Copy)] +pub enum ExtensionId { + Demo, +} + +pub struct InvalidExtId(u32); + +impl TryFrom for ExtensionId { + type Error = InvalidExtId; + + fn try_from(t: u32) -> Result { + match t { + 0 => Ok(ExtensionId::Demo), + n => Err(InvalidExtId(n)), + } + } +} + +impl From for u32 { + fn from(type_id: ExtensionId) -> u32 { + match type_id { + ExtensionId::Demo => 0, + } + } +} + +/// The complete set of context data that is available to any extension having +/// an assigned extension type ID. +pub struct Context<'a> { + pub height: i32, + pub tx: &'a Transaction, +} + +impl<'a> Context<'a> { + pub fn new(height: i32, tx: &'a Transaction) -> Self { + Context { height, tx } + } +} + +pub trait Epoch { + type Error; + + fn verify<'a>( + &self, + precondition: &Precondition, + witness: &Witness, + ctx: &Context<'a>, + ) -> Result<(), Error>; +} + +/// Implementation of required operations for the demo extension, as satisfied +/// by the context. +impl<'a> demo::Context for Context<'a> { + fn is_tze_only(&self) -> bool { + self.tx.vin.is_empty() + && self.tx.vout.is_empty() + && self.tx.shielded_spends.is_empty() + && self.tx.shielded_outputs.is_empty() + && self.tx.joinsplits.is_empty() + } + + fn tx_tze_outputs(&self) -> &[TzeOut] { + &self.tx.tze_outputs + } +} + +/// Wire identifier for the dummy network upgrade epoch. +pub const NEXT_BRANCH_ID: u32 = 0x7374f403; + +/// A set of demo TZEs associated with the dummy network upgrade. +struct EpochV1; + +impl Epoch for EpochV1 { + type Error = String; + + fn verify<'a>( + &self, + precondition: &Precondition, + witness: &Witness, + ctx: &Context<'a>, + ) -> Result<(), Error> { + // This epoch contains the following set of programs: + let ext_id = ExtensionId::try_from(precondition.extension_id) + .map_err(|InvalidExtId(id)| Error::InvalidExtensionId(id))?; + match ext_id { + ExtensionId::Demo => demo::Program + .verify(precondition, witness, ctx) + .map_err(|e| Error::ProgramError(format!("Epoch v1 program error: {}", e))), + } + } +} + +pub fn epoch_for_branch(consensus_branch_id: u32) -> Option>> { + // Map from consensus branch IDs to epochs. + let _tmp_branch_id = NEXT_BRANCH_ID; + match consensus_branch_id { + NEXT_BRANCH_ID => Some(Box::new(EpochV1)), + _ => None, + } +} diff --git a/zcash_extensions/src/lib.rs b/zcash_extensions/src/lib.rs new file mode 100644 index 0000000000..3b6ce6424b --- /dev/null +++ b/zcash_extensions/src/lib.rs @@ -0,0 +1,2 @@ +pub mod consensus; +pub mod transparent; diff --git a/zcash_extensions/src/transparent.rs b/zcash_extensions/src/transparent.rs new file mode 100644 index 0000000000..48aca679f4 --- /dev/null +++ b/zcash_extensions/src/transparent.rs @@ -0,0 +1,3 @@ +//! Zcash transparent extensions. + +pub mod demo; diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs new file mode 100644 index 0000000000..bab8f975f8 --- /dev/null +++ b/zcash_extensions/src/transparent/demo.rs @@ -0,0 +1,734 @@ +//! Demo implementation of TZE consensus rules. +//! +//! The demo program implements a dual-hash-lock encumbrance with the following form: +//! +//! > `hash = BLAKE2b_256(preimage_1 || BLAKE2b_256(preimage_2))` +//! +//! The two preimages are revealed in sequential transactions, demonstrating how TZEs can +//! impose constraints on how program modes are chained together. +//! +//! The demo program has two modes: +//! +//! - Mode 0: `hash_1 = BLAKE2b_256(preimage_1 || hash_2)` +//! - Mode 1: `hash_2 = BLAKE2b_256(preimage_2)` +//! +//! and uses the following transaction formats: +//! +//! - `tx_a`: `[ [any input types...] ----> TzeOut(value, hash_1) ]` +//! - `tx_b`: `[ TzeIn(tx_a, preimage_1) -> TzeOut(value, hash_2) ]` +//! - `tx_c`: `[ TzeIn(tx_b, preimage_2) -> [any output types...] ]` + +use std::convert::TryFrom; +use std::fmt; + +use blake2b_simd::Params; + +use zcash_primitives::{ + extensions::transparent::{Extension, ExtensionTxBuilder, FromPayload, ToPayload}, + transaction::components::{amount::Amount, OutPoint, TzeOut}, +}; + +mod open { + pub const MODE: u32 = 0; + + #[derive(Debug, PartialEq)] + pub struct Precondition(pub [u8; 32]); + + #[derive(Debug, PartialEq)] + pub struct Witness(pub [u8; 32]); +} + +mod close { + pub const MODE: u32 = 1; + + #[derive(Debug, PartialEq)] + pub struct Precondition(pub [u8; 32]); + + #[derive(Debug, PartialEq)] + pub struct Witness(pub [u8; 32]); +} + +#[derive(Debug, PartialEq)] +pub enum Precondition { + Open(open::Precondition), + Close(close::Precondition), +} + +impl Precondition { + pub fn open(hash: [u8; 32]) -> Self { + Precondition::Open(open::Precondition(hash)) + } + + pub fn close(hash: [u8; 32]) -> Self { + Precondition::Close(close::Precondition(hash)) + } +} + +#[derive(Debug, PartialEq)] +pub enum Error { + IllegalPayloadLength(usize), + ModeInvalid(u32), + NonTzeTxn, + HashMismatch, // include hashes? + ModeMismatch, + ExpectedClose, + InvalidOutputQty(usize), +} + +impl fmt::Display for Error { + fn fmt<'a>(&self, f: &mut fmt::Formatter<'a>) -> fmt::Result { + match self { + Error::IllegalPayloadLength(sz) => write!(f, "Illegal payload length for demo: {}", sz), + Error::ModeInvalid(m) => write!(f, "Invalid TZE mode for demo program: {}", m), + Error::NonTzeTxn => write!(f, "Transaction has non-TZE inputs."), + Error::HashMismatch => write!(f, "Hash mismatch"), + Error::ModeMismatch => write!(f, "Extension operation mode mismatch."), + Error::ExpectedClose => write!(f, "Got open, expected close."), + Error::InvalidOutputQty(qty) => write!(f, "Incorrect number of outputs: {}", qty), + } + } +} + +impl TryFrom<(u32, Precondition)> for Precondition { + type Error = Error; + + fn try_from(from: (u32, Self)) -> Result { + match from { + (open::MODE, Precondition::Open(p)) => Ok(Precondition::Open(p)), + (close::MODE, Precondition::Close(p)) => Ok(Precondition::Close(p)), + _ => Err(Error::ModeInvalid(from.0)), + } + } +} + +impl FromPayload for Precondition { + type Error = Error; + + fn from_payload(mode: u32, payload: &[u8]) -> Result { + match mode { + open::MODE => { + if payload.len() == 32 { + let mut hash = [0; 32]; + hash.copy_from_slice(&payload); + Ok(Precondition::Open(open::Precondition(hash))) + } else { + Err(Error::IllegalPayloadLength(payload.len())) + } + } + close::MODE => { + if payload.len() == 32 { + let mut hash = [0; 32]; + hash.copy_from_slice(&payload); + Ok(Precondition::Close(close::Precondition(hash))) + } else { + Err(Error::IllegalPayloadLength(payload.len())) + } + } + _ => Err(Error::ModeInvalid(mode)), + } + } +} + +impl ToPayload for Precondition { + fn to_payload(&self) -> (u32, Vec) { + match self { + Precondition::Open(p) => (open::MODE, p.0.to_vec()), + Precondition::Close(p) => (close::MODE, p.0.to_vec()), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Witness { + Open(open::Witness), + Close(close::Witness), +} + +impl Witness { + pub fn open(preimage: [u8; 32]) -> Self { + Witness::Open(open::Witness(preimage)) + } + + pub fn close(preimage: [u8; 32]) -> Self { + Witness::Close(close::Witness(preimage)) + } +} + +impl TryFrom<(u32, Witness)> for Witness { + type Error = Error; + + fn try_from(from: (u32, Self)) -> Result { + match from { + (open::MODE, Witness::Open(p)) => Ok(Witness::Open(p)), + (close::MODE, Witness::Close(p)) => Ok(Witness::Close(p)), + _ => Err(Error::ModeInvalid(from.0)), + } + } +} + +impl FromPayload for Witness { + type Error = Error; + + fn from_payload(mode: u32, payload: &[u8]) -> Result { + match mode { + open::MODE => { + if payload.len() == 32 { + let mut preimage = [0; 32]; + preimage.copy_from_slice(&payload); + Ok(Witness::Open(open::Witness(preimage))) + } else { + Err(Error::IllegalPayloadLength(payload.len())) + } + } + close::MODE => { + if payload.len() == 32 { + let mut preimage = [0; 32]; + preimage.copy_from_slice(&payload); + Ok(Witness::Close(close::Witness(preimage))) + } else { + Err(Error::IllegalPayloadLength(payload.len())) + } + } + _ => Err(Error::ModeInvalid(mode)), + } + } +} + +impl ToPayload for Witness { + fn to_payload(&self) -> (u32, Vec) { + match self { + Witness::Open(w) => (open::MODE, w.0.to_vec()), + Witness::Close(w) => (close::MODE, w.0.to_vec()), + } + } +} + +pub trait Context { + fn is_tze_only(&self) -> bool; + fn tx_tze_outputs(&self) -> &[TzeOut]; +} + +pub struct Program; + +impl Extension for Program { + type P = Precondition; + type W = Witness; + type Error = Error; + + /// Runs the program against the given precondition, witness, and context. + /// + /// At this point the precondition and witness have been parsed and validated + /// non-contextually, and are guaranteed to both be for this program. All subsequent + /// validation is this function's responsibility. + fn verify_inner( + &self, + precondition: &Precondition, + witness: &Witness, + context: &C, + ) -> Result<(), Error> { + // This match statement is selecting the mode that the program is operating in, + // based on the enums defined in the parser. + match (precondition, witness) { + (Precondition::Open(p_open), Witness::Open(w_open)) => { + // In OPEN mode, we enforce that the transaction must only contain inputs + // and outputs from this program. The consensus rules enforce that if a + // transaction contains both TZE inputs and TZE outputs, they must all be + // of the same program type. Therefore we only need to check that the + // transaction does not contain any other type of input or output. + if !context.is_tze_only() { + return Err(Error::NonTzeTxn); + } + + // Next, check that there is only a single TZE output of the correct type. + let outputs = context.tx_tze_outputs(); + match outputs { + [tze_out] => match Precondition::from_payload( + tze_out.precondition.mode, + &tze_out.precondition.payload, + ) { + Ok(Precondition::Close(p_close)) => { + // Finally, check the precondition: + // precondition_open = BLAKE2b_256(witness_open || precondition_close) + let mut h = Params::new().hash_length(32).to_state(); + h.update(&w_open.0); + h.update(&p_close.0); + let hash = h.finalize(); + if hash.as_bytes() == p_open.0 { + Ok(()) + } else { + Err(Error::HashMismatch) + } + } + Ok(Precondition::Open(_)) => Err(Error::ExpectedClose), + Err(e) => Err(e), + }, + _ => Err(Error::InvalidOutputQty(outputs.len())), + } + } + (Precondition::Close(p), Witness::Close(w)) => { + // In CLOSE mode, we only require that the precondition is satisfied: + // precondition_close = BLAKE2b_256(witness_close) + let hash = Params::new().hash_length(32).hash(&w.0); + if hash.as_bytes() == p.0 { + Ok(()) + } else { + Err(Error::HashMismatch) + } + } + _ => Err(Error::ModeMismatch), + } + } +} + +fn builder_hashes(preimage_1: &[u8; 32], preimage_2: &[u8; 32]) -> ([u8; 32], [u8; 32]) { + let hash_2 = { + let mut hash = [0; 32]; + hash.copy_from_slice(Params::new().hash_length(32).hash(preimage_2).as_bytes()); + hash + }; + + let hash_1 = { + let mut hash = [0; 32]; + hash.copy_from_slice( + Params::new() + .hash_length(32) + .to_state() + .update(preimage_1) + .update(&hash_2) + .finalize() + .as_bytes(), + ); + hash + }; + + (hash_1, hash_2) +} + +pub struct DemoBuilder { + pub txn_builder: B, + pub extension_id: u32, +} + +#[derive(Debug)] +pub enum DemoBuildError { + BaseBuilderError(E), + ExpectedOpen, + ExpectedClose, + PrevoutParseFailure(Error), + TransferMismatch { + expected: [u8; 32], + actual: [u8; 32], + }, + CloseMismatch { + expected: [u8; 32], + actual: [u8; 32], + }, +} + +impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<&mut B> { + pub fn demo_open( + &mut self, + value: Amount, + preimage_1: [u8; 32], + preimage_2: [u8; 32], + ) -> Result<(), DemoBuildError> { + let (hash_1, _) = builder_hashes(&preimage_1, &preimage_2); + + // Call through to the generic builder. + self.txn_builder + .add_tze_output(self.extension_id, value, &Precondition::open(hash_1)) + .map_err(DemoBuildError::BaseBuilderError) + } + + pub fn demo_transfer_to_close( + &mut self, + prevout: (OutPoint, TzeOut), + transfer_amount: Amount, + preimage_1: [u8; 32], + preimage_2: [u8; 32], + ) -> Result<(), DemoBuildError> { + let (hash_1, hash_2) = builder_hashes(&preimage_1, &preimage_2); + + // eagerly validate the relationship between prevout.1 and preimage_1 + match Precondition::from_payload( + prevout.1.precondition.mode, + &prevout.1.precondition.payload, + ) { + Ok(Precondition::Open(hash)) => { + if hash.0 != hash_1 { + Err(DemoBuildError::TransferMismatch { + expected: hash.0, + actual: hash_1, + })? + } + } + Ok(Precondition::Close(_)) => Err(DemoBuildError::ExpectedOpen)?, + Err(parse_failure) => Err(DemoBuildError::PrevoutParseFailure(parse_failure))?, + } + + self.txn_builder + .add_tze_input(self.extension_id, prevout, move |_| { + Ok(Witness::open(preimage_1)) + }) + .map_err(DemoBuildError::BaseBuilderError)?; + + self.txn_builder + .add_tze_output( + self.extension_id, + transfer_amount, // can this be > prevout.1.value? + &Precondition::close(hash_2), + ) + .map_err(DemoBuildError::BaseBuilderError) + } + + pub fn demo_close( + &mut self, + prevout: (OutPoint, TzeOut), + preimage_2: [u8; 32], + ) -> Result<(), DemoBuildError> { + let hash_2 = { + let mut hash = [0; 32]; + hash.copy_from_slice(Params::new().hash_length(32).hash(&preimage_2).as_bytes()); + hash + }; + + // eagerly validate the relationship between prevout.1 and preimage_2 + match Precondition::from_payload( + prevout.1.precondition.mode, + &prevout.1.precondition.payload, + ) { + Ok(Precondition::Open(_)) => Err(DemoBuildError::ExpectedClose)?, + Ok(Precondition::Close(hash)) => { + if hash.0 != hash_2 { + Err(DemoBuildError::CloseMismatch { + expected: hash.0, + actual: hash_2, + })? + } + } + Err(parse_failure) => Err(DemoBuildError::PrevoutParseFailure(parse_failure))?, + } + + self.txn_builder + .add_tze_input(self.extension_id, prevout, move |_| { + Ok(Witness::close(preimage_2)) + }) + .map_err(DemoBuildError::BaseBuilderError) + } +} + +#[cfg(test)] +mod tests { + use blake2b_simd::Params; + use ff::{Field, PrimeField}; + use rand_core::OsRng; + + use zcash_proofs::prover::LocalTxProver; + + use zcash_primitives::{ + consensus::{BranchId, TestNetwork}, + extensions::transparent::{self as tze, Extension, FromPayload, ToPayload}, + legacy::TransparentAddress, + merkle_tree::{CommitmentTree, IncrementalWitness}, + primitives::Rseed, + sapling::Node, + transaction::{ + builder::Builder, + components::{Amount, OutPoint, TzeIn, TzeOut}, + Transaction, TransactionData, + }, + zip32::ExtendedSpendingKey, + }; + + use super::{close, open, Context, DemoBuilder, Precondition, Program, Witness}; + + #[test] + fn precondition_open_round_trip() { + let data = vec![7; 32]; + let p = Precondition::from_payload(open::MODE, &data).unwrap(); + assert_eq!(p, Precondition::Open(open::Precondition([7; 32]))); + assert_eq!(p.to_payload(), (open::MODE, data)); + } + + #[test] + fn precondition_close_round_trip() { + let data = vec![7; 32]; + let p = Precondition::from_payload(close::MODE, &data).unwrap(); + assert_eq!(p, Precondition::Close(close::Precondition([7; 32]))); + assert_eq!(p.to_payload(), (close::MODE, data)); + } + + #[test] + fn precondition_rejects_invalid_mode_or_length() { + for mode in 0..3 { + for len in &[31, 33] { + let p = Precondition::from_payload(mode, &vec![7; *len]); + assert!(p.is_err()); + } + } + } + + #[test] + fn witness_open_round_trip() { + let data = vec![7; 32]; + let w = Witness::from_payload(open::MODE, &data).unwrap(); + assert_eq!(w, Witness::Open(open::Witness([7; 32]))); + assert_eq!(w.to_payload(), (open::MODE, data)); + } + + #[test] + fn witness_close_round_trip() { + let data = vec![7; 32]; + let p = Witness::from_payload(close::MODE, &data).unwrap(); + assert_eq!(p, Witness::Close(close::Witness([7; 32]))); + assert_eq!(p.to_payload(), (close::MODE, data)); + } + + #[test] + fn witness_rejects_invalid_mode_or_length() { + for mode in 0..3 { + for len in &[31, 33] { + let p = Witness::from_payload(mode, &vec![7; *len]); + assert!(p.is_err()); + } + } + } + + /// Dummy context + pub struct Ctx<'a> { + pub tx: &'a Transaction, + } + + /// Implementation of required operations for the demo extension, as satisfied + /// by the context. + impl<'a> Context for Ctx<'a> { + fn is_tze_only(&self) -> bool { + self.tx.vin.is_empty() + && self.tx.vout.is_empty() + && self.tx.shielded_spends.is_empty() + && self.tx.shielded_outputs.is_empty() + && self.tx.joinsplits.is_empty() + } + + fn tx_tze_outputs(&self) -> &[TzeOut] { + &self.tx.tze_outputs + } + } + + #[test] + fn demo_program() { + let preimage_1 = [1; 32]; + let preimage_2 = [2; 32]; + + let hash_2 = { + let mut hash = [0; 32]; + hash.copy_from_slice(Params::new().hash_length(32).hash(&preimage_2).as_bytes()); + hash + }; + + let hash_1 = { + let mut hash = [0; 32]; + hash.copy_from_slice( + Params::new() + .hash_length(32) + .to_state() + .update(&preimage_1) + .update(&hash_2) + .finalize() + .as_bytes(), + ); + hash + }; + + // + // Opening transaction + // + + let out_a = TzeOut { + value: Amount::from_u64(1).unwrap(), + precondition: tze::Precondition::from(0, &Precondition::open(hash_1)), + }; + + let mut mtx_a = TransactionData::future(); + mtx_a.tze_outputs.push(out_a); + let tx_a = mtx_a.freeze().unwrap(); + + // + // Transfer + // + + let in_b = TzeIn { + prevout: OutPoint::new(tx_a.txid().0, 0), + witness: tze::Witness::from(0, &Witness::open(preimage_1)), + }; + let out_b = TzeOut { + value: Amount::from_u64(1).unwrap(), + precondition: tze::Precondition::from(0, &Precondition::close(hash_2)), + }; + let mut mtx_b = TransactionData::future(); + mtx_b.tze_inputs.push(in_b); + mtx_b.tze_outputs.push(out_b); + let tx_b = mtx_b.freeze().unwrap(); + + // + // Closing transaction + // + + let in_c = TzeIn { + prevout: OutPoint::new(tx_b.txid().0, 0), + witness: tze::Witness::from(0, &Witness::close(preimage_2)), + }; + + let mut mtx_c = TransactionData::future(); + mtx_c.tze_inputs.push(in_c); + let tx_c = mtx_c.freeze().unwrap(); + + // Verify tx_b + { + let ctx = Ctx { tx: &tx_b }; + assert_eq!( + Program.verify( + &tx_a.tze_outputs[0].precondition, + &tx_b.tze_inputs[0].witness, + &ctx + ), + Ok(()) + ); + } + + // Verify tx_c + { + let ctx = Ctx { tx: &tx_b }; + assert_eq!( + Program.verify( + &tx_b.tze_outputs[0].precondition, + &tx_c.tze_inputs[0].witness, + &ctx + ), + Ok(()) + ); + } + } + + #[test] + fn demo_builder_program() { + let preimage_1 = [1; 32]; + let preimage_2 = [2; 32]; + + // Only run the test if we have the prover parameters. + let prover = match LocalTxProver::with_default_location() { + Some(prover) => prover, + None => return, + }; + + // + // Opening transaction + // + + let mut rng = OsRng; + let mut builder_a: Builder<'_, TestNetwork, OsRng> = Builder::new_future(0); + + // create some inputs to spend + let extsk = ExtendedSpendingKey::master(&[]); + let to = extsk.default_address().unwrap().1; + let note1 = to + .create_note(110000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) + .unwrap(); + let cm1 = Node::new(note1.cmu().to_repr()); + let mut tree = CommitmentTree::new(); + // fake that the note appears in some previous + // shielded output + tree.append(cm1).unwrap(); + let witness1 = IncrementalWitness::from_tree(&tree); + + builder_a + .add_sapling_spend( + extsk.clone(), + *to.diversifier(), + note1.clone(), + witness1.path().unwrap(), + ) + .unwrap(); + + let mut db_a = DemoBuilder { + txn_builder: &mut builder_a, + extension_id: 0, + }; + + let value = Amount::from_u64(100000).unwrap(); + db_a.demo_open(value, preimage_1, preimage_2) + .map_err(|e| format!("open failure: {:?}", e)) + .unwrap(); + let (tx_a, _) = builder_a + .build(BranchId::Canopy, &prover) + .map_err(|e| format!("build failure: {:?}", e)) + .unwrap(); + + // + // Transfer + // + + let mut builder_b: Builder<'_, TestNetwork, OsRng> = Builder::new_future(0); + let mut db_b = DemoBuilder { + txn_builder: &mut builder_b, + extension_id: 0, + }; + let prevout_a = (OutPoint::new(tx_a.txid().0, 0), tx_a.tze_outputs[0].clone()); + let value_xfr = Amount::from_u64(90000).unwrap(); + db_b.demo_transfer_to_close(prevout_a, value_xfr, preimage_1, preimage_2) + .map_err(|e| format!("transfer failure: {:?}", e)) + .unwrap(); + let (tx_b, _) = builder_b + .build(BranchId::Canopy, &prover) + .map_err(|e| format!("build failure: {:?}", e)) + .unwrap(); + + // + // Closing transaction + // + + let mut builder_c: Builder<'_, TestNetwork, OsRng> = Builder::new_future(0); + let mut db_c = DemoBuilder { + txn_builder: &mut builder_c, + extension_id: 0, + }; + let prevout_b = (OutPoint::new(tx_a.txid().0, 0), tx_b.tze_outputs[0].clone()); + db_c.demo_close(prevout_b, preimage_2) + .map_err(|e| format!("close failure: {:?}", e)) + .unwrap(); + + builder_c + .add_transparent_output( + &TransparentAddress::PublicKey([0; 20]), + Amount::from_u64(80000).unwrap(), + ) + .unwrap(); + + let (tx_c, _) = builder_c + .build(BranchId::Canopy, &prover) + .map_err(|e| format!("build failure: {:?}", e)) + .unwrap(); + + // Verify tx_b + let ctx0 = Ctx { tx: &tx_b }; + assert_eq!( + Program.verify( + &tx_a.tze_outputs[0].precondition, + &tx_b.tze_inputs[0].witness, + &ctx0 + ), + Ok(()) + ); + + // Verify tx_c + let ctx1 = Ctx { tx: &tx_b }; + assert_eq!( + Program.verify( + &tx_b.tze_outputs[0].precondition, + &tx_c.tze_inputs[0].witness, + &ctx1 + ), + Ok(()) + ); + } +} diff --git a/zcash_history/Cargo.toml b/zcash_history/Cargo.toml index 8f254e28ed..d25a1347c3 100644 --- a/zcash_history/Cargo.toml +++ b/zcash_history/Cargo.toml @@ -9,7 +9,7 @@ description = "Library for Zcash blockchain history tools" [dev-dependencies] assert_matches = "1.3.0" -quickcheck = "0.8" +quickcheck = "0.9" [dependencies] bigint = "4" diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index b40744db01..0c99a59d92 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash_primitives" description = "Rust implementations of the Zcash primitives" -version = "0.2.0" +version = "0.3.0" authors = [ "Jack Grigg ", ] @@ -11,24 +11,30 @@ readme = "README.md" license = "MIT OR Apache-2.0" edition = "2018" +[package.metadata.docs.rs] +all-features = true + [dependencies] -aes = "0.3" +aes = "0.5" blake2b_simd = "0.5" blake2s_simd = "0.5" +bls12_381 = "0.2" byteorder = "1" -crypto_api_chachapoly = "0.2.1" -ff = { version = "0.6", path = "../ff" } -fpe = "0.2" -hex = "0.3" +crypto_api_chachapoly = "0.4" +equihash = { version = "0.1", path = "../components/equihash" } +ff = "0.7" +fpe = "0.3" +group = "0.7" +hex = "0.4" +jubjub = "0.4" lazy_static = "1" log = "0.4" -pairing = { version = "0.16", path = "../pairing" } rand = "0.7" rand_core = "0.5.1" -ripemd160 = { version = "0.8", optional = true } -secp256k1 = { version = "=0.15.0", optional = true } -sha2 = "0.8" -subtle = "2.2.1" +ripemd160 = { version = "0.9", optional = true } +secp256k1 = { version = "0.17", optional = true } +sha2 = "0.9" +subtle = "2.2.3" [dev-dependencies] criterion = "0.3" diff --git a/zcash_primitives/benches/pedersen_hash.rs b/zcash_primitives/benches/pedersen_hash.rs index 6510936021..86cd2366ee 100644 --- a/zcash_primitives/benches/pedersen_hash.rs +++ b/zcash_primitives/benches/pedersen_hash.rs @@ -1,11 +1,8 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use pairing::bls12_381::Bls12; use rand_core::{OsRng, RngCore}; -use zcash_primitives::jubjub::JubjubBls12; use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization}; fn bench_pedersen_hash(c: &mut Criterion) { - let params = JubjubBls12::new(); let rng = &mut OsRng; let bits = (0..510) .map(|_| (rng.next_u32() % 2) != 0) @@ -13,7 +10,7 @@ fn bench_pedersen_hash(c: &mut Criterion) { let personalization = Personalization::MerkleTree(31); c.bench_function("Pedersen hash", |b| { - b.iter(|| pedersen_hash::(personalization, bits.clone(), ¶ms)) + b.iter(|| pedersen_hash(personalization, bits.clone())) }); } diff --git a/zcash_primitives/src/block.rs b/zcash_primitives/src/block.rs index 8432cd4c93..fd1edbc032 100644 --- a/zcash_primitives/src/block.rs +++ b/zcash_primitives/src/block.rs @@ -9,7 +9,7 @@ use std::ops::Deref; use crate::serialize::Vector; -pub mod equihash; +pub use equihash; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BlockHash(pub [u8; 32]); diff --git a/zcash_primitives/src/block/equihash.rs b/zcash_primitives/src/block/equihash.rs deleted file mode 100644 index 38518baa05..0000000000 --- a/zcash_primitives/src/block/equihash.rs +++ /dev/null @@ -1,469 +0,0 @@ -//! Verification functions for the [Equihash] proof-of-work algorithm. -//! -//! [Equihash]: https://zips.z.cash/protocol/protocol.pdf#equihash - -use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams, State as Blake2bState}; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; -use log::error; -use std::io::Cursor; -use std::mem::size_of; - -struct Params { - n: u32, - k: u32, -} - -#[derive(Clone)] -struct Node { - hash: Vec, - indices: Vec, -} - -impl Params { - fn indices_per_hash_output(&self) -> u32 { - 512 / self.n - } - fn hash_output(&self) -> u8 { - (self.indices_per_hash_output() * self.n / 8) as u8 - } - fn collision_bit_length(&self) -> usize { - (self.n / (self.k + 1)) as usize - } - fn collision_byte_length(&self) -> usize { - (self.collision_bit_length() + 7) / 8 - } - fn hash_length(&self) -> usize { - ((self.k as usize) + 1) * self.collision_byte_length() - } -} - -impl Node { - fn new(p: &Params, state: &Blake2bState, i: u32) -> Self { - let hash = generate_hash(state, i / p.indices_per_hash_output()); - let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize; - let end = start + (p.n as usize) / 8; - Node { - hash: expand_array(&hash.as_bytes()[start..end], p.collision_bit_length(), 0), - indices: vec![i], - } - } - - fn from_children(a: Node, b: Node, trim: usize) -> Self { - let hash: Vec<_> = a - .hash - .iter() - .zip(b.hash.iter()) - .skip(trim) - .map(|(a, b)| a ^ b) - .collect(); - let indices = if a.indices_before(&b) { - let mut indices = a.indices; - indices.extend(b.indices.iter()); - indices - } else { - let mut indices = b.indices; - indices.extend(a.indices.iter()); - indices - }; - Node { hash, indices } - } - - fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self { - let hash: Vec<_> = a - .hash - .iter() - .zip(b.hash.iter()) - .skip(trim) - .map(|(a, b)| a ^ b) - .collect(); - let mut indices = Vec::with_capacity(a.indices.len() + b.indices.len()); - if a.indices_before(b) { - indices.extend(a.indices.iter()); - indices.extend(b.indices.iter()); - } else { - indices.extend(b.indices.iter()); - indices.extend(a.indices.iter()); - } - Node { hash, indices } - } - - fn indices_before(&self, other: &Node) -> bool { - // Indices are serialized in big-endian so that integer - // comparison is equivalent to array comparison - self.indices[0] < other.indices[0] - } - - fn is_zero(&self, len: usize) -> bool { - self.hash.iter().take(len).all(|v| *v == 0) - } -} - -fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2bState { - let mut personalization: Vec = Vec::from("ZcashPoW"); - personalization.write_u32::(n).unwrap(); - personalization.write_u32::(k).unwrap(); - - Blake2bParams::new() - .hash_length(digest_len as usize) - .personal(&personalization) - .to_state() -} - -fn generate_hash(base_state: &Blake2bState, i: u32) -> Blake2bHash { - let mut lei = [0u8; 4]; - (&mut lei[..]).write_u32::(i).unwrap(); - - let mut state = base_state.clone(); - state.update(&lei); - state.finalize() -} - -fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { - assert!(bit_len >= 8); - assert!(8 * size_of::() >= 7 + bit_len); - - let out_width = (bit_len + 7) / 8 + byte_pad; - let out_len = 8 * out_width * vin.len() / bit_len; - - // Shortcut for parameters where expansion is a no-op - if out_len == vin.len() { - return vin.to_vec(); - } - - let mut vout: Vec = vec![0; out_len]; - let bit_len_mask: u32 = (1 << bit_len) - 1; - - // The acc_bits least-significant bits of acc_value represent a bit sequence - // in big-endian order. - let mut acc_bits = 0; - let mut acc_value: u32 = 0; - - let mut j = 0; - for b in vin { - acc_value = (acc_value << 8) | u32::from(*b); - acc_bits += 8; - - // When we have bit_len or more bits in the accumulator, write the next - // output element. - if acc_bits >= bit_len { - acc_bits -= bit_len; - for x in byte_pad..out_width { - 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; - } - j += out_width; - } - } - - vout -} - -fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec { - assert!(((c_bit_len + 1) + 7) / 8 <= size_of::()); - let len_indices = 8 * size_of::() * minimal.len() / (c_bit_len + 1); - let byte_pad = size_of::() - ((c_bit_len + 1) + 7) / 8; - - let mut csr = Cursor::new(expand_array(minimal, c_bit_len + 1, byte_pad)); - let mut ret = Vec::with_capacity(len_indices); - - // Big-endian so that lexicographic array comparison is equivalent to integer - // comparison - while let Ok(i) = csr.read_u32::() { - ret.push(i); - } - - ret -} - -fn has_collision(a: &Node, b: &Node, len: usize) -> bool { - a.hash - .iter() - .zip(b.hash.iter()) - .take(len) - .all(|(a, b)| a == b) -} - -fn distinct_indices(a: &Node, b: &Node) -> bool { - for i in &(a.indices) { - for j in &(b.indices) { - if i == j { - return false; - } - } - } - true -} - -fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> bool { - if !has_collision(a, b, p.collision_byte_length()) { - error!("Invalid solution: invalid collision length between StepRows"); - false - } else if b.indices_before(a) { - error!("Invalid solution: Index tree incorrectly ordered"); - false - } else if !distinct_indices(a, b) { - error!("Invalid solution: duplicate indices"); - false - } else { - true - } -} - -pub fn is_valid_solution_iterative( - n: u32, - k: u32, - input: &[u8], - nonce: &[u8], - indices: &[u32], -) -> bool { - let p = Params { n, k }; - - let mut state = initialise_state(p.n, p.k, p.hash_output()); - state.update(input); - state.update(nonce); - - let mut rows = Vec::new(); - for i in indices { - rows.push(Node::new(&p, &state, *i)); - } - - let mut hash_len = p.hash_length(); - while rows.len() > 1 { - let mut cur_rows = Vec::new(); - for pair in rows.chunks(2) { - let a = &pair[0]; - let b = &pair[1]; - if !validate_subtrees(&p, a, b) { - return false; - } - cur_rows.push(Node::from_children_ref(a, b, p.collision_byte_length())); - } - rows = cur_rows; - hash_len -= p.collision_byte_length(); - } - - assert!(rows.len() == 1); - rows[0].is_zero(hash_len) -} - -fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Option { - if indices.len() > 1 { - let end = indices.len(); - let mid = end / 2; - match ( - tree_validator(p, state, &indices[0..mid]), - tree_validator(p, state, &indices[mid..end]), - ) { - (Some(a), Some(b)) => { - if validate_subtrees(p, &a, &b) { - Some(Node::from_children(a, b, p.collision_byte_length())) - } else { - None - } - } - _ => None, - } - } else { - Some(Node::new(&p, &state, indices[0])) - } -} - -pub fn is_valid_solution_recursive( - n: u32, - k: u32, - input: &[u8], - nonce: &[u8], - indices: &[u32], -) -> bool { - let p = Params { n, k }; - - let mut state = initialise_state(p.n, p.k, p.hash_output()); - state.update(input); - state.update(nonce); - - match tree_validator(&p, &state, indices) { - Some(root) => { - // Hashes were trimmed, so only need to check remaining length - root.is_zero(p.collision_byte_length()) - } - None => false, - } -} - -pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool { - let p = Params { n, k }; - let indices = indices_from_minimal(soln, p.collision_bit_length()); - - // Recursive validation is faster - is_valid_solution_recursive(n, k, input, nonce, &indices) -} - -#[cfg(test)] -mod tests { - use super::is_valid_solution_iterative; - use super::is_valid_solution_recursive; - - fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], indices: &[u32]) -> bool { - let a = is_valid_solution_iterative(n, k, input, nonce, indices); - let b = is_valid_solution_recursive(n, k, input, nonce, indices); - assert!(a == b); - a - } - - #[test] - fn equihash_test_cases() { - let input = b"block header"; - let mut nonce = [0 as u8; 32]; - let mut indices = vec![ - 976, 126621, 100174, 123328, 38477, 105390, 38834, 90500, 6411, 116489, 51107, 129167, - 25557, 92292, 38525, 56514, 1110, 98024, 15426, 74455, 3185, 84007, 24328, 36473, - 17427, 129451, 27556, 119967, 31704, 62448, 110460, 117894, - ]; - assert!(is_valid_solution(96, 5, input, &nonce, &indices)); - - indices = vec![ - 1008, 18280, 34711, 57439, 3903, 104059, 81195, 95931, 58336, 118687, 67931, 123026, - 64235, 95595, 84355, 122946, 8131, 88988, 45130, 58986, 59899, 78278, 94769, 118158, - 25569, 106598, 44224, 96285, 54009, 67246, 85039, 127667, - ]; - assert!(is_valid_solution(96, 5, input, &nonce, &indices)); - - indices = vec![ - 4313, 223176, 448870, 1692641, 214911, 551567, 1696002, 1768726, 500589, 938660, - 724628, 1319625, 632093, 1474613, 665376, 1222606, 244013, 528281, 1741992, 1779660, - 313314, 996273, 435612, 1270863, 337273, 1385279, 1031587, 1147423, 349396, 734528, - 902268, 1678799, 10902, 1231236, 1454381, 1873452, 120530, 2034017, 948243, 1160178, - 198008, 1704079, 1087419, 1734550, 457535, 698704, 649903, 1029510, 75564, 1860165, - 1057819, 1609847, 449808, 527480, 1106201, 1252890, 207200, 390061, 1557573, 1711408, - 396772, 1026145, 652307, 1712346, 10680, 1027631, 232412, 974380, 457702, 1827006, - 1316524, 1400456, 91745, 2032682, 192412, 710106, 556298, 1963798, 1329079, 1504143, - 102455, 974420, 639216, 1647860, 223846, 529637, 425255, 680712, 154734, 541808, - 443572, 798134, 322981, 1728849, 1306504, 1696726, 57884, 913814, 607595, 1882692, - 236616, 1439683, 420968, 943170, 1014827, 1446980, 1468636, 1559477, 1203395, 1760681, - 1439278, 1628494, 195166, 198686, 349906, 1208465, 917335, 1361918, 937682, 1885495, - 494922, 1745948, 1320024, 1826734, 847745, 894084, 1484918, 1523367, 7981, 1450024, - 861459, 1250305, 226676, 329669, 339783, 1935047, 369590, 1564617, 939034, 1908111, - 1147449, 1315880, 1276715, 1428599, 168956, 1442649, 766023, 1171907, 273361, 1902110, - 1169410, 1786006, 413021, 1465354, 707998, 1134076, 977854, 1604295, 1369720, 1486036, - 330340, 1587177, 502224, 1313997, 400402, 1667228, 889478, 946451, 470672, 2019542, - 1023489, 2067426, 658974, 876859, 794443, 1667524, 440815, 1099076, 897391, 1214133, - 953386, 1932936, 1100512, 1362504, 874364, 975669, 1277680, 1412800, 1227580, 1857265, - 1312477, 1514298, 12478, 219890, 534265, 1351062, 65060, 651682, 627900, 1331192, - 123915, 865936, 1218072, 1732445, 429968, 1097946, 947293, 1323447, 157573, 1212459, - 923792, 1943189, 488881, 1697044, 915443, 2095861, 333566, 732311, 336101, 1600549, - 575434, 1978648, 1071114, 1473446, 50017, 54713, 367891, 2055483, 561571, 1714951, - 715652, 1347279, 584549, 1642138, 1002587, 1125289, 1364767, 1382627, 1387373, 2054399, - 97237, 1677265, 707752, 1265819, 121088, 1810711, 1755448, 1858538, 444653, 1130822, - 514258, 1669752, 578843, 729315, 1164894, 1691366, 15609, 1917824, 173620, 587765, - 122779, 2024998, 804857, 1619761, 110829, 1514369, 410197, 493788, 637666, 1765683, - 782619, 1186388, 494761, 1536166, 1582152, 1868968, 825150, 1709404, 1273757, 1657222, - 817285, 1955796, 1014018, 1961262, 873632, 1689675, 985486, 1008905, 130394, 897076, - 419669, 535509, 980696, 1557389, 1244581, 1738170, 197814, 1879515, 297204, 1165124, - 883018, 1677146, 1545438, 2017790, 345577, 1821269, 761785, 1014134, 746829, 751041, - 930466, 1627114, 507500, 588000, 1216514, 1501422, 991142, 1378804, 1797181, 1976685, - 60742, 780804, 383613, 645316, 770302, 952908, 1105447, 1878268, 504292, 1961414, - 693833, 1198221, 906863, 1733938, 1315563, 2049718, 230826, 2064804, 1224594, 1434135, - 897097, 1961763, 993758, 1733428, 306643, 1402222, 532661, 627295, 453009, 973231, - 1746809, 1857154, 263652, 1683026, 1082106, 1840879, 768542, 1056514, 888164, 1529401, - 327387, 1708909, 961310, 1453127, 375204, 878797, 1311831, 1969930, 451358, 1229838, - 583937, 1537472, 467427, 1305086, 812115, 1065593, 532687, 1656280, 954202, 1318066, - 1164182, 1963300, 1232462, 1722064, 17572, 923473, 1715089, 2079204, 761569, 1557392, - 1133336, 1183431, 175157, 1560762, 418801, 927810, 734183, 825783, 1844176, 1951050, - 317246, 336419, 711727, 1630506, 634967, 1595955, 683333, 1461390, 458765, 1834140, - 1114189, 1761250, 459168, 1897513, 1403594, 1478683, 29456, 1420249, 877950, 1371156, - 767300, 1848863, 1607180, 1819984, 96859, 1601334, 171532, 2068307, 980009, 2083421, - 1329455, 2030243, 69434, 1965626, 804515, 1339113, 396271, 1252075, 619032, 2080090, - 84140, 658024, 507836, 772757, 154310, 1580686, 706815, 1024831, 66704, 614858, 256342, - 957013, 1488503, 1615769, 1515550, 1888497, 245610, 1333432, 302279, 776959, 263110, - 1523487, 623933, 2013452, 68977, 122033, 680726, 1849411, 426308, 1292824, 460128, - 1613657, 234271, 971899, 1320730, 1559313, 1312540, 1837403, 1690310, 2040071, 149918, - 380012, 785058, 1675320, 267071, 1095925, 1149690, 1318422, 361557, 1376579, 1587551, - 1715060, 1224593, 1581980, 1354420, 1850496, 151947, 748306, 1987121, 2070676, 273794, - 981619, 683206, 1485056, 766481, 2047708, 930443, 2040726, 1136227, 1945705, 1722044, - 1971986, - ]; - assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); - assert!(is_valid_solution(200, 9, input, &nonce, &indices)); - - nonce[0] = 1; - assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); - assert!(!is_valid_solution(200, 9, input, &nonce, &indices)); - - indices = vec![ - 1911, 96020, 94086, 96830, 7895, 51522, 56142, 62444, 15441, 100732, 48983, 64776, - 27781, 85932, 101138, 114362, 4497, 14199, 36249, 41817, 23995, 93888, 35798, 96337, - 5530, 82377, 66438, 85247, 39332, 78978, 83015, 123505, - ]; - assert!(is_valid_solution(96, 5, input, &nonce, &indices)); - - indices = vec![ - 1505, 1380774, 200806, 1787044, 101056, 1697952, 281464, 374899, 263712, 1532496, - 264180, 637056, 734225, 1882676, 1112004, 2093109, 193394, 1459136, 525171, 657480, - 214528, 1221365, 574444, 594726, 501919, 1309358, 1740268, 1989610, 654491, 1068055, - 919416, 1993208, 17599, 1858176, 1315176, 1901532, 108258, 109600, 1117445, 1936058, - 70247, 1036984, 628234, 1800109, 149791, 365740, 345683, 563554, 21678, 822781, - 1423722, 1644228, 792912, 1409641, 805060, 2041985, 453824, 1003179, 934427, 1068834, - 629003, 1456111, 670049, 1558594, 19016, 1343657, 1698188, 1865216, 45723, 1820952, - 1160970, 1585983, 422549, 1973097, 1296271, 2006382, 650084, 809838, 871727, 1080419, - 28500, 1471829, 384406, 619459, 212041, 1466258, 481435, 866461, 145340, 1403843, - 1339592, 1405761, 163425, 1073771, 285027, 1488210, 167744, 1182267, 1354059, 2089602, - 921700, 2059931, 1704721, 1853088, 585171, 739246, 747551, 1520527, 590255, 1175747, - 705292, 998433, 522014, 1931179, 1629531, 1692879, 588830, 1799457, 963672, 1664237, - 775408, 1926741, 907030, 1466738, 784179, 1972599, 1494787, 1598114, 1736, 1039487, - 88704, 1302687, 579526, 1476728, 1677992, 1854526, 432470, 2062305, 1471132, 1747579, - 1521894, 1917599, 1590975, 1936227, 151871, 1999775, 224664, 461809, 704084, 1306665, - 1316156, 1529628, 876811, 2086004, 1986383, 2012147, 1039505, 1637502, 1432721, - 1565477, 110385, 342650, 659137, 1285167, 367416, 2007586, 445677, 2084877, 285692, - 1144365, 988840, 1990372, 748425, 1617758, 1267712, 1510433, 152291, 1256291, 1722179, - 1995439, 864844, 1623380, 1071853, 1731862, 699978, 1407662, 1048047, 1849702, 962900, - 1083340, 1378752, 1534902, 11843, 115329, 454796, 548919, 148184, 1686936, 862432, - 873854, 60753, 999864, 385959, 1528101, 534420, 678401, 590419, 1962518, 54984, - 1141820, 243305, 1349970, 599681, 1817233, 1632537, 1698724, 580004, 673073, 1403350, - 2026104, 758881, 970056, 1717966, 2062827, 19624, 148580, 609748, 1588928, 456321, - 834920, 700532, 1682606, 20012, 441139, 1591072, 1923394, 194034, 1741063, 1156906, - 1983067, 20703, 1939972, 604581, 963600, 128170, 731716, 606773, 1626824, 139460, - 1386775, 521911, 2043473, 392180, 449532, 895678, 1453340, 7085, 598416, 1514260, - 2061068, 279532, 678363, 943255, 1405306, 119114, 2075865, 592839, 1972064, 254647, - 2078288, 946282, 1567138, 120422, 767626, 213242, 448366, 438457, 1768467, 853790, - 1509505, 735780, 1979631, 1461410, 1462050, 739008, 1572606, 920754, 1507358, 12883, - 1681167, 1308399, 1839490, 85599, 1387522, 703262, 1949514, 18523, 1236125, 669105, - 1464132, 68670, 2085647, 333393, 1731573, 21714, 637827, 985912, 2091029, 84065, - 1688993, 1574405, 1899543, 134032, 179206, 671016, 1118310, 288960, 861994, 622074, - 1738892, 10936, 343910, 598016, 1741971, 586348, 1956071, 851053, 1715626, 531385, - 1213667, 1093995, 1863757, 630365, 1851894, 1328101, 1770446, 31900, 734027, 1078651, - 1701535, 123276, 1916343, 581822, 1681706, 573135, 818091, 1454710, 2052521, 1150284, - 1451159, 1482280, 1811430, 26321, 785837, 877980, 2073103, 107324, 727248, 1785460, - 1840517, 184560, 185640, 364103, 1878753, 518459, 1984029, 964109, 1884200, 74003, - 527272, 516232, 711247, 148582, 209254, 634610, 1534140, 376714, 1573267, 421225, - 1265101, 1078858, 1374310, 1806283, 2091298, 23392, 389637, 413663, 1066737, 226164, - 762552, 1048220, 1583397, 40092, 277435, 775449, 1533894, 202582, 390703, 346741, - 1027320, 523034, 809424, 584882, 1296934, 528062, 733331, 1212771, 1958651, 653372, - 1313962, 1366332, 1784489, 1542466, 1580386, 1628948, 2000957, 57069, 1398636, 1250431, - 1698486, 57289, 596009, 582428, 966130, 167657, 1025537, 1227498, 1630134, 234060, - 1285209, 265623, 1165779, 68485, 632055, 96019, 1854676, 98410, 158575, 168035, - 1296171, 158847, 1243959, 977212, 1113647, 363568, 891940, 954593, 1987111, 90101, - 133251, 1136222, 1255117, 543075, 732768, 749576, 1174878, 422226, 1854657, 1143029, - 1457135, 927105, 1137382, 1566306, 1661926, 103057, 425126, 698089, 1774942, 911019, - 1793511, 1623559, 2002409, 457796, 1196971, 724257, 1811147, 956269, 1165590, 1137531, - 1381215, 201063, 1938529, 986021, 1297857, 921334, 1259083, 1440074, 1939366, 232907, - 747213, 1349009, 1945364, 689906, 1116453, 1904207, 1916192, 229793, 1576982, 1420059, - 1644978, 278248, 2024807, 297914, 419798, 555747, 712605, 1012424, 1428921, 890113, - 1822645, 1082368, 1392894, - ]; - assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); - assert!(is_valid_solution(200, 9, input, &nonce, &indices)); - - let input2 = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem."; - indices = vec![ - 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, - 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, - 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, - ]; - assert!(is_valid_solution(96, 5, input2, &nonce, &indices)); - } -} diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 8a7385c7e7..cba33e4524 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -1,4 +1,4 @@ -//! Consensus parameters. +//! Consensus logic and parameters. use std::convert::TryFrom; use std::fmt; @@ -25,7 +25,8 @@ impl Parameters for MainNetwork { NetworkUpgrade::Overwinter => Some(347_500), NetworkUpgrade::Sapling => Some(419_200), NetworkUpgrade::Blossom => Some(653_600), - NetworkUpgrade::Heartwood => None, + NetworkUpgrade::Heartwood => Some(903_000), + NetworkUpgrade::Canopy => Some(1_046_400), } } } @@ -40,7 +41,8 @@ impl Parameters for TestNetwork { NetworkUpgrade::Overwinter => Some(207_500), NetworkUpgrade::Sapling => Some(280_000), NetworkUpgrade::Blossom => Some(584_000), - NetworkUpgrade::Heartwood => None, + NetworkUpgrade::Heartwood => Some(903_800), + NetworkUpgrade::Canopy => Some(1_028_500), } } } @@ -67,6 +69,10 @@ pub enum NetworkUpgrade { /// /// [Heartwood]: https://z.cash/upgrade/heartwood/ Heartwood, + /// The [Canopy] network upgrade. + /// + /// [Canopy]: https://z.cash/upgrade/canopy/ + Canopy, } impl fmt::Display for NetworkUpgrade { @@ -76,6 +82,7 @@ impl fmt::Display for NetworkUpgrade { NetworkUpgrade::Sapling => write!(f, "Sapling"), NetworkUpgrade::Blossom => write!(f, "Blossom"), NetworkUpgrade::Heartwood => write!(f, "Heartwood"), + NetworkUpgrade::Canopy => write!(f, "Canopy"), } } } @@ -87,6 +94,7 @@ impl NetworkUpgrade { NetworkUpgrade::Sapling => BranchId::Sapling, NetworkUpgrade::Blossom => BranchId::Blossom, NetworkUpgrade::Heartwood => BranchId::Heartwood, + NetworkUpgrade::Canopy => BranchId::Canopy, } } } @@ -100,8 +108,11 @@ const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[ NetworkUpgrade::Sapling, NetworkUpgrade::Blossom, NetworkUpgrade::Heartwood, + NetworkUpgrade::Canopy, ]; +pub const ZIP212_GRACE_PERIOD: u32 = 32256; + /// A globally-unique identifier for a set of consensus rules within the Zcash chain. /// /// Each branch ID in this enum corresponds to one of the epochs between a pair of Zcash @@ -127,6 +138,8 @@ pub enum BranchId { Blossom, /// The consensus rules deployed by [`NetworkUpgrade::Heartwood`]. Heartwood, + /// The consensus rules deployed by [`NetworkUpgrade::Canopy`]. + Canopy, } impl TryFrom for BranchId { @@ -139,6 +152,7 @@ impl TryFrom for BranchId { 0x76b8_09bb => Ok(BranchId::Sapling), 0x2bb4_0e60 => Ok(BranchId::Blossom), 0xf5b9_230b => Ok(BranchId::Heartwood), + 0xe9ff_75a6 => Ok(BranchId::Canopy), _ => Err("Unknown consensus branch ID"), } } @@ -152,6 +166,7 @@ impl From for u32 { BranchId::Sapling => 0x76b8_09bb, BranchId::Blossom => 0x2bb4_0e60, BranchId::Heartwood => 0xf5b9_230b, + BranchId::Canopy => 0xe9ff_75a6, } } } @@ -229,9 +244,17 @@ mod tests { BranchId::for_height::(419_200), BranchId::Sapling, ); + assert_eq!( + BranchId::for_height::(903_000), + BranchId::Heartwood, + ); + assert_eq!( + BranchId::for_height::(1_046_400), + BranchId::Canopy, + ); assert_eq!( BranchId::for_height::(5_000_000), - BranchId::Blossom, + BranchId::Canopy, ); } } diff --git a/zcash_primitives/src/constants.rs b/zcash_primitives/src/constants.rs index 37d337ddb9..ce8965af76 100644 --- a/zcash_primitives/src/constants.rs +++ b/zcash_primitives/src/constants.rs @@ -1,5 +1,10 @@ //! Various constants used by the Zcash primitives. +use ff::PrimeField; +use group::Group; +use jubjub::SubgroupPoint; +use lazy_static::lazy_static; + /// First 64 bytes of the BLAKE2s input during group hash. /// This is chosen to be some random string that we couldn't have anticipated when we designed /// the algorithm, for rigidity purposes. @@ -32,3 +37,400 @@ pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_cv"; /// BLAKE2s Personalization for the nullifier position generator (for computing rho) pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_J_"; + +/// The prover will demonstrate knowledge of discrete log with respect to this base when +/// they are constructing a proof, in order to authorize proof construction. +pub const PROOF_GENERATION_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x3af2_dbef_b96e_2571, + 0xadf2_d038_f2fb_b820, + 0x7043_03f1_e890_6081, + 0x1457_a502_31cd_e2df, + ]), + bls12_381::Scalar::from_raw([ + 0x467a_f9f7_e05d_e8e7, + 0x50df_51ea_f5a1_49d2, + 0xdec9_0184_0f49_48cc, + 0x54b6_d107_18df_2a7a, + ]), +); + +/// The note commitment is randomized over this generator. +pub const NOTE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0xa514_3b34_a8e3_6462, + 0xf091_9d06_ffb1_ecda, + 0xa140_9aa1_f33b_ec2c, + 0x26eb_9f8a_9ec7_2a8c, + ]), + bls12_381::Scalar::from_raw([ + 0xd4fc_6365_796c_77ac, + 0x96b7_8bea_fa9c_c44c, + 0x949d_7747_6e26_2c95, + 0x114b_7501_ad10_4c57, + ]), +); + +/// The node commitment is randomized again by the position in order to supply the +/// nullifier computation with a unique input w.r.t. the note being spent, to prevent +/// Faerie gold attacks. +pub const NULLIFIER_POSITION_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x2ce3_3921_888d_30db, + 0xe81c_ee09_a561_229e, + 0xdb56_b6db_8d80_75ed, + 0x2400_c2e2_e336_2644, + ]), + bls12_381::Scalar::from_raw([ + 0xa3f7_fa36_c72b_0065, + 0xe155_b8e8_ffff_2e42, + 0xfc9e_8a15_a096_ba8f, + 0x6136_9d54_40bf_84a5, + ]), +); + +/// The value commitment is used to check balance between inputs and outputs. The value is +/// placed over this generator. +pub const VALUE_COMMITMENT_VALUE_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x3618_3b2c_b4d7_ef51, + 0x9472_c89a_c043_042d, + 0xd861_8ed1_d15f_ef4e, + 0x273f_910d_9ecc_1615, + ]), + bls12_381::Scalar::from_raw([ + 0xa77a_81f5_0667_c8d7, + 0xbc33_32d0_fa1c_cd18, + 0xd322_94fd_8977_4ad6, + 0x466a_7e3a_82f6_7ab1, + ]), +); + +/// The value commitment is randomized over this generator, for privacy. +pub const VALUE_COMMITMENT_RANDOMNESS_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x3bce_3b77_9366_4337, + 0xd1d8_da41_af03_744e, + 0x7ff6_826a_d580_04b4, + 0x6800_f4fa_0f00_1cfc, + ]), + bls12_381::Scalar::from_raw([ + 0x3cae_fab9_380b_6a8b, + 0xad46_f1b0_473b_803b, + 0xe6fb_2a6e_1e22_ab50, + 0x6d81_d3a9_cb45_dedb, + ]), +); + +/// The spender proves discrete log with respect to this base at spend time. +pub const SPENDING_KEY_GENERATOR: SubgroupPoint = SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x47bf_4692_0a95_a753, + 0xd5b9_a7d3_ef8e_2827, + 0xd418_a7ff_2675_3b6a, + 0x0926_d4f3_2059_c712, + ]), + bls12_381::Scalar::from_raw([ + 0x3056_32ad_aaf2_b530, + 0x6d65_674d_cedb_ddbc, + 0x53bb_37d0_c21c_fd05, + 0x57a1_019e_6de9_b675, + ]), +); + +/// The generators (for each segment) used in all Pedersen commitments. +pub const PEDERSEN_HASH_GENERATORS: &[SubgroupPoint] = &[ + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x194e_4292_6f66_1b51, + 0x2f0c_718f_6f0f_badd, + 0xb5ea_25de_7ec0_e378, + 0x73c0_16a4_2ded_9578, + ]), + bls12_381::Scalar::from_raw([ + 0x77bf_abd4_3224_3cca, + 0xf947_2e8b_c04e_4632, + 0x79c9_166b_837e_dc5e, + 0x289e_87a2_d352_1b57, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0xb981_9dc8_2d90_607e, + 0xa361_ee3f_d48f_df77, + 0x52a3_5a8c_1908_dd87, + 0x15a3_6d1f_0f39_0d88, + ]), + bls12_381::Scalar::from_raw([ + 0x7b0d_c53c_4ebf_1891, + 0x1f3a_beeb_98fa_d3e8, + 0xf789_1142_c001_d925, + 0x015d_8c7f_5b43_fe33, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x76d6_f7c2_b67f_c475, + 0xbae8_e5c4_6641_ae5c, + 0xeb69_ae39_f5c8_4210, + 0x6643_21a5_8246_e2f6, + ]), + bls12_381::Scalar::from_raw([ + 0x80ed_502c_9793_d457, + 0x8bb2_2a7f_1784_b498, + 0xe000_a46c_8e8c_e853, + 0x362e_1500_d24e_ee9e, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x4c76_7804_c1c4_a2cc, + 0x7d02_d50e_654b_87f2, + 0xedc5_f4a9_cff2_9fd5, + 0x323a_6548_ce9d_9876, + ]), + bls12_381::Scalar::from_raw([ + 0x8471_4bec_a335_70e9, + 0x5103_afa1_a11f_6a85, + 0x9107_0acb_d8d9_47b7, + 0x2f7e_e40c_4b56_cad8, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0x4680_9430_657f_82d1, + 0xefd5_9313_05f2_f0bf, + 0x89b6_4b4e_0336_2796, + 0x3bd2_6660_00b5_4796, + ]), + bls12_381::Scalar::from_raw([ + 0x9996_8299_c365_8aef, + 0xb3b9_d809_5859_d14c, + 0x3978_3238_1406_c9e5, + 0x494b_c521_03ab_9d0a, + ]), + ), + SubgroupPoint::from_raw_unchecked( + bls12_381::Scalar::from_raw([ + 0xcb3c_0232_58d3_2079, + 0x1d9e_5ca2_1135_ff6f, + 0xda04_9746_d76d_3ee5, + 0x6344_7b2b_a31b_b28a, + ]), + bls12_381::Scalar::from_raw([ + 0x4360_8211_9f8d_629a, + 0xa802_00d2_c66b_13a7, + 0x64cd_b107_0a13_6a28, + 0x64ec_4689_e8bf_b6e5, + ]), + ), +]; + +/// The maximum number of chunks per segment of the Pedersen hash. +pub const PEDERSEN_HASH_CHUNKS_PER_GENERATOR: usize = 63; + +/// The window size for exponentiation of Pedersen hash generators outside the circuit. +pub const PEDERSEN_HASH_EXP_WINDOW_SIZE: u32 = 8; + +lazy_static! { + /// The exp table for [`PEDERSEN_HASH_GENERATORS`]. + pub static ref PEDERSEN_HASH_EXP_TABLE: Vec>> = + generate_pedersen_hash_exp_table(); +} + +/// Creates the exp table for the Pedersen hash generators. +fn generate_pedersen_hash_exp_table() -> Vec>> { + let window = PEDERSEN_HASH_EXP_WINDOW_SIZE; + + PEDERSEN_HASH_GENERATORS + .iter() + .cloned() + .map(|mut g| { + let mut tables = vec![]; + + let mut num_bits = 0; + while num_bits <= jubjub::Fr::NUM_BITS { + let mut table = Vec::with_capacity(1 << window); + let mut base = SubgroupPoint::identity(); + + for _ in 0..(1 << window) { + table.push(base.clone()); + base += g; + } + + tables.push(table); + num_bits += window; + + for _ in 0..window { + g = g.double(); + } + } + + tables + }) + .collect() +} + +#[cfg(test)] +mod tests { + use jubjub::SubgroupPoint; + + use super::*; + use crate::group_hash::group_hash; + + fn find_group_hash(m: &[u8], personalization: &[u8; 8]) -> SubgroupPoint { + let mut tag = m.to_vec(); + let i = tag.len(); + tag.push(0u8); + + loop { + let gh = group_hash(&tag, personalization); + + // We don't want to overflow and start reusing generators + assert!(tag[i] != u8::max_value()); + tag[i] += 1; + + if let Some(gh) = gh { + break gh; + } + } + } + + #[test] + fn proof_generation_key_base_generator() { + assert_eq!( + find_group_hash(&[], PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION), + PROOF_GENERATION_KEY_GENERATOR, + ); + } + + #[test] + fn note_commitment_randomness_generator() { + assert_eq!( + find_group_hash(b"r", PEDERSEN_HASH_GENERATORS_PERSONALIZATION), + NOTE_COMMITMENT_RANDOMNESS_GENERATOR, + ); + } + + #[test] + fn nullifier_position_generator() { + assert_eq!( + find_group_hash(&[], NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION), + NULLIFIER_POSITION_GENERATOR, + ); + } + + #[test] + fn value_commitment_value_generator() { + assert_eq!( + find_group_hash(b"v", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION), + VALUE_COMMITMENT_VALUE_GENERATOR, + ); + } + + #[test] + fn value_commitment_randomness_generator() { + assert_eq!( + find_group_hash(b"r", VALUE_COMMITMENT_GENERATOR_PERSONALIZATION), + VALUE_COMMITMENT_RANDOMNESS_GENERATOR, + ); + } + + #[test] + fn spending_key_generator() { + assert_eq!( + find_group_hash(&[], SPENDING_KEY_GENERATOR_PERSONALIZATION), + SPENDING_KEY_GENERATOR, + ); + } + + #[test] + fn pedersen_hash_generators() { + for (m, actual) in PEDERSEN_HASH_GENERATORS.iter().enumerate() { + assert_eq!( + &find_group_hash( + &(m as u32).to_le_bytes(), + PEDERSEN_HASH_GENERATORS_PERSONALIZATION + ), + actual + ); + } + } + + #[test] + fn no_duplicate_fixed_base_generators() { + let fixed_base_generators = [ + PROOF_GENERATION_KEY_GENERATOR, + NOTE_COMMITMENT_RANDOMNESS_GENERATOR, + NULLIFIER_POSITION_GENERATOR, + VALUE_COMMITMENT_VALUE_GENERATOR, + VALUE_COMMITMENT_RANDOMNESS_GENERATOR, + SPENDING_KEY_GENERATOR, + ]; + + // Check for duplicates, far worse than spec inconsistencies! + for (i, p1) in fixed_base_generators.iter().enumerate() { + if p1.is_identity().into() { + panic!("Neutral element!"); + } + + for p2 in fixed_base_generators.iter().skip(i + 1) { + if p1 == p2 { + panic!("Duplicate generator!"); + } + } + } + } + + /// Check for simple relations between the generators, that make finding collisions easy; + /// far worse than spec inconsistencies! + fn check_consistency_of_pedersen_hash_generators( + pedersen_hash_generators: &[jubjub::SubgroupPoint], + ) { + for (i, p1) in pedersen_hash_generators.iter().enumerate() { + if p1.is_identity().into() { + panic!("Neutral element!"); + } + for p2 in pedersen_hash_generators.iter().skip(i + 1) { + if p1 == p2 { + panic!("Duplicate generator!"); + } + if *p1 == -p2 { + panic!("Inverse generator!"); + } + } + + // check for a generator being the sum of any other two + for (j, p2) in pedersen_hash_generators.iter().enumerate() { + if j == i { + continue; + } + for (k, p3) in pedersen_hash_generators.iter().enumerate() { + if k == j || k == i { + continue; + } + let sum = p2 + p3; + if sum == *p1 { + panic!("Linear relation between generators!"); + } + } + } + } + } + + #[test] + fn pedersen_hash_generators_consistency() { + check_consistency_of_pedersen_hash_generators(PEDERSEN_HASH_GENERATORS); + } + + #[test] + #[should_panic(expected = "Linear relation between generators!")] + fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() { + let mut pedersen_hash_generators = PEDERSEN_HASH_GENERATORS.to_vec(); + + // Test for linear relation + pedersen_hash_generators.push(PEDERSEN_HASH_GENERATORS[0] + PEDERSEN_HASH_GENERATORS[1]); + + check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators); + } +} diff --git a/zcash_primitives/src/extensions.rs b/zcash_primitives/src/extensions.rs new file mode 100644 index 0000000000..7d540a8bac --- /dev/null +++ b/zcash_primitives/src/extensions.rs @@ -0,0 +1 @@ +pub mod transparent; diff --git a/zcash_primitives/src/extensions/transparent.rs b/zcash_primitives/src/extensions/transparent.rs new file mode 100644 index 0000000000..577fad011a --- /dev/null +++ b/zcash_primitives/src/extensions/transparent.rs @@ -0,0 +1,155 @@ +//! Core traits and structs for Transparent Zcash Extensions. + +use crate::transaction::components::{Amount, OutPoint, TzeOut}; +use std::fmt; + +pub trait FromPayload: Sized { + type Error; + + /// Parses an extension type from a mode and payload. + fn from_payload(mode: u32, payload: &[u8]) -> Result; +} + +pub trait ToPayload { + /// Returns a serialized payload and its corresponding mode. + fn to_payload(&self) -> (u32, Vec); +} + +/// A condition that can be used to encumber transparent funds. +#[derive(Clone, Debug)] +pub struct Precondition { + pub extension_id: u32, + pub mode: u32, + pub payload: Vec, +} + +impl Precondition { + pub fn from(extension_id: u32, value: &P) -> Precondition { + let (mode, payload) = value.to_payload(); + Precondition { + extension_id, + mode, + payload, + } + } + + pub fn try_to(&self) -> Result { + P::from_payload(self.mode, &self.payload) + } +} + +/// Data that satisfies the precondition for prior encumbered funds, enabling them to be +/// spent. +#[derive(Clone, Debug)] +pub struct Witness { + pub extension_id: u32, + pub mode: u32, + pub payload: Vec, +} + +impl Witness { + pub fn from(extension_id: u32, value: &P) -> Witness { + let (mode, payload) = value.to_payload(); + Witness { + extension_id, + mode, + payload, + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Error { + InvalidForEpoch(u32, u32), + InvalidExtensionId(u32), + ProgramError(E), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::InvalidForEpoch(cid, ptype) => write!( + f, + "Program type {} is invalid for consensus branch id {}", + ptype, cid + ), + + Error::InvalidExtensionId(extension_id) => { + write!(f, "Unrecognized program type id {}", extension_id) + } + + Error::ProgramError(err) => write!(f, "Program error: {}", err), + } + } +} + +pub trait Extension { + type P; + type W; + type Error; + + fn verify_inner( + &self, + precondition: &Self::P, + witness: &Self::W, + context: &C, + ) -> Result<(), Self::Error>; + + fn verify( + &self, + precondition: &Precondition, + witness: &Witness, + context: &C, + ) -> Result<(), Self::Error> + where + Self::P: FromPayload, + Self::W: FromPayload, + { + self.verify_inner( + &Self::P::from_payload(precondition.mode, &precondition.payload)?, + &Self::W::from_payload(witness.mode, &witness.payload)?, + &context, + ) + } +} + +// This extension trait is satisfied by the transaction::builder::Builder type. It provides a +// minimal contract for interacting with the transaction builder, that extension library authors +// can use to add extension-specific builder traits that may be used to interact with the +// transaction builder. This may make it simpler for projects that include transaction-builder +// functionality to integrate with third-party extensions without those extensions being coupled to +// a particular transaction or builder representation. +pub trait ExtensionTxBuilder<'a> { + type BuildCtx; + type BuildError; + + fn add_tze_input( + &mut self, + extension_id: u32, + prevout: (OutPoint, TzeOut), + witness_builder: WBuilder, + ) -> Result<(), Self::BuildError> + where + WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result); + + fn add_tze_output( + &mut self, + extension_id: u32, + value: Amount, + guarded_by: &P, + ) -> Result<(), Self::BuildError>; +} + +pub trait Epoch { + type VerifyError; + + // Implementation of this method should check that the provided witness + // satisfies the specified precondition, given the context. This verification + // becomes part of the consensus rules. + fn verify( + &self, + precondition: &Precondition, + witness: &Witness, + ctx: &VerifyCtx, + ) -> Result<(), Error>; +} diff --git a/zcash_primitives/src/group_hash.rs b/zcash_primitives/src/group_hash.rs index 6aac847e86..da1a54e14d 100644 --- a/zcash_primitives/src/group_hash.rs +++ b/zcash_primitives/src/group_hash.rs @@ -2,9 +2,8 @@ //! //! [grouphash]: https://zips.z.cash/protocol/protocol.pdf#concretegrouphashjubjub -use crate::jubjub::{edwards, JubjubEngine, PrimeOrder}; - use ff::PrimeField; +use group::{cofactor::CofactorGroup, Group, GroupEncoding}; use crate::constants; use blake2s_simd::Params; @@ -12,15 +11,11 @@ use blake2s_simd::Params; /// Produces a random point in the Jubjub curve. /// The point is guaranteed to be prime order /// and not the identity. -pub fn group_hash( - tag: &[u8], - personalization: &[u8], - params: &E::Params, -) -> Option> { +pub fn group_hash(tag: &[u8], personalization: &[u8]) -> Option { assert_eq!(personalization.len(), 8); // Check to see that scalar field is 255 bits - assert!(E::Fr::NUM_BITS == 255); + assert!(bls12_381::Scalar::NUM_BITS == 255); let h = Params::new() .hash_length(32) @@ -30,16 +25,18 @@ pub fn group_hash( .update(tag) .finalize(); - match edwards::Point::::read(h.as_ref(), params) { - Ok(p) => { - let p = p.mul_by_cofactor(params); + let p = jubjub::ExtendedPoint::from_bytes(h.as_array()); + if p.is_some().into() { + // ::clear_cofactor is implemented using + // ExtendedPoint::mul_by_cofactor in the jubjub crate. + let p = CofactorGroup::clear_cofactor(&p.unwrap()); - if p != edwards::Point::zero() { - Some(p) - } else { - None - } + if p.is_identity().into() { + None + } else { + Some(p) } - Err(_) => None, + } else { + None } } diff --git a/zcash_primitives/src/jubjub/edwards.rs b/zcash_primitives/src/jubjub/edwards.rs deleted file mode 100644 index 1b3ebc0b65..0000000000 --- a/zcash_primitives/src/jubjub/edwards.rs +++ /dev/null @@ -1,481 +0,0 @@ -use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use subtle::CtOption; - -use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; - -use rand_core::RngCore; - -use std::marker::PhantomData; - -use std::io::{self, Read, Write}; - -// Represents the affine point (X/Z, Y/Z) via the extended -// twisted Edwards coordinates. -// -// See "Twisted Edwards Curves Revisited" -// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson -#[derive(Debug)] -pub struct Point { - x: E::Fr, - y: E::Fr, - t: E::Fr, - z: E::Fr, - _marker: PhantomData, -} - -fn convert_subgroup(from: &Point) -> Point { - Point { - x: from.x, - y: from.y, - t: from.t, - z: from.z, - _marker: PhantomData, - } -} - -impl From<&Point> for Point { - fn from(p: &Point) -> Point { - p.clone() - } -} - -impl From> for Point { - fn from(p: Point) -> Point { - convert_subgroup(&p) - } -} - -impl From<&Point> for Point { - fn from(p: &Point) -> Point { - convert_subgroup(p) - } -} - -impl Clone for Point { - fn clone(&self) -> Self { - convert_subgroup(self) - } -} - -impl PartialEq for Point { - fn eq(&self, other: &Point) -> bool { - // p1 = (x1/z1, y1/z1) - // p2 = (x2/z2, y2/z2) - // Deciding that these two points are equal is a matter of - // determining that x1/z1 = x2/z2, or equivalently that - // x1*z2 = x2*z1, and similarly for y. - - let mut x1 = self.x; - x1.mul_assign(&other.z); - - let mut y1 = self.y; - y1.mul_assign(&other.z); - - let mut x2 = other.x; - x2.mul_assign(&self.z); - - let mut y2 = other.y; - y2.mul_assign(&self.z); - - x1 == x2 && y1 == y2 - } -} - -impl Point { - pub fn read(reader: R, params: &E::Params) -> io::Result { - let mut y_repr = ::Repr::default(); - y_repr.read_le(reader)?; - - let x_sign = (y_repr.as_ref()[3] >> 63) == 1; - y_repr.as_mut()[3] &= 0x7fffffffffffffff; - - match E::Fr::from_repr(y_repr) { - Ok(y) => { - let p = Self::get_for_y(y, x_sign, params); - if bool::from(p.is_some()) { - Ok(p.unwrap()) - } else { - Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")) - } - } - Err(_) => Err(io::Error::new( - io::ErrorKind::InvalidInput, - "y is not in field", - )), - } - } - - pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> CtOption { - // Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1) - // This is defined for all valid y-coordinates, - // as dy^2 + 1 = 0 has no solution in Fr. - - // tmp1 = y^2 - let mut tmp1 = y.square(); - - // tmp2 = (y^2 * d) + 1 - let mut tmp2 = tmp1; - tmp2.mul_assign(params.edwards_d()); - tmp2.add_assign(&E::Fr::one()); - - // tmp1 = y^2 - 1 - tmp1.sub_assign(&E::Fr::one()); - - tmp2.invert().and_then(|tmp2| { - // tmp1 = (y^2 - 1) / (dy^2 + 1) - tmp1.mul_assign(&tmp2); - - tmp1.sqrt().map(|mut x| { - if x.into_repr().is_odd() != sign { - x = x.neg(); - } - - let mut t = x; - t.mul_assign(&y); - - Point { - x, - y, - t, - z: E::Fr::one(), - _marker: PhantomData, - } - }) - }) - } - - /// This guarantees the point is in the prime order subgroup - #[must_use] - pub fn mul_by_cofactor(&self, params: &E::Params) -> Point { - let tmp = self.double(params).double(params).double(params); - - convert_subgroup(&tmp) - } - - pub fn rand(rng: &mut R, params: &E::Params) -> Self { - loop { - let y = E::Fr::random(rng); - let sign = rng.next_u32() % 2 != 0; - - let p = Self::get_for_y(y, sign, params); - if bool::from(p.is_some()) { - return p.unwrap(); - } - } - } -} - -impl Point { - pub fn write(&self, writer: W) -> io::Result<()> { - let (x, y) = self.to_xy(); - - assert_eq!(E::Fr::NUM_BITS, 255); - - let x_repr = x.into_repr(); - let mut y_repr = y.into_repr(); - if x_repr.is_odd() { - y_repr.as_mut()[3] |= 0x8000000000000000u64; - } - - y_repr.write_le(writer) - } - - /// Convert from a Montgomery point - pub fn from_montgomery(m: &montgomery::Point, params: &E::Params) -> Self { - match m.to_xy() { - None => { - // Map the point at infinity to the neutral element. - Point::zero() - } - Some((x, y)) => { - // The map from a Montgomery curve is defined as: - // (x, y) -> (u, v) where - // u = x / y - // v = (x - 1) / (x + 1) - // - // This map is not defined for y = 0 and x = -1. - // - // y = 0 is a valid point only for x = 0: - // y^2 = x^3 + A.x^2 + x - // 0 = x^3 + A.x^2 + x - // 0 = x(x^2 + A.x + 1) - // We have: x = 0 OR x^2 + A.x + 1 = 0 - // x^2 + A.x + 1 = 0 - // (2.x + A)^2 = A^2 - 4 (Complete the square.) - // The left hand side is a square, and so if A^2 - 4 - // is nonsquare, there is no solution. Indeed, A^2 - 4 - // is nonsquare. - // - // (0, 0) is a point of order 2, and so we map it to - // (0, -1) in the twisted Edwards curve, which is the - // only point of order 2 that is not the neutral element. - if y.is_zero() { - // This must be the point (0, 0) as above. - Point { - x: E::Fr::zero(), - y: E::Fr::one().neg(), - t: E::Fr::zero(), - z: E::Fr::one(), - _marker: PhantomData, - } - } else { - // Otherwise, as stated above, the mapping is still - // not defined at x = -1. However, x = -1 is not - // on the curve when A - 2 is nonsquare: - // y^2 = x^3 + A.x^2 + x - // y^2 = (-1) + A + (-1) - // y^2 = A - 2 - // Indeed, A - 2 is nonsquare. - // - // We need to map into (projective) extended twisted - // Edwards coordinates (X, Y, T, Z) which represents - // the point (X/Z, Y/Z) with Z nonzero and T = XY/Z. - // - // Thus, we compute... - // - // u = x(x + 1) - // v = y(x - 1) - // t = x(x - 1) - // z = y(x + 1) (Cannot be nonzero, as above.) - // - // ... which represents the point ( x / y , (x - 1) / (x + 1) ) - // as required by the mapping and preserves the property of - // the auxiliary coordinate t. - // - // We need to scale the coordinate, so u and t will have - // an extra factor s. - - // u = xs - let mut u = x; - u.mul_assign(params.scale()); - - // v = x - 1 - let mut v = x; - v.sub_assign(&E::Fr::one()); - - // t = xs(x - 1) - let mut t = u; - t.mul_assign(&v); - - // z = (x + 1) - let mut z = x; - z.add_assign(&E::Fr::one()); - - // u = xs(x + 1) - u.mul_assign(&z); - - // z = y(x + 1) - z.mul_assign(&y); - - // v = y(x - 1) - v.mul_assign(&y); - - Point { - x: u, - y: v, - t, - z, - _marker: PhantomData, - } - } - } - } - } - - /// Attempts to cast this as a prime order element, failing if it's - /// not in the prime order subgroup. - pub fn as_prime_order(&self, params: &E::Params) -> Option> { - if self.mul(E::Fs::char(), params) == Point::zero() { - Some(convert_subgroup(self)) - } else { - None - } - } - - pub fn zero() -> Self { - Point { - x: E::Fr::zero(), - y: E::Fr::one(), - t: E::Fr::zero(), - z: E::Fr::one(), - _marker: PhantomData, - } - } - - /// Convert to affine coordinates - pub fn to_xy(&self) -> (E::Fr, E::Fr) { - let zinv = self.z.invert().unwrap(); - - let mut x = self.x; - x.mul_assign(&zinv); - - let mut y = self.y; - y.mul_assign(&zinv); - - (x, y) - } - - #[must_use] - pub fn negate(&self) -> Self { - let mut p = self.clone(); - - p.x = p.x.neg(); - p.t = p.t.neg(); - - p - } - - #[must_use] - pub fn double(&self, _: &E::Params) -> Self { - // See "Twisted Edwards Curves Revisited" - // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson - // Section 3.3 - // http://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd - - // A = X1^2 - let a = self.x.square(); - - // B = Y1^2 - let b = self.y.square(); - - // C = 2*Z1^2 - let c = self.z.square().double(); - - // D = a*A - // = -A - let d = a.neg(); - - // E = (X1+Y1)^2 - A - B - let mut e = self.x; - e.add_assign(&self.y); - e = e.square(); - e.add_assign(&d); // -A = D - e.sub_assign(&b); - - // G = D+B - let mut g = d; - g.add_assign(&b); - - // F = G-C - let mut f = g; - f.sub_assign(&c); - - // H = D-B - let mut h = d; - h.sub_assign(&b); - - // X3 = E*F - let mut x3 = e; - x3.mul_assign(&f); - - // Y3 = G*H - let mut y3 = g; - y3.mul_assign(&h); - - // T3 = E*H - let mut t3 = e; - t3.mul_assign(&h); - - // Z3 = F*G - let mut z3 = f; - z3.mul_assign(&g); - - Point { - x: x3, - y: y3, - t: t3, - z: z3, - _marker: PhantomData, - } - } - - #[must_use] - pub fn add(&self, other: &Self, params: &E::Params) -> Self { - // See "Twisted Edwards Curves Revisited" - // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson - // 3.1 Unified Addition in E^e - - // A = x1 * x2 - let mut a = self.x; - a.mul_assign(&other.x); - - // B = y1 * y2 - let mut b = self.y; - b.mul_assign(&other.y); - - // C = d * t1 * t2 - let mut c = *params.edwards_d(); - c.mul_assign(&self.t); - c.mul_assign(&other.t); - - // D = z1 * z2 - let mut d = self.z; - d.mul_assign(&other.z); - - // H = B - aA - // = B + A - let mut h = b; - h.add_assign(&a); - - // E = (x1 + y1) * (x2 + y2) - A - B - // = (x1 + y1) * (x2 + y2) - H - let mut e = self.x; - e.add_assign(&self.y); - { - let mut tmp = other.x; - tmp.add_assign(&other.y); - e.mul_assign(&tmp); - } - e.sub_assign(&h); - - // F = D - C - let mut f = d; - f.sub_assign(&c); - - // G = D + C - let mut g = d; - g.add_assign(&c); - - // x3 = E * F - let mut x3 = e; - x3.mul_assign(&f); - - // y3 = G * H - let mut y3 = g; - y3.mul_assign(&h); - - // t3 = E * H - let mut t3 = e; - t3.mul_assign(&h); - - // z3 = F * G - let mut z3 = f; - z3.mul_assign(&g); - - Point { - x: x3, - y: y3, - t: t3, - z: z3, - _marker: PhantomData, - } - } - - #[must_use] - pub fn mul::Repr>>(&self, scalar: S, params: &E::Params) -> Self { - // Standard double-and-add scalar multiplication - - let mut res = Self::zero(); - - for b in BitIterator::new(scalar.into()) { - res = res.double(params); - - if b { - res = res.add(self, params); - } - } - - res - } -} diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs deleted file mode 100644 index ef1ccbea46..0000000000 --- a/zcash_primitives/src/jubjub/fs.rs +++ /dev/null @@ -1,1701 +0,0 @@ -use byteorder::{ByteOrder, LittleEndian}; -use ff::{ - adc, mac_with_carry, sbb, BitIterator, Field, PrimeField, PrimeFieldDecodingError, - PrimeFieldRepr, SqrtField, -}; -use rand_core::RngCore; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use super::ToUniform; - -// s = 6554484396890773809930967563523245729705921265872317281365359162392183254199 -const MODULUS: FsRepr = FsRepr([ - 0xd0970e5ed6f72cb7, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9, -]); - -// The number of bits needed to represent the modulus. -const MODULUS_BITS: u32 = 252; - -// The number of bits that must be shaved from the beginning of -// the representation when randomly sampling. -const REPR_SHAVE_BITS: u32 = 4; - -// R = 2**256 % s -const R: FsRepr = FsRepr([ - 0x25f80bb3b99607d9, - 0xf315d62f66b6e750, - 0x932514eeeb8814f4, - 0x9a6fc6f479155c6, -]); - -// R2 = R^2 % s -const R2: FsRepr = FsRepr([ - 0x67719aa495e57731, - 0x51b0cef09ce3fc26, - 0x69dab7fac026e9a5, - 0x4f6547b8d127688, -]); - -// INV = -(s^{-1} mod 2^64) mod s -const INV: u64 = 0x1ba3a358ef788ef9; - -// GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) -const GENERATOR: FsRepr = FsRepr([ - 0x720b1b19d49ea8f1, - 0xbf4aa36101f13a58, - 0x5fa8cc968193ccbb, - 0xe70cbdc7dccf3ac, -]); - -// 2^S * t = MODULUS - 1 with t odd -const S: u32 = 1; - -// 2^S root of unity computed by GENERATOR^t -const ROOT_OF_UNITY: FsRepr = FsRepr([ - 0xaa9f02ab1d6124de, - 0xb3524a6466112932, - 0x7342261215ac260b, - 0x4d6b87b1da259e2, -]); - -// -((2**256) mod s) mod s -const NEGATIVE_ONE: Fs = Fs(FsRepr([ - 0xaa9f02ab1d6124de, - 0xb3524a6466112932, - 0x7342261215ac260b, - 0x4d6b87b1da259e2, -])); - -/// This is the underlying representation of an element of `Fs`. -#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] -pub struct FsRepr(pub [u64; 4]); - -impl ::std::fmt::Display for FsRepr { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "0x")?; - for i in self.0.iter().rev() { - write!(f, "{:016x}", *i)?; - } - - Ok(()) - } -} - -impl AsRef<[u64]> for FsRepr { - #[inline(always)] - fn as_ref(&self) -> &[u64] { - &self.0 - } -} - -impl AsMut<[u64]> for FsRepr { - #[inline(always)] - fn as_mut(&mut self) -> &mut [u64] { - &mut self.0 - } -} - -impl From for FsRepr { - #[inline(always)] - fn from(val: u64) -> FsRepr { - let mut repr = Self::default(); - repr.0[0] = val; - repr - } -} - -impl Ord for FsRepr { - #[inline(always)] - fn cmp(&self, other: &FsRepr) -> ::std::cmp::Ordering { - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if a < b { - return ::std::cmp::Ordering::Less; - } else if a > b { - return ::std::cmp::Ordering::Greater; - } - } - - ::std::cmp::Ordering::Equal - } -} - -impl PartialOrd for FsRepr { - #[inline(always)] - fn partial_cmp(&self, other: &FsRepr) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } -} - -impl PrimeFieldRepr for FsRepr { - #[inline(always)] - fn is_odd(&self) -> bool { - self.0[0] & 1 == 1 - } - - #[inline(always)] - fn is_even(&self) -> bool { - !self.is_odd() - } - - #[inline(always)] - fn is_zero(&self) -> bool { - self.0.iter().all(|&e| e == 0) - } - - #[inline(always)] - fn shr(&mut self, mut n: u32) { - if n >= 64 * 4 { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - ::std::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn div2(&mut self) { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << 63; - *i >>= 1; - *i |= t; - t = t2; - } - } - - #[inline(always)] - fn mul2(&mut self) { - let mut last = 0; - for i in &mut self.0 { - let tmp = *i >> 63; - *i <<= 1; - *i |= last; - last = tmp; - } - } - - #[inline(always)] - fn shl(&mut self, mut n: u32) { - if n >= 64 * 4 { - *self = Self::from(0); - return; - } - - while n >= 64 { - let mut t = 0; - for i in &mut self.0 { - ::std::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in &mut self.0 { - let t2 = *i >> (64 - n); - *i <<= n; - *i |= t; - t = t2; - } - } - } - - #[inline(always)] - fn num_bits(&self) -> u32 { - let mut ret = (4 as u32) * 64; - for i in self.0.iter().rev() { - let leading = i.leading_zeros(); - ret -= leading; - if leading != 64 { - break; - } - } - - ret - } - - #[inline(always)] - fn add_nocarry(&mut self, other: &FsRepr) { - let mut carry = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = adc(*a, *b, &mut carry); - } - } - - #[inline(always)] - fn sub_noborrow(&mut self, other: &FsRepr) { - let mut borrow = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = sbb(*a, *b, &mut borrow); - } - } -} - -/// This is an element of the scalar field of the Jubjub curve. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Fs(FsRepr); - -impl Default for Fs { - fn default() -> Self { - Fs::zero() - } -} - -impl ConstantTimeEq for Fs { - fn ct_eq(&self, other: &Fs) -> Choice { - (self.0).0[0].ct_eq(&(other.0).0[0]) - & (self.0).0[1].ct_eq(&(other.0).0[1]) - & (self.0).0[2].ct_eq(&(other.0).0[2]) - & (self.0).0[3].ct_eq(&(other.0).0[3]) - } -} - -impl ::std::fmt::Display for Fs { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - write!(f, "Fs({})", self.into_repr()) - } -} - -impl From for FsRepr { - fn from(e: Fs) -> FsRepr { - e.into_repr() - } -} - -impl ConditionallySelectable for Fs { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fs(FsRepr([ - u64::conditional_select(&(a.0).0[0], &(b.0).0[0], choice), - u64::conditional_select(&(a.0).0[1], &(b.0).0[1], choice), - u64::conditional_select(&(a.0).0[2], &(b.0).0[2], choice), - u64::conditional_select(&(a.0).0[3], &(b.0).0[3], choice), - ])) - } -} - -impl Neg for Fs { - type Output = Self; - - #[inline] - fn neg(mut self) -> Self { - if !self.is_zero() { - let mut tmp = MODULUS; - tmp.sub_noborrow(&self.0); - self.0 = tmp; - } - self - } -} - -impl<'r> Add<&'r Fs> for Fs { - type Output = Self; - - #[inline] - fn add(self, other: &Self) -> Self { - let mut ret = self; - ret.add_assign(other); - ret - } -} - -impl Add for Fs { - type Output = Self; - - #[inline] - fn add(self, other: Self) -> Self { - self + &other - } -} - -impl<'r> AddAssign<&'r Fs> for Fs { - #[inline] - fn add_assign(&mut self, other: &Self) { - // This cannot exceed the backing capacity. - self.0.add_nocarry(&other.0); - - // However, it may need to be reduced. - self.reduce(); - } -} - -impl AddAssign for Fs { - #[inline] - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } -} - -impl<'r> Sub<&'r Fs> for Fs { - type Output = Self; - - #[inline] - fn sub(self, other: &Self) -> Self { - let mut ret = self; - ret.sub_assign(other); - ret - } -} - -impl Sub for Fs { - type Output = Self; - - #[inline] - fn sub(self, other: Self) -> Self { - self - &other - } -} - -impl<'r> SubAssign<&'r Fs> for Fs { - #[inline] - fn sub_assign(&mut self, other: &Self) { - // If `other` is larger than `self`, we'll need to add the modulus to self first. - if other.0 > self.0 { - self.0.add_nocarry(&MODULUS); - } - - self.0.sub_noborrow(&other.0); - } -} - -impl SubAssign for Fs { - #[inline] - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } -} - -impl<'r> Mul<&'r Fs> for Fs { - type Output = Self; - - #[inline] - fn mul(self, other: &Self) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } -} - -impl Mul for Fs { - type Output = Self; - - #[inline] - fn mul(self, other: Self) -> Self { - self * &other - } -} - -impl<'r> MulAssign<&'r Fs> for Fs { - #[inline] - fn mul_assign(&mut self, other: &Self) { - let mut carry = 0; - let r0 = mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry); - let r1 = mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry); - let r2 = mac_with_carry(0, (self.0).0[0], (other.0).0[2], &mut carry); - let r3 = mac_with_carry(0, (self.0).0[0], (other.0).0[3], &mut carry); - let r4 = carry; - let mut carry = 0; - let r1 = mac_with_carry(r1, (self.0).0[1], (other.0).0[0], &mut carry); - let r2 = mac_with_carry(r2, (self.0).0[1], (other.0).0[1], &mut carry); - let r3 = mac_with_carry(r3, (self.0).0[1], (other.0).0[2], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[1], (other.0).0[3], &mut carry); - let r5 = carry; - let mut carry = 0; - let r2 = mac_with_carry(r2, (self.0).0[2], (other.0).0[0], &mut carry); - let r3 = mac_with_carry(r3, (self.0).0[2], (other.0).0[1], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[2], (other.0).0[2], &mut carry); - let r5 = mac_with_carry(r5, (self.0).0[2], (other.0).0[3], &mut carry); - let r6 = carry; - let mut carry = 0; - let r3 = mac_with_carry(r3, (self.0).0[3], (other.0).0[0], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[3], (other.0).0[1], &mut carry); - let r5 = mac_with_carry(r5, (self.0).0[3], (other.0).0[2], &mut carry); - let r6 = mac_with_carry(r6, (self.0).0[3], (other.0).0[3], &mut carry); - let r7 = carry; - self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); - } -} - -impl MulAssign for Fs { - #[inline] - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl PrimeField for Fs { - type Repr = FsRepr; - - fn from_repr(r: FsRepr) -> Result { - let mut r = Fs(r); - if r.is_valid() { - r.mul_assign(&Fs(R2)); - - Ok(r) - } else { - Err(PrimeFieldDecodingError::NotInField) - } - } - - fn into_repr(&self) -> FsRepr { - let mut r = *self; - r.mont_reduce( - (self.0).0[0], - (self.0).0[1], - (self.0).0[2], - (self.0).0[3], - 0, - 0, - 0, - 0, - ); - r.0 - } - - fn char() -> FsRepr { - MODULUS - } - - const NUM_BITS: u32 = MODULUS_BITS; - - const CAPACITY: u32 = Self::NUM_BITS - 1; - - fn multiplicative_generator() -> Self { - Fs(GENERATOR) - } - - const S: u32 = S; - - fn root_of_unity() -> Self { - Fs(ROOT_OF_UNITY) - } -} - -impl Field for Fs { - fn random(rng: &mut R) -> Self { - loop { - let mut tmp = { - let mut repr = [0u64; 4]; - for limb in &mut repr { - *limb = rng.next_u64(); - } - Fs(FsRepr(repr)) - }; - - // Mask away the unused most-significant bits. - tmp.0.as_mut()[3] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; - - if tmp.is_valid() { - return tmp; - } - } - } - - #[inline] - fn zero() -> Self { - Fs(FsRepr::from(0)) - } - - #[inline] - fn one() -> Self { - Fs(R) - } - - #[inline] - fn is_zero(&self) -> bool { - self.0.is_zero() - } - - #[inline] - fn double(&self) -> Self { - let mut ret = *self; - - // This cannot exceed the backing capacity. - ret.0.mul2(); - - // However, it may need to be reduced. - ret.reduce(); - - ret - } - - /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! - /// THIS WILL BE REPLACED BY THE jubjub CRATE, WHICH IS CONSTANT TIME! - fn invert(&self) -> CtOption { - if self.is_zero() { - CtOption::new(Self::zero(), Choice::from(0)) - } else { - // Guajardo Kumar Paar Pelzl - // Efficient Software-Implementation of Finite Fields with Applications to Cryptography - // Algorithm 16 (BEA for Inversion in Fp) - - let one = FsRepr::from(1); - - let mut u = self.0; - let mut v = MODULUS; - let mut b = Fs(R2); // Avoids unnecessary reduction step. - let mut c = Self::zero(); - - while u != one && v != one { - while u.is_even() { - u.div2(); - - if b.0.is_even() { - b.0.div2(); - } else { - b.0.add_nocarry(&MODULUS); - b.0.div2(); - } - } - - while v.is_even() { - v.div2(); - - if c.0.is_even() { - c.0.div2(); - } else { - c.0.add_nocarry(&MODULUS); - c.0.div2(); - } - } - - if v < u { - u.sub_noborrow(&v); - b.sub_assign(&c); - } else { - v.sub_noborrow(&u); - c.sub_assign(&b); - } - } - - if u == one { - CtOption::new(b, Choice::from(1)) - } else { - CtOption::new(c, Choice::from(1)) - } - } - } - - #[inline(always)] - fn frobenius_map(&mut self, _: usize) { - // This has no effect in a prime field. - } - - #[inline] - fn square(&self) -> Self { - let mut carry = 0; - let r1 = mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry); - let r2 = mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry); - let r3 = mac_with_carry(0, (self.0).0[0], (self.0).0[3], &mut carry); - let r4 = carry; - let mut carry = 0; - let r3 = mac_with_carry(r3, (self.0).0[1], (self.0).0[2], &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[1], (self.0).0[3], &mut carry); - let r5 = carry; - let mut carry = 0; - let r5 = mac_with_carry(r5, (self.0).0[2], (self.0).0[3], &mut carry); - let r6 = carry; - - let r7 = r6 >> 63; - let r6 = (r6 << 1) | (r5 >> 63); - let r5 = (r5 << 1) | (r4 >> 63); - let r4 = (r4 << 1) | (r3 >> 63); - let r3 = (r3 << 1) | (r2 >> 63); - let r2 = (r2 << 1) | (r1 >> 63); - let r1 = r1 << 1; - - let mut carry = 0; - let r0 = mac_with_carry(0, (self.0).0[0], (self.0).0[0], &mut carry); - let r1 = adc(r1, 0, &mut carry); - let r2 = mac_with_carry(r2, (self.0).0[1], (self.0).0[1], &mut carry); - let r3 = adc(r3, 0, &mut carry); - let r4 = mac_with_carry(r4, (self.0).0[2], (self.0).0[2], &mut carry); - let r5 = adc(r5, 0, &mut carry); - let r6 = mac_with_carry(r6, (self.0).0[3], (self.0).0[3], &mut carry); - let r7 = adc(r7, 0, &mut carry); - - let mut ret = *self; - ret.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); - ret - } -} - -impl Fs { - /// Determines if the element is really in the field. This is only used - /// internally. - #[inline(always)] - fn is_valid(&self) -> bool { - self.0 < MODULUS - } - - /// Subtracts the modulus from this element if this element is not in the - /// field. Only used internally. - #[inline(always)] - fn reduce(&mut self) { - if !self.is_valid() { - self.0.sub_noborrow(&MODULUS); - } - } - - #[inline(always)] - fn mont_reduce( - &mut self, - r0: u64, - mut r1: u64, - mut r2: u64, - mut r3: u64, - mut r4: u64, - mut r5: u64, - mut r6: u64, - mut r7: u64, - ) { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = r0.wrapping_mul(INV); - let mut carry = 0; - mac_with_carry(r0, k, MODULUS.0[0], &mut carry); - r1 = mac_with_carry(r1, k, MODULUS.0[1], &mut carry); - r2 = mac_with_carry(r2, k, MODULUS.0[2], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS.0[3], &mut carry); - r4 = adc(r4, 0, &mut carry); - let carry2 = carry; - let k = r1.wrapping_mul(INV); - let mut carry = 0; - mac_with_carry(r1, k, MODULUS.0[0], &mut carry); - r2 = mac_with_carry(r2, k, MODULUS.0[1], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS.0[2], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS.0[3], &mut carry); - r5 = adc(r5, carry2, &mut carry); - let carry2 = carry; - let k = r2.wrapping_mul(INV); - let mut carry = 0; - mac_with_carry(r2, k, MODULUS.0[0], &mut carry); - r3 = mac_with_carry(r3, k, MODULUS.0[1], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS.0[2], &mut carry); - r5 = mac_with_carry(r5, k, MODULUS.0[3], &mut carry); - r6 = adc(r6, carry2, &mut carry); - let carry2 = carry; - let k = r3.wrapping_mul(INV); - let mut carry = 0; - mac_with_carry(r3, k, MODULUS.0[0], &mut carry); - r4 = mac_with_carry(r4, k, MODULUS.0[1], &mut carry); - r5 = mac_with_carry(r5, k, MODULUS.0[2], &mut carry); - r6 = mac_with_carry(r6, k, MODULUS.0[3], &mut carry); - r7 = adc(r7, carry2, &mut carry); - (self.0).0[0] = r4; - (self.0).0[1] = r5; - (self.0).0[2] = r6; - (self.0).0[3] = r7; - self.reduce(); - } - - fn mul_bits>(&self, bits: BitIterator) -> Self { - let mut res = Self::zero(); - for bit in bits { - res = res.double(); - - if bit { - res.add_assign(self) - } - } - res - } -} - -impl ToUniform for Fs { - /// Convert a little endian byte string into a uniform - /// field element. The number is reduced mod s. The caller - /// is responsible for ensuring the input is 64 bytes of - /// Random Oracle output. - fn to_uniform(digest: &[u8]) -> Self { - assert_eq!(digest.len(), 64); - let mut repr: [u64; 8] = [0; 8]; - LittleEndian::read_u64_into(digest, &mut repr); - Self::one().mul_bits(BitIterator::new(repr)) - } -} - -impl SqrtField for Fs { - fn sqrt(&self) -> CtOption { - // Shank's algorithm for s mod 4 = 3 - // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) - - // a1 = self^((s - 3) // 4) - let mut a1 = self.pow_vartime([ - 0xb425c397b5bdcb2d, - 0x299a0824f3320420, - 0x4199cec0404d0ec0, - 0x39f6d3a994cebea, - ]); - let mut a0 = a1.square(); - a0.mul_assign(self); - a1.mul_assign(self); - - CtOption::new(a1, !a0.ct_eq(&NEGATIVE_ONE)) - } -} - -#[test] -fn test_neg_one() { - let o = Fs::one().neg(); - - assert_eq!(NEGATIVE_ONE, o); -} - -#[cfg(test)] -use rand_core::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_fs_repr_ordering() { - fn assert_equality(a: FsRepr, b: FsRepr) { - assert_eq!(a, b); - assert!(a.cmp(&b) == ::std::cmp::Ordering::Equal); - } - - fn assert_lt(a: FsRepr, b: FsRepr) { - assert!(a < b); - assert!(b > a); - } - - assert_equality( - FsRepr([9999, 9999, 9999, 9999]), - FsRepr([9999, 9999, 9999, 9999]), - ); - assert_equality( - FsRepr([9999, 9998, 9999, 9999]), - FsRepr([9999, 9998, 9999, 9999]), - ); - assert_equality( - FsRepr([9999, 9999, 9999, 9997]), - FsRepr([9999, 9999, 9999, 9997]), - ); - assert_lt( - FsRepr([9999, 9997, 9999, 9998]), - FsRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FsRepr([9999, 9997, 9998, 9999]), - FsRepr([9999, 9997, 9999, 9999]), - ); - assert_lt( - FsRepr([9, 9999, 9999, 9997]), - FsRepr([9999, 9999, 9999, 9997]), - ); -} - -#[test] -fn test_fs_repr_from() { - assert_eq!(FsRepr::from(100), FsRepr([100, 0, 0, 0])); -} - -#[test] -fn test_fs_repr_is_odd() { - assert!(!FsRepr::from(0).is_odd()); - assert!(FsRepr::from(0).is_even()); - assert!(FsRepr::from(1).is_odd()); - assert!(!FsRepr::from(1).is_even()); - assert!(!FsRepr::from(324834872).is_odd()); - assert!(FsRepr::from(324834872).is_even()); - assert!(FsRepr::from(324834873).is_odd()); - assert!(!FsRepr::from(324834873).is_even()); -} - -#[test] -fn test_fs_repr_is_zero() { - assert!(FsRepr::from(0).is_zero()); - assert!(!FsRepr::from(1).is_zero()); - assert!(!FsRepr([0, 0, 1, 0]).is_zero()); -} - -#[test] -fn test_fs_repr_div2() { - let mut a = FsRepr([ - 0xbd2920b19c972321, - 0x174ed0466a3be37e, - 0xd468d5e3b551f0b5, - 0xcb67c072733beefc, - ]); - a.div2(); - assert_eq!( - a, - FsRepr([ - 0x5e949058ce4b9190, - 0x8ba76823351df1bf, - 0x6a346af1daa8f85a, - 0x65b3e039399df77e - ]) - ); - for _ in 0..10 { - a.div2(); - } - assert_eq!( - a, - FsRepr([ - 0x6fd7a524163392e4, - 0x16a2e9da08cd477c, - 0xdf9a8d1abc76aa3e, - 0x196cf80e4e677d - ]) - ); - for _ in 0..200 { - a.div2(); - } - assert_eq!(a, FsRepr([0x196cf80e4e67, 0x0, 0x0, 0x0])); - for _ in 0..40 { - a.div2(); - } - assert_eq!(a, FsRepr([0x19, 0x0, 0x0, 0x0])); - for _ in 0..4 { - a.div2(); - } - assert_eq!(a, FsRepr([0x1, 0x0, 0x0, 0x0])); - a.div2(); - assert!(a.is_zero()); -} - -#[test] -fn test_fs_repr_shr() { - let mut a = FsRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1, - ]); - a.shr(0); - assert_eq!( - a, - FsRepr([ - 0xb33fbaec482a283f, - 0x997de0d3a88cb3df, - 0x9af62d2a9a0e5525, - 0x36003ab08de70da1 - ]) - ); - a.shr(1); - assert_eq!( - a, - FsRepr([ - 0xd99fdd762415141f, - 0xccbef069d44659ef, - 0xcd7b16954d072a92, - 0x1b001d5846f386d0 - ]) - ); - a.shr(50); - assert_eq!( - a, - FsRepr([ - 0xbc1a7511967bf667, - 0xc5a55341caa4b32f, - 0x75611bce1b4335e, - 0x6c0 - ]) - ); - a.shr(130); - assert_eq!(a, FsRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0])); - a.shr(64); - assert_eq!(a, FsRepr([0x1b0, 0x0, 0x0, 0x0])); -} - -#[test] -fn test_fs_repr_mul2() { - let mut a = FsRepr::from(23712937547); - a.mul2(); - assert_eq!(a, FsRepr([0xb0acd6c96, 0x0, 0x0, 0x0])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FsRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0])); - for _ in 0..128 { - a.mul2(); - } - assert_eq!(a, FsRepr([0x0, 0x0, 0x6000000000000000, 0xb0acd6c9])); - for _ in 0..60 { - a.mul2(); - } - assert_eq!(a, FsRepr([0x0, 0x0, 0x0, 0x9600000000000000])); - for _ in 0..7 { - a.mul2(); - } - assert!(a.is_zero()); -} - -#[test] -fn test_fs_repr_num_bits() { - let mut a = FsRepr::from(0); - assert_eq!(0, a.num_bits()); - a = FsRepr::from(1); - for i in 1..257 { - assert_eq!(i, a.num_bits()); - a.mul2(); - } - assert_eq!(0, a.num_bits()); -} - -#[test] -fn test_fs_repr_sub_noborrow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FsRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ]); - t.sub_noborrow(&FsRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ])); - assert!( - t == FsRepr([ - 0xb813415048991c1f, - 0x10ad07ae88725d92, - 0x5a7b851271759961, - 0x36850eedd30c39c5 - ]) - ); - - for _ in 0..1000 { - let mut a = Fs::random(&mut rng).into_repr(); - 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); - } -} - -#[test] -fn test_fr_repr_add_nocarry() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let mut t = FsRepr([ - 0xd64f669809cbc6a4, - 0xfa76cb9d90cf7637, - 0xfefb0df9038d43b3, - 0x298a30c744b31acf, - ]); - t.add_nocarry(&FsRepr([ - 0x8e62a7e85264e2c3, - 0xb23d34c1941d3ca, - 0x5976930b7502dd15, - 0x600f3fb517bf5495, - ])); - assert_eq!( - t, - FsRepr([ - 0x64b20e805c30a967, - 0x59a9ee9aa114a02, - 0x5871a104789020c9, - 0x8999707c5c726f65 - ]) - ); - - // Test for the associativity of addition. - for _ in 0..1000 { - let mut a = Fs::random(&mut rng).into_repr(); - let mut b = Fs::random(&mut rng).into_repr(); - let mut c = Fs::random(&mut rng).into_repr(); - - // 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); - } -} - -#[test] -fn test_fs_is_valid() { - let mut a = Fs(MODULUS); - assert!(!a.is_valid()); - a.0.sub_noborrow(&FsRepr::from(1)); - assert!(a.is_valid()); - assert!(Fs(FsRepr::from(0)).is_valid()); - assert!(Fs(FsRepr([ - 0xd0970e5ed6f72cb6, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9 - ])) - .is_valid()); - assert!(!Fs(FsRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ])) - .is_valid()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let a = Fs::random(&mut rng); - assert!(a.is_valid()); - } -} - -#[test] -fn test_fs_add_assign() { - { - // Random number - let mut tmp = Fs::from_str( - "4577408157467272683998459759522778614363623736323078995109579213719612604198", - ) - .unwrap(); - assert!(tmp.is_valid()); - // Test that adding zero has no effect. - tmp.add_assign(&Fs(FsRepr::from(0))); - assert_eq!( - tmp, - Fs(FsRepr([ - 0x8e6bfff4722d6e67, - 0x5643da5c892044f9, - 0x9465f4b281921a69, - 0x25f752d3edd7162 - ])) - ); - // Add one and test for the result. - tmp.add_assign(&Fs(FsRepr::from(1))); - assert_eq!( - tmp, - Fs(FsRepr([ - 0x8e6bfff4722d6e68, - 0x5643da5c892044f9, - 0x9465f4b281921a69, - 0x25f752d3edd7162 - ])) - ); - // Add another random number that exercises the reduction. - tmp.add_assign(&Fs(FsRepr([ - 0xb634d07bc42d4a70, - 0xf724f0c008411f5f, - 0x456d4053d865af34, - 0x24ce814e8c63027, - ]))); - assert_eq!( - tmp, - Fs(FsRepr([ - 0x44a0d070365ab8d8, - 0x4d68cb1c91616459, - 0xd9d3350659f7c99e, - 0x4ac5d4227a3a189 - ])) - ); - // Add one to (s - 1) and test for the result. - tmp = Fs(FsRepr([ - 0xd0970e5ed6f72cb6, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9, - ])); - tmp.add_assign(&Fs(FsRepr::from(1))); - assert!(tmp.0.is_zero()); - // Add a random number to another one such that the result is s - 1 - tmp = Fs(FsRepr([ - 0xa11fda5950ce3636, - 0x922e0dbccfe0ca0e, - 0xacebb6e215b82d4a, - 0x97ffb8cdc3aee93, - ])); - tmp.add_assign(&Fs(FsRepr([ - 0x2f7734058628f680, - 0x143a12d6fce74674, - 0x597b841eeb7c0db6, - 0x4fdb95d88f8c115, - ]))); - assert_eq!( - tmp, - Fs(FsRepr([ - 0xd0970e5ed6f72cb6, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9 - ])) - ); - // Add one to the result and test for it. - tmp.add_assign(&Fs(FsRepr::from(1))); - assert!(tmp.0.is_zero()); - } - - // Test associativity - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Generate a, b, c and ensure (a + b) + c == a + (b + c). - let a = Fs::random(&mut rng); - let b = Fs::random(&mut rng); - let c = Fs::random(&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_fs_sub_assign() { - { - // Test arbitrary subtraction that tests reduction. - let mut tmp = Fs(FsRepr([ - 0xb384d9f6877afd99, - 0x4442513958e1a1c1, - 0x352c4b8a95eccc3f, - 0x2db62dee4b0f2, - ])); - tmp.sub_assign(&Fs(FsRepr([ - 0xec5bd2d13ed6b05a, - 0x2adc0ab3a39b5fa, - 0x82d3360a493e637e, - 0x53ccff4a64d6679, - ]))); - assert_eq!( - tmp, - Fs(FsRepr([ - 0x97c015841f9b79f6, - 0xe7fcb121eb6ffc49, - 0xb8c050814de2a3c1, - 0x943c0589dcafa21 - ])) - ); - - // Test the opposite subtraction which doesn't test reduction. - tmp = Fs(FsRepr([ - 0xec5bd2d13ed6b05a, - 0x2adc0ab3a39b5fa, - 0x82d3360a493e637e, - 0x53ccff4a64d6679, - ])); - tmp.sub_assign(&Fs(FsRepr([ - 0xb384d9f6877afd99, - 0x4442513958e1a1c1, - 0x352c4b8a95eccc3f, - 0x2db62dee4b0f2, - ]))); - assert_eq!( - tmp, - Fs(FsRepr([ - 0x38d6f8dab75bb2c1, - 0xbe6b6f71e1581439, - 0x4da6ea7fb351973e, - 0x539f491c768b587 - ])) - ); - - // Test for sensible results with zero - tmp = Fs(FsRepr::from(0)); - tmp.sub_assign(&Fs(FsRepr::from(0))); - assert!(tmp.is_zero()); - - tmp = Fs(FsRepr([ - 0x361e16aef5cce835, - 0x55bbde2536e274c1, - 0x4dc77a63fd15ee75, - 0x1e14bb37c14f230, - ])); - tmp.sub_assign(&Fs(FsRepr::from(0))); - assert_eq!( - tmp, - Fs(FsRepr([ - 0x361e16aef5cce835, - 0x55bbde2536e274c1, - 0x4dc77a63fd15ee75, - 0x1e14bb37c14f230 - ])) - ); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure that (a - b) + (b - a) = 0. - let a = Fs::random(&mut rng); - let b = Fs::random(&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_fs_mul_assign() { - let mut tmp = Fs(FsRepr([ - 0xb433b01287f71744, - 0x4eafb86728c4d108, - 0xfdd52c14b9dfbe65, - 0x2ff1f3434821118, - ])); - tmp.mul_assign(&Fs(FsRepr([ - 0xdae00fc63c9fa90f, - 0x5a5ed89b96ce21ce, - 0x913cd26101bd6f58, - 0x3f0822831697fe9, - ]))); - assert!( - tmp == Fs(FsRepr([ - 0xb68ecb61d54d2992, - 0x5ff95874defce6a6, - 0x3590eb053894657d, - 0x53823a118515933 - ])) - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * b) * c = a * (b * c) - let a = Fs::random(&mut rng); - let b = Fs::random(&mut rng); - let c = Fs::random(&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 = Fs::random(&mut rng); - let mut a = Fs::random(&mut rng); - let mut b = Fs::random(&mut rng); - let mut c = Fs::random(&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 a = Fs(FsRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xe7db4ea6533afa8, - ])); - assert!(a.is_valid()); - assert_eq!( - a.square(), - Fs::from_repr(FsRepr([ - 0x12c7f55cbc52fbaa, - 0xdedc98a0b5e6ce9e, - 0xad2892726a5396a, - 0x9fe82af8fee77b3 - ])) - .unwrap() - ); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000000 { - // Ensure that (a * a) = a^2 - let a = Fs::random(&mut rng); - - let tmp = a.square(); - - let mut tmp2 = a; - tmp2.mul_assign(&a); - - assert_eq!(tmp, tmp2); - } -} - -#[test] -fn test_fs_invert() { - assert!(bool::from(Fs::zero().invert().is_none())); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let one = Fs::one(); - - for _ in 0..1000 { - // Ensure that a * a^-1 = 1 - let mut a = Fs::random(&mut rng); - let ainv = a.invert().unwrap(); - a.mul_assign(&ainv); - assert_eq!(a, one); - } -} - -#[test] -fn test_fs_double() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure doubling a is equivalent to adding a to itself. - let a = Fs::random(&mut rng); - assert_eq!(a.double(), a + a); - } -} - -#[test] -fn test_fs_neg() { - { - let a = Fs::zero().neg(); - - assert!(a.is_zero()); - } - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Ensure (a - (-a)) = 0. - let mut a = Fs::random(&mut rng); - let b = a.neg(); - a.add_assign(&b); - - assert!(a.is_zero()); - } -} - -#[test] -fn test_fs_pow() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for i in 0..1000 { - // Exponentiate by various small numbers and ensure it consists with repeated - // multiplication. - let a = Fs::random(&mut rng); - let target = a.pow_vartime(&[i]); - let mut c = Fs::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 = Fs::random(&mut rng); - - assert_eq!(a, a.pow_vartime(Fs::char())); - } -} - -#[test] -fn test_fs_sqrt() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - assert_eq!(Fs::zero().sqrt().unwrap(), Fs::zero()); - - for _ in 0..1000 { - // Ensure sqrt(a^2) = a or -a - let a = Fs::random(&mut rng); - let nega = a.neg(); - let b = a.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 = Fs::random(&mut rng); - - let tmp = a.sqrt(); - if tmp.is_some().into() { - assert_eq!(a, tmp.unwrap().square()); - } - } -} - -#[test] -fn test_fs_from_into_repr() { - // r + 1 should not be in the field - assert!(Fs::from_repr(FsRepr([ - 0xd0970e5ed6f72cb8, - 0xa6682093ccc81082, - 0x6673b0101343b00, - 0xe7db4ea6533afa9 - ])) - .is_err()); - - // r should not be in the field - assert!(Fs::from_repr(Fs::char()).is_err()); - - // Multiply some arbitrary representations to see if the result is as expected. - let a = FsRepr([ - 0x5f2d0c05d0337b71, - 0xa1df2b0f8a20479, - 0xad73785e71bb863, - 0x504a00480c9acec, - ]); - let mut a_fs = Fs::from_repr(a).unwrap(); - let b = FsRepr([ - 0x66356ff51e477562, - 0x60a92ab55cf7603, - 0x8e4273c7364dd192, - 0x36df8844a344dc5, - ]); - let b_fs = Fs::from_repr(b).unwrap(); - let c = FsRepr([ - 0x7eef61708f4f2868, - 0x747a7e6cf52946fb, - 0x83dd75d7c9120017, - 0x762f5177f0f3df7, - ]); - a_fs.mul_assign(&b_fs); - assert_eq!(a_fs.into_repr(), c); - - // Zero should be in the field. - assert!(Fs::from_repr(FsRepr::from(0)).unwrap().is_zero()); - - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - // Try to turn Fs elements into representations and back again, and compare. - let a = Fs::random(&mut rng); - let a_repr = a.into_repr(); - let b_repr = FsRepr::from(a); - assert_eq!(a_repr, b_repr); - let a_again = Fs::from_repr(a_repr).unwrap(); - - assert_eq!(a, a_again); - } -} - -#[test] -fn test_fs_repr_display() { - assert_eq!( - format!( - "{}", - FsRepr([ - 0xa296db59787359df, - 0x8d3e33077430d318, - 0xd1abf5c606102eb7, - 0xcbc33ee28108f0 - ]) - ), - "0x00cbc33ee28108f0d1abf5c606102eb78d3e33077430d318a296db59787359df".to_string() - ); - assert_eq!( - format!( - "{}", - FsRepr([ - 0x14cb03535054a620, - 0x312aa2bf2d1dff52, - 0x970fe98746ab9361, - 0xc1e18acf82711e6 - ]) - ), - "0x0c1e18acf82711e6970fe98746ab9361312aa2bf2d1dff5214cb03535054a620".to_string() - ); - assert_eq!( - format!( - "{}", - FsRepr([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff - ]) - ), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() - ); - assert_eq!( - format!("{}", FsRepr([0, 0, 0, 0])), - "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() - ); -} - -#[test] -fn test_fs_display() { - assert_eq!( - format!( - "{}", - Fs::from_repr(FsRepr([ - 0x5528efb9998a01a3, - 0x5bd2add5cb357089, - 0xc061fa6adb491f98, - 0x70db9d143db03d9 - ])) - .unwrap() - ), - "Fs(0x070db9d143db03d9c061fa6adb491f985bd2add5cb3570895528efb9998a01a3)".to_string() - ); - assert_eq!( - format!( - "{}", - Fs::from_repr(FsRepr([ - 0xd674745e2717999e, - 0xbeb1f52d3e96f338, - 0x9c7ae147549482b9, - 0x999706024530d22 - ])) - .unwrap() - ), - "Fs(0x0999706024530d229c7ae147549482b9beb1f52d3e96f338d674745e2717999e)".to_string() - ); -} - -#[test] -fn test_fs_num_bits() { - assert_eq!(Fs::NUM_BITS, 252); - assert_eq!(Fs::CAPACITY, 251); -} - -#[test] -fn test_fs_root_of_unity() { - assert_eq!(Fs::S, 1); - assert_eq!( - Fs::multiplicative_generator(), - Fs::from_repr(FsRepr::from(6)).unwrap() - ); - assert_eq!( - Fs::multiplicative_generator().pow_vartime([ - 0x684b872f6b7b965b, - 0x53341049e6640841, - 0x83339d80809a1d80, - 0x73eda753299d7d4 - ]), - Fs::root_of_unity() - ); - assert_eq!(Fs::root_of_unity().pow_vartime([1 << Fs::S]), Fs::one()); - assert!(bool::from(Fs::multiplicative_generator().sqrt().is_none())); -} diff --git a/zcash_primitives/src/jubjub/mod.rs b/zcash_primitives/src/jubjub/mod.rs deleted file mode 100644 index 06a3810bf2..0000000000 --- a/zcash_primitives/src/jubjub/mod.rs +++ /dev/null @@ -1,537 +0,0 @@ -//! The [Jubjub] curve for efficient elliptic curve operations in circuits built -//! over [BLS12-381]. -//! -//! Jubjub is a twisted Edwards curve defined over the BLS12-381 scalar -//! field, Fr. It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with -//! `d = -(10240/10241)`. It is birationally equivalent to a Montgomery -//! curve of the form `y^2 = x^3 + Ax^2 + x` with `A = 40962`. This -//! value `A` is the smallest integer choice such that: -//! -//! * `(A - 2) / 4` is a small integer (`10240`). -//! * `A^2 - 4` is quadratic nonresidue. -//! * The group order of the curve and its quadratic twist has a large -//! prime factor. -//! -//! Jubjub has `s = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` -//! as the prime subgroup order, with cofactor 8. (The twist has -//! cofactor 4.) -//! -//! It is a complete twisted Edwards curve, so the equivalence with -//! the Montgomery curve forms a group isomorphism, allowing points -//! to be freely converted between the two forms. -//! -//! [Jubjub]: https://zips.z.cash/protocol/protocol.pdf#jubjub -//! [BLS12-381]: pairing::bls12_381 - -use ff::{Field, PrimeField, SqrtField}; -use pairing::Engine; - -use crate::group_hash::group_hash; - -use crate::constants; - -use pairing::bls12_381::{Bls12, Fr}; - -/// This is an implementation of the twisted Edwards Jubjub curve. -pub mod edwards; - -/// This is an implementation of the birationally equivalent -/// Montgomery curve. -pub mod montgomery; - -/// This is an implementation of the scalar field for Jubjub. -pub mod fs; - -#[cfg(test)] -pub mod tests; - -/// Point of unknown order. -#[derive(Debug)] -pub enum Unknown {} - -/// Point of prime order. -#[derive(Debug)] -pub enum PrimeOrder {} - -/// Fixed generators of the Jubjub curve of unknown -/// exponent. -#[derive(Copy, Clone)] -pub enum FixedGenerators { - /// The prover will demonstrate knowledge of discrete log - /// with respect to this base when they are constructing - /// a proof, in order to authorize proof construction. - ProofGenerationKey = 0, - - /// The note commitment is randomized over this generator. - NoteCommitmentRandomness = 1, - - /// The node commitment is randomized again by the position - /// in order to supply the nullifier computation with a - /// unique input w.r.t. the note being spent, to prevent - /// Faerie gold attacks. - NullifierPosition = 2, - - /// The value commitment is used to check balance between - /// inputs and outputs. The value is placed over this - /// generator. - ValueCommitmentValue = 3, - /// The value commitment is randomized over this generator, - /// for privacy. - ValueCommitmentRandomness = 4, - - /// The spender proves discrete log with respect to this - /// base at spend time. - SpendingKeyGenerator = 5, - - Max = 6, -} - -pub trait ToUniform { - fn to_uniform(digest: &[u8]) -> Self; -} - -/// This is an extension to the pairing Engine trait which -/// offers a scalar field for the embedded curve (Jubjub) -/// and some pre-computed parameters. -pub trait JubjubEngine: Engine { - /// The scalar field of the Jubjub curve - type Fs: PrimeField + SqrtField + ToUniform; - /// The parameters of Jubjub and the Sapling protocol - type Params: JubjubParams; -} - -/// The pre-computed parameters for Jubjub, including curve -/// constants and various limits and window tables. -pub trait JubjubParams: Sized { - /// The `d` constant of the twisted Edwards curve. - fn edwards_d(&self) -> &E::Fr; - /// The `A` constant of the birationally equivalent Montgomery curve. - fn montgomery_a(&self) -> &E::Fr; - /// The `A` constant, doubled. - fn montgomery_2a(&self) -> &E::Fr; - /// The scaling factor used for conversion from the Montgomery form. - fn scale(&self) -> &E::Fr; - /// Returns the generators (for each segment) used in all Pedersen commitments. - fn pedersen_hash_generators(&self) -> &[edwards::Point]; - /// Returns the exp table for Pedersen hashes. - fn pedersen_hash_exp_table(&self) -> &[Vec>>]; - /// Returns the maximum number of chunks per segment of the Pedersen hash. - fn pedersen_hash_chunks_per_generator(&self) -> usize; - /// Returns the pre-computed window tables [-4, 3, 2, 1, 1, 2, 3, 4] of different - /// magnitudes of the Pedersen hash segment generators. - fn pedersen_circuit_generators(&self) -> &[Vec>]; - - /// Returns the number of chunks needed to represent a full scalar during fixed-base - /// exponentiation. - fn fixed_base_chunks_per_generator(&self) -> usize; - /// Returns a fixed generator. - fn generator(&self, base: FixedGenerators) -> &edwards::Point; - /// Returns a window table [0, 1, ..., 8] for different magnitudes of some - /// fixed generator. - fn circuit_generators(&self, _: FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>]; - /// Returns the window size for exponentiation of Pedersen hash generators - /// outside the circuit - fn pedersen_hash_exp_window_size() -> u32; -} - -impl JubjubEngine for Bls12 { - type Fs = self::fs::Fs; - type Params = JubjubBls12; -} - -pub struct JubjubBls12 { - edwards_d: Fr, - montgomery_a: Fr, - montgomery_2a: Fr, - scale: Fr, - - pedersen_hash_generators: Vec>, - pedersen_hash_exp: Vec>>>, - pedersen_circuit_generators: Vec>>, - - fixed_base_generators: Vec>, - fixed_base_circuit_generators: Vec>>, -} - -impl JubjubParams for JubjubBls12 { - fn edwards_d(&self) -> &Fr { - &self.edwards_d - } - fn montgomery_a(&self) -> &Fr { - &self.montgomery_a - } - fn montgomery_2a(&self) -> &Fr { - &self.montgomery_2a - } - fn scale(&self) -> &Fr { - &self.scale - } - fn pedersen_hash_generators(&self) -> &[edwards::Point] { - &self.pedersen_hash_generators - } - fn pedersen_hash_exp_table(&self) -> &[Vec>>] { - &self.pedersen_hash_exp - } - fn pedersen_hash_chunks_per_generator(&self) -> usize { - 63 - } - fn fixed_base_chunks_per_generator(&self) -> usize { - 84 - } - fn pedersen_circuit_generators(&self) -> &[Vec>] { - &self.pedersen_circuit_generators - } - fn generator(&self, base: FixedGenerators) -> &edwards::Point { - &self.fixed_base_generators[base as usize] - } - fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>] { - &self.fixed_base_circuit_generators[base as usize][..] - } - fn pedersen_hash_exp_window_size() -> u32 { - 8 - } -} - -impl JubjubBls12 { - pub fn new() -> Self { - let montgomery_a = Fr::from_str("40962").unwrap(); - let montgomery_2a = montgomery_a.double(); - - let mut tmp_params = JubjubBls12 { - // d = -(10240/10241) - edwards_d: Fr::from_str( - "19257038036680949359750312669786877991949435402254120286184196891950884077233", - ) - .unwrap(), - // A = 40962 - montgomery_a, - // 2A = 2.A - montgomery_2a, - // scaling factor = sqrt(4 / (a - d)) - scale: Fr::from_str( - "17814886934372412843466061268024708274627479829237077604635722030778476050649", - ) - .unwrap(), - - // We'll initialize these below - pedersen_hash_generators: vec![], - pedersen_hash_exp: vec![], - pedersen_circuit_generators: vec![], - fixed_base_generators: vec![], - fixed_base_circuit_generators: vec![], - }; - - // Create the bases for the Pedersen hashes - { - let mut pedersen_hash_generators = vec![]; - - for m in 0..6 { - use byteorder::{LittleEndian, WriteBytesExt}; - - let mut segment_number = [0u8; 4]; - (&mut segment_number[0..4]) - .write_u32::(m) - .unwrap(); - - pedersen_hash_generators.push(JubjubBls12::find_group_hash( - &segment_number, - constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, - &tmp_params, - )); - } - - JubjubBls12::check_consistency_of_pedersen_hash_generators( - &tmp_params, - &pedersen_hash_generators, - ); - tmp_params.pedersen_hash_generators = pedersen_hash_generators; - } - - // Create the exp table for the Pedersen hash generators - { - let mut pedersen_hash_exp = vec![]; - - for g in &tmp_params.pedersen_hash_generators { - let mut g = g.clone(); - - let window = JubjubBls12::pedersen_hash_exp_window_size(); - - let mut tables = vec![]; - - let mut num_bits = 0; - while num_bits <= fs::Fs::NUM_BITS { - let mut table = Vec::with_capacity(1 << window); - - let mut base = edwards::Point::zero(); - - for _ in 0..(1 << window) { - table.push(base.clone()); - base = base.add(&g, &tmp_params); - } - - tables.push(table); - num_bits += window; - - for _ in 0..window { - g = g.double(&tmp_params); - } - } - - pedersen_hash_exp.push(tables); - } - - tmp_params.pedersen_hash_exp = pedersen_hash_exp; - } - - // Create the bases for other parts of the protocol - { - let mut fixed_base_generators = - vec![edwards::Point::zero(); FixedGenerators::Max as usize]; - - fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = - JubjubBls12::find_group_hash( - &[], - constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, - &tmp_params, - ); - - fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] = - JubjubBls12::find_group_hash( - b"r", - constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, - &tmp_params, - ); - - fixed_base_generators[FixedGenerators::NullifierPosition as usize] = - JubjubBls12::find_group_hash( - &[], - constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, - &tmp_params, - ); - - fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = - JubjubBls12::find_group_hash( - b"v", - constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, - &tmp_params, - ); - - fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] = - JubjubBls12::find_group_hash( - b"r", - constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, - &tmp_params, - ); - - fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = - JubjubBls12::find_group_hash( - &[], - constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, - &tmp_params, - ); - - // Check for duplicates, far worse than spec inconsistencies! - for (i, p1) in fixed_base_generators.iter().enumerate() { - if p1 == &edwards::Point::zero() { - panic!("Neutral element!"); - } - - for p2 in fixed_base_generators.iter().skip(i + 1) { - if p1 == p2 { - panic!("Duplicate generator!"); - } - } - } - - tmp_params.fixed_base_generators = fixed_base_generators; - } - - // Create the 2-bit window table lookups for each 4-bit - // "chunk" in each segment of the Pedersen hash - { - let mut pedersen_circuit_generators = vec![]; - - // Process each segment - for gen in tmp_params.pedersen_hash_generators.iter().cloned() { - let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params); - let mut windows = vec![]; - for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() { - // Create (x, y) coeffs for this chunk - let mut coeffs = vec![]; - let mut g = gen.clone(); - - // coeffs = g, g*2, g*3, g*4 - for _ in 0..4 { - coeffs.push(g.to_xy().expect("cannot produce O")); - g = g.add(&gen, &tmp_params); - } - windows.push(coeffs); - - // Our chunks are separated by 2 bits to prevent overlap. - for _ in 0..4 { - gen = gen.double(&tmp_params); - } - } - pedersen_circuit_generators.push(windows); - } - - tmp_params.pedersen_circuit_generators = pedersen_circuit_generators; - } - - // Create the 3-bit window table lookups for fixed-base - // exp of each base in the protocol. - { - let mut fixed_base_circuit_generators = vec![]; - - for mut gen in tmp_params.fixed_base_generators.iter().cloned() { - let mut windows = vec![]; - for _ in 0..tmp_params.fixed_base_chunks_per_generator() { - let mut coeffs = vec![(Fr::zero(), Fr::one())]; - let mut g = gen.clone(); - for _ in 0..7 { - coeffs.push(g.to_xy()); - g = g.add(&gen, &tmp_params); - } - windows.push(coeffs); - - // gen = gen * 8 - gen = g; - } - fixed_base_circuit_generators.push(windows); - } - - tmp_params.fixed_base_circuit_generators = fixed_base_circuit_generators; - } - - tmp_params - } - - fn find_group_hash( - m: &[u8], - personalization: &[u8; 8], - params: &E::Params, - ) -> edwards::Point { - let mut tag = m.to_vec(); - let i = tag.len(); - tag.push(0u8); - - loop { - let gh = group_hash(&tag, personalization, params); - - // We don't want to overflow and start reusing generators - assert!(tag[i] != u8::max_value()); - tag[i] += 1; - - if let Some(gh) = gh { - break gh; - } - } - } - - /// Check for simple relations between the generators, that make finding collisions easy; - /// far worse than spec inconsistencies! - fn check_consistency_of_pedersen_hash_generators( - tmp_params: &E::Params, - pedersen_hash_generators: &[edwards::Point], - ) { - for (i, p1) in pedersen_hash_generators.iter().enumerate() { - if p1 == &edwards::Point::zero() { - panic!("Neutral element!"); - } - for p2 in pedersen_hash_generators.iter().skip(i + 1) { - if p1 == p2 { - panic!("Duplicate generator!"); - } - if p1 == &p2.negate() { - panic!("Inverse generator!"); - } - } - - // check for a generator being the sum of any other two - for (j, p2) in pedersen_hash_generators.iter().enumerate() { - if j == i { - continue; - } - for (k, p3) in pedersen_hash_generators.iter().enumerate() { - if k == j || k == i { - continue; - } - let sum = &p2.add(&p3, &tmp_params); - if sum == p1 { - panic!("Linear relation between generators!"); - } - } - } - } - } -} - -#[test] -fn test_jubjub_bls12() { - use hex_literal::hex; - - let params = JubjubBls12::new(); - - tests::test_suite::(¶ms); - - let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31"); - let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); - let q = edwards::Point::::get_for_y( - Fr::from_str( - "22440861827555040311190986994816762244378363690614952020532787748720529117853", - ) - .unwrap(), - false, - ¶ms, - ) - .unwrap(); - - assert!(p == q); - - // Same thing, but sign bit set - let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1"); - let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); - let q = edwards::Point::::get_for_y( - Fr::from_str( - "22440861827555040311190986994816762244378363690614952020532787748720529117853", - ) - .unwrap(), - true, - ¶ms, - ) - .unwrap(); - - assert!(p == q); -} - -#[test] -#[should_panic(expected = "Linear relation between generators!")] -fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() { - let params = JubjubBls12::new(); - - let mut pedersen_hash_generators: Vec> = vec![]; - - use byteorder::{LittleEndian, WriteBytesExt}; - - for m in 0..5 { - let mut segment_number = [0u8; 4]; - (&mut segment_number[0..4]) - .write_u32::(m) - .unwrap(); - - let p = JubjubBls12::find_group_hash( - &segment_number, - constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, - ¶ms, - ); - pedersen_hash_generators.push(p); - } - - let p1 = pedersen_hash_generators[0].clone(); - let p2 = pedersen_hash_generators[1].clone(); - - //test for linear relation - pedersen_hash_generators.push(p1.add(&p2, ¶ms)); - - JubjubBls12::check_consistency_of_pedersen_hash_generators(¶ms, &pedersen_hash_generators); -} diff --git a/zcash_primitives/src/jubjub/montgomery.rs b/zcash_primitives/src/jubjub/montgomery.rs deleted file mode 100644 index 9cad803e82..0000000000 --- a/zcash_primitives/src/jubjub/montgomery.rs +++ /dev/null @@ -1,317 +0,0 @@ -use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -use subtle::CtOption; - -use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; - -use rand_core::RngCore; - -use std::marker::PhantomData; - -// Represents the affine point (X, Y) -pub struct Point { - x: E::Fr, - y: E::Fr, - infinity: bool, - _marker: PhantomData, -} - -fn convert_subgroup(from: &Point) -> Point { - Point { - x: from.x, - y: from.y, - infinity: from.infinity, - _marker: PhantomData, - } -} - -impl From> for Point { - fn from(p: Point) -> Point { - convert_subgroup(&p) - } -} - -impl Clone for Point { - fn clone(&self) -> Self { - convert_subgroup(self) - } -} - -impl PartialEq for Point { - fn eq(&self, other: &Point) -> bool { - match (self.infinity, other.infinity) { - (true, true) => true, - (true, false) | (false, true) => false, - (false, false) => self.x == other.x && self.y == other.y, - } - } -} - -impl Point { - pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> CtOption { - // Given an x on the curve, y = sqrt(x^3 + A*x^2 + x) - - let mut x2 = x.square(); - - let mut rhs = x2; - rhs.mul_assign(params.montgomery_a()); - rhs.add_assign(&x); - x2.mul_assign(&x); - rhs.add_assign(&x2); - - rhs.sqrt().map(|mut y| { - if y.into_repr().is_odd() != sign { - y = y.neg(); - } - - Point { - x, - y, - infinity: false, - _marker: PhantomData, - } - }) - } - - /// This guarantees the point is in the prime order subgroup - #[must_use] - pub fn mul_by_cofactor(&self, params: &E::Params) -> Point { - let tmp = self.double(params).double(params).double(params); - - convert_subgroup(&tmp) - } - - pub fn rand(rng: &mut R, params: &E::Params) -> Self { - loop { - let x = E::Fr::random(rng); - let sign = rng.next_u32() % 2 != 0; - - let p = Self::get_for_x(x, sign, params); - if p.is_some().into() { - return p.unwrap(); - } - } - } -} - -impl Point { - /// Convert from an Edwards point - pub fn from_edwards(e: &edwards::Point, params: &E::Params) -> Self { - let (x, y) = e.to_xy(); - - if y == E::Fr::one() { - // The only solution for y = 1 is x = 0. (0, 1) is - // the neutral element, so we map this to the point - // at infinity. - - Point::zero() - } else { - // The map from a twisted Edwards curve is defined as - // (x, y) -> (u, v) where - // u = (1 + y) / (1 - y) - // v = u / x - // - // This mapping is not defined for y = 1 and for x = 0. - // - // We have that y != 1 above. If x = 0, the only - // solutions for y are 1 (contradiction) or -1. - if x.is_zero() { - // (0, -1) is the point of order two which is not - // the neutral element, so we map it to (0, 0) which is - // the only affine point of order 2. - - Point { - x: E::Fr::zero(), - y: E::Fr::zero(), - infinity: false, - _marker: PhantomData, - } - } else { - // The mapping is defined as above. - // - // (x, y) -> (u, v) where - // u = (1 + y) / (1 - y) - // v = u / x - - let mut u = E::Fr::one(); - u.add_assign(&y); - { - let mut tmp = E::Fr::one(); - tmp.sub_assign(&y); - u.mul_assign(&tmp.invert().unwrap()) - } - - let mut v = u; - v.mul_assign(&x.invert().unwrap()); - - // Scale it into the correct curve constants - v.mul_assign(params.scale()); - - Point { - x: u, - y: v, - infinity: false, - _marker: PhantomData, - } - } - } - } - - /// Attempts to cast this as a prime order element, failing if it's - /// not in the prime order subgroup. - pub fn as_prime_order(&self, params: &E::Params) -> Option> { - if self.mul(E::Fs::char(), params) == Point::zero() { - Some(convert_subgroup(self)) - } else { - None - } - } - - pub fn zero() -> Self { - Point { - x: E::Fr::zero(), - y: E::Fr::zero(), - infinity: true, - _marker: PhantomData, - } - } - - pub fn to_xy(&self) -> Option<(E::Fr, E::Fr)> { - if self.infinity { - None - } else { - Some((self.x, self.y)) - } - } - - #[must_use] - pub fn negate(&self) -> Self { - let mut p = self.clone(); - - p.y = p.y.neg(); - - p - } - - #[must_use] - pub fn double(&self, params: &E::Params) -> Self { - if self.infinity { - return Point::zero(); - } - - // (0, 0) is the point of order 2. Doubling - // produces the point at infinity. - if self.y == E::Fr::zero() { - return Point::zero(); - } - - // This is a standard affine point doubling formula - // See 4.3.2 The group law for Weierstrass curves - // Montgomery curves and the Montgomery Ladder - // Daniel J. Bernstein and Tanja Lange - - let mut delta = E::Fr::one(); - { - let mut tmp = *params.montgomery_a(); - tmp.mul_assign(&self.x); - tmp = tmp.double(); - delta.add_assign(&tmp); - } - { - let mut tmp = self.x.square(); - delta.add_assign(&tmp); - tmp = tmp.double(); - delta.add_assign(&tmp); - } - { - let tmp = self.y.double(); - // y is nonzero so this must be nonzero - delta.mul_assign(&tmp.invert().unwrap()); - } - - let mut x3 = delta.square(); - x3.sub_assign(params.montgomery_a()); - x3.sub_assign(&self.x); - x3.sub_assign(&self.x); - - let mut y3 = x3; - y3.sub_assign(&self.x); - y3.mul_assign(&delta); - y3.add_assign(&self.y); - y3 = y3.neg(); - - Point { - x: x3, - y: y3, - infinity: false, - _marker: PhantomData, - } - } - - #[must_use] - pub fn add(&self, other: &Self, params: &E::Params) -> Self { - // This is a standard affine point addition formula - // See 4.3.2 The group law for Weierstrass curves - // Montgomery curves and the Montgomery Ladder - // Daniel J. Bernstein and Tanja Lange - - match (self.infinity, other.infinity) { - (true, true) => Point::zero(), - (true, false) => other.clone(), - (false, true) => self.clone(), - (false, false) => { - if self.x == other.x { - if self.y == other.y { - self.double(params) - } else { - Point::zero() - } - } else { - let mut delta = other.y; - delta.sub_assign(&self.y); - { - let mut tmp = other.x; - tmp.sub_assign(&self.x); - // self.x != other.x, so this must be nonzero - delta.mul_assign(&tmp.invert().unwrap()); - } - - let mut x3 = delta.square(); - x3.sub_assign(params.montgomery_a()); - x3.sub_assign(&self.x); - x3.sub_assign(&other.x); - - let mut y3 = x3; - y3.sub_assign(&self.x); - y3.mul_assign(&delta); - y3.add_assign(&self.y); - y3 = y3.neg(); - - Point { - x: x3, - y: y3, - infinity: false, - _marker: PhantomData, - } - } - } - } - } - - #[must_use] - pub fn mul::Repr>>(&self, scalar: S, params: &E::Params) -> Self { - // Standard double-and-add scalar multiplication - - let mut res = Self::zero(); - - for b in BitIterator::new(scalar.into()) { - res = res.double(params); - - if b { - res = res.add(self, params); - } - } - - res - } -} diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs deleted file mode 100644 index 84b3a96497..0000000000 --- a/zcash_primitives/src/jubjub/tests.rs +++ /dev/null @@ -1,413 +0,0 @@ -use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; - -use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; -use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; - -use rand_core::{RngCore, SeedableRng}; -use rand_xorshift::XorShiftRng; - -pub fn test_suite(params: &E::Params) { - test_back_and_forth::(params); - test_jubjub_params::(params); - test_rand::(params); - test_get_for::(params); - test_identities::(params); - test_addition_associativity::(params); - test_order::(params); - test_mul_associativity::(params); - test_loworder::(params); - test_read_write::(params); -} - -fn is_on_mont_curve>(x: E::Fr, y: E::Fr, params: &P) -> bool { - let lhs = y.square(); - - let x2 = x.square(); - - let mut x3 = x2; - x3.mul_assign(&x); - - let mut rhs = x2; - rhs.mul_assign(params.montgomery_a()); - rhs.add_assign(&x); - rhs.add_assign(&x3); - - lhs == rhs -} - -fn is_on_twisted_edwards_curve>( - x: E::Fr, - y: E::Fr, - params: &P, -) -> bool { - let x2 = x.square(); - - let y2 = y.square(); - - // -x^2 + y^2 - let mut lhs = y2; - lhs.sub_assign(&x2); - - // 1 + d x^2 y^2 - let mut rhs = y2; - rhs.mul_assign(&x2); - rhs.mul_assign(params.edwards_d()); - rhs.add_assign(&E::Fr::one()); - - lhs == rhs -} - -fn test_loworder(params: &E::Params) { - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - let inf = montgomery::Point::zero(); - - // try to find a point of order 8 - let p = loop { - let r = montgomery::Point::::rand(rng, params).mul(E::Fs::char(), params); - - let r2 = r.double(params); - let r4 = r2.double(params); - let r8 = r4.double(params); - - if r2 != inf && r4 != inf && r8 == inf { - break r; - } - }; - - let mut loworder_points = vec![]; - { - let mut tmp = p.clone(); - - for _ in 0..8 { - assert!(!loworder_points.contains(&tmp)); - loworder_points.push(tmp.clone()); - tmp = tmp.add(&p, params); - } - } - assert!(loworder_points[7] == inf); -} - -fn test_mul_associativity(params: &E::Params) { - use self::edwards::Point; - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..100 { - // Pick a random point and multiply it by the cofactor - let base = Point::::rand(rng, params).mul_by_cofactor(params); - - let mut a = E::Fs::random(rng); - let b = E::Fs::random(rng); - let c = E::Fs::random(rng); - - let res1 = base.mul(a, params).mul(b, params).mul(c, params); - let res2 = base.mul(b, params).mul(c, params).mul(a, params); - let res3 = base.mul(c, params).mul(a, params).mul(b, params); - a.mul_assign(&b); - a.mul_assign(&c); - let res4 = base.mul(a, params); - - assert!(res1 == res2); - assert!(res2 == res3); - assert!(res3 == res4); - - let (x, y) = res1.to_xy(); - assert!(is_on_twisted_edwards_curve(x, y, params)); - - let (x, y) = res2.to_xy(); - assert!(is_on_twisted_edwards_curve(x, y, params)); - - let (x, y) = res3.to_xy(); - assert!(is_on_twisted_edwards_curve(x, y, params)); - } -} - -fn test_order(params: &E::Params) { - use self::edwards::Point; - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - // The neutral element is in the prime order subgroup. - assert!(Point::::zero() - .as_prime_order(params) - .is_some()); - - for _ in 0..50 { - // Pick a random point and multiply it by the cofactor - let base = Point::::rand(rng, params).mul_by_cofactor(params); - - // Any point multiplied by the cofactor will be in the prime - // order subgroup - assert!(base.as_prime_order(params).is_some()); - } - - // It's very likely that at least one out of 50 random points on the curve - // is not in the prime order subgroup. - let mut at_least_one_not_in_prime_order_subgroup = false; - for _ in 0..50 { - // Pick a random point. - let base = Point::::rand(rng, params); - - at_least_one_not_in_prime_order_subgroup |= base.as_prime_order(params).is_none(); - } - assert!(at_least_one_not_in_prime_order_subgroup); -} - -fn test_addition_associativity(params: &E::Params) { - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - use self::montgomery::Point; - - let a = Point::::rand(rng, params); - let b = Point::::rand(rng, params); - let c = Point::::rand(rng, params); - - assert!(a.add(&b, ¶ms).add(&c, ¶ms) == c.add(&a, ¶ms).add(&b, ¶ms)); - } - - for _ in 0..1000 { - use self::edwards::Point; - - let a = Point::::rand(rng, params); - let b = Point::::rand(rng, params); - let c = Point::::rand(rng, params); - - assert!(a.add(&b, ¶ms).add(&c, ¶ms) == c.add(&a, ¶ms).add(&b, ¶ms)); - } -} - -fn test_identities(params: &E::Params) { - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - { - use self::edwards::Point; - - let z = Point::::zero(); - assert!(z.double(¶ms) == z); - assert!(z.negate() == z); - - for _ in 0..100 { - let r = Point::::rand(rng, params); - - assert!(r.add(&Point::zero(), ¶ms) == r); - assert!(r.add(&r.negate(), ¶ms) == Point::zero()); - } - } - - { - use self::montgomery::Point; - - let z = Point::::zero(); - assert!(z.double(¶ms) == z); - assert!(z.negate() == z); - - for _ in 0..100 { - let r = Point::::rand(rng, params); - - assert!(r.add(&Point::zero(), ¶ms) == r); - assert!(r.add(&r.negate(), ¶ms) == Point::zero()); - } - } -} - -fn test_get_for(params: &E::Params) { - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let y = E::Fr::random(rng); - let sign = rng.next_u32() % 2 == 1; - - let p = edwards::Point::::get_for_y(y, sign, params); - if bool::from(p.is_some()) { - let mut p = p.unwrap(); - assert!(p.to_xy().0.into_repr().is_odd() == sign); - p = p.negate(); - assert!(edwards::Point::::get_for_y(y, !sign, params).unwrap() == p); - } - } -} - -fn test_read_write(params: &E::Params) { - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let e = edwards::Point::::rand(rng, params); - - let mut v = vec![]; - e.write(&mut v).unwrap(); - - let e2 = edwards::Point::read(&v[..], params).unwrap(); - - assert!(e == e2); - } -} - -fn test_rand(params: &E::Params) { - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let p = montgomery::Point::::rand(rng, params); - let e = edwards::Point::::rand(rng, params); - - { - let (x, y) = p.to_xy().unwrap(); - assert!(is_on_mont_curve(x, y, params)); - } - - { - let (x, y) = e.to_xy(); - assert!(is_on_twisted_edwards_curve(x, y, params)); - } - } -} - -fn test_back_and_forth(params: &E::Params) { - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x5d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - for _ in 0..1000 { - let s = E::Fs::random(rng); - let edwards_p1 = edwards::Point::::rand(rng, params); - let mont_p1 = montgomery::Point::from_edwards(&edwards_p1, params); - let mont_p2 = montgomery::Point::::rand(rng, params); - let edwards_p2 = edwards::Point::from_montgomery(&mont_p2, params); - - let mont = mont_p1.add(&mont_p2, params).mul(s, params); - let edwards = edwards_p1.add(&edwards_p2, params).mul(s, params); - - assert!(montgomery::Point::from_edwards(&edwards, params) == mont); - - assert!(edwards::Point::from_montgomery(&mont, params) == edwards); - } -} - -fn test_jubjub_params(params: &E::Params) { - // a = -1 - let a = E::Fr::one().neg(); - - { - // Check that 2A is consistent with A - assert_eq!(¶ms.montgomery_a().double(), params.montgomery_2a()); - } - - { - // The twisted Edwards addition law is complete when d is nonsquare - // and a is square. - - assert!(bool::from(params.edwards_d().sqrt().is_none())); - assert!(bool::from(a.sqrt().is_some())); - } - - { - // Other convenient sanity checks regarding d - - // tmp = d - let mut tmp = *params.edwards_d(); - - // 1 / d is nonsquare - assert!(bool::from(tmp.invert().unwrap().sqrt().is_none())); - - // tmp = -d - tmp = tmp.neg(); - - // -d is nonsquare - assert!(bool::from(tmp.sqrt().is_none())); - - // 1 / -d is nonsquare - assert!(bool::from(tmp.invert().unwrap().sqrt().is_none())); - } - - { - // Check that A^2 - 4 is nonsquare: - let mut tmp = params.montgomery_a().square(); - tmp.sub_assign(&E::Fr::from_str("4").unwrap()); - assert!(bool::from(tmp.sqrt().is_none())); - } - - { - // Check that A - 2 is nonsquare: - let mut tmp = params.montgomery_a().clone(); - tmp.sub_assign(&E::Fr::from_str("2").unwrap()); - assert!(bool::from(tmp.sqrt().is_none())); - } - - { - // Check the validity of the scaling factor - let mut tmp = a; - tmp.sub_assign(¶ms.edwards_d()); - tmp = tmp.invert().unwrap(); - tmp.mul_assign(&E::Fr::from_str("4").unwrap()); - tmp = tmp.sqrt().unwrap(); - assert_eq!(&tmp, params.scale()); - } - - { - // Check that the number of windows per generator - // in the Pedersen hash does not allow for collisions - - let mut cur = E::Fs::one().into_repr(); - - let mut max = E::Fs::char(); - { - max.sub_noborrow(&E::Fs::one().into_repr()); - max.div2(); - } - - let mut pacc = E::Fs::zero().into_repr(); - let mut nacc = E::Fs::char(); - - for _ in 0..params.pedersen_hash_chunks_per_generator() { - // tmp = cur * 4 - let mut tmp = cur; - tmp.mul2(); - tmp.mul2(); - - pacc.add_nocarry(&tmp); - nacc.sub_noborrow(&tmp); - - assert!(pacc < max); - assert!(pacc < nacc); - - // cur = cur * 16 - for _ in 0..4 { - cur.mul2(); - } - } - } - - { - // Check that the number of windows for fixed-base - // scalar multiplication is sufficient for all scalars. - - assert!(params.fixed_base_chunks_per_generator() * 3 >= E::Fs::NUM_BITS as usize); - - // ... and that it's *just* efficient enough. - - assert!((params.fixed_base_chunks_per_generator() - 1) * 3 < E::Fs::NUM_BITS as usize); - } -} diff --git a/zcash_primitives/src/keys.rs b/zcash_primitives/src/keys.rs index 76914baaae..fdde7f3acf 100644 --- a/zcash_primitives/src/keys.rs +++ b/zcash_primitives/src/keys.rs @@ -5,12 +5,14 @@ //! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents use crate::{ - jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown}, + constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR}, primitives::{ProofGenerationKey, ViewingKey}, }; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; +use group::{Group, GroupEncoding}; use std::io::{self, Read, Write}; +use subtle::CtOption; pub const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed"; @@ -37,50 +39,48 @@ pub struct OutgoingViewingKey(pub [u8; 32]); /// A Sapling expanded spending key #[derive(Clone)] -pub struct ExpandedSpendingKey { - pub ask: E::Fs, - pub nsk: E::Fs, +pub struct ExpandedSpendingKey { + pub ask: jubjub::Fr, + pub nsk: jubjub::Fr, pub ovk: OutgoingViewingKey, } /// A Sapling full viewing key #[derive(Debug)] -pub struct FullViewingKey { - pub vk: ViewingKey, +pub struct FullViewingKey { + pub vk: ViewingKey, pub ovk: OutgoingViewingKey, } -impl ExpandedSpendingKey { +impl ExpandedSpendingKey { pub fn from_spending_key(sk: &[u8]) -> Self { - let ask = E::Fs::to_uniform(prf_expand(sk, &[0x00]).as_bytes()); - let nsk = E::Fs::to_uniform(prf_expand(sk, &[0x01]).as_bytes()); + let ask = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x00]).as_array()); + let nsk = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x01]).as_array()); let mut ovk = OutgoingViewingKey([0u8; 32]); ovk.0 .copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]); ExpandedSpendingKey { ask, nsk, ovk } } - pub fn proof_generation_key(&self, params: &E::Params) -> ProofGenerationKey { + pub fn proof_generation_key(&self) -> ProofGenerationKey { ProofGenerationKey { - ak: params - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(self.ask, params), + ak: SPENDING_KEY_GENERATOR * self.ask, nsk: self.nsk, } } pub fn read(mut reader: R) -> io::Result { - let mut ask_repr = ::Repr::default(); - ask_repr.read_le(&mut reader)?; - let ask = E::Fs::from_repr(ask_repr) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let mut ask_repr = [0u8; 32]; + reader.read_exact(ask_repr.as_mut())?; + let ask = jubjub::Fr::from_repr(ask_repr) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "ask not in field"))?; - let mut nsk_repr = ::Repr::default(); - nsk_repr.read_le(&mut reader)?; - let nsk = E::Fs::from_repr(nsk_repr) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let mut nsk_repr = [0u8; 32]; + reader.read_exact(nsk_repr.as_mut())?; + let nsk = jubjub::Fr::from_repr(nsk_repr) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?; - let mut ovk = [0; 32]; + let mut ovk = [0u8; 32]; reader.read_exact(&mut ovk)?; Ok(ExpandedSpendingKey { @@ -91,8 +91,8 @@ impl ExpandedSpendingKey { } pub fn write(&self, mut writer: W) -> io::Result<()> { - self.ask.into_repr().write_le(&mut writer)?; - self.nsk.into_repr().write_le(&mut writer)?; + writer.write_all(self.ask.to_repr().as_ref())?; + writer.write_all(self.nsk.to_repr().as_ref())?; writer.write_all(&self.ovk.0)?; Ok(()) @@ -106,7 +106,7 @@ impl ExpandedSpendingKey { } } -impl Clone for FullViewingKey { +impl Clone for FullViewingKey { fn clone(&self) -> Self { FullViewingKey { vk: ViewingKey { @@ -118,51 +118,44 @@ impl Clone for FullViewingKey { } } -impl FullViewingKey { - pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey, params: &E::Params) -> Self { +impl FullViewingKey { + pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self { FullViewingKey { vk: ViewingKey { - ak: params - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(expsk.ask, params), - nk: params - .generator(FixedGenerators::ProofGenerationKey) - .mul(expsk.nsk, params), + ak: SPENDING_KEY_GENERATOR * expsk.ask, + nk: PROOF_GENERATION_KEY_GENERATOR * expsk.nsk, }, ovk: expsk.ovk, } } - pub fn read(mut reader: R, params: &E::Params) -> io::Result { - let ak = edwards::Point::::read(&mut reader, params)?; - let ak = match ak.as_prime_order(params) { - Some(p) => p, - None => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "ak not in prime-order subgroup", - )); - } + pub fn read(mut reader: R) -> io::Result { + let ak = { + let mut buf = [0u8; 32]; + reader.read_exact(&mut buf)?; + jubjub::SubgroupPoint::from_bytes(&buf).and_then(|p| CtOption::new(p, !p.is_identity())) + }; + let nk = { + let mut buf = [0u8; 32]; + reader.read_exact(&mut buf)?; + jubjub::SubgroupPoint::from_bytes(&buf) }; - if ak == edwards::Point::zero() { + if ak.is_none().into() { return Err(io::Error::new( - io::ErrorKind::InvalidData, + io::ErrorKind::InvalidInput, "ak not of prime order", )); } + if nk.is_none().into() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "nk not in prime-order subgroup", + )); + } + let ak = ak.unwrap(); + let nk = nk.unwrap(); - let nk = edwards::Point::::read(&mut reader, params)?; - let nk = match nk.as_prime_order(params) { - Some(p) => p, - None => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "nk not in prime-order subgroup", - )); - } - }; - - let mut ovk = [0; 32]; + let mut ovk = [0u8; 32]; reader.read_exact(&mut ovk)?; Ok(FullViewingKey { @@ -172,8 +165,8 @@ impl FullViewingKey { } pub fn write(&self, mut writer: W) -> io::Result<()> { - self.vk.ak.write(&mut writer)?; - self.vk.nk.write(&mut writer)?; + writer.write_all(&self.vk.ak.to_bytes())?; + writer.write_all(&self.vk.nk.to_bytes())?; writer.write_all(&self.ovk.0)?; Ok(()) @@ -189,35 +182,31 @@ impl FullViewingKey { #[cfg(test)] mod tests { - use crate::jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder}; - use pairing::bls12_381::Bls12; - use std::error::Error; + use group::{Group, GroupEncoding}; use super::FullViewingKey; - use crate::JUBJUB; + use crate::constants::SPENDING_KEY_GENERATOR; #[test] fn ak_must_be_prime_order() { let mut buf = [0; 96]; - let identity = edwards::Point::::zero(); + let identity = jubjub::SubgroupPoint::identity(); // Set both ak and nk to the identity. - identity.write(&mut buf[0..32]).unwrap(); - identity.write(&mut buf[32..64]).unwrap(); + buf[0..32].copy_from_slice(&identity.to_bytes()); + buf[32..64].copy_from_slice(&identity.to_bytes()); // ak is not allowed to be the identity. assert_eq!( - FullViewingKey::::read(&buf[..], &JUBJUB) - .unwrap_err() - .description(), + FullViewingKey::read(&buf[..]).unwrap_err().to_string(), "ak not of prime order" ); // Set ak to a basepoint. - let basepoint = JUBJUB.generator(FixedGenerators::SpendingKeyGenerator); - basepoint.write(&mut buf[0..32]).unwrap(); + let basepoint = SPENDING_KEY_GENERATOR; + buf[0..32].copy_from_slice(&basepoint.to_bytes()); // nk is allowed to be the identity. - assert!(FullViewingKey::::read(&buf[..], &JUBJUB).is_ok()); + assert!(FullViewingKey::read(&buf[..]).is_ok()); } } diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index 8175a2628e..36fee0cd2c 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -3,16 +3,15 @@ //! `zcash_primitives` is a library that provides the core structs and functions necessary //! for working with Zcash. +#![cfg_attr(docsrs, feature(doc_cfg))] // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] -use lazy_static::lazy_static; - pub mod block; pub mod consensus; pub mod constants; +pub mod extensions; pub mod group_hash; -pub mod jubjub; pub mod keys; pub mod legacy; pub mod merkle_tree; @@ -24,14 +23,8 @@ pub mod redjubjub; pub mod sapling; pub mod serialize; pub mod transaction; -mod util; +pub mod util; pub mod zip32; #[cfg(test)] mod test_vectors; - -use crate::jubjub::JubjubBls12; - -lazy_static! { - pub static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; -} diff --git a/zcash_primitives/src/merkle_tree.rs b/zcash_primitives/src/merkle_tree.rs index 53600e5f3b..15550cf494 100644 --- a/zcash_primitives/src/merkle_tree.rs +++ b/zcash_primitives/src/merkle_tree.rs @@ -194,13 +194,7 @@ impl CommitmentTree { /// # Examples /// /// ``` -/// extern crate ff; -/// extern crate pairing; -/// extern crate rand_core; -/// extern crate zcash_primitives; -/// /// use ff::{Field, PrimeField}; -/// use pairing::bls12_381::Fr; /// use rand_core::OsRng; /// use zcash_primitives::{ /// merkle_tree::{CommitmentTree, IncrementalWitness}, @@ -211,13 +205,13 @@ impl CommitmentTree { /// /// let mut tree = CommitmentTree::::new(); /// -/// tree.append(Node::new(Fr::random(&mut rng).into_repr())); -/// tree.append(Node::new(Fr::random(&mut rng).into_repr())); +/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr())); +/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr())); /// let mut witness = IncrementalWitness::from_tree(&tree); /// assert_eq!(witness.position(), 1); /// assert_eq!(tree.root(), witness.root()); /// -/// let cmu = Node::new(Fr::random(&mut rng).into_repr()); +/// let cmu = Node::new(bls12_381::Scalar::random(&mut rng).to_repr()); /// tree.append(cmu); /// witness.append(cmu); /// assert_eq!(tree.root(), witness.root()); @@ -511,9 +505,8 @@ mod tests { use super::{CommitmentTree, Hashable, IncrementalWitness, MerklePath, PathFiller}; use crate::sapling::Node; - use ff::PrimeFieldRepr; use hex; - use pairing::bls12_381::FrRepr; + use std::convert::TryInto; use std::io::{self, Read, Write}; const HEX_EMPTY_ROOTS: [&str; 33] = [ @@ -1012,21 +1005,19 @@ mod tests { assert_eq!(tree.size(), 0); let mut witnesses = vec![]; - let mut last_cm = None; + let mut last_cmu = None; let mut paths_i = 0; let mut witness_ser_i = 0; for i in 0..16 { - let mut cm = FrRepr::default(); - cm.read_le(&hex::decode(commitments[i]).unwrap()[..]) - .expect("length is 32 bytes"); + let cmu = hex::decode(commitments[i]).unwrap(); - let cm = Node::new(cm); + let cmu = Node::new(cmu[..].try_into().unwrap()); // Witness here - witnesses.push((TestIncrementalWitness::from_tree(&tree), last_cm)); + witnesses.push((TestIncrementalWitness::from_tree(&tree), last_cmu)); // Now append a commitment to the tree - assert!(tree.append(cm).is_ok()); + assert!(tree.append(cmu).is_ok()); // Size incremented by one. assert_eq!(tree.size(), i + 1); @@ -1039,7 +1030,7 @@ mod tests { for (witness, leaf) in witnesses.as_mut_slice() { // Append the same commitment to all the witnesses - assert!(witness.append(cm).is_ok()); + assert!(witness.append(cmu).is_ok()); if let Some(leaf) = leaf { let path = witness.path().expect("should be able to create a path"); @@ -1063,7 +1054,7 @@ mod tests { assert_eq!(witness.root(), tree.root()); } - last_cm = Some(cm); + last_cmu = Some(cmu); } // Tree should be full now diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 747bd8d8ce..292724d1f9 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1,40 +1,31 @@ //! Implementation of in-band secret distribution for Zcash transactions. use crate::{ - jubjub::{ - edwards, - fs::{Fs, FsRepr}, - PrimeOrder, ToUniform, Unknown, - }, - primitives::{Diversifier, Note, PaymentAddress}, + consensus::{self, NetworkUpgrade, ZIP212_GRACE_PERIOD}, + primitives::{Diversifier, Note, PaymentAddress, Rseed}, }; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf}; -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::{Bls12, Fr}; +use ff::PrimeField; +use group::{cofactor::CofactorGroup, GroupEncoding}; use rand_core::{CryptoRng, RngCore}; +use std::convert::TryInto; use std::fmt; use std::str; -use crate::{keys::OutgoingViewingKey, JUBJUB}; +use crate::keys::OutgoingViewingKey; pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingKDF"; pub const PRF_OCK_PERSONALIZATION: &[u8; 16] = b"Zcash_Derive_ock"; -const COMPACT_NOTE_SIZE: usize = ( - 1 + // version +const COMPACT_NOTE_SIZE: usize = 1 + // version 11 + // diversifier 8 + // value - 32 - // rcv -); + 32; // rcv const NOTE_PLAINTEXT_SIZE: usize = COMPACT_NOTE_SIZE + 512; -const OUT_PLAINTEXT_SIZE: usize = ( - 32 + // pk_d - 32 - // esk -); +const OUT_PLAINTEXT_SIZE: usize = 32 + // pk_d + 32; // esk const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + 16; const OUT_CIPHERTEXT_SIZE: usize = OUT_PLAINTEXT_SIZE + 16; @@ -138,67 +129,47 @@ impl str::FromStr for Memo { } } -pub fn generate_esk(rng: &mut R) -> Fs { - // create random 64 byte buffer - let mut buffer = [0u8; 64]; - rng.fill_bytes(&mut buffer); - - // reduce to uniform value - Fs::to_uniform(&buffer[..]) -} - /// Sapling key agreement for note encryption. /// /// Implements section 5.4.4.3 of the Zcash Protocol Specification. -pub fn sapling_ka_agree<'a, P>(esk: &Fs, pk_d: &'a P) -> edwards::Point -where - edwards::Point: From<&'a P>, -{ - let p: edwards::Point = pk_d.into(); - - // Multiply by 8 - let p = p.mul_by_cofactor(&JUBJUB); - - // Multiply by esk - p.mul(*esk, &JUBJUB) +pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubjub::SubgroupPoint { + // [8 esk] pk_d + // ::clear_cofactor is implemented using + // ExtendedPoint::mul_by_cofactor in the jubjub crate. + CofactorGroup::clear_cofactor(&(pk_d * esk)) } /// Sapling KDF for note encryption. /// /// Implements section 5.4.4.4 of the Zcash Protocol Specification. -fn kdf_sapling( - dhsecret: edwards::Point, - epk: &edwards::Point, -) -> Blake2bHash { - let mut input = [0u8; 64]; - dhsecret.write(&mut input[0..32]).unwrap(); - epk.write(&mut input[32..64]).unwrap(); - +fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, epk: &jubjub::SubgroupPoint) -> Blake2bHash { Blake2bParams::new() .hash_length(32) .personal(KDF_SAPLING_PERSONALIZATION) - .hash(&input) + .to_state() + .update(&dhsecret.to_bytes()) + .update(&epk.to_bytes()) + .finalize() } /// Sapling PRF^ock. /// /// Implemented per section 5.4.2 of the Zcash Protocol Specification. -fn prf_ock( +pub fn prf_ock( ovk: &OutgoingViewingKey, - cv: &edwards::Point, - cmu: &Fr, - epk: &edwards::Point, + cv: &jubjub::ExtendedPoint, + cmu: &bls12_381::Scalar, + epk: &jubjub::SubgroupPoint, ) -> Blake2bHash { - let mut ock_input = [0u8; 128]; - ock_input[0..32].copy_from_slice(&ovk.0); - cv.write(&mut ock_input[32..64]).unwrap(); - cmu.into_repr().write_le(&mut ock_input[64..96]).unwrap(); - epk.write(&mut ock_input[96..128]).unwrap(); - Blake2bParams::new() .hash_length(32) .personal(PRF_OCK_PERSONALIZATION) - .hash(&ock_input) + .to_state() + .update(&ovk.0) + .update(&cv.to_bytes()) + .update(&cmu.to_repr()) + .update(&epk.to_bytes()) + .finalize() } /// An API for encrypting Sapling notes. @@ -208,51 +179,49 @@ fn prf_ock( /// are consistent with each other. /// /// Implements section 4.17.1 of the Zcash Protocol Specification. +/// NB: the example code is only covering the pre-Canopy case. /// /// # Examples /// /// ``` /// extern crate ff; -/// extern crate pairing; /// extern crate rand_core; /// extern crate zcash_primitives; /// /// use ff::Field; -/// use pairing::bls12_381::Bls12; /// use rand_core::OsRng; /// use zcash_primitives::{ -/// jubjub::fs::Fs, -/// keys::OutgoingViewingKey, +/// keys::{OutgoingViewingKey, prf_expand}, /// note_encryption::{Memo, SaplingNoteEncryption}, -/// primitives::{Diversifier, PaymentAddress, ValueCommitment}, -/// JUBJUB, +/// primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, /// }; /// /// let mut rng = OsRng; /// /// let diversifier = Diversifier([0; 11]); -/// let pk_d = diversifier.g_d::(&JUBJUB).unwrap(); +/// let pk_d = diversifier.g_d().unwrap(); /// let to = PaymentAddress::from_parts(diversifier, pk_d).unwrap(); /// let ovk = OutgoingViewingKey([0; 32]); /// /// let value = 1000; -/// let rcv = Fs::random(&mut rng); -/// let cv = ValueCommitment:: { +/// let rcv = jubjub::Fr::random(&mut rng); +/// let cv = ValueCommitment { /// value, /// randomness: rcv.clone(), /// }; -/// let note = to.create_note(value, rcv, &JUBJUB).unwrap(); -/// let cmu = note.cm(&JUBJUB); +/// let rcm = jubjub::Fr::random(&mut rng); +/// let note = to.create_note(value, Rseed::BeforeZip212(rcm)).unwrap(); +/// let cmu = note.cmu(); /// /// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), &mut rng); /// let encCiphertext = enc.encrypt_note_plaintext(); -/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.cm(&JUBJUB).into(), &cmu); +/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.commitment().into(), &cmu); /// ``` pub struct SaplingNoteEncryption { - epk: edwards::Point, - esk: Fs, - note: Note, - to: PaymentAddress, + epk: jubjub::SubgroupPoint, + esk: jubjub::Fr, + note: Note, + to: PaymentAddress, memo: Memo, ovk: OutgoingViewingKey, } @@ -261,13 +230,13 @@ impl SaplingNoteEncryption { /// Creates a new encryption context for the given note. pub fn new( ovk: OutgoingViewingKey, - note: Note, - to: PaymentAddress, + note: Note, + to: PaymentAddress, memo: Memo, rng: &mut R, ) -> SaplingNoteEncryption { - let esk = generate_esk(rng); - let epk = note.g_d.mul(esk, &JUBJUB); + let esk = note.generate_or_derive_esk(rng); + let epk = note.g_d * esk; SaplingNoteEncryption { epk, @@ -280,33 +249,39 @@ impl SaplingNoteEncryption { } /// Exposes the ephemeral secret key being used to encrypt this note. - pub fn esk(&self) -> &Fs { + pub fn esk(&self) -> &jubjub::Fr { &self.esk } /// Exposes the ephemeral public key being used to encrypt this note. - pub fn epk(&self) -> &edwards::Point { + pub fn epk(&self) -> &jubjub::SubgroupPoint { &self.epk } /// Generates `encCiphertext` for this note. pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] { - let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d()); + let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d().into()); let key = kdf_sapling(shared_secret, &self.epk); // Note plaintext encoding is defined in section 5.5 of the Zcash Protocol // Specification. let mut input = [0; NOTE_PLAINTEXT_SIZE]; - input[0] = 1; + input[0] = match self.note.rseed { + Rseed::BeforeZip212(_) => 1, + Rseed::AfterZip212(_) => 2, + }; input[1..12].copy_from_slice(&self.to.diversifier().0); (&mut input[12..20]) .write_u64::(self.note.value) .unwrap(); - self.note - .r - .into_repr() - .write_le(&mut input[20..COMPACT_NOTE_SIZE]) - .unwrap(); + match self.note.rseed { + Rseed::BeforeZip212(rcm) => { + input[20..COMPACT_NOTE_SIZE].copy_from_slice(rcm.to_repr().as_ref()); + } + Rseed::AfterZip212(rseed) => { + input[20..COMPACT_NOTE_SIZE].copy_from_slice(&rseed); + } + } input[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE].copy_from_slice(&self.memo.0); let mut output = [0u8; ENC_CIPHERTEXT_SIZE]; @@ -323,17 +298,14 @@ impl SaplingNoteEncryption { /// Generates `outCiphertext` for this note. pub fn encrypt_outgoing_plaintext( &self, - cv: &edwards::Point, - cmu: &Fr, + cv: &jubjub::ExtendedPoint, + cmu: &bls12_381::Scalar, ) -> [u8; OUT_CIPHERTEXT_SIZE] { let key = prf_ock(&self.ovk, &cv, &cmu, &self.epk); let mut input = [0u8; OUT_PLAINTEXT_SIZE]; - self.note.pk_d.write(&mut input[0..32]).unwrap(); - self.esk - .into_repr() - .write_le(&mut input[32..OUT_PLAINTEXT_SIZE]) - .unwrap(); + input[0..32].copy_from_slice(&self.note.pk_d.to_bytes()); + input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(self.esk.to_repr().as_ref()); let mut output = [0u8; OUT_CIPHERTEXT_SIZE]; assert_eq!( @@ -347,15 +319,16 @@ impl SaplingNoteEncryption { } } -fn parse_note_plaintext_without_memo( - ivk: &Fs, - cmu: &Fr, +fn parse_note_plaintext_without_memo( + height: u32, + ivk: &jubjub::Fr, + epk: &jubjub::SubgroupPoint, + cmu: &bls12_381::Scalar, plaintext: &[u8], -) -> Option<(Note, PaymentAddress)> { +) -> Option<(Note, PaymentAddress)> { // Check note plaintext version - match plaintext[0] { - 0x01 => (), - _ => return None, + if !plaintext_version_is_valid::

(height, plaintext[0]) { + return None; } let mut d = [0u8; 11]; @@ -363,26 +336,58 @@ fn parse_note_plaintext_without_memo( let v = (&plaintext[12..20]).read_u64::().ok()?; - let mut rcm = FsRepr::default(); - rcm.read_le(&plaintext[20..COMPACT_NOTE_SIZE]).ok()?; - let rcm = Fs::from_repr(rcm).ok()?; + let r: [u8; 32] = plaintext[20..COMPACT_NOTE_SIZE] + .try_into() + .expect("slice is the correct length"); + + let rseed = if plaintext[0] == 0x01 { + let rcm = jubjub::Fr::from_repr(r)?; + Rseed::BeforeZip212(rcm) + } else { + Rseed::AfterZip212(r) + }; let diversifier = Diversifier(d); - let pk_d = diversifier - .g_d::(&JUBJUB)? - .mul(ivk.into_repr(), &JUBJUB); + let pk_d = diversifier.g_d()? * ivk; let to = PaymentAddress::from_parts(diversifier, pk_d)?; - let note = to.create_note(v, rcm, &JUBJUB).unwrap(); + let note = to.create_note(v, rseed).unwrap(); - if note.cm(&JUBJUB) != *cmu { + if note.cmu() != *cmu { // Published commitment doesn't match calculated commitment return None; } + if let Some(derived_esk) = note.derive_esk() { + if (note.g_d * derived_esk) != *epk { + return None; + } + } + Some((note, to)) } +pub fn plaintext_version_is_valid(height: u32, leadbyte: u8) -> bool { + if P::is_nu_active(NetworkUpgrade::Canopy, height) { + let grace_period_end_height = P::activation_height(NetworkUpgrade::Canopy) + .expect("Should have Canopy activation height") + + ZIP212_GRACE_PERIOD; + + if height < grace_period_end_height && leadbyte != 0x01 && leadbyte != 0x02 { + // non-{0x01,0x02} received after Canopy activation and before grace period has elapsed + false + } else if height >= grace_period_end_height && leadbyte != 0x02 { + // non-0x02 received past (Canopy activation height + grace period) + false + } else { + true + } + } else { + // return false if non-0x01 received when Canopy is not active + leadbyte == 0x01 + } +} + /// Trial decryption of the full note plaintext by the recipient. /// /// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ivk`. @@ -390,15 +395,16 @@ fn parse_note_plaintext_without_memo( /// `PaymentAddress` to which the note was sent. /// /// Implements section 4.17.2 of the Zcash Protocol Specification. -pub fn try_sapling_note_decryption( - ivk: &Fs, - epk: &edwards::Point, - cmu: &Fr, +pub fn try_sapling_note_decryption( + height: u32, + ivk: &jubjub::Fr, + epk: &jubjub::SubgroupPoint, + cmu: &bls12_381::Scalar, enc_ciphertext: &[u8], -) -> Option<(Note, PaymentAddress, Memo)> { +) -> Option<(Note, PaymentAddress, Memo)> { assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE); - let shared_secret = sapling_ka_agree(ivk, epk); + let shared_secret = sapling_ka_agree(ivk, epk.into()); let key = kdf_sapling(shared_secret, &epk); let mut plaintext = [0; ENC_CIPHERTEXT_SIZE]; @@ -415,7 +421,7 @@ pub fn try_sapling_note_decryption( NOTE_PLAINTEXT_SIZE ); - let (note, to) = parse_note_plaintext_without_memo(ivk, cmu, &plaintext)?; + let (note, to) = parse_note_plaintext_without_memo::

(height, ivk, epk, cmu, &plaintext)?; let mut memo = [0u8; 512]; memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); @@ -431,16 +437,17 @@ pub fn try_sapling_note_decryption( /// /// Implements the procedure specified in [`ZIP 307`]. /// -/// [`ZIP 307`]: https://github.com/zcash/zips/pull/226 -pub fn try_sapling_compact_note_decryption( - ivk: &Fs, - epk: &edwards::Point, - cmu: &Fr, +/// [`ZIP 307`]: https://zips.z.cash/zip-0307 +pub fn try_sapling_compact_note_decryption( + height: u32, + ivk: &jubjub::Fr, + epk: &jubjub::SubgroupPoint, + cmu: &bls12_381::Scalar, enc_ciphertext: &[u8], -) -> Option<(Note, PaymentAddress)> { +) -> Option<(Note, PaymentAddress)> { assert_eq!(enc_ciphertext.len(), COMPACT_NOTE_SIZE); - let shared_secret = sapling_ka_agree(ivk, epk); + let shared_secret = sapling_ka_agree(ivk, epk.into()); let key = kdf_sapling(shared_secret, &epk); // Start from block 1 to skip over Poly1305 keying output @@ -448,46 +455,53 @@ pub fn try_sapling_compact_note_decryption( plaintext.copy_from_slice(&enc_ciphertext); ChaCha20Ietf::xor(key.as_bytes(), &[0u8; 12], 1, &mut plaintext); - parse_note_plaintext_without_memo(ivk, cmu, &plaintext) + parse_note_plaintext_without_memo::

(height, ivk, epk, cmu, &plaintext) } /// Recovery of the full note plaintext by the sender. /// -/// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ovk`. +/// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ock`. /// If successful, the corresponding Sapling note and memo are returned, along with the /// `PaymentAddress` to which the note was sent. /// -/// Implements section 4.17.3 of the Zcash Protocol Specification. -pub fn try_sapling_output_recovery( - ovk: &OutgoingViewingKey, - cv: &edwards::Point, - cmu: &Fr, - epk: &edwards::Point, +/// Implements part of section 4.17.3 of the Zcash Protocol Specification. +/// For decryption using a Full Viewing Key see [`try_sapling_output_recovery`]. +pub fn try_sapling_output_recovery_with_ock( + height: u32, + ock: &[u8], + cmu: &bls12_381::Scalar, + epk: &jubjub::SubgroupPoint, enc_ciphertext: &[u8], out_ciphertext: &[u8], -) -> Option<(Note, PaymentAddress, Memo)> { +) -> Option<(Note, PaymentAddress, Memo)> { assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE); assert_eq!(out_ciphertext.len(), OUT_CIPHERTEXT_SIZE); - let ock = prf_ock(&ovk, &cv, &cmu, &epk); - let mut op = [0; OUT_CIPHERTEXT_SIZE]; assert_eq!( ChachaPolyIetf::aead_cipher() - .open_to(&mut op, &out_ciphertext, &[], ock.as_bytes(), &[0u8; 12]) + .open_to(&mut op, &out_ciphertext, &[], &ock, &[0u8; 12]) .ok()?, OUT_PLAINTEXT_SIZE ); - let pk_d = edwards::Point::::read(&op[0..32], &JUBJUB) - .ok()? - .as_prime_order(&JUBJUB)?; + let pk_d = { + let pk_d = jubjub::SubgroupPoint::from_bytes( + op[0..32].try_into().expect("slice is the correct length"), + ); + if pk_d.is_none().into() { + return None; + } + pk_d.unwrap() + }; - let mut esk = FsRepr::default(); - esk.read_le(&op[32..OUT_PLAINTEXT_SIZE]).ok()?; - let esk = Fs::from_repr(esk).ok()?; + let esk = jubjub::Fr::from_repr( + op[32..OUT_PLAINTEXT_SIZE] + .try_into() + .expect("slice is the correct length"), + )?; - let shared_secret = sapling_ka_agree(&esk, &pk_d); + let shared_secret = sapling_ka_agree(&esk, &pk_d.into()); let key = kdf_sapling(shared_secret, &epk); let mut plaintext = [0; ENC_CIPHERTEXT_SIZE]; @@ -505,9 +519,8 @@ pub fn try_sapling_output_recovery( ); // Check note plaintext version - match plaintext[0] { - 0x01 => (), - _ => return None, + if !plaintext_version_is_valid::

(height, plaintext[0]) { + return None; } let mut d = [0u8; 11]; @@ -515,58 +528,97 @@ pub fn try_sapling_output_recovery( let v = (&plaintext[12..20]).read_u64::().ok()?; - let mut rcm = FsRepr::default(); - rcm.read_le(&plaintext[20..COMPACT_NOTE_SIZE]).ok()?; - let rcm = Fs::from_repr(rcm).ok()?; + let r: [u8; 32] = plaintext[20..COMPACT_NOTE_SIZE] + .try_into() + .expect("slice is the correct length"); + + let rseed = if plaintext[0] == 0x01 { + let rcm = jubjub::Fr::from_repr(r)?; + Rseed::BeforeZip212(rcm) + } else { + Rseed::AfterZip212(r) + }; let mut memo = [0u8; 512]; memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]); let diversifier = Diversifier(d); - if diversifier - .g_d::(&JUBJUB)? - .mul(esk.into_repr(), &JUBJUB) - != *epk - { + if diversifier.g_d()? * esk != *epk { // Published epk doesn't match calculated epk return None; } let to = PaymentAddress::from_parts(diversifier, pk_d)?; - let note = to.create_note(v, rcm, &JUBJUB).unwrap(); + let note = to.create_note(v, rseed).unwrap(); - if note.cm(&JUBJUB) != *cmu { + if note.cmu() != *cmu { // Published commitment doesn't match calculated commitment return None; } + if let Some(derived_esk) = note.derive_esk() { + if derived_esk != esk { + return None; + } + } + Some((note, to, Memo(memo))) } +/// Recovery of the full note plaintext by the sender. +/// +/// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ovk`. +/// If successful, the corresponding Sapling note and memo are returned, along with the +/// `PaymentAddress` to which the note was sent. +/// +/// Implements section 4.17.3 of the Zcash Protocol Specification. +pub fn try_sapling_output_recovery( + height: u32, + ovk: &OutgoingViewingKey, + cv: &jubjub::ExtendedPoint, + cmu: &bls12_381::Scalar, + epk: &jubjub::SubgroupPoint, + enc_ciphertext: &[u8], + out_ciphertext: &[u8], +) -> Option<(Note, PaymentAddress, Memo)> { + try_sapling_output_recovery_with_ock::

( + height, + prf_ock(&ovk, &cv, &cmu, &epk).as_bytes(), + cmu, + epk, + enc_ciphertext, + out_ciphertext, + ) +} + #[cfg(test)] mod tests { - use crate::{ - jubjub::{ - edwards, - fs::{Fs, FsRepr}, - PrimeOrder, Unknown, - }, - primitives::{Diversifier, PaymentAddress, ValueCommitment}, - }; + use blake2b_simd::Hash as Blake2bHash; use crypto_api_chachapoly::ChachaPolyIetf; - use ff::{Field, PrimeField, PrimeFieldRepr}; - use pairing::bls12_381::{Bls12, Fr, FrRepr}; + use ff::{Field, PrimeField}; + use group::Group; + use group::{cofactor::CofactorGroup, GroupEncoding}; use rand_core::OsRng; use rand_core::{CryptoRng, RngCore}; + use std::convert::TryInto; use std::str::FromStr; use super::{ kdf_sapling, prf_ock, sapling_ka_agree, try_sapling_compact_note_decryption, - try_sapling_note_decryption, try_sapling_output_recovery, Memo, SaplingNoteEncryption, - COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, - OUT_PLAINTEXT_SIZE, + try_sapling_note_decryption, try_sapling_output_recovery, + try_sapling_output_recovery_with_ock, Memo, SaplingNoteEncryption, COMPACT_NOTE_SIZE, + ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, OUT_PLAINTEXT_SIZE, + }; + use crate::{ + consensus::{ + NetworkUpgrade, + NetworkUpgrade::{Canopy, Sapling}, + Parameters, TestNetwork, ZIP212_GRACE_PERIOD, + }, + keys::OutgoingViewingKey, + primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment}, + util::generate_random_rseed, }; - use crate::{keys::OutgoingViewingKey, JUBJUB}; #[test] fn memo_from_str() { @@ -685,79 +737,105 @@ mod tests { } fn random_enc_ciphertext( + height: u32, mut rng: &mut R, ) -> ( OutgoingViewingKey, - Fs, - edwards::Point, - Fr, - edwards::Point, + Blake2bHash, + jubjub::Fr, + jubjub::ExtendedPoint, + bls12_381::Scalar, + jubjub::SubgroupPoint, [u8; ENC_CIPHERTEXT_SIZE], [u8; OUT_CIPHERTEXT_SIZE], ) { - let ivk = Fs::random(&mut rng); + let ivk = jubjub::Fr::random(&mut rng); - let (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = - random_enc_ciphertext_with(ivk, rng); + let (ovk, ock, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext_with(height, ivk, rng); - assert!(try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext).is_some()); - assert!(try_sapling_compact_note_decryption( + assert!(try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ) + .is_some()); + assert!(try_sapling_compact_note_decryption::( + height, &ivk, &epk, &cmu, &enc_ciphertext[..COMPACT_NOTE_SIZE] ) .is_some()); - assert!(try_sapling_output_recovery( + + let ovk_output_recovery = try_sapling_output_recovery::( + height, &ovk, &cv, &cmu, &epk, &enc_ciphertext, - &out_ciphertext - ) - .is_some()); + &out_ciphertext, + ); + let ock_output_recovery = try_sapling_output_recovery_with_ock::( + height, + ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext, + ); + assert!(ovk_output_recovery.is_some()); + assert!(ock_output_recovery.is_some()); + assert_eq!(ovk_output_recovery, ock_output_recovery); - (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) + (ovk, ock, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) } fn random_enc_ciphertext_with( - ivk: Fs, + height: u32, + ivk: jubjub::Fr, mut rng: &mut R, ) -> ( OutgoingViewingKey, - Fs, - edwards::Point, - Fr, - edwards::Point, + Blake2bHash, + jubjub::Fr, + jubjub::ExtendedPoint, + bls12_381::Scalar, + jubjub::SubgroupPoint, [u8; ENC_CIPHERTEXT_SIZE], [u8; OUT_CIPHERTEXT_SIZE], ) { let diversifier = Diversifier([0; 11]); - let pk_d = diversifier.g_d::(&JUBJUB).unwrap().mul(ivk, &JUBJUB); + let pk_d = diversifier.g_d().unwrap() * ivk; let pa = PaymentAddress::from_parts_unchecked(diversifier, pk_d); // Construct the value commitment for the proof instance let value = 100; - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value, - randomness: Fs::random(&mut rng), + randomness: jubjub::Fr::random(&mut rng), }; - let cv = value_commitment.cm(&JUBJUB).into(); + let cv = value_commitment.commitment().into(); - let note = pa - .create_note(value, Fs::random(&mut rng), &JUBJUB) - .unwrap(); - let cmu = note.cm(&JUBJUB); + let rseed = generate_random_rseed::(height, &mut rng); + + let note = pa.create_note(value, rseed).unwrap(); + let cmu = note.cmu(); let ovk = OutgoingViewingKey([0; 32]); - let ne = SaplingNoteEncryption::new(ovk, note, pa, Memo([0; 512]), rng); + let ne = SaplingNoteEncryption::new(ovk, note, pa, Memo([0; 512]), &mut rng); let epk = ne.epk(); let enc_ciphertext = ne.encrypt_note_plaintext(); let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu); + let ock = prf_ock(&ovk, &cv, &cmu, &epk); ( ovk, + ock, ivk, cv, cmu, @@ -769,9 +847,9 @@ mod tests { fn reencrypt_enc_ciphertext( ovk: &OutgoingViewingKey, - cv: &edwards::Point, - cmu: &Fr, - epk: &edwards::Point, + cv: &jubjub::ExtendedPoint, + cmu: &bls12_381::Scalar, + epk: &jubjub::SubgroupPoint, enc_ciphertext: &mut [u8; ENC_CIPHERTEXT_SIZE], out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE], modify_plaintext: impl Fn(&mut [u8; NOTE_PLAINTEXT_SIZE]), @@ -786,16 +864,11 @@ mod tests { OUT_PLAINTEXT_SIZE ); - let pk_d = edwards::Point::::read(&op[0..32], &JUBJUB) - .unwrap() - .as_prime_order(&JUBJUB) - .unwrap(); + let pk_d = jubjub::SubgroupPoint::from_bytes(&op[0..32].try_into().unwrap()).unwrap(); - let mut esk = FsRepr::default(); - esk.read_le(&op[32..OUT_PLAINTEXT_SIZE]).unwrap(); - let esk = Fs::from_repr(esk).unwrap(); + let esk = jubjub::Fr::from_repr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap()).unwrap(); - let shared_secret = sapling_ka_agree(&esk, &pk_d); + let shared_secret = sapling_ka_agree(&esk, &pk_d.into()); let key = kdf_sapling(shared_secret, &epk); let mut plaintext = { @@ -831,7 +904,7 @@ mod tests { break; } } - if d.g_d::(&JUBJUB).is_none() { + if d.g_d().is_none() { break; } } @@ -848,7 +921,7 @@ mod tests { break; } } - if d.g_d::(&JUBJUB).is_some() { + if d.g_d().is_some() { break; } } @@ -858,487 +931,845 @@ mod tests { #[test] fn decryption_with_invalid_ivk() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (_, _, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_note_decryption(&Fs::random(&mut rng), &epk, &cmu, &enc_ciphertext), - None - ); + assert_eq!( + try_sapling_note_decryption::( + height, + &jubjub::Fr::random(&mut rng), + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_epk() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (_, _, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_note_decryption( - &ivk, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - &cmu, - &enc_ciphertext - ), - None - ); + assert_eq!( + try_sapling_note_decryption::( + height, + &ivk, + &jubjub::SubgroupPoint::random(&mut rng), + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_cmu() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (_, _, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &Fr::random(&mut rng), &enc_ciphertext), - None - ); + assert_eq!( + try_sapling_note_decryption::( + height, + &ivk, + &epk, + &bls12_381::Scalar::random(&mut rng), + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_tag() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (_, ivk, _, cmu, epk, mut enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (_, _, ivk, _, cmu, epk, mut enc_ciphertext, _) = + random_enc_ciphertext(height, &mut rng); - enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext), - None - ); + enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; + assert_eq!( + try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_version_byte() { let mut rng = OsRng; - - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); - - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[0] = 0x02, - ); - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext), - None - ); + let canopy_activation_height = TestNetwork::activation_height(Canopy).unwrap(); + let heights = [ + canopy_activation_height - 1, + canopy_activation_height, + canopy_activation_height + ZIP212_GRACE_PERIOD, + ]; + let leadbytes = [0x02, 0x03, 0x01]; + + for (&height, &leadbyte) in heights.iter().zip(leadbytes.iter()) { + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); + + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[0] = leadbyte, + ); + assert_eq!( + try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_invalid_diversifier() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), - ); - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext), - None - ); + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), + ); + assert_eq!( + try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn decryption_with_incorrect_diversifier() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), - ); - assert_eq!( - try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext), - None - ); + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), + ); + assert_eq!( + try_sapling_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_ivk() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (_, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (_, _, _, _, cmu, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_compact_note_decryption( - &Fs::random(&mut rng), - &epk, - &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + assert_eq!( + try_sapling_compact_note_decryption::( + height, + &jubjub::Fr::random(&mut rng), + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_epk() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (_, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (_, _, ivk, _, cmu, _, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + assert_eq!( + try_sapling_compact_note_decryption::( + height, + &ivk, + &jubjub::SubgroupPoint::random(&mut rng), + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_cmu() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (_, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (_, _, ivk, _, _, epk, enc_ciphertext, _) = random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &epk, - &Fr::random(&mut rng), - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + assert_eq!( + try_sapling_compact_note_decryption::( + height, + &ivk, + &epk, + &bls12_381::Scalar::random(&mut rng), + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_version_byte() { let mut rng = OsRng; - - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); - - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[0] = 0x02, - ); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &epk, + let canopy_activation_height = TestNetwork::activation_height(Canopy).unwrap(); + let heights = [ + canopy_activation_height - 1, + canopy_activation_height, + canopy_activation_height + ZIP212_GRACE_PERIOD, + ]; + let leadbytes = [0x02, 0x03, 0x01]; + + for (&height, &leadbyte) in heights.iter().zip(leadbytes.iter()) { + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); + + reencrypt_enc_ciphertext( + &ovk, + &cv, &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[0] = leadbyte, + ); + assert_eq!( + try_sapling_compact_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_invalid_diversifier() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), - ); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &epk, + reencrypt_enc_ciphertext( + &ovk, + &cv, &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), + ); + assert_eq!( + try_sapling_compact_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn compact_decryption_with_incorrect_diversifier() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, _, ivk, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), - ); - assert_eq!( - try_sapling_compact_note_decryption( - &ivk, - &epk, + reencrypt_enc_ciphertext( + &ovk, + &cv, &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ), - None - ); + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), + ); + assert_eq!( + try_sapling_compact_note_decryption::( + height, + &ivk, + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ), + None + ); + } } #[test] fn recovery_with_invalid_ovk() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (mut ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (mut ovk, _, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - ovk.0[0] ^= 0xff; - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + ovk.0[0] ^= 0xff; + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } + } + + #[test] + fn recovery_with_invalid_ock() { + let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; + + for &height in heights.iter() { + let (_, _, _, _, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); + + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &[0u8; 32], + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_cv() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, _, _, cmu, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, _, _, _, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_output_recovery( - &ovk, - &edwards::Point::::rand(&mut rng, &JUBJUB), - &cmu, - &epk, - &enc_ciphertext, - &out_ciphertext - ), - None - ); + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &jubjub::ExtendedPoint::random(&mut rng), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_cmu() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, _, cv, _, epk, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, ock, _, cv, _, epk, enc_ctext, out_ctext) = + random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_output_recovery( - &ovk, - &cv, - &Fr::random(&mut rng), - &epk, - &enc_ciphertext, - &out_ciphertext - ), - None - ); + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &cv, + &bls12_381::Scalar::random(&mut rng), + &epk, + &enc_ctext, + &out_ctext + ), + None + ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &bls12_381::Scalar::random(&mut rng), + &epk, + &enc_ctext, + &out_ctext + ), + None + ); + } } #[test] fn recovery_with_invalid_epk() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, _, cv, cmu, _, enc_ciphertext, out_ciphertext) = random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, ock, _, cv, cmu, _, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - assert_eq!( - try_sapling_output_recovery( - &ovk, - &cv, - &cmu, - &edwards::Point::::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB), - &enc_ciphertext, - &out_ciphertext - ), - None - ); + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &cv, + &cmu, + &jubjub::SubgroupPoint::random(&mut rng), + &enc_ciphertext, + &out_ciphertext + ), + None + ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &jubjub::SubgroupPoint::random(&mut rng), + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_enc_tag() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, ock, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + enc_ciphertext[ENC_CIPHERTEXT_SIZE - 1] ^= 0xff; + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_out_tag() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, _, cv, cmu, epk, enc_ciphertext, mut out_ciphertext) = - random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, ock, _, cv, cmu, epk, enc_ciphertext, mut out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - out_ciphertext[OUT_CIPHERTEXT_SIZE - 1] ^= 0xff; - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + out_ciphertext[OUT_CIPHERTEXT_SIZE - 1] ^= 0xff; + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_version_byte() { let mut rng = OsRng; - - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); - - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[0] = 0x02, - ); - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + let canopy_activation_height = TestNetwork::activation_height(Canopy).unwrap(); + let heights = [ + canopy_activation_height - 1, + canopy_activation_height, + canopy_activation_height + ZIP212_GRACE_PERIOD, + ]; + let leadbytes = [0x02, 0x03, 0x01]; + + for (&height, &leadbyte) in heights.iter().zip(leadbytes.iter()) { + let (ovk, ock, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); + + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[0] = leadbyte, + ); + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_diversifier() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, ock, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), - ); - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_invalid_diversifier().0), + ); + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_incorrect_diversifier() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let (ovk, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = - random_enc_ciphertext(&mut rng); + for &height in heights.iter() { + let (ovk, ock, _, cv, cmu, epk, mut enc_ciphertext, out_ciphertext) = + random_enc_ciphertext(height, &mut rng); - reencrypt_enc_ciphertext( - &ovk, - &cv, - &cmu, - &epk, - &mut enc_ciphertext, - &out_ciphertext, - |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), - ); - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + reencrypt_enc_ciphertext( + &ovk, + &cv, + &cmu, + &epk, + &mut enc_ciphertext, + &out_ciphertext, + |pt| pt[1..12].copy_from_slice(&find_valid_diversifier().0), + ); + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn recovery_with_invalid_pk_d() { let mut rng = OsRng; + let heights = [ + TestNetwork::activation_height(Sapling).unwrap(), + TestNetwork::activation_height(Canopy).unwrap(), + ]; - let ivk = Fs::zero(); - let (ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = - random_enc_ciphertext_with(ivk, &mut rng); + for &height in heights.iter() { + let ivk = jubjub::Fr::zero(); + let (ovk, ock, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext_with(height, ivk, &mut rng); - assert_eq!( - try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), - None - ); + assert_eq!( + try_sapling_output_recovery::( + height, + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + assert_eq!( + try_sapling_output_recovery_with_ock::( + height, + &ock.as_bytes(), + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ), + None + ); + } } #[test] fn test_vectors() { let test_vectors = crate::test_vectors::note_encryption::make_test_vectors(); - macro_rules! read_fr { + macro_rules! read_bls12_381_scalar { ($field:expr) => {{ - let mut repr = FrRepr::default(); - repr.read_le(&$field[..]).unwrap(); - Fr::from_repr(repr).unwrap() + bls12_381::Scalar::from_repr($field[..].try_into().unwrap()).unwrap() }}; } - macro_rules! read_fs { + macro_rules! read_jubjub_scalar { ($field:expr) => {{ - let mut repr = FsRepr::default(); - repr.read_le(&$field[..]).unwrap(); - Fs::from_repr(repr).unwrap() + jubjub::Fr::from_repr($field[..].try_into().unwrap()).unwrap() }}; } macro_rules! read_point { ($field:expr) => { - edwards::Point::::read(&$field[..], &JUBJUB).unwrap() + jubjub::ExtendedPoint::from_bytes(&$field).unwrap() }; } + let height = TestNetwork::activation_height(NetworkUpgrade::Sapling) + .expect("Should have Sapling activation height"); + for tv in test_vectors { // // Load the test vector components // - let ivk = read_fs!(tv.ivk); - let pk_d = read_point!(tv.default_pk_d) - .as_prime_order(&JUBJUB) - .unwrap(); - let rcm = read_fs!(tv.rcm); + let ivk = read_jubjub_scalar!(tv.ivk); + let pk_d = read_point!(tv.default_pk_d).into_subgroup().unwrap(); + let rcm = read_jubjub_scalar!(tv.rcm); let cv = read_point!(tv.cv); - let cmu = read_fr!(tv.cmu); - let esk = read_fs!(tv.esk); - let epk = read_point!(tv.epk).as_prime_order(&JUBJUB).unwrap(); + let cmu = read_bls12_381_scalar!(tv.cmu); + let esk = read_jubjub_scalar!(tv.esk); + let epk = read_point!(tv.epk).into_subgroup().unwrap(); // // Test the individual components // - let shared_secret = sapling_ka_agree(&esk, &pk_d); - { - let mut encoded = [0; 32]; - shared_secret - .write(&mut encoded[..]) - .expect("length is not 32 bytes"); - assert_eq!(encoded, tv.shared_secret); - } + let shared_secret = sapling_ka_agree(&esk, &pk_d.into()); + assert_eq!(shared_secret.to_bytes(), tv.shared_secret); let k_enc = kdf_sapling(shared_secret, &epk); assert_eq!(k_enc.as_bytes(), tv.k_enc); @@ -1348,15 +1779,15 @@ mod tests { assert_eq!(ock.as_bytes(), tv.ock); let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap(); - let note = to.create_note(tv.v, rcm, &JUBJUB).unwrap(); - assert_eq!(note.cm(&JUBJUB), cmu); + let note = to.create_note(tv.v, Rseed::BeforeZip212(rcm)).unwrap(); + assert_eq!(note.cmu(), cmu); // // Test decryption // (Tested first because it only requires immutable references.) // - match try_sapling_note_decryption(&ivk, &epk, &cmu, &tv.c_enc) { + match try_sapling_note_decryption::(height, &ivk, &epk, &cmu, &tv.c_enc) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); assert_eq!(decrypted_to, to); @@ -1365,7 +1796,8 @@ mod tests { None => panic!("Note decryption failed"), } - match try_sapling_compact_note_decryption( + match try_sapling_compact_note_decryption::( + height, &ivk, &epk, &cmu, @@ -1378,7 +1810,9 @@ mod tests { None => panic!("Compact note decryption failed"), } - match try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &tv.c_enc, &tv.c_out) { + match try_sapling_output_recovery::( + height, &ovk, &cv, &cmu, &epk, &tv.c_enc, &tv.c_out, + ) { Some((decrypted_note, decrypted_to, decrypted_memo)) => { assert_eq!(decrypted_note, note); assert_eq!(decrypted_to, to); diff --git a/zcash_primitives/src/pedersen_hash.rs b/zcash_primitives/src/pedersen_hash.rs index ea182a5ac1..6f5160f702 100644 --- a/zcash_primitives/src/pedersen_hash.rs +++ b/zcash_primitives/src/pedersen_hash.rs @@ -1,9 +1,14 @@ //! Implementation of the Pedersen hash function used in Sapling. -use crate::jubjub::*; -use ff::{Field, PrimeField, PrimeFieldRepr}; +use byteorder::{ByteOrder, LittleEndian}; +use ff::PrimeField; +use group::Group; use std::ops::{AddAssign, Neg}; +use crate::constants::{ + PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_EXP_TABLE, PEDERSEN_HASH_EXP_WINDOW_SIZE, +}; + #[derive(Copy, Clone)] pub enum Personalization { NoteCommitment, @@ -23,27 +28,22 @@ impl Personalization { } } -pub fn pedersen_hash( - personalization: Personalization, - bits: I, - params: &E::Params, -) -> edwards::Point +pub fn pedersen_hash(personalization: Personalization, bits: I) -> jubjub::SubgroupPoint where I: IntoIterator, - E: JubjubEngine, { let mut bits = personalization .get_bits() .into_iter() .chain(bits.into_iter()); - let mut result = edwards::Point::zero(); - let mut generators = params.pedersen_hash_exp_table().iter(); + let mut result = jubjub::SubgroupPoint::identity(); + let mut generators = PEDERSEN_HASH_EXP_TABLE.iter(); loop { - let mut acc = E::Fs::zero(); - let mut cur = E::Fs::one(); - let mut chunks_remaining = params.pedersen_hash_chunks_per_generator(); + let mut acc = jubjub::Fr::zero(); + let mut cur = jubjub::Fr::one(); + let mut chunks_remaining = PEDERSEN_HASH_CHUNKS_PER_GENERATOR; let mut encountered_bits = false; // Grab three bits from the input @@ -83,25 +83,37 @@ where break; } - let mut table: &[Vec>] = + let mut table: &[Vec] = &generators.next().expect("we don't have enough generators"); - let window = JubjubBls12::pedersen_hash_exp_window_size(); - let window_mask = (1 << window) - 1; - - let mut acc = acc.into_repr(); - - let mut tmp = edwards::Point::zero(); - - while !acc.is_zero() { - let i = (acc.as_ref()[0] & window_mask) as usize; + let window = PEDERSEN_HASH_EXP_WINDOW_SIZE as usize; + let window_mask = (1u64 << window) - 1; + + let acc = acc.to_repr(); + let num_limbs: usize = acc.as_ref().len() / 8; + let mut limbs = vec![0u64; num_limbs + 1]; + LittleEndian::read_u64_into(acc.as_ref(), &mut limbs[..num_limbs]); + + let mut tmp = jubjub::SubgroupPoint::identity(); + + let mut pos = 0; + while pos < jubjub::Fr::NUM_BITS as usize { + let u64_idx = pos / 64; + let bit_idx = pos % 64; + let i = (if bit_idx + window < 64 { + // This window's bits are contained in a single u64. + limbs[u64_idx] >> bit_idx + } else { + // Combine the current u64's bits with the bits from the next u64. + (limbs[u64_idx] >> bit_idx) | (limbs[u64_idx + 1] << (64 - bit_idx)) + } & window_mask) as usize; - tmp = tmp.add(&table[0][i], params); + tmp += table[0][i]; - acc.shr(window); + pos += window; table = &table[1..]; } - result = result.add(&tmp, params); + result += tmp; } result @@ -109,16 +121,16 @@ where #[cfg(test)] pub mod test { + use group::Curve; use super::*; use crate::test_vectors::pedersen_hash_vectors; - use pairing::bls12_381::Bls12; pub struct TestVector<'a> { pub personalization: Personalization, pub input_bits: Vec, - pub hash_x: &'a str, - pub hash_y: &'a str, + pub hash_u: &'a str, + pub hash_v: &'a str, } #[test] @@ -128,22 +140,19 @@ pub mod test { assert!(test_vectors.len() > 0); for v in test_vectors.iter() { - let params = &JubjubBls12::new(); - let input_bools: Vec = v.input_bits.iter().map(|&i| i == 1).collect(); // The 6 bits prefix is handled separately assert_eq!(v.personalization.get_bits(), &input_bools[..6]); - let (x, y) = pedersen_hash::( + let p = jubjub::ExtendedPoint::from(pedersen_hash( v.personalization, input_bools.into_iter().skip(6), - params, - ) - .to_xy(); + )) + .to_affine(); - assert_eq!(x.to_string(), v.hash_x); - assert_eq!(y.to_string(), v.hash_y); + assert_eq!(p.get_u().to_string(), v.hash_u); + assert_eq!(p.get_v().to_string(), v.hash_v); } } } diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index af4fa3ad09..79e1b0d8e1 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -1,6 +1,8 @@ //! Structs for core Zcash primitives. -use ff::{Field, PrimeField, PrimeFieldRepr}; +use ff::PrimeField; +use group::{Curve, Group, GroupEncoding}; +use std::convert::TryInto; use crate::constants; @@ -10,94 +12,73 @@ use crate::pedersen_hash::{pedersen_hash, Personalization}; use byteorder::{LittleEndian, WriteBytesExt}; -use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder}; +use crate::keys::prf_expand; use blake2s_simd::Params as Blake2sParams; +use rand_core::{CryptoRng, RngCore}; + #[derive(Clone)] -pub struct ValueCommitment { +pub struct ValueCommitment { pub value: u64, - pub randomness: E::Fs, + pub randomness: jubjub::Fr, } -impl ValueCommitment { - pub fn cm(&self, params: &E::Params) -> edwards::Point { - params - .generator(FixedGenerators::ValueCommitmentValue) - .mul(self.value, params) - .add( - ¶ms - .generator(FixedGenerators::ValueCommitmentRandomness) - .mul(self.randomness, params), - params, - ) +impl ValueCommitment { + pub fn commitment(&self) -> jubjub::SubgroupPoint { + (constants::VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Fr::from(self.value)) + + (constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * self.randomness) } } #[derive(Clone)] -pub struct ProofGenerationKey { - pub ak: edwards::Point, - pub nsk: E::Fs, +pub struct ProofGenerationKey { + pub ak: jubjub::SubgroupPoint, + pub nsk: jubjub::Fr, } -impl ProofGenerationKey { - pub fn to_viewing_key(&self, params: &E::Params) -> ViewingKey { +impl ProofGenerationKey { + pub fn to_viewing_key(&self) -> ViewingKey { ViewingKey { ak: self.ak.clone(), - nk: params - .generator(FixedGenerators::ProofGenerationKey) - .mul(self.nsk, params), + nk: constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk, } } } #[derive(Debug)] -pub struct ViewingKey { - pub ak: edwards::Point, - pub nk: edwards::Point, +pub struct ViewingKey { + pub ak: jubjub::SubgroupPoint, + pub nk: jubjub::SubgroupPoint, } -impl ViewingKey { - pub fn rk(&self, ar: E::Fs, params: &E::Params) -> edwards::Point { - self.ak.add( - ¶ms - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(ar, params), - params, - ) +impl ViewingKey { + pub fn rk(&self, ar: jubjub::Fr) -> jubjub::SubgroupPoint { + self.ak + constants::SPENDING_KEY_GENERATOR * ar } - pub fn ivk(&self) -> E::Fs { - let mut preimage = [0; 64]; - - self.ak.write(&mut preimage[0..32]).unwrap(); - self.nk.write(&mut preimage[32..64]).unwrap(); - + pub fn ivk(&self) -> jubjub::Fr { let mut h = [0; 32]; h.copy_from_slice( Blake2sParams::new() .hash_length(32) .personal(constants::CRH_IVK_PERSONALIZATION) - .hash(&preimage) + .to_state() + .update(&self.ak.to_bytes()) + .update(&self.nk.to_bytes()) + .finalize() .as_bytes(), ); // Drop the most significant five bits, so it can be interpreted as a scalar. h[31] &= 0b0000_0111; - let mut e = ::Repr::default(); - e.read_le(&h[..]).unwrap(); - - E::Fs::from_repr(e).expect("should be a valid scalar") + jubjub::Fr::from_repr(h).expect("should be a valid scalar") } - pub fn to_payment_address( - &self, - diversifier: Diversifier, - params: &E::Params, - ) -> Option> { - diversifier.g_d(params).and_then(|g_d| { - let pk_d = g_d.mul(self.ivk(), params); + pub fn to_payment_address(&self, diversifier: Diversifier) -> Option { + diversifier.g_d().and_then(|g_d| { + let pk_d = g_d * self.ivk(); PaymentAddress::from_parts(diversifier, pk_d) }) @@ -108,15 +89,8 @@ impl ViewingKey { pub struct Diversifier(pub [u8; 11]); impl Diversifier { - pub fn g_d( - &self, - params: &E::Params, - ) -> Option> { - group_hash::( - &self.0, - constants::KEY_DIVERSIFICATION_PERSONALIZATION, - params, - ) + pub fn g_d(&self) -> Option { + group_hash(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION) } } @@ -127,26 +101,23 @@ impl Diversifier { /// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub, /// and not the identity). #[derive(Clone, Debug)] -pub struct PaymentAddress { - pk_d: edwards::Point, +pub struct PaymentAddress { + pk_d: jubjub::SubgroupPoint, diversifier: Diversifier, } -impl PartialEq for PaymentAddress { +impl PartialEq for PaymentAddress { fn eq(&self, other: &Self) -> bool { self.pk_d == other.pk_d && self.diversifier == other.diversifier } } -impl PaymentAddress { +impl PaymentAddress { /// Constructs a PaymentAddress from a diversifier and a Jubjub point. /// /// Returns None if `pk_d` is the identity. - pub fn from_parts( - diversifier: Diversifier, - pk_d: edwards::Point, - ) -> Option { - if pk_d == edwards::Point::zero() { + pub fn from_parts(diversifier: Diversifier, pk_d: jubjub::SubgroupPoint) -> Option { + if pk_d.is_identity().into() { None } else { Some(PaymentAddress { pk_d, diversifier }) @@ -159,34 +130,36 @@ impl PaymentAddress { #[cfg(test)] pub(crate) fn from_parts_unchecked( diversifier: Diversifier, - pk_d: edwards::Point, + pk_d: jubjub::SubgroupPoint, ) -> Self { PaymentAddress { pk_d, diversifier } } /// Parses a PaymentAddress from bytes. - pub fn from_bytes(bytes: &[u8; 43], params: &E::Params) -> Option { + pub fn from_bytes(bytes: &[u8; 43]) -> Option { let diversifier = { let mut tmp = [0; 11]; tmp.copy_from_slice(&bytes[0..11]); Diversifier(tmp) }; // Check that the diversifier is valid - if diversifier.g_d::(params).is_none() { + if diversifier.g_d().is_none() { return None; } - edwards::Point::::read(&bytes[11..43], params) - .ok()? - .as_prime_order(params) - .and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d)) + let pk_d = jubjub::SubgroupPoint::from_bytes(bytes[11..43].try_into().unwrap()); + if pk_d.is_some().into() { + PaymentAddress::from_parts(diversifier, pk_d.unwrap()) + } else { + None + } } /// Returns the byte encoding of this `PaymentAddress`. pub fn to_bytes(&self) -> [u8; 43] { let mut bytes = [0; 43]; bytes[0..11].copy_from_slice(&self.diversifier.0); - self.pk_d.write(&mut bytes[11..]).unwrap(); + bytes[11..].copy_from_slice(&self.pk_d.to_bytes()); bytes } @@ -196,62 +169,65 @@ impl PaymentAddress { } /// Returns `pk_d` for this `PaymentAddress`. - pub fn pk_d(&self) -> &edwards::Point { + pub fn pk_d(&self) -> &jubjub::SubgroupPoint { &self.pk_d } - pub fn g_d(&self, params: &E::Params) -> Option> { - self.diversifier.g_d(params) + pub fn g_d(&self) -> Option { + self.diversifier.g_d() } - pub fn create_note( - &self, - value: u64, - randomness: E::Fs, - params: &E::Params, - ) -> Option> { - self.g_d(params).map(|g_d| Note { + pub fn create_note(&self, value: u64, randomness: Rseed) -> Option { + self.g_d().map(|g_d| Note { value, - r: randomness, + rseed: randomness, g_d, pk_d: self.pk_d.clone(), }) } } +/// Enum for note randomness before and after [ZIP 212](https://zips.z.cash/zip-0212). +/// +/// Before ZIP 212, the note commitment trapdoor `rcm` must be a scalar value. +/// After ZIP 212, the note randomness `rseed` is a 32-byte sequence, used to derive +/// both the note commitment trapdoor `rcm` and the ephemeral private key `esk`. +#[derive(Copy, Clone, Debug)] +pub enum Rseed { + BeforeZip212(jubjub::Fr), + AfterZip212([u8; 32]), +} + #[derive(Clone, Debug)] -pub struct Note { +pub struct Note { /// The value of the note pub value: u64, /// The diversified base of the address, GH(d) - pub g_d: edwards::Point, + pub g_d: jubjub::SubgroupPoint, /// The public key of the address, g_d^ivk - pub pk_d: edwards::Point, - /// The commitment randomness - pub r: E::Fs, + pub pk_d: jubjub::SubgroupPoint, + /// rseed + pub rseed: Rseed, } -impl PartialEq for Note { +impl PartialEq for Note { fn eq(&self, other: &Self) -> bool { self.value == other.value && self.g_d == other.g_d && self.pk_d == other.pk_d - && self.r == other.r + && self.rcm() == other.rcm() } } -impl Note { - pub fn uncommitted() -> E::Fr { +impl Note { + pub fn uncommitted() -> bls12_381::Scalar { // The smallest u-coordinate that is not on the curve // is one. - // TODO: This should be relocated to JubjubEngine as - // it's specific to the curve we're using, not all - // twisted edwards curves. - E::Fr::one() + bls12_381::Scalar::one() } /// Computes the note commitment, returning the full point. - fn cm_full_point(&self, params: &E::Params) -> edwards::Point { + fn cm_full_point(&self) -> jubjub::SubgroupPoint { // Calculate the note contents, as bytes let mut note_contents = vec![]; @@ -261,10 +237,10 @@ impl Note { .unwrap(); // Write g_d - self.g_d.write(&mut note_contents).unwrap(); + note_contents.extend_from_slice(&self.g_d.to_bytes()); // Write pk_d - self.pk_d.write(&mut note_contents).unwrap(); + note_contents.extend_from_slice(&self.pk_d.to_bytes()); assert_eq!(note_contents.len(), 32 + 32 + 8); @@ -274,43 +250,70 @@ impl Note { note_contents .into_iter() .flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)), - params, ); // Compute final commitment - params - .generator(FixedGenerators::NoteCommitmentRandomness) - .mul(self.r, params) - .add(&hash_of_contents, params) + (constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR * self.rcm()) + hash_of_contents } /// Computes the nullifier given the viewing key and /// note position - pub fn nf(&self, viewing_key: &ViewingKey, position: u64, params: &E::Params) -> Vec { + pub fn nf(&self, viewing_key: &ViewingKey, position: u64) -> Vec { // Compute rho = cm + position.G - let rho = self.cm_full_point(params).add( - ¶ms - .generator(FixedGenerators::NullifierPosition) - .mul(position, params), - params, - ); + let rho = self.cm_full_point() + + (constants::NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position)); // Compute nf = BLAKE2s(nk | rho) - let mut nf_preimage = [0u8; 64]; - viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap(); - rho.write(&mut nf_preimage[32..64]).unwrap(); Blake2sParams::new() .hash_length(32) .personal(constants::PRF_NF_PERSONALIZATION) - .hash(&nf_preimage) + .to_state() + .update(&viewing_key.nk.to_bytes()) + .update(&rho.to_bytes()) + .finalize() .as_bytes() .to_vec() } /// Computes the note commitment - pub fn cm(&self, params: &E::Params) -> E::Fr { + pub fn cmu(&self) -> bls12_381::Scalar { // The commitment is in the prime order subgroup, so mapping the - // commitment to the x-coordinate is an injective encoding. - self.cm_full_point(params).to_xy().0 + // commitment to the u-coordinate is an injective encoding. + jubjub::ExtendedPoint::from(self.cm_full_point()) + .to_affine() + .get_u() + } + + pub fn rcm(&self) -> jubjub::Fr { + match self.rseed { + Rseed::BeforeZip212(rcm) => rcm, + Rseed::AfterZip212(rseed) => { + jubjub::Fr::from_bytes_wide(prf_expand(&rseed, &[0x04]).as_array()) + } + } + } + + pub fn generate_or_derive_esk(&self, rng: &mut R) -> jubjub::Fr { + match self.derive_esk() { + None => { + // create random 64 byte buffer + let mut buffer = [0u8; 64]; + rng.fill_bytes(&mut buffer); + + // reduce to uniform value + jubjub::Fr::from_bytes_wide(&buffer) + } + Some(esk) => esk, + } + } + + /// Returns the derived `esk` if this note was created after ZIP 212 activated. + pub fn derive_esk(&self) -> Option { + match self.rseed { + Rseed::BeforeZip212(_) => None, + Rseed::AfterZip212(rseed) => Some(jubjub::Fr::from_bytes_wide( + prf_expand(&rseed, &[0x05]).as_array(), + )), + } } } diff --git a/zcash_primitives/src/prover.rs b/zcash_primitives/src/prover.rs index 932573d991..9efc0cdb09 100644 --- a/zcash_primitives/src/prover.rs +++ b/zcash_primitives/src/prover.rs @@ -1,10 +1,6 @@ //! Abstractions over the proving system and parameters. -use crate::{ - jubjub::{edwards, fs::Fs, Unknown}, - primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, -}; -use pairing::bls12_381::{Bls12, Fr}; +use crate::primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed}; use crate::{ merkle_tree::MerklePath, @@ -29,21 +25,14 @@ pub trait TxProver { fn spend_proof( &self, ctx: &mut Self::SaplingProvingContext, - proof_generation_key: ProofGenerationKey, + proof_generation_key: ProofGenerationKey, diversifier: Diversifier, - rcm: Fs, - ar: Fs, + rseed: Rseed, + ar: jubjub::Fr, value: u64, - anchor: Fr, + anchor: bls12_381::Scalar, merkle_path: MerklePath, - ) -> Result< - ( - [u8; GROTH_PROOF_SIZE], - edwards::Point, - PublicKey, - ), - (), - >; + ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()>; /// Create the value commitment and proof for a Sapling [`OutputDescription`], /// while accumulating its value commitment randomness inside the context for later @@ -53,11 +42,11 @@ pub trait TxProver { fn output_proof( &self, ctx: &mut Self::SaplingProvingContext, - esk: Fs, - payment_address: PaymentAddress, - rcm: Fs, + esk: jubjub::Fr, + payment_address: PaymentAddress, + rcm: jubjub::Fr, value: u64, - ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point); + ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint); /// Create the `bindingSig` for a Sapling transaction. All calls to /// [`TxProver::spend_proof`] and [`TxProver::output_proof`] must be completed before @@ -71,14 +60,13 @@ pub trait TxProver { } #[cfg(test)] -pub(crate) mod mock { +pub mod mock { use ff::Field; - use pairing::bls12_381::{Bls12, Fr}; use rand_core::OsRng; use crate::{ - jubjub::{edwards, fs::Fs, FixedGenerators, Unknown}, - primitives::{Diversifier, PaymentAddress, ProofGenerationKey, ValueCommitment}, + constants::SPENDING_KEY_GENERATOR, + primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment}, }; use crate::{ @@ -86,7 +74,6 @@ pub(crate) mod mock { redjubjub::{PublicKey, Signature}, sapling::Node, transaction::components::{Amount, GROTH_PROOF_SIZE}, - JUBJUB, }; use super::TxProver; @@ -102,35 +89,25 @@ pub(crate) mod mock { fn spend_proof( &self, _ctx: &mut Self::SaplingProvingContext, - proof_generation_key: ProofGenerationKey, + proof_generation_key: ProofGenerationKey, _diversifier: Diversifier, - _rcm: Fs, - ar: Fs, + _rcm: Rseed, + ar: jubjub::Fr, value: u64, - _anchor: Fr, + _anchor: bls12_381::Scalar, _merkle_path: MerklePath, - ) -> Result< - ( - [u8; GROTH_PROOF_SIZE], - edwards::Point, - PublicKey, - ), - (), - > { + ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()> { let mut rng = OsRng; - let cv = ValueCommitment:: { + let cv = ValueCommitment { value, - randomness: Fs::random(&mut rng), + randomness: jubjub::Fr::random(&mut rng), } - .cm(&JUBJUB) + .commitment() .into(); - let rk = PublicKey::(proof_generation_key.ak.clone().into()).randomize( - ar, - FixedGenerators::SpendingKeyGenerator, - &JUBJUB, - ); + let rk = PublicKey(proof_generation_key.ak.clone().into()) + .randomize(ar, SPENDING_KEY_GENERATOR); Ok(([0u8; GROTH_PROOF_SIZE], cv, rk)) } @@ -138,18 +115,18 @@ pub(crate) mod mock { fn output_proof( &self, _ctx: &mut Self::SaplingProvingContext, - _esk: Fs, - _payment_address: PaymentAddress, - _rcm: Fs, + _esk: jubjub::Fr, + _payment_address: PaymentAddress, + _rcm: jubjub::Fr, value: u64, - ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point) { + ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint) { let mut rng = OsRng; - let cv = ValueCommitment:: { + let cv = ValueCommitment { value, - randomness: Fs::random(&mut rng), + randomness: jubjub::Fr::random(&mut rng), } - .cm(&JUBJUB) + .commitment() .into(); ([0u8; GROTH_PROOF_SIZE], cv) diff --git a/zcash_primitives/src/redjubjub.rs b/zcash_primitives/src/redjubjub.rs index fcc3900f6c..2cb6c45224 100644 --- a/zcash_primitives/src/redjubjub.rs +++ b/zcash_primitives/src/redjubjub.rs @@ -3,28 +3,29 @@ //! //! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa -use crate::jubjub::{edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown}; -use ff::{Field, PrimeField, PrimeFieldRepr}; +use ff::{Field, PrimeField}; +use group::GroupEncoding; +use jubjub::{ExtendedPoint, SubgroupPoint}; use rand_core::RngCore; use std::io::{self, Read, Write}; use std::ops::{AddAssign, MulAssign, Neg}; use crate::util::hash_to_scalar; -fn read_scalar(reader: R) -> io::Result { - let mut s_repr = ::Repr::default(); - s_repr.read_le(reader)?; +fn read_scalar(mut reader: R) -> io::Result { + let mut s_repr = [0u8; 32]; + reader.read_exact(s_repr.as_mut())?; - E::Fs::from_repr(s_repr) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field")) + jubjub::Fr::from_repr(s_repr) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field")) } -fn write_scalar(s: &E::Fs, writer: W) -> io::Result<()> { - s.into_repr().write_le(writer) +fn write_scalar(s: &jubjub::Fr, mut writer: W) -> io::Result<()> { + writer.write_all(s.to_repr().as_ref()) } -fn h_star(a: &[u8], b: &[u8]) -> E::Fs { - hash_to_scalar::(b"Zcash_RedJubjubH", a, b) +fn h_star(a: &[u8], b: &[u8]) -> jubjub::Fr { + hash_to_scalar(b"Zcash_RedJubjubH", a, b) } #[derive(Copy, Clone, Debug)] @@ -33,10 +34,10 @@ pub struct Signature { sbar: [u8; 32], } -pub struct PrivateKey(pub E::Fs); +pub struct PrivateKey(pub jubjub::Fr); #[derive(Debug)] -pub struct PublicKey(pub Point); +pub struct PublicKey(pub ExtendedPoint); impl Signature { pub fn read(mut reader: R) -> io::Result { @@ -53,167 +54,156 @@ impl Signature { } } -impl PrivateKey { - pub fn randomize(&self, alpha: E::Fs) -> Self { +impl PrivateKey { + pub fn randomize(&self, alpha: jubjub::Fr) -> Self { let mut tmp = self.0; tmp.add_assign(&alpha); PrivateKey(tmp) } pub fn read(reader: R) -> io::Result { - let pk = read_scalar::(reader)?; + let pk = read_scalar::(reader)?; Ok(PrivateKey(pk)) } pub fn write(&self, writer: W) -> io::Result<()> { - write_scalar::(&self.0, writer) + write_scalar::(&self.0, writer) } - pub fn sign( - &self, - msg: &[u8], - rng: &mut R, - p_g: FixedGenerators, - params: &E::Params, - ) -> Signature { + pub fn sign(&self, msg: &[u8], rng: &mut R, p_g: SubgroupPoint) -> Signature { // T = (l_H + 128) bits of randomness // For H*, l_H = 512 bits let mut t = [0u8; 80]; rng.fill_bytes(&mut t[..]); // r = H*(T || M) - let r = h_star::(&t[..], msg); + let r = h_star(&t[..], msg); // R = r . P_G - let r_g = params.generator(p_g).mul(r, params); - let mut rbar = [0u8; 32]; - r_g.write(&mut rbar[..]) - .expect("Jubjub points should serialize to 32 bytes"); + let r_g = p_g * r; + let rbar = r_g.to_bytes(); // S = r + H*(Rbar || M) . sk - let mut s = h_star::(&rbar[..], msg); + let mut s = h_star(&rbar[..], msg); s.mul_assign(&self.0); s.add_assign(&r); let mut sbar = [0u8; 32]; - write_scalar::(&s, &mut sbar[..]) + write_scalar::<&mut [u8]>(&s, &mut sbar[..]) .expect("Jubjub scalars should serialize to 32 bytes"); Signature { rbar, sbar } } } -impl PublicKey { - pub fn from_private(privkey: &PrivateKey, p_g: FixedGenerators, params: &E::Params) -> Self { - let res = params.generator(p_g).mul(privkey.0, params).into(); - PublicKey(res) +impl PublicKey { + pub fn from_private(privkey: &PrivateKey, p_g: SubgroupPoint) -> Self { + PublicKey((p_g * privkey.0).into()) } - pub fn randomize(&self, alpha: E::Fs, p_g: FixedGenerators, params: &E::Params) -> Self { - let res: Point = params.generator(p_g).mul(alpha, params).into(); - let res = res.add(&self.0, params); - PublicKey(res) + pub fn randomize(&self, alpha: jubjub::Fr, p_g: SubgroupPoint) -> Self { + PublicKey(ExtendedPoint::from(p_g * alpha) + self.0) } - pub fn read(reader: R, params: &E::Params) -> io::Result { - let p = Point::read(reader, params)?; - Ok(PublicKey(p)) + pub fn read(mut reader: R) -> io::Result { + let mut bytes = [0u8; 32]; + reader.read_exact(&mut bytes)?; + let p = ExtendedPoint::from_bytes(&bytes).map(PublicKey); + if p.is_some().into() { + Ok(p.unwrap()) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid RedJubjub public key", + )) + } } - pub fn write(&self, writer: W) -> io::Result<()> { - self.0.write(writer) + pub fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_all(&self.0.to_bytes()) } - pub fn verify( - &self, - msg: &[u8], - sig: &Signature, - p_g: FixedGenerators, - params: &E::Params, - ) -> bool { + pub fn verify(&self, msg: &[u8], sig: &Signature, p_g: SubgroupPoint) -> bool { // c = H*(Rbar || M) - let c = h_star::(&sig.rbar[..], msg); + let c = h_star(&sig.rbar[..], msg); // Signature checks: // R != invalid - let r = match Point::read(&sig.rbar[..], params) { - Ok(r) => r, - Err(_) => return false, + let r = { + let r = ExtendedPoint::from_bytes(&sig.rbar); + if r.is_none().into() { + return false; + } + r.unwrap() }; // S < order(G) - // (E::Fs guarantees its representation is in the field) - let s = match read_scalar::(&sig.sbar[..]) { + // (jubjub::Scalar guarantees its representation is in the field) + let s = match read_scalar::<&[u8]>(&sig.sbar[..]) { Ok(s) => s, Err(_) => return false, }; // 0 = h_G(-S . P_G + R + c . vk) - self.0 - .mul(c, params) - .add(&r, params) - .add( - ¶ms.generator(p_g).mul(s, params).negate().into(), - params, - ) - .mul_by_cofactor(params) - .eq(&Point::zero()) + ((self.0 * c) + r - (p_g * s)) + .mul_by_cofactor() + .is_identity() + .into() } } -pub struct BatchEntry<'a, E: JubjubEngine> { - vk: PublicKey, +pub struct BatchEntry<'a> { + vk: PublicKey, msg: &'a [u8], sig: Signature, } // TODO: #82: This is a naive implementation currently, // and doesn't use multiexp. -pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>( +pub fn batch_verify<'a, R: RngCore>( rng: &mut R, - batch: &[BatchEntry<'a, E>], - p_g: FixedGenerators, - params: &E::Params, + batch: &[BatchEntry<'a>], + p_g: SubgroupPoint, ) -> bool { - let mut acc = Point::::zero(); + let mut acc = ExtendedPoint::identity(); for entry in batch { - let mut r = match Point::::read(&entry.sig.rbar[..], params) { - Ok(r) => r, - Err(_) => return false, + let mut r = { + let r = ExtendedPoint::from_bytes(&entry.sig.rbar); + if r.is_none().into() { + return false; + } + r.unwrap() }; - let mut s = match read_scalar::(&entry.sig.sbar[..]) { + let mut s = match read_scalar::<&[u8]>(&entry.sig.sbar[..]) { Ok(s) => s, Err(_) => return false, }; - let mut c = h_star::(&entry.sig.rbar[..], entry.msg); + let mut c = h_star(&entry.sig.rbar[..], entry.msg); - let z = E::Fs::random(rng); + let z = jubjub::Fr::random(rng); s.mul_assign(&z); s = s.neg(); - r = r.mul(z, params); + r = r * z; c.mul_assign(&z); - acc = acc.add(&r, params); - acc = acc.add(&entry.vk.0.mul(c, params), params); - acc = acc.add(¶ms.generator(p_g).mul(s, params).into(), params); + acc = acc + r + (&entry.vk.0 * c) + (p_g * s); } - acc = acc.mul_by_cofactor(params).into(); + acc = acc.mul_by_cofactor().into(); - acc.eq(&Point::zero()) + acc.is_identity().into() } #[cfg(test)] mod tests { - use pairing::bls12_381::Bls12; + use group::Group; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; - use crate::jubjub::{edwards, fs::Fs, JubjubBls12}; - use super::*; + use crate::constants::SPENDING_KEY_GENERATOR; #[test] fn test_batch_verify() { @@ -221,20 +211,19 @@ mod tests { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); - let p_g = FixedGenerators::SpendingKeyGenerator; + let p_g = SPENDING_KEY_GENERATOR; - let sk1 = PrivateKey::(Fs::random(rng)); - let vk1 = PublicKey::from_private(&sk1, p_g, params); + let sk1 = PrivateKey(jubjub::Fr::random(rng)); + let vk1 = PublicKey::from_private(&sk1, p_g); let msg1 = b"Foo bar"; - let sig1 = sk1.sign(msg1, rng, p_g, params); - assert!(vk1.verify(msg1, &sig1, p_g, params)); + let sig1 = sk1.sign(msg1, rng, p_g); + assert!(vk1.verify(msg1, &sig1, p_g)); - let sk2 = PrivateKey::(Fs::random(rng)); - let vk2 = PublicKey::from_private(&sk2, p_g, params); + let sk2 = PrivateKey(jubjub::Fr::random(rng)); + let vk2 = PublicKey::from_private(&sk2, p_g); let msg2 = b"Foo bar"; - let sig2 = sk2.sign(msg2, rng, p_g, params); - assert!(vk2.verify(msg2, &sig2, p_g, params)); + let sig2 = sk2.sign(msg2, rng, p_g); + assert!(vk2.verify(msg2, &sig2, p_g)); let mut batch = vec![ BatchEntry { @@ -249,11 +238,11 @@ mod tests { }, ]; - assert!(batch_verify(rng, &batch, p_g, params)); + assert!(batch_verify(rng, &batch, p_g)); batch[0].sig = sig2; - assert!(!batch_verify(rng, &batch, p_g, params)); + assert!(!batch_verify(rng, &batch, p_g)); } #[test] @@ -262,33 +251,34 @@ mod tests { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); - let zero = edwards::Point::zero(); - let p_g = FixedGenerators::SpendingKeyGenerator; + let zero = jubjub::ExtendedPoint::identity(); + let p_g = SPENDING_KEY_GENERATOR; // Get a point of order 8 let p8 = loop { - let r = edwards::Point::::rand(rng, params).mul(Fs::char(), params); + let r = jubjub::ExtendedPoint::random(rng) + .to_niels() + .multiply_bits(&jubjub::Fr::char()); - let r2 = r.double(params); - let r4 = r2.double(params); - let r8 = r4.double(params); + let r2 = r.double(); + let r4 = r2.double(); + let r8 = r4.double(); if r2 != zero && r4 != zero && r8 == zero { break r; } }; - let sk = PrivateKey::(Fs::random(rng)); - let vk = PublicKey::from_private(&sk, p_g, params); + let sk = PrivateKey(jubjub::Fr::random(rng)); + let vk = PublicKey::from_private(&sk, p_g); // TODO: This test will need to change when #77 is fixed let msg = b"Foo bar"; - let sig = sk.sign(msg, rng, p_g, params); - assert!(vk.verify(msg, &sig, p_g, params)); + let sig = sk.sign(msg, rng, p_g); + assert!(vk.verify(msg, &sig, p_g)); - let vktorsion = PublicKey(vk.0.add(&p8, params)); - assert!(vktorsion.verify(msg, &sig, p_g, params)); + let vktorsion = PublicKey(vk.0 + p8); + assert!(vktorsion.verify(msg, &sig, p_g)); } #[test] @@ -297,14 +287,13 @@ mod tests { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let p_g = FixedGenerators::SpendingKeyGenerator; - let params = &JubjubBls12::new(); + let p_g = SPENDING_KEY_GENERATOR; for _ in 0..1000 { - let sk = PrivateKey::(Fs::random(rng)); - let vk = PublicKey::from_private(&sk, p_g, params); + let sk = PrivateKey(jubjub::Fr::random(rng)); + let vk = PublicKey::from_private(&sk, p_g); let msg = b"Foo bar"; - let sig = sk.sign(msg, rng, p_g, params); + let sig = sk.sign(msg, rng, p_g); let mut sk_bytes = [0u8; 32]; let mut vk_bytes = [0u8; 32]; @@ -313,17 +302,17 @@ mod tests { vk.write(&mut vk_bytes[..]).unwrap(); sig.write(&mut sig_bytes[..]).unwrap(); - let sk_2 = PrivateKey::::read(&sk_bytes[..]).unwrap(); - let vk_2 = PublicKey::from_private(&sk_2, p_g, params); + let sk_2 = PrivateKey::read(&sk_bytes[..]).unwrap(); + let vk_2 = PublicKey::from_private(&sk_2, p_g); let mut vk_2_bytes = [0u8; 32]; vk_2.write(&mut vk_2_bytes[..]).unwrap(); assert!(vk_bytes == vk_2_bytes); - let vk_2 = PublicKey::::read(&vk_bytes[..], params).unwrap(); + let vk_2 = PublicKey::read(&vk_bytes[..]).unwrap(); let sig_2 = Signature::read(&sig_bytes[..]).unwrap(); - assert!(vk.verify(msg, &sig_2, p_g, params)); - assert!(vk_2.verify(msg, &sig, p_g, params)); - assert!(vk_2.verify(msg, &sig_2, p_g, params)); + assert!(vk.verify(msg, &sig_2, p_g)); + assert!(vk_2.verify(msg, &sig, p_g)); + assert!(vk_2.verify(msg, &sig_2, p_g)); } } @@ -333,35 +322,34 @@ mod tests { 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let p_g = FixedGenerators::SpendingKeyGenerator; - let params = &JubjubBls12::new(); + let p_g = SPENDING_KEY_GENERATOR; for _ in 0..1000 { - let sk = PrivateKey::(Fs::random(rng)); - let vk = PublicKey::from_private(&sk, p_g, params); + let sk = PrivateKey(jubjub::Fr::random(rng)); + let vk = PublicKey::from_private(&sk, p_g); let msg1 = b"Foo bar"; let msg2 = b"Spam eggs"; - let sig1 = sk.sign(msg1, rng, p_g, params); - let sig2 = sk.sign(msg2, rng, p_g, params); + let sig1 = sk.sign(msg1, rng, p_g); + let sig2 = sk.sign(msg2, rng, p_g); - assert!(vk.verify(msg1, &sig1, p_g, params)); - assert!(vk.verify(msg2, &sig2, p_g, params)); - assert!(!vk.verify(msg1, &sig2, p_g, params)); - assert!(!vk.verify(msg2, &sig1, p_g, params)); + assert!(vk.verify(msg1, &sig1, p_g)); + assert!(vk.verify(msg2, &sig2, p_g)); + assert!(!vk.verify(msg1, &sig2, p_g)); + assert!(!vk.verify(msg2, &sig1, p_g)); - let alpha = Fs::random(rng); + let alpha = jubjub::Fr::random(rng); let rsk = sk.randomize(alpha); - let rvk = vk.randomize(alpha, p_g, params); + let rvk = vk.randomize(alpha, p_g); - let sig1 = rsk.sign(msg1, rng, p_g, params); - let sig2 = rsk.sign(msg2, rng, p_g, params); + let sig1 = rsk.sign(msg1, rng, p_g); + let sig2 = rsk.sign(msg2, rng, p_g); - assert!(rvk.verify(msg1, &sig1, p_g, params)); - assert!(rvk.verify(msg2, &sig2, p_g, params)); - assert!(!rvk.verify(msg1, &sig2, p_g, params)); - assert!(!rvk.verify(msg2, &sig1, p_g, params)); + assert!(rvk.verify(msg1, &sig1, p_g)); + assert!(rvk.verify(msg2, &sig2, p_g)); + assert!(!rvk.verify(msg1, &sig2, p_g)); + assert!(!rvk.verify(msg2, &sig1, p_g)); } } } diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index da8b8384f7..eafb087361 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -1,27 +1,26 @@ //! Structs and constants specific to the Sapling shielded pool. use crate::{ - jubjub::{fs::Fs, FixedGenerators, JubjubBls12}, + constants::SPENDING_KEY_GENERATOR, pedersen_hash::{pedersen_hash, Personalization}, primitives::Note, }; -use ff::{BitIterator, PrimeField, PrimeFieldRepr}; +use ff::{BitIterator, PrimeField}; +use group::{Curve, GroupEncoding}; use lazy_static::lazy_static; -use pairing::bls12_381::{Bls12, Fr, FrRepr}; use rand_core::{CryptoRng, RngCore}; use std::io::{self, Read, Write}; use crate::merkle_tree::Hashable; use crate::redjubjub::{PrivateKey, PublicKey, Signature}; -use crate::JUBJUB; pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32; /// Compute a parent node in the Sapling commitment tree given its two children. -pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { +pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> [u8; 32] { let lhs = { let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().rev().zip(BitIterator::new(lhs)) { + for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(lhs)) { *a = b; } tmp @@ -29,46 +28,49 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr { let rhs = { let mut tmp = [false; 256]; - for (a, b) in tmp.iter_mut().rev().zip(BitIterator::new(rhs)) { + for (a, b) in tmp.iter_mut().rev().zip(BitIterator::::new(rhs)) { *a = b; } tmp }; - pedersen_hash::( + jubjub::ExtendedPoint::from(pedersen_hash( Personalization::MerkleTree(depth), lhs.iter() .copied() - .take(Fr::NUM_BITS as usize) - .chain(rhs.iter().copied().take(Fr::NUM_BITS as usize)), - &JUBJUB, - ) - .to_xy() - .0 - .into_repr() + .take(bls12_381::Scalar::NUM_BITS as usize) + .chain( + rhs.iter() + .copied() + .take(bls12_381::Scalar::NUM_BITS as usize), + ), + )) + .to_affine() + .get_u() + .to_repr() } /// A node within the Sapling commitment tree. #[derive(Clone, Copy, Debug, PartialEq)] pub struct Node { - repr: FrRepr, + repr: [u8; 32], } impl Node { - pub fn new(repr: FrRepr) -> Self { + pub fn new(repr: [u8; 32]) -> Self { Node { repr } } } impl Hashable for Node { fn read(mut reader: R) -> io::Result { - let mut repr = FrRepr::default(); - repr.read_le(&mut reader)?; + let mut repr = [0u8; 32]; + reader.read_exact(&mut repr)?; Ok(Node::new(repr)) } fn write(&self, mut writer: W) -> io::Result<()> { - self.repr.write_le(&mut writer) + writer.write_all(self.repr.as_ref()) } fn combine(depth: usize, lhs: &Self, rhs: &Self) -> Self { @@ -79,7 +81,7 @@ impl Hashable for Node { fn blank() -> Self { Node { - repr: Note::::uncommitted().into_repr(), + repr: Note::uncommitted().to_repr(), } } @@ -88,9 +90,9 @@ impl Hashable for Node { } } -impl From for Fr { +impl From for bls12_381::Scalar { fn from(node: Node) -> Self { - Fr::from_repr(node.repr).expect("Tree nodes should be in the prime field") + bls12_381::Scalar::from_repr(node.repr).expect("Tree nodes should be in the prime field") } } @@ -107,29 +109,22 @@ lazy_static! { /// Create the spendAuthSig for a Sapling SpendDescription. pub fn spend_sig( - ask: PrivateKey, - ar: Fs, + ask: PrivateKey, + ar: jubjub::Fr, sighash: &[u8; 32], rng: &mut R, - params: &JubjubBls12, ) -> Signature { // We compute `rsk`... let rsk = ask.randomize(ar); // We compute `rk` from there (needed for key prefixing) - let rk = PublicKey::from_private(&rsk, FixedGenerators::SpendingKeyGenerator, params); + let rk = PublicKey::from_private(&rsk, SPENDING_KEY_GENERATOR); // Compute the signature's message for rk/spend_auth_sig let mut data_to_be_signed = [0u8; 64]; - rk.0.write(&mut data_to_be_signed[0..32]) - .expect("message buffer should be 32 bytes"); + data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes()); (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash[..]); // Do the signing - rsk.sign( - &data_to_be_signed, - rng, - FixedGenerators::SpendingKeyGenerator, - params, - ) + rsk.sign(&data_to_be_signed, rng, SPENDING_KEY_GENERATOR) } diff --git a/zcash_primitives/src/serialize.rs b/zcash_primitives/src/serialize.rs index 4e0fb9338c..5e56fbb3d9 100644 --- a/zcash_primitives/src/serialize.rs +++ b/zcash_primitives/src/serialize.rs @@ -3,10 +3,10 @@ use std::io::{self, Read, Write}; const MAX_SIZE: usize = 0x02000000; -struct CompactSize; +pub(crate) struct CompactSize; impl CompactSize { - fn read(mut reader: R) -> io::Result { + pub(crate) fn read(mut reader: R) -> io::Result { let flag = reader.read_u8()?; match if flag < 253 { Ok(flag as usize) @@ -43,7 +43,7 @@ impl CompactSize { } } - fn write(mut writer: W, size: usize) -> io::Result<()> { + pub(crate) fn write(mut writer: W, size: usize) -> io::Result<()> { match size { s if s < 253 => writer.write_u8(s as u8), s if s <= 0xFFFF => { diff --git a/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs b/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs index ba4f2a83eb..c638f74861 100644 --- a/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs +++ b/zcash_primitives/src/test_vectors/pedersen_hash_vectors.rs @@ -7,26 +7,26 @@ pub fn get_vectors<'a>() -> Vec> { TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1], - hash_x: "Fr(0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b)", - hash_y: "Fr(0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982)", + hash_u: "0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b", + hash_v: "0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982", }, TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1, 0], - hash_x: "Fr(0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a)", - hash_y: "Fr(0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638)", + hash_u: "0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a", + hash_v: "0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638", }, TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1, 1], - hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)", - hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)", + hash_u: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97", + hash_v: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346", }, TestVector { personalization: Personalization::NoteCommitment, input_bits: vec![1, 1, 1, 1, 1, 1, 1, 0, 0], - hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)", - hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)", + hash_u: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97", + hash_v: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346", }, TestVector { personalization: Personalization::NoteCommitment, @@ -39,8 +39,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, ], - hash_x: "Fr(0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f)", - hash_y: "Fr(0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527)", + hash_u: "0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f", + hash_v: "0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527", }, TestVector { personalization: Personalization::NoteCommitment, @@ -53,8 +53,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, ], - hash_x: "Fr(0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd)", - hash_y: "Fr(0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2)", + hash_u: "0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd", + hash_v: "0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2", }, TestVector { personalization: Personalization::NoteCommitment, @@ -67,8 +67,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, ], - hash_x: "Fr(0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40)", - hash_y: "Fr(0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743)", + hash_u: "0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40", + hash_v: "0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743", }, TestVector { personalization: Personalization::NoteCommitment, @@ -101,8 +101,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, ], - hash_x: "Fr(0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27)", - hash_y: "Fr(0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74)", + hash_u: "0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27", + hash_v: "0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74", }, TestVector { personalization: Personalization::NoteCommitment, @@ -136,8 +136,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, ], - hash_x: "Fr(0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e)", - hash_y: "Fr(0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab)", + hash_u: "0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e", + hash_v: "0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab", }, TestVector { personalization: Personalization::NoteCommitment, @@ -177,8 +177,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, ], - hash_x: "Fr(0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f)", - hash_y: "Fr(0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71)", + hash_u: "0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f", + hash_v: "0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71", }, TestVector { personalization: Personalization::NoteCommitment, @@ -218,32 +218,32 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, ], - hash_x: "Fr(0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7)", - hash_y: "Fr(0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f)", + hash_u: "0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7", + hash_v: "0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0], - hash_x: "Fr(0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d)", - hash_y: "Fr(0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330)", + hash_u: "0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d", + hash_v: "0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0, 0], - hash_x: "Fr(0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea)", - hash_y: "Fr(0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4)", + hash_u: "0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea", + hash_v: "0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0, 1], - hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)", - hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)", + hash_u: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d", + hash_v: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622", }, TestVector { personalization: Personalization::MerkleTree(0), input_bits: vec![0, 0, 0, 0, 0, 0, 1, 0, 0], - hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)", - hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)", + hash_u: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d", + hash_v: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -256,8 +256,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, ], - hash_x: "Fr(0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd)", - hash_y: "Fr(0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544)", + hash_u: "0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd", + hash_v: "0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -270,8 +270,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, ], - hash_x: "Fr(0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555)", - hash_y: "Fr(0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb)", + hash_u: "0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555", + hash_v: "0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -284,8 +284,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, ], - hash_x: "Fr(0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d)", - hash_y: "Fr(0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f)", + hash_u: "0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d", + hash_v: "0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -318,8 +318,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, ], - hash_x: "Fr(0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85)", - hash_y: "Fr(0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a)", + hash_u: "0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85", + hash_v: "0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -353,8 +353,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, ], - hash_x: "Fr(0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68)", - hash_y: "Fr(0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee)", + hash_u: "0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68", + hash_v: "0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -394,8 +394,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, ], - hash_x: "Fr(0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37)", - hash_y: "Fr(0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6)", + hash_u: "0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37", + hash_v: "0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -435,32 +435,32 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, ], - hash_x: "Fr(0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05)", - hash_y: "Fr(0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33)", + hash_u: "0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05", + hash_v: "0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1], - hash_x: "Fr(0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a)", - hash_y: "Fr(0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a)", + hash_u: "0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a", + hash_v: "0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1, 0], - hash_x: "Fr(0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c)", - hash_y: "Fr(0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df)", + hash_u: "0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c", + hash_v: "0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1, 1], - hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)", - hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)", + hash_u: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc", + hash_v: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c", }, TestVector { personalization: Personalization::MerkleTree(34), input_bits: vec![0, 1, 0, 0, 0, 1, 1, 0, 0], - hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)", - hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)", + hash_u: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc", + hash_v: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -473,8 +473,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, ], - hash_x: "Fr(0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2)", - hash_y: "Fr(0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31)", + hash_u: "0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2", + hash_v: "0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -487,8 +487,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, ], - hash_x: "Fr(0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb)", - hash_y: "Fr(0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f)", + hash_u: "0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb", + hash_v: "0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -501,8 +501,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, ], - hash_x: "Fr(0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2)", - hash_y: "Fr(0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de)", + hash_u: "0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2", + hash_v: "0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -535,8 +535,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, ], - hash_x: "Fr(0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc)", - hash_y: "Fr(0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de)", + hash_u: "0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc", + hash_v: "0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -570,8 +570,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, ], - hash_x: "Fr(0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda)", - hash_y: "Fr(0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d)", + hash_u: "0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda", + hash_v: "0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -611,8 +611,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, ], - hash_x: "Fr(0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666)", - hash_y: "Fr(0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289)", + hash_u: "0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666", + hash_v: "0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289", }, TestVector { personalization: Personalization::MerkleTree(34), @@ -652,8 +652,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, ], - hash_x: "Fr(0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7)", - hash_y: "Fr(0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad)", + hash_u: "0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7", + hash_v: "0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad", }, TestVector { personalization: Personalization::MerkleTree(27), @@ -666,8 +666,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, ], - hash_x: "Fr(0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4)", - hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)", + hash_u: "0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4", + hash_v: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9", }, TestVector { personalization: Personalization::MerkleTree(36), @@ -680,8 +680,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, ], - hash_x: "Fr(0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024)", - hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)", + hash_u: "0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024", + hash_v: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1", }, TestVector { personalization: Personalization::MerkleTree(0), @@ -694,8 +694,8 @@ pub fn get_vectors<'a>() -> Vec> { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], - hash_x: "Fr(0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd)", - hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)", + hash_u: "0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd", + hash_v: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1", }, TestVector { personalization: Personalization::NoteCommitment, @@ -708,8 +708,8 @@ pub fn get_vectors<'a>() -> Vec> { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ], - hash_x: "Fr(0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d)", - hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)", + hash_u: "0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d", + hash_v: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9", }, ]; } diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 9acb996d41..a9ec48247f 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -1,28 +1,33 @@ //! Structs for building transactions. -use crate::zip32::ExtendedSpendingKey; -use crate::{ - jubjub::fs::Fs, - primitives::{Diversifier, Note, PaymentAddress}, -}; +use std::boxed::Box; +use std::error; +use std::fmt; +use std::marker::PhantomData; + use ff::Field; -use pairing::bls12_381::{Bls12, Fr}; use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore}; use crate::{ consensus, + extensions::transparent::{self as tze, ExtensionTxBuilder, ToPayload}, keys::OutgoingViewingKey, legacy::TransparentAddress, merkle_tree::MerklePath, - note_encryption::{generate_esk, Memo, SaplingNoteEncryption}, + note_encryption::{Memo, SaplingNoteEncryption}, + primitives::{Diversifier, Note, PaymentAddress}, prover::TxProver, redjubjub::PrivateKey, sapling::{spend_sig, Node}, transaction::{ - components::{amount::DEFAULT_FEE, Amount, OutputDescription, SpendDescription, TxOut}, - signature_hash_data, Transaction, TransactionData, SIGHASH_ALL, + components::{ + amount::Amount, amount::DEFAULT_FEE, OutPoint, OutputDescription, SpendDescription, + TxOut, TzeIn, TzeOut, + }, + signature_hash_data, SignableInput, Transaction, TransactionData, SIGHASH_ALL, }, - JUBJUB, + util::generate_random_rseed, + zip32::ExtendedSpendingKey, }; #[cfg(feature = "transparent-inputs")] @@ -48,30 +53,51 @@ pub enum Error { SpendProof, } +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::AnchorMismatch => { + write!(f, "Anchor mismatch (anchors for all spends must be equal)") + } + Error::BindingSig => write!(f, "Failed to create bindingSig"), + Error::ChangeIsNegative(amount) => { + write!(f, "Change is negative ({:?} zatoshis)", amount) + } + Error::InvalidAddress => write!(f, "Invalid address"), + Error::InvalidAmount => write!(f, "Invalid amount"), + Error::NoChangeAddress => write!(f, "No change address specified or discoverable"), + Error::SpendProof => write!(f, "Failed to create Sapling spend proof"), + } + } +} + +impl error::Error for Error {} + struct SpendDescriptionInfo { extsk: ExtendedSpendingKey, diversifier: Diversifier, - note: Note, - alpha: Fs, + note: Note, + alpha: jubjub::Fr, merkle_path: MerklePath, } pub struct SaplingOutput { ovk: OutgoingViewingKey, - to: PaymentAddress, - note: Note, + to: PaymentAddress, + note: Note, memo: Memo, } impl SaplingOutput { - pub fn new( + pub fn new( + height: u32, rng: &mut R, ovk: OutgoingViewingKey, - to: PaymentAddress, + to: PaymentAddress, value: Amount, memo: Option, ) -> Result { - let g_d = match to.g_d(&JUBJUB) { + let g_d = match to.g_d() { Some(g_d) => g_d, None => return Err(Error::InvalidAddress), }; @@ -79,13 +105,13 @@ impl SaplingOutput { return Err(Error::InvalidAmount); } - let rcm = Fs::random(rng); + let rseed = generate_random_rseed::(height, rng); let note = Note { g_d, pk_d: to.pk_d().clone(), value: value.into(), - r: rcm, + rseed, }; Ok(SaplingOutput { @@ -114,11 +140,11 @@ impl SaplingOutput { ctx, encryptor.esk().clone(), self.to, - self.note.r, + self.note.rcm(), self.note.value, ); - let cmu = self.note.cm(&JUBJUB); + let cmu = self.note.cmu(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); let out_ciphertext = encryptor.encrypt_outgoing_plaintext(&cv, &cmu); @@ -165,17 +191,14 @@ struct TransparentInputs; impl TransparentInputs { #[cfg(feature = "transparent-inputs")] - fn push( - &mut self, - mtx: &mut TransactionData, - sk: secp256k1::SecretKey, - utxo: OutPoint, - coin: TxOut, - ) -> Result<(), Error> { + fn push(&mut self, sk: secp256k1::SecretKey, coin: TxOut) -> Result<(), Error> { if coin.value.is_negative() { return Err(Error::InvalidAmount); } + // ensure that the ripemd160 digest of the public key associated with the + // provided secret key matches that of the address to which the provided + // output may be spent let pubkey = secp256k1::PublicKey::from_secret_key(&self.secp, &sk).serialize(); match coin.script_pubkey.address() { Some(TransparentAddress::PublicKey(hash)) => { @@ -189,7 +212,6 @@ impl TransparentInputs { _ => return Err(Error::InvalidAddress), } - mtx.vin.push(TxIn::new(utxo)); self.inputs.push(TransparentInputInfo { sk, pubkey, coin }); Ok(()) @@ -222,7 +244,7 @@ impl TransparentInputs { mtx, consensus_branch_id, SIGHASH_ALL, - Some((i, &info.coin.script_pubkey, info.coin.value)), + SignableInput::transparent(i, &info.coin.script_pubkey, info.coin.value), )); let msg = secp256k1::Message::from_slice(&sighash).expect("32 bytes"); @@ -241,6 +263,46 @@ impl TransparentInputs { fn apply_signatures(&self, _: &mut TransactionData, _: consensus::BranchId) {} } +struct TzeInputInfo<'a, BuildCtx> { + prevout: TzeOut, + builder: Box Result + 'a>, +} + +struct TzeInputs<'a, BuildCtx> { + builders: Vec>, +} + +impl<'a, BuildCtx> TzeInputs<'a, BuildCtx> { + fn default() -> Self { + TzeInputs { builders: vec![] } + } + + fn push( + &mut self, + extension_id: u32, + prevout: (OutPoint, TzeOut), + builder: WBuilder, + ) where + WBuilder: 'a + FnOnce(&BuildCtx) -> Result, + { + let (outpoint, tzeout) = prevout; + self.builders.push(TzeInputInfo { + prevout: tzeout, + builder: Box::new(move |ctx| { + let (mode, payload) = builder(&ctx).map(|x| x.to_payload())?; + Ok(TzeIn { + prevout: outpoint, + witness: tze::Witness { + extension_id, + mode, + payload, + }, + }) + }), + }); + } +} + /// Metadata about a transaction created by a [`Builder`]. #[derive(Debug, PartialEq)] pub struct TransactionMetadata { @@ -280,18 +342,21 @@ impl TransactionMetadata { } /// Generates a [`Transaction`] from its inputs and outputs. -pub struct Builder { +pub struct Builder<'a, P: consensus::Parameters, R: RngCore + CryptoRng> { rng: R, + height: u32, mtx: TransactionData, fee: Amount, - anchor: Option, + anchor: Option, spends: Vec, outputs: Vec, transparent_inputs: TransparentInputs, - change_address: Option<(OutgoingViewingKey, PaymentAddress)>, + tze_inputs: TzeInputs<'a, TransactionData>, + change_address: Option<(OutgoingViewingKey, PaymentAddress)>, + phantom: PhantomData

, } -impl Builder { +impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng> { /// Creates a new `Builder` targeted for inclusion in the block with the given height, /// using default values for general transaction fields and the default OS random. /// @@ -304,34 +369,50 @@ impl Builder { pub fn new(height: u32) -> Self { Builder::new_with_rng(height, OsRng) } + + pub fn new_future(height: u32) -> Self { + Builder::new_with_rng_future(height, OsRng) + } } -impl Builder { - /// Creates a new `Builder` targeted for inclusion in the block with the given height - /// and randomness source, using default values for general transaction fields. - /// - /// # Default values - /// - /// The expiry height will be set to the given height plus the default transaction - /// expiry delta (20 blocks). - /// - /// The fee will be set to the default fee (0.0001 ZEC). - pub fn new_with_rng(height: u32, rng: R) -> Builder { - let mut mtx = TransactionData::new(); +impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { + fn new_with_mtx(height: u32, rng: R, mut mtx: TransactionData) -> Builder<'a, P, R> { mtx.expiry_height = height + DEFAULT_TX_EXPIRY_DELTA; Builder { rng, + height, mtx, fee: DEFAULT_FEE, anchor: None, spends: vec![], outputs: vec![], transparent_inputs: TransparentInputs::default(), + tze_inputs: TzeInputs::default(), change_address: None, + phantom: PhantomData, } } + /// Creates a new `Builder` targeted for inclusion in the block with the given height + /// and randomness source, using default values for general transaction fields. + /// + /// # Default values + /// + /// The expiry height will be set to the given height plus the default transaction + /// expiry delta (20 blocks). + /// + /// The fee will be set to the default fee (0.0001 ZEC). + pub fn new_with_rng(height: u32, rng: R) -> Builder<'a, P, R> { + let mtx = TransactionData::new(); + Self::new_with_mtx(height, rng, mtx) + } + + pub fn new_with_rng_future(height: u32, rng: R) -> Builder<'a, P, R> { + let mtx = TransactionData::future(); + Self::new_with_mtx(height, rng, mtx) + } + /// Adds a Sapling note to be spent in this transaction. /// /// Returns an error if the given Merkle path does not have the same anchor as the @@ -340,21 +421,21 @@ impl Builder { &mut self, extsk: ExtendedSpendingKey, diversifier: Diversifier, - note: Note, + note: Note, merkle_path: MerklePath, ) -> Result<(), Error> { // Consistency check: all anchors must equal the first one - let cm = Node::new(note.cm(&JUBJUB).into()); + let cmu = Node::new(note.cmu().into()); if let Some(anchor) = self.anchor { - let path_root: Fr = merkle_path.root(cm).into(); + let path_root: bls12_381::Scalar = merkle_path.root(cmu).into(); if path_root != anchor { return Err(Error::AnchorMismatch); } } else { - self.anchor = Some(merkle_path.root(cm).into()) + self.anchor = Some(merkle_path.root(cmu).into()) } - let alpha = Fs::random(&mut self.rng); + let alpha = jubjub::Fr::random(&mut self.rng); self.mtx.value_balance += Amount::from_u64(note.value).map_err(|_| Error::InvalidAmount)?; @@ -373,11 +454,11 @@ impl Builder { pub fn add_sapling_output( &mut self, ovk: OutgoingViewingKey, - to: PaymentAddress, + to: PaymentAddress, value: Amount, memo: Option, ) -> Result<(), Error> { - let output = SaplingOutput::new(&mut self.rng, ovk, to, value, memo)?; + let output = SaplingOutput::new::(self.height, &mut self.rng, ovk, to, value, memo)?; self.mtx.value_balance -= value; @@ -388,13 +469,16 @@ impl Builder { /// Adds a transparent coin to be spent in this transaction. #[cfg(feature = "transparent-inputs")] + #[cfg_attr(docsrs, doc(cfg(feature = "transparent-inputs")))] pub fn add_transparent_input( &mut self, sk: secp256k1::SecretKey, utxo: OutPoint, coin: TxOut, ) -> Result<(), Error> { - self.transparent_inputs.push(&mut self.mtx, sk, utxo, coin) + self.transparent_inputs.push(sk, coin)?; + self.mtx.vin.push(TxIn::new(utxo)); + Ok(()); } /// Adds a transparent address to send funds to. @@ -419,7 +503,7 @@ impl Builder { /// /// By default, change is sent to the Sapling address corresponding to the first note /// being spent (i.e. the first call to [`Builder::add_sapling_spend`]). - pub fn send_change_to(&mut self, ovk: OutgoingViewingKey, to: PaymentAddress) { + pub fn send_change_to(&mut self, ovk: OutgoingViewingKey, to: PaymentAddress) { self.change_address = Some((ovk, to)); } @@ -445,12 +529,20 @@ impl Builder { // Valid change let change = self.mtx.value_balance - self.fee + self.transparent_inputs.value_sum() + - self.mtx.vout.iter().map(|vo| vo.value).sum::() + + self + .tze_inputs + .builders + .iter() + .map(|ein| ein.prevout.value) + .sum::() - self .mtx - .vout + .tze_outputs .iter() - .map(|output| output.value) + .map(|tzo| tzo.value) .sum::(); + if change.is_negative() { return Err(Error::ChangeIsNegative(change)); } @@ -511,18 +603,20 @@ impl Builder { tx_metadata.spend_indices.resize(spends.len(), 0); tx_metadata.output_indices.resize(orig_outputs_len, 0); + // Record if we'll need a binding signature + let binding_sig_needed = !spends.is_empty() || !outputs.is_empty(); + // Create Sapling SpendDescriptions if !spends.is_empty() { let anchor = self.anchor.expect("anchor was set if spends were added"); for (i, (pos, spend)) in spends.iter().enumerate() { - let proof_generation_key = spend.extsk.expsk.proof_generation_key(&JUBJUB); + let proof_generation_key = spend.extsk.expsk.proof_generation_key(); let mut nullifier = [0u8; 32]; nullifier.copy_from_slice(&spend.note.nf( - &proof_generation_key.to_viewing_key(&JUBJUB), + &proof_generation_key.to_viewing_key(), spend.merkle_path.position, - &JUBJUB, )); let (zkproof, cv, rk) = prover @@ -530,7 +624,7 @@ impl Builder { &mut ctx, proof_generation_key, spend.diversifier, - spend.note.r, + spend.note.rseed, spend.alpha, spend.note.value, anchor, @@ -569,7 +663,7 @@ impl Builder { let mut d = [0; 11]; self.rng.fill_bytes(&mut d); diversifier = Diversifier(d); - if let Some(val) = diversifier.g_d::(&JUBJUB) { + if let Some(val) = diversifier.g_d() { g_d = val; break; } @@ -578,31 +672,38 @@ impl Builder { }; let (pk_d, payment_address) = loop { - let dummy_ivk = Fs::random(&mut self.rng); - let pk_d = g_d.mul(dummy_ivk, &JUBJUB); + let dummy_ivk = jubjub::Fr::random(&mut self.rng); + let pk_d = g_d * dummy_ivk; if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d.clone()) { break (pk_d, addr); } }; + let rseed = generate_random_rseed::(self.height, &mut self.rng); + ( payment_address, Note { g_d, pk_d, - r: Fs::random(&mut self.rng), + rseed, value: 0, }, ) }; - let esk = generate_esk(&mut self.rng); - let epk = dummy_note.g_d.mul(esk, &JUBJUB); + let esk = dummy_note.generate_or_derive_esk(&mut self.rng); + let epk = dummy_note.g_d * esk; - let (zkproof, cv) = - prover.output_proof(&mut ctx, esk, dummy_to, dummy_note.r, dummy_note.value); + let (zkproof, cv) = prover.output_proof( + &mut ctx, + esk, + dummy_to, + dummy_note.rcm(), + dummy_note.value, + ); - let cmu = dummy_note.cm(&JUBJUB); + let cmu = dummy_note.cmu(); let mut enc_ciphertext = [0u8; 580]; let mut out_ciphertext = [0u8; 80]; @@ -623,7 +724,7 @@ impl Builder { } // - // Signatures + // Signatures -- all effects must have been applied. // let mut sighash = [0u8; 32]; @@ -631,7 +732,7 @@ impl Builder { &self.mtx, consensus_branch_id, SIGHASH_ALL, - None, + SignableInput::Shielded, )); // Create Sapling spendAuth and binding signatures @@ -641,14 +742,32 @@ impl Builder { spend.alpha, &sighash, &mut self.rng, - &JUBJUB, )); } - self.mtx.binding_sig = Some( - prover - .binding_sig(&mut ctx, self.mtx.value_balance, &sighash) - .map_err(|()| Error::BindingSig)?, - ); + + // Add a binding signature if needed + self.mtx.binding_sig = if binding_sig_needed { + Some( + prover + .binding_sig(&mut ctx, self.mtx.value_balance, &sighash) + .map_err(|_| Error::BindingSig)?, + ) + } else { + None + }; + + // // Create TZE input witnesses + for tze_in in self.tze_inputs.builders { + // Need to enable witness to commit to the amount. + // - So hardware wallets "know" the amount without having to be sent all the + // prior TZE outputs to which this witness gives evidence. + // + // The witness is expected to commit to the precommitment internally? + // (Or make it part of the sighash?) + // - TODO: Check whether transparent inputs committing to script_pubkey was + // only so that hardware wallets "knew" what address was being spent from. + self.mtx.tze_inputs.push((tze_in.builder)(&self.mtx)?); + } // Transparent signatures self.transparent_inputs @@ -661,25 +780,69 @@ impl Builder { } } +impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a> + for Builder<'a, P, R> +{ + type BuildCtx = TransactionData; + type BuildError = Error; + + fn add_tze_input( + &mut self, + extension_id: u32, + prevout: (OutPoint, TzeOut), + witness_builder: WBuilder, + ) -> Result<(), Self::BuildError> + where + WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result), + { + self.tze_inputs.push(extension_id, prevout, witness_builder); + Ok(()) + } + + fn add_tze_output( + &mut self, + extension_id: u32, + value: Amount, + guarded_by: &G, + ) -> Result<(), Self::BuildError> { + if value.is_negative() { + return Err(Error::InvalidAmount); + } + + let (mode, payload) = guarded_by.to_payload(); + self.mtx.tze_outputs.push(TzeOut { + value, + precondition: tze::Precondition { + extension_id, + mode, + payload, + }, + }); + + Ok(()) + } +} + #[cfg(test)] mod tests { use ff::{Field, PrimeField}; use rand_core::OsRng; + use std::marker::PhantomData; - use crate::jubjub::fs::Fs; - - use super::{Builder, Error}; use crate::{ consensus, + consensus::TestNetwork, legacy::TransparentAddress, merkle_tree::{CommitmentTree, IncrementalWitness}, + primitives::Rseed, prover::mock::MockTxProver, sapling::Node, transaction::components::Amount, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, - JUBJUB, }; + use super::{Builder, Error, TzeInputs}; + #[test] fn fails_on_negative_output() { let extsk = ExtendedSpendingKey::master(&[]); @@ -687,16 +850,94 @@ mod tests { let ovk = extfvk.fvk.ovk; let to = extfvk.default_address().unwrap().1; - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); assert_eq!( builder.add_sapling_output(ovk, to, Amount::from_i64(-1).unwrap(), None), Err(Error::InvalidAmount) ); } + #[test] + fn binding_sig_absent_if_no_shielded_spend_or_output() { + use crate::consensus::{NetworkUpgrade, Parameters}; + use crate::transaction::{ + builder::{self, TransparentInputs}, + TransactionData, + }; + + let sapling_activation_height = + TestNetwork::activation_height(NetworkUpgrade::Sapling).unwrap(); + + // Create a builder with 0 fee, so we can construct t outputs + let mut builder = builder::Builder::<'_, TestNetwork, OsRng> { + rng: OsRng, + height: sapling_activation_height, + mtx: TransactionData::new(), + fee: Amount::zero(), + anchor: None, + spends: vec![], + outputs: vec![], + transparent_inputs: TransparentInputs::default(), + tze_inputs: TzeInputs::default(), + change_address: None, + phantom: PhantomData, + }; + + // Create a tx with only t output. No binding_sig should be present + builder + .add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero()) + .unwrap(); + + let (tx, _) = builder + .build(consensus::BranchId::Sapling, &MockTxProver) + .unwrap(); + // No binding signature, because only t input and outputs + assert!(tx.binding_sig.is_none()); + } + + #[test] + fn binding_sig_present_if_shielded_spend() { + let extsk = ExtendedSpendingKey::master(&[]); + let extfvk = ExtendedFullViewingKey::from(&extsk); + let to = extfvk.default_address().unwrap().1; + + let mut rng = OsRng; + + let note1 = to + .create_note(50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) + .unwrap(); + let cmu1 = Node::new(note1.cmu().to_repr()); + let mut tree = CommitmentTree::new(); + tree.append(cmu1).unwrap(); + let witness1 = IncrementalWitness::from_tree(&tree); + + let mut builder = Builder::::new(0); + + // Create a tx with a sapling spend. binding_sig should be present + builder + .add_sapling_spend( + extsk.clone(), + *to.diversifier(), + note1.clone(), + witness1.path().unwrap(), + ) + .unwrap(); + + builder + .add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero()) + .unwrap(); + + // Expect a binding signature error, because our inputs aren't valid, but this shows + // that a binding signature was attempted + assert_eq!( + builder.build(consensus::BranchId::Sapling, &MockTxProver), + Err(Error::BindingSig) + ); + } + #[test] fn fails_on_negative_transparent_output() { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); assert_eq!( builder.add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -716,7 +957,7 @@ mod tests { // Fails with no inputs or outputs // 0.0001 t-ZEC fee { - let builder = Builder::new(0); + let builder = Builder::::new(0); assert_eq!( builder.build(consensus::BranchId::Sapling, &MockTxProver), Err(Error::ChangeIsNegative(Amount::from_i64(-10000).unwrap())) @@ -730,7 +971,7 @@ mod tests { // Fail if there is only a Sapling output // 0.0005 z-ZEC out, 0.0001 t-ZEC fee { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); builder .add_sapling_output( ovk.clone(), @@ -748,7 +989,7 @@ mod tests { // Fail if there is only a transparent output // 0.0005 t-ZEC out, 0.0001 t-ZEC fee { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); builder .add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -762,17 +1003,17 @@ mod tests { } let note1 = to - .create_note(59999, Fs::random(&mut rng), &JUBJUB) + .create_note(59999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) .unwrap(); - let cm1 = Node::new(note1.cm(&JUBJUB).into_repr()); + let cmu1 = Node::new(note1.cmu().to_repr()); let mut tree = CommitmentTree::new(); - tree.append(cm1).unwrap(); + tree.append(cmu1).unwrap(); let mut witness1 = IncrementalWitness::from_tree(&tree); // Fail if there is insufficient input // 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.0001 t-ZEC fee, 0.00059999 z-ZEC in { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); builder .add_sapling_spend( extsk.clone(), @@ -801,10 +1042,12 @@ mod tests { ); } - let note2 = to.create_note(1, Fs::random(&mut rng), &JUBJUB).unwrap(); - let cm2 = Node::new(note2.cm(&JUBJUB).into_repr()); - tree.append(cm2).unwrap(); - witness1.append(cm2).unwrap(); + let note2 = to + .create_note(1, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) + .unwrap(); + let cmu2 = Node::new(note2.cmu().to_repr()); + tree.append(cmu2).unwrap(); + witness1.append(cmu2).unwrap(); let witness2 = IncrementalWitness::from_tree(&tree); // Succeeds if there is sufficient input @@ -813,7 +1056,7 @@ mod tests { // (Still fails because we are using a MockTxProver which doesn't correctly // compute bindingSig.) { - let mut builder = Builder::new(0); + let mut builder = Builder::::new(0); builder .add_sapling_spend( extsk.clone(), diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index dfc54fdc6a..39934baac9 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -1,22 +1,25 @@ //! Structs representing the components within Zcash transactions. -use crate::jubjub::{edwards, Unknown}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::{Bls12, Fr, FrRepr}; + +use ff::PrimeField; +use group::GroupEncoding; + +use std::convert::TryFrom; use std::io::{self, Read, Write}; +use crate::extensions::transparent as tze; use crate::legacy::Script; use crate::redjubjub::{PublicKey, Signature}; -use crate::JUBJUB; +use crate::serialize::{CompactSize, Vector}; pub mod amount; pub use self::amount::Amount; // Ï€_A + Ï€_B + Ï€_C -pub const GROTH_PROOF_SIZE: usize = (48 + 96 + 48); +pub const GROTH_PROOF_SIZE: usize = 48 + 96 + 48; // Ï€_A + Ï€_A' + Ï€_B + Ï€_B' + Ï€_C + Ï€_C' + Ï€_K + Ï€_H -const PHGR_PROOF_SIZE: usize = (33 + 33 + 65 + 33 + 33 + 33 + 33 + 33); +const PHGR_PROOF_SIZE: usize = 33 + 33 + 65 + 33 + 33 + 33 + 33 + 33; const ZC_NUM_JS_INPUTS: usize = 2; const ZC_NUM_JS_OUTPUTS: usize = 2; @@ -33,7 +36,7 @@ impl OutPoint { } pub fn read(mut reader: R) -> io::Result { - let mut hash = [0; 32]; + let mut hash = [0u8; 32]; reader.read_exact(&mut hash)?; let n = reader.read_u32::()?; Ok(OutPoint { hash, n }) @@ -43,6 +46,14 @@ impl OutPoint { writer.write_all(&self.hash)?; writer.write_u32::(self.n) } + + pub fn n(&self) -> u32 { + self.n + } + + pub fn hash(&self) -> &[u8; 32] { + &self.hash + } } #[derive(Debug)] @@ -54,6 +65,7 @@ pub struct TxIn { impl TxIn { #[cfg(feature = "transparent-inputs")] + #[cfg_attr(docsrs, doc(cfg(feature = "transparent-inputs")))] pub fn new(prevout: OutPoint) -> Self { TxIn { prevout, @@ -90,7 +102,7 @@ pub struct TxOut { impl TxOut { pub fn read(mut reader: &mut R) -> io::Result { let value = { - let mut tmp = [0; 8]; + let mut tmp = [0u8; 8]; reader.read_exact(&mut tmp)?; Amount::from_nonnegative_i64_le_bytes(tmp) } @@ -109,11 +121,100 @@ impl TxOut { } } +#[derive(Clone, Debug)] +pub struct TzeIn { + pub prevout: OutPoint, + pub witness: tze::Witness, +} + +fn to_io_error(_: std::num::TryFromIntError) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, "value out of range") +} + +impl TzeIn { + pub fn read(mut reader: &mut R) -> io::Result { + let prevout = OutPoint::read(&mut reader)?; + + let extension_id = CompactSize::read(&mut reader)?; + let mode = CompactSize::read(&mut reader)?; + let payload = Vector::read(&mut reader, |r| r.read_u8())?; + + Ok(TzeIn { + prevout, + witness: tze::Witness { + extension_id: u32::try_from(extension_id).map_err(|e| to_io_error(e))?, + mode: u32::try_from(mode).map_err(|e| to_io_error(e))?, + payload, + }, + }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + self.prevout.write(&mut writer)?; + + CompactSize::write( + &mut writer, + usize::try_from(self.witness.extension_id).map_err(|e| to_io_error(e))?, + )?; + CompactSize::write( + &mut writer, + usize::try_from(self.witness.mode).map_err(|e| to_io_error(e))?, + )?; + Vector::write(&mut writer, &self.witness.payload, |w, b| w.write_u8(*b)) + } +} + +#[derive(Clone, Debug)] +pub struct TzeOut { + pub value: Amount, + pub precondition: tze::Precondition, +} + +impl TzeOut { + pub fn read(mut reader: &mut R) -> io::Result { + let value = { + let mut tmp = [0; 8]; + reader.read_exact(&mut tmp)?; + Amount::from_nonnegative_i64_le_bytes(tmp) + } + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "value out of range"))?; + + let extension_id = CompactSize::read(&mut reader)?; + let mode = CompactSize::read(&mut reader)?; + let payload = Vector::read(&mut reader, |r| r.read_u8())?; + + Ok(TzeOut { + value, + precondition: tze::Precondition { + extension_id: u32::try_from(extension_id).map_err(|e| to_io_error(e))?, + mode: u32::try_from(mode).map_err(|e| to_io_error(e))?, + payload, + }, + }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_all(&self.value.to_i64_le_bytes())?; + + CompactSize::write( + &mut writer, + usize::try_from(self.precondition.extension_id).map_err(|e| to_io_error(e))?, + )?; + CompactSize::write( + &mut writer, + usize::try_from(self.precondition.mode).map_err(|e| to_io_error(e))?, + )?; + Vector::write(&mut writer, &self.precondition.payload, |w, b| { + w.write_u8(*b) + }) + } +} + pub struct SpendDescription { - pub cv: edwards::Point, - pub anchor: Fr, + pub cv: jubjub::ExtendedPoint, + pub anchor: bls12_381::Scalar, pub nullifier: [u8; 32], - pub rk: PublicKey, + pub rk: PublicKey, pub zkproof: [u8; GROTH_PROOF_SIZE], pub spend_auth_sig: Option, } @@ -134,28 +235,37 @@ impl SpendDescription { // - Canonical encoding is enforced here. // - "Not small order" is enforced in SaplingVerificationContext::check_spend() // (located in zcash_proofs::sapling::verifier). - let cv = edwards::Point::::read(&mut reader, &JUBJUB)?; + let cv = { + let mut bytes = [0u8; 32]; + reader.read_exact(&mut bytes)?; + let cv = jubjub::ExtendedPoint::from_bytes(&bytes); + if cv.is_none().into() { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv")); + } + cv.unwrap() + }; // Consensus rule (§7.3): Canonical encoding is enforced here let anchor = { - let mut f = FrRepr::default(); - f.read_le(&mut reader)?; - Fr::from_repr(f).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))? + let mut f = [0u8; 32]; + reader.read_exact(&mut f)?; + bls12_381::Scalar::from_repr(f) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "anchor not in field"))? }; - let mut nullifier = [0; 32]; + let mut nullifier = [0u8; 32]; reader.read_exact(&mut nullifier)?; // Consensus rules (§4.4): // - Canonical encoding is enforced here. // - "Not small order" is enforced in SaplingVerificationContext::check_spend() - let rk = PublicKey::::read(&mut reader, &JUBJUB)?; + let rk = PublicKey::read(&mut reader)?; // Consensus rules (§4.4): // - Canonical encoding is enforced by the API of SaplingVerificationContext::check_spend() // due to the need to parse this into a bellman::groth16::Proof. // - Proof validity is enforced in SaplingVerificationContext::check_spend() - let mut zkproof = [0; GROTH_PROOF_SIZE]; + let mut zkproof = [0u8; GROTH_PROOF_SIZE]; reader.read_exact(&mut zkproof)?; // Consensus rules (§4.4): @@ -174,8 +284,8 @@ impl SpendDescription { } pub fn write(&self, mut writer: W) -> io::Result<()> { - self.cv.write(&mut writer)?; - self.anchor.into_repr().write_le(&mut writer)?; + writer.write_all(&self.cv.to_bytes())?; + writer.write_all(self.anchor.to_repr().as_ref())?; writer.write_all(&self.nullifier)?; self.rk.write(&mut writer)?; writer.write_all(&self.zkproof)?; @@ -190,9 +300,9 @@ impl SpendDescription { } pub struct OutputDescription { - pub cv: edwards::Point, - pub cmu: Fr, - pub ephemeral_key: edwards::Point, + pub cv: jubjub::ExtendedPoint, + pub cmu: bls12_381::Scalar, + pub ephemeral_key: jubjub::ExtendedPoint, pub enc_ciphertext: [u8; 580], pub out_ciphertext: [u8; 80], pub zkproof: [u8; GROTH_PROOF_SIZE], @@ -209,27 +319,47 @@ impl std::fmt::Debug for OutputDescription { } impl OutputDescription { - pub fn read(mut reader: &mut R) -> io::Result { + pub fn read(reader: &mut R) -> io::Result { // Consensus rules (§4.5): // - Canonical encoding is enforced here. // - "Not small order" is enforced in SaplingVerificationContext::check_output() // (located in zcash_proofs::sapling::verifier). - let cv = edwards::Point::::read(&mut reader, &JUBJUB)?; + let cv = { + let mut bytes = [0u8; 32]; + reader.read_exact(&mut bytes)?; + let cv = jubjub::ExtendedPoint::from_bytes(&bytes); + if cv.is_none().into() { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv")); + } + cv.unwrap() + }; // Consensus rule (§7.4): Canonical encoding is enforced here let cmu = { - let mut f = FrRepr::default(); - f.read_le(&mut reader)?; - Fr::from_repr(f).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))? + let mut f = [0u8; 32]; + reader.read_exact(&mut f)?; + bls12_381::Scalar::from_repr(f) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cmu not in field"))? }; // Consensus rules (§4.5): // - Canonical encoding is enforced here. // - "Not small order" is enforced in SaplingVerificationContext::check_output() - let ephemeral_key = edwards::Point::::read(&mut reader, &JUBJUB)?; + let ephemeral_key = { + let mut bytes = [0u8; 32]; + reader.read_exact(&mut bytes)?; + let ephemeral_key = jubjub::ExtendedPoint::from_bytes(&bytes); + if ephemeral_key.is_none().into() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid ephemeral_key", + )); + } + ephemeral_key.unwrap() + }; - let mut enc_ciphertext = [0; 580]; - let mut out_ciphertext = [0; 80]; + let mut enc_ciphertext = [0u8; 580]; + let mut out_ciphertext = [0u8; 80]; reader.read_exact(&mut enc_ciphertext)?; reader.read_exact(&mut out_ciphertext)?; @@ -237,7 +367,7 @@ impl OutputDescription { // - Canonical encoding is enforced by the API of SaplingVerificationContext::check_output() // due to the need to parse this into a bellman::groth16::Proof. // - Proof validity is enforced in SaplingVerificationContext::check_output() - let mut zkproof = [0; GROTH_PROOF_SIZE]; + let mut zkproof = [0u8; GROTH_PROOF_SIZE]; reader.read_exact(&mut zkproof)?; Ok(OutputDescription { @@ -251,9 +381,9 @@ impl OutputDescription { } pub fn write(&self, mut writer: W) -> io::Result<()> { - self.cv.write(&mut writer)?; - self.cmu.into_repr().write_le(&mut writer)?; - self.ephemeral_key.write(&mut writer)?; + writer.write_all(&self.cv.to_bytes())?; + writer.write_all(self.cmu.to_repr().as_ref())?; + writer.write_all(&self.ephemeral_key.to_bytes())?; writer.write_all(&self.enc_ciphertext)?; writer.write_all(&self.out_ciphertext)?; writer.write_all(&self.zkproof) @@ -315,7 +445,7 @@ impl JSDescription { pub fn read(mut reader: R, use_groth: bool) -> io::Result { // Consensus rule (§4.3): Canonical encoding is enforced here let vpub_old = { - let mut tmp = [0; 8]; + let mut tmp = [0u8; 8]; reader.read_exact(&mut tmp)?; Amount::from_u64_le_bytes(tmp) } @@ -323,7 +453,7 @@ impl JSDescription { // Consensus rule (§4.3): Canonical encoding is enforced here let vpub_new = { - let mut tmp = [0; 8]; + let mut tmp = [0u8; 8]; reader.read_exact(&mut tmp)?; Amount::from_u64_le_bytes(tmp) } @@ -332,16 +462,16 @@ impl JSDescription { // Consensus rule (§4.3): One of vpub_old and vpub_new being zero is // enforced by CheckTransactionWithoutProofVerification() in zcashd. - let mut anchor = [0; 32]; + let mut anchor = [0u8; 32]; reader.read_exact(&mut anchor)?; - let mut nullifiers = [[0; 32]; ZC_NUM_JS_INPUTS]; + let mut nullifiers = [[0u8; 32]; ZC_NUM_JS_INPUTS]; nullifiers .iter_mut() .map(|nf| reader.read_exact(nf)) .collect::>()?; - let mut commitments = [[0; 32]; ZC_NUM_JS_OUTPUTS]; + let mut commitments = [[0u8; 32]; ZC_NUM_JS_OUTPUTS]; commitments .iter_mut() .map(|cm| reader.read_exact(cm)) @@ -349,13 +479,13 @@ impl JSDescription { // Consensus rule (§4.3): Canonical encoding is enforced by // ZCNoteDecryption::decrypt() in zcashd - let mut ephemeral_key = [0; 32]; + let mut ephemeral_key = [0u8; 32]; reader.read_exact(&mut ephemeral_key)?; - let mut random_seed = [0; 32]; + let mut random_seed = [0u8; 32]; reader.read_exact(&mut random_seed)?; - let mut macs = [[0; 32]; ZC_NUM_JS_INPUTS]; + let mut macs = [[0u8; 32]; ZC_NUM_JS_INPUTS]; macs.iter_mut() .map(|mac| reader.read_exact(mac)) .collect::>()?; @@ -364,19 +494,19 @@ impl JSDescription { // Consensus rules (§4.3): // - Canonical encoding is enforced in librustzcash_sprout_verify() // - Proof validity is enforced in librustzcash_sprout_verify() - let mut proof = [0; GROTH_PROOF_SIZE]; + let mut proof = [0u8; GROTH_PROOF_SIZE]; reader.read_exact(&mut proof)?; SproutProof::Groth(proof) } else { // Consensus rules (§4.3): // - Canonical encoding is enforced by PHGRProof in zcashd // - Proof validity is enforced by JSDescription::Verify() in zcashd - let mut proof = [0; PHGR_PROOF_SIZE]; + let mut proof = [0u8; PHGR_PROOF_SIZE]; reader.read_exact(&mut proof)?; SproutProof::PHGR(proof) }; - let mut ciphertexts = [[0; 601]; ZC_NUM_JS_OUTPUTS]; + let mut ciphertexts = [[0u8; 601]; ZC_NUM_JS_OUTPUTS]; ciphertexts .iter_mut() .map(|ct| reader.read_exact(ct)) diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 24b812703c..90399ace1f 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -17,14 +17,18 @@ mod sighash; #[cfg(test)] mod tests; -pub use self::sighash::{signature_hash, signature_hash_data, SIGHASH_ALL}; +pub use self::sighash::{signature_hash, signature_hash_data, SignableInput, SIGHASH_ALL}; -use self::components::{Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut}; +use self::components::{ + Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut, TzeIn, TzeOut, +}; const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C48270; const OVERWINTER_TX_VERSION: u32 = 3; const SAPLING_VERSION_GROUP_ID: u32 = 0x892F2085; const SAPLING_TX_VERSION: u32 = 4; +const FUTURE_VERSION_GROUP_ID: u32 = 0xFFFFFFFF; +const FUTURE_TX_VERSION: u32 = 0x0000FFFF; #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct TxId(pub [u8; 32]); @@ -64,6 +68,8 @@ pub struct TransactionData { pub version_group_id: u32, pub vin: Vec, pub vout: Vec, + pub tze_inputs: Vec, + pub tze_outputs: Vec, pub lock_time: u32, pub expiry_height: u32, pub value_balance: Amount, @@ -85,6 +91,8 @@ impl std::fmt::Debug for TransactionData { version_group_id = {:?}, vin = {:?}, vout = {:?}, + tze_inputs = {:?}, + tze_outputs = {:?}, lock_time = {:?}, expiry_height = {:?}, value_balance = {:?}, @@ -98,6 +106,8 @@ impl std::fmt::Debug for TransactionData { self.version_group_id, self.vin, self.vout, + self.tze_inputs, + self.tze_outputs, self.lock_time, self.expiry_height, self.value_balance, @@ -118,6 +128,29 @@ impl TransactionData { version_group_id: SAPLING_VERSION_GROUP_ID, vin: vec![], vout: vec![], + tze_inputs: vec![], + tze_outputs: vec![], + lock_time: 0, + expiry_height: 0, + value_balance: Amount::zero(), + shielded_spends: vec![], + shielded_outputs: vec![], + joinsplits: vec![], + joinsplit_pubkey: None, + joinsplit_sig: None, + binding_sig: None, + } + } + + pub fn future() -> Self { + TransactionData { + overwintered: true, + version: FUTURE_TX_VERSION, + version_group_id: FUTURE_VERSION_GROUP_ID, + vin: vec![], + vout: vec![], + tze_inputs: vec![], + tze_outputs: vec![], lock_time: 0, expiry_height: 0, value_balance: Amount::zero(), @@ -178,7 +211,11 @@ impl Transaction { let is_sapling_v4 = overwintered && version_group_id == SAPLING_VERSION_GROUP_ID && version == SAPLING_TX_VERSION; - if overwintered && !(is_overwinter_v3 || is_sapling_v4) { + let has_tze = overwintered + && version_group_id == FUTURE_VERSION_GROUP_ID + && version == FUTURE_TX_VERSION; + + if overwintered && !(is_overwinter_v3 || is_sapling_v4 || has_tze) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Unknown transaction format", @@ -187,14 +224,22 @@ impl Transaction { let vin = Vector::read(&mut reader, TxIn::read)?; let vout = Vector::read(&mut reader, TxOut::read)?; + let (tze_inputs, tze_outputs) = if has_tze { + let wi = Vector::read(&mut reader, TzeIn::read)?; + let wo = Vector::read(&mut reader, TzeOut::read)?; + (wi, wo) + } else { + (vec![], vec![]) + }; + let lock_time = reader.read_u32::()?; - let expiry_height = if is_overwinter_v3 || is_sapling_v4 { + let expiry_height = if is_overwinter_v3 || is_sapling_v4 || has_tze { reader.read_u32::()? } else { 0 }; - let (value_balance, shielded_spends, shielded_outputs) = if is_sapling_v4 { + let (value_balance, shielded_spends, shielded_outputs) = if is_sapling_v4 || has_tze { let vb = { let mut tmp = [0; 8]; reader.read_exact(&mut tmp)?; @@ -226,12 +271,13 @@ impl Transaction { (vec![], None, None) }; - let binding_sig = - if is_sapling_v4 && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) { - Some(Signature::read(&mut reader)?) - } else { - None - }; + let binding_sig = if (is_sapling_v4 || has_tze) + && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) + { + Some(Signature::read(&mut reader)?) + } else { + None + }; Transaction::from_data(TransactionData { overwintered, @@ -239,6 +285,8 @@ impl Transaction { version_group_id, vin, vout, + tze_inputs, + tze_outputs, lock_time, expiry_height, value_balance, @@ -263,7 +311,11 @@ impl Transaction { let is_sapling_v4 = self.overwintered && self.version_group_id == SAPLING_VERSION_GROUP_ID && self.version == SAPLING_TX_VERSION; - if self.overwintered && !(is_overwinter_v3 || is_sapling_v4) { + let has_tze = self.overwintered + && self.version_group_id == FUTURE_VERSION_GROUP_ID + && self.version == FUTURE_TX_VERSION; + + if self.overwintered && !(is_overwinter_v3 || is_sapling_v4 || has_tze) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "Unknown transaction format", @@ -272,12 +324,16 @@ impl Transaction { Vector::write(&mut writer, &self.vin, |w, e| e.write(w))?; Vector::write(&mut writer, &self.vout, |w, e| e.write(w))?; + if has_tze { + Vector::write(&mut writer, &self.tze_inputs, |w, e| e.write(w))?; + Vector::write(&mut writer, &self.tze_outputs, |w, e| e.write(w))?; + } writer.write_u32::(self.lock_time)?; - if is_overwinter_v3 || is_sapling_v4 { + if is_overwinter_v3 || is_sapling_v4 || has_tze { writer.write_u32::(self.expiry_height)?; } - if is_sapling_v4 { + if is_sapling_v4 || has_tze { writer.write_all(&self.value_balance.to_i64_le_bytes())?; Vector::write(&mut writer, &self.shielded_spends, |w, e| e.write(w))?; Vector::write(&mut writer, &self.shielded_outputs, |w, e| e.write(w))?; @@ -322,7 +378,9 @@ impl Transaction { } } - if is_sapling_v4 && !(self.shielded_spends.is_empty() && self.shielded_outputs.is_empty()) { + if (is_sapling_v4 || has_tze) + && !(self.shielded_spends.is_empty() && self.shielded_outputs.is_empty()) + { match self.binding_sig { Some(sig) => sig.write(&mut writer)?, None => { diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index 4319f41752..7c7691ff95 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -1,13 +1,14 @@ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, WriteBytesExt}; -use ff::{PrimeField, PrimeFieldRepr}; +use ff::PrimeField; +use group::GroupEncoding; use super::{ components::{Amount, TxOut}, - Transaction, TransactionData, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION, - SAPLING_VERSION_GROUP_ID, + Transaction, TransactionData, FUTURE_VERSION_GROUP_ID, OVERWINTER_VERSION_GROUP_ID, + SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID, }; -use crate::{consensus, legacy::Script}; +use crate::{consensus, extensions::transparent::Precondition, legacy::Script}; const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &[u8; 12] = b"ZcashSigHash"; const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashPrevoutHash"; @@ -16,6 +17,7 @@ const ZCASH_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashOutputsHash"; const ZCASH_JOINSPLITS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashJSplitsHash"; const ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSSpendsHash"; const ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSOutputHash"; +const ZCASH_TZE_SIGNED_INPUT_DOMAIN_SEPARATOR: &[u8; 16] = b"ZcashTZE_SigHash"; pub const SIGHASH_ALL: u32 = 1; const SIGHASH_NONE: u32 = 2; @@ -45,6 +47,7 @@ enum SigHashVersion { Sprout, Overwinter, Sapling, + Future, } impl SigHashVersion { @@ -53,6 +56,7 @@ impl SigHashVersion { match tx.version_group_id { OVERWINTER_VERSION_GROUP_ID => SigHashVersion::Overwinter, SAPLING_VERSION_GROUP_ID => SigHashVersion::Sapling, + FUTURE_VERSION_GROUP_ID => SigHashVersion::Future, _ => unimplemented!(), } } else { @@ -127,8 +131,8 @@ fn joinsplits_hash(tx: &TransactionData) -> Blake2bHash { fn shielded_spends_hash(tx: &TransactionData) -> Blake2bHash { let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384); for s_spend in &tx.shielded_spends { - s_spend.cv.write(&mut data).unwrap(); - s_spend.anchor.into_repr().write_le(&mut data).unwrap(); + data.extend_from_slice(&s_spend.cv.to_bytes()); + data.extend_from_slice(s_spend.anchor.to_repr().as_ref()); data.extend_from_slice(&s_spend.nullifier); s_spend.rk.write(&mut data).unwrap(); data.extend_from_slice(&s_spend.zkproof); @@ -150,15 +154,47 @@ fn shielded_outputs_hash(tx: &TransactionData) -> Blake2bHash { .hash(&data) } -pub fn signature_hash_data( +pub enum SignableInput<'a> { + Shielded, + Transparent { + index: usize, + script_code: &'a Script, + value: Amount, + }, + Tze { + index: usize, + precondition: &'a Precondition, + value: Amount, + }, +} + +impl<'a> SignableInput<'a> { + pub fn transparent(index: usize, script_code: &'a Script, value: Amount) -> Self { + SignableInput::Transparent { + index, + script_code, + value, + } + } + + pub fn tze(index: usize, precondition: &'a Precondition, value: Amount) -> Self { + SignableInput::Tze { + index, + precondition, + value, + } + } +} + +pub fn signature_hash_data<'a>( tx: &TransactionData, consensus_branch_id: consensus::BranchId, hash_type: u32, - transparent_input: Option<(usize, &Script, Amount)>, + signable_input: SignableInput<'a>, ) -> Vec { let sigversion = SigHashVersion::from_tx(tx); match sigversion { - SigHashVersion::Overwinter | SigHashVersion::Sapling => { + SigHashVersion::Overwinter | SigHashVersion::Sapling | SigHashVersion::Future => { let mut personal = [0; 16]; (&mut personal[..12]).copy_from_slice(ZCASH_SIGHASH_PERSONALIZATION_PREFIX); (&mut personal[12..]) @@ -181,17 +217,18 @@ pub fn signature_hash_data( && (hash_type & SIGHASH_MASK) != SIGHASH_NONE, sequence_hash(tx) ); + if (hash_type & SIGHASH_MASK) != SIGHASH_SINGLE && (hash_type & SIGHASH_MASK) != SIGHASH_NONE { h.update(outputs_hash(tx).as_ref()); - } else if (hash_type & SIGHASH_MASK) == SIGHASH_SINGLE - && transparent_input.is_some() - && transparent_input.as_ref().unwrap().0 < tx.vout.len() - { - h.update( - single_output_hash(&tx.vout[transparent_input.as_ref().unwrap().0]).as_ref(), - ); + } else if (hash_type & SIGHASH_MASK) == SIGHASH_SINGLE { + match signable_input { + SignableInput::Transparent { index, .. } if index < tx.vout.len() => { + h.update(single_output_hash(&tx.vout[index]).as_ref()) + } + _ => h.update(&[0; 32]), + }; } else { h.update(&[0; 32]); }; @@ -211,15 +248,38 @@ pub fn signature_hash_data( } update_u32!(h, hash_type, tmp); - if let Some((n, script_code, amount)) = transparent_input { - let mut data = vec![]; - tx.vin[n].prevout.write(&mut data).unwrap(); - script_code.write(&mut data).unwrap(); - data.extend_from_slice(&amount.to_i64_le_bytes()); - (&mut data) - .write_u32::(tx.vin[n].sequence) - .unwrap(); - h.update(&data); + match signable_input { + SignableInput::Transparent { + index, + script_code, + value, + } => { + let mut data = vec![]; + tx.vin[index].prevout.write(&mut data).unwrap(); + script_code.write(&mut data).unwrap(); + data.extend_from_slice(&value.to_i64_le_bytes()); + (&mut data) + .write_u32::(tx.vin[index].sequence) + .unwrap(); + h.update(&data); + } + + SignableInput::Tze { + index, + precondition, + value, + } if sigversion == SigHashVersion::Future => { + let mut data = ZCASH_TZE_SIGNED_INPUT_DOMAIN_SEPARATOR.to_vec(); + + tx.tze_inputs[index].prevout.write(&mut data).unwrap(); + data.write_u32::(precondition.extension_id) + .unwrap(); + data.write_u32::(precondition.mode).unwrap(); + data.extend(&precondition.payload); + data.extend_from_slice(&value.to_i64_le_bytes()); + h.update(&data); + } + _ => (), } h.finalize().as_ref().to_vec() @@ -228,11 +288,11 @@ pub fn signature_hash_data( } } -pub fn signature_hash( +pub fn signature_hash<'a>( tx: &Transaction, consensus_branch_id: consensus::BranchId, hash_type: u32, - transparent_input: Option<(usize, &Script, Amount)>, + signable_input: SignableInput<'a>, ) -> Vec { - signature_hash_data(tx, consensus_branch_id, hash_type, transparent_input) + signature_hash_data(tx, consensus_branch_id, hash_type, signable_input) } diff --git a/zcash_primitives/src/transaction/tests.rs b/zcash_primitives/src/transaction/tests.rs index 2fc267c6f2..1e7a4427c2 100644 --- a/zcash_primitives/src/transaction/tests.rs +++ b/zcash_primitives/src/transaction/tests.rs @@ -1,12 +1,13 @@ use ff::Field; -use pairing::bls12_381::Bls12; use rand_core::OsRng; -use crate::jubjub::{fs::Fs, FixedGenerators}; +use crate::{constants::SPENDING_KEY_GENERATOR, redjubjub::PrivateKey}; -use super::{components::Amount, sighash::signature_hash, Transaction, TransactionData}; -use crate::redjubjub::PrivateKey; -use crate::JUBJUB; +use super::{ + components::Amount, + sighash::{signature_hash, SignableInput}, + Transaction, TransactionData, +}; #[test] fn tx_read_write() { @@ -56,13 +57,8 @@ fn tx_write_rejects_unexpected_binding_sig() { // Fails with an unexpected binding signature { let rng = &mut OsRng; - let sk = PrivateKey::(Fs::random(rng)); - let sig = sk.sign( - b"Foo bar", - rng, - FixedGenerators::SpendingKeyGenerator, - &JUBJUB, - ); + let sk = PrivateKey(jubjub::Fr::random(rng)); + let sig = sk.sign(b"Foo bar", rng, SPENDING_KEY_GENERATOR); let mut tx = TransactionData::new(); tx.binding_sig = Some(sig); @@ -70,21 +66,52 @@ fn tx_write_rejects_unexpected_binding_sig() { } } +#[test] +fn test_tze_tx_parse() { + let txn_bytes = vec![ + 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x52, 0x52, 0x52, 0x52, + 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, + 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x20, 0xd9, 0x81, 0x80, 0x87, 0xde, 0x72, 0x44, 0xab, 0xc1, 0xb5, 0xfc, + 0xf2, 0x8e, 0x55, 0xe4, 0x2c, 0x7f, 0xf9, 0xc6, 0x78, 0xc0, 0x60, 0x51, 0x81, 0xf3, 0x7a, + 0xc5, 0xd7, 0x41, 0x4a, 0x7b, 0x95, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let tx = Transaction::read(&txn_bytes[..]); + + match tx { + Ok(tx) => assert!(!tx.tze_inputs.is_empty()), + + Err(e) => assert!( + false, + format!( + "An error occurred parsing a serialized TZE transaction: {}", + e + ) + ), + } +} + mod data; #[test] fn zip_0143() { for tv in self::data::zip_0143::make_test_vectors() { let tx = Transaction::read(&tv.tx[..]).unwrap(); - let transparent_input = tv.transparent_input.map(|n| { - ( + let signable_input = match tv.transparent_input { + Some(n) => SignableInput::transparent( n as usize, &tv.script_code, Amount::from_nonnegative_i64(tv.amount).unwrap(), - ) - }); + ), + _ => SignableInput::Shielded, + }; assert_eq!( - signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, transparent_input), + signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, signable_input), tv.sighash ); } @@ -94,16 +121,17 @@ fn zip_0143() { fn zip_0243() { for tv in self::data::zip_0243::make_test_vectors() { let tx = Transaction::read(&tv.tx[..]).unwrap(); - let transparent_input = tv.transparent_input.map(|n| { - ( + let signable_input = match tv.transparent_input { + Some(n) => SignableInput::transparent( n as usize, &tv.script_code, Amount::from_nonnegative_i64(tv.amount).unwrap(), - ) - }); + ), + _ => SignableInput::Shielded, + }; assert_eq!( - signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, transparent_input), + signature_hash(&tx, tv.consensus_branch_id, tv.hash_type, signable_input), tv.sighash ); } diff --git a/zcash_primitives/src/util.rs b/zcash_primitives/src/util.rs index 1fdcde750e..c5442049d8 100644 --- a/zcash_primitives/src/util.rs +++ b/zcash_primitives/src/util.rs @@ -1,11 +1,26 @@ use blake2b_simd::Params; -use crate::jubjub::{JubjubEngine, ToUniform}; +use crate::{consensus, consensus::NetworkUpgrade, primitives::Rseed}; +use ff::Field; +use rand_core::{CryptoRng, RngCore}; -pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs { +pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr { let mut hasher = Params::new().hash_length(64).personal(persona).to_state(); hasher.update(a); hasher.update(b); let ret = hasher.finalize(); - E::Fs::to_uniform(ret.as_ref()) + jubjub::Fr::from_bytes_wide(ret.as_array()) +} + +pub fn generate_random_rseed( + height: u32, + rng: &mut R, +) -> Rseed { + if P::is_nu_active(NetworkUpgrade::Canopy, height) { + let mut buffer = [0u8; 32]; + &rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(jubjub::Fr::random(rng)) + } } diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index e34767b46c..4a7c6d5162 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -6,18 +6,16 @@ use aes::Aes256; use blake2b_simd::Params as Blake2bParams; use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; use fpe::ff1::{BinaryNumeralString, FF1}; -use pairing::bls12_381::Bls12; use std::ops::AddAssign; use crate::{ - jubjub::{fs::Fs, FixedGenerators, JubjubEngine, JubjubParams, ToUniform}, + constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR}, primitives::{Diversifier, PaymentAddress, ViewingKey}, }; use std::io::{self, Read, Write}; -use crate::{ - keys::{prf_expand, prf_expand_vec, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey}, - JUBJUB, +use crate::keys::{ + prf_expand, prf_expand_vec, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey, }; pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling"; @@ -36,8 +34,8 @@ fn derive_child_ovk(parent: &OutgoingViewingKey, i_l: &[u8]) -> OutgoingViewingK /// A Sapling full viewing key fingerprint struct FVKFingerprint([u8; 32]); -impl From<&FullViewingKey> for FVKFingerprint { - fn from(fvk: &FullViewingKey) -> Self { +impl From<&FullViewingKey> for FVKFingerprint { + fn from(fvk: &FullViewingKey) -> Self { let mut h = Blake2bParams::new() .hash_length(32) .personal(ZIP32_SAPLING_FVFP_PERSONALIZATION) @@ -154,7 +152,7 @@ impl DiversifierKey { let d_j = Diversifier(d_j); // Return (j, d_j) if valid, else increment j and try again - match d_j.g_d::(&JUBJUB) { + match d_j.g_d() { Some(_) => return Ok((j, d_j)), None => { if j.increment().is_err() { @@ -173,7 +171,7 @@ pub struct ExtendedSpendingKey { parent_fvk_tag: FVKTag, child_index: ChildIndex, chain_code: ChainCode, - pub expsk: ExpandedSpendingKey, + pub expsk: ExpandedSpendingKey, dk: DiversifierKey, } @@ -184,7 +182,7 @@ pub struct ExtendedFullViewingKey { parent_fvk_tag: FVKTag, child_index: ChildIndex, chain_code: ChainCode, - pub fvk: FullViewingKey, + pub fvk: FullViewingKey, dk: DiversifierKey, } @@ -297,7 +295,7 @@ impl ExtendedSpendingKey { } pub fn derive_child(&self, i: ChildIndex) -> Self { - let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk, &JUBJUB); + let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk); let tmp = match i { ChildIndex::Hardened(i) => { let mut le_i = [0; 4]; @@ -326,8 +324,8 @@ impl ExtendedSpendingKey { child_index: i, chain_code: ChainCode(c_i), expsk: { - let mut ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); - let mut nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); + let mut ask = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array()); + let mut nsk = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array()); ask.add_assign(&self.expsk.ask); nsk.add_assign(&self.expsk.nsk); let ovk = derive_child_ovk(&self.expsk.ovk, i_l); @@ -337,7 +335,7 @@ impl ExtendedSpendingKey { } } - pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { + pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { ExtendedFullViewingKey::from(self).default_address() } } @@ -349,7 +347,7 @@ impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { parent_fvk_tag: xsk.parent_fvk_tag, child_index: xsk.child_index, chain_code: xsk.chain_code, - fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk, &JUBJUB), + fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk), dk: xsk.dk, } } @@ -363,7 +361,7 @@ impl ExtendedFullViewingKey { let i = reader.read_u32::()?; let mut c = [0; 32]; reader.read_exact(&mut c)?; - let fvk = FullViewingKey::read(&mut reader, &*JUBJUB)?; + let fvk = FullViewingKey::read(&mut reader)?; let mut dk = [0; 32]; reader.read_exact(&mut dk)?; @@ -410,16 +408,10 @@ impl ExtendedFullViewingKey { child_index: i, chain_code: ChainCode(c_i), fvk: { - let i_ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); - let i_nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); - let ak = JUBJUB - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(i_ask, &JUBJUB) - .add(&self.fvk.vk.ak, &JUBJUB); - let nk = JUBJUB - .generator(FixedGenerators::ProofGenerationKey) - .mul(i_nsk, &JUBJUB) - .add(&self.fvk.vk.nk, &JUBJUB); + let i_ask = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array()); + let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array()); + let ak = (SPENDING_KEY_GENERATOR * i_ask) + self.fvk.vk.ak; + let nk = (PROOF_GENERATION_KEY_GENERATOR * i_nsk) + self.fvk.vk.nk; FullViewingKey { vk: ViewingKey { ak, nk }, @@ -430,21 +422,18 @@ impl ExtendedFullViewingKey { }) } - pub fn address( - &self, - j: DiversifierIndex, - ) -> Result<(DiversifierIndex, PaymentAddress), ()> { + pub fn address(&self, j: DiversifierIndex) -> Result<(DiversifierIndex, PaymentAddress), ()> { let (j, d_j) = match self.dk.diversifier(j) { Ok(ret) => ret, Err(()) => return Err(()), }; - match self.fvk.vk.to_payment_address(d_j, &JUBJUB) { + match self.fvk.vk.to_payment_address(d_j) { Some(addr) => Ok((j, addr)), None => Err(()), } } - pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { + pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { self.address(DiversifierIndex::new()) } } @@ -453,7 +442,8 @@ impl ExtendedFullViewingKey { mod tests { use super::*; - use ff::{PrimeField, PrimeFieldRepr}; + use ff::PrimeField; + use group::GroupEncoding; #[test] fn derive_nonhardened_child() { @@ -1014,11 +1004,8 @@ mod tests { let xsk = &xsks[j]; let tv = &test_vectors[j]; - let mut buf = [0; 32]; - xsk.expsk.ask.into_repr().write_le(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.ask.unwrap()); - xsk.expsk.nsk.into_repr().write_le(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.nsk.unwrap()); + assert_eq!(xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap()); + assert_eq!(xsk.expsk.nsk.to_repr().as_ref(), tv.nsk.unwrap()); assert_eq!(xsk.expsk.ovk.0, tv.ovk); assert_eq!(xsk.dk.0, tv.dk); @@ -1033,23 +1020,14 @@ mod tests { let xfvk = &xfvks[j]; let tv = &test_vectors[j]; - let mut buf = [0; 32]; - xfvk.fvk.vk.ak.write(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.ak); - xfvk.fvk.vk.nk.write(&mut buf[..]).unwrap(); - assert_eq!(buf, tv.nk); + assert_eq!(xfvk.fvk.vk.ak.to_bytes(), tv.ak); + assert_eq!(xfvk.fvk.vk.nk.to_bytes(), tv.nk); assert_eq!(xfvk.fvk.ovk.0, tv.ovk); assert_eq!(xfvk.dk.0, tv.dk); assert_eq!(xfvk.chain_code.0, tv.c); - xfvk.fvk - .vk - .ivk() - .into_repr() - .write_le(&mut buf[..]) - .unwrap(); - assert_eq!(buf, tv.ivk); + assert_eq!(xfvk.fvk.vk.ivk().to_repr().as_ref(), tv.ivk); let mut ser = vec![]; xfvk.write(&mut ser).unwrap(); diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 3d2a8d4bec..b1b83b768b 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash_proofs" description = "Zcash zk-SNARK circuits and proving APIs" -version = "0.2.0" +version = "0.3.0" authors = [ "Jack Grigg ", ] @@ -11,23 +11,41 @@ readme = "README.md" license = "MIT OR Apache-2.0" edition = "2018" +[package.metadata.docs.rs] +all-features = true + [dependencies] -bellman = { version = "0.6", path = "../bellman", default-features = false, features = ["groth16"] } +bellman = { version = "0.7", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" +bls12_381 = "0.2" byteorder = "1" -directories = { version = "1", optional = true } -ff = { version = "0.6", path = "../ff" } -pairing = { version = "0.16", path = "../pairing" } +directories = { version = "3", optional = true } +ff = "0.7" +group = "0.7" +jubjub = "0.4" +lazy_static = "1" +minreq = { version = "2", features = ["https"], optional = true } rand_core = "0.5.1" -zcash_primitives = { version = "0.2", path = "../zcash_primitives" } +wagyu-zcash-parameters = { version = "0.2", optional = true } +zcash_primitives = { version = "0.3", path = "../zcash_primitives" } [dev-dependencies] rand_xorshift = "0.2" [features] default = ["local-prover", "multicore"] +bundled-prover = ["wagyu-zcash-parameters"] +download-params = ["minreq"] local-prover = ["directories"] multicore = ["bellman/multicore"] +[[example]] +name = "get-params-path" +required-features = ["directories"] + +[[example]] +name = "download-params" +required-features = ["download-params"] + [badges] maintenance = { status = "actively-developed" } diff --git a/zcash_proofs/examples/bench.rs b/zcash_proofs/examples/bench.rs index 2f48786857..776d428313 100644 --- a/zcash_proofs/examples/bench.rs +++ b/zcash_proofs/examples/bench.rs @@ -1,17 +1,16 @@ use bellman::groth16::*; +use bls12_381::Bls12; use ff::Field; -use pairing::bls12_381::{Bls12, Fr}; +use group::Group; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use std::time::{Duration, Instant}; -use zcash_primitives::jubjub::{edwards, fs, JubjubBls12}; use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, ValueCommitment}; use zcash_proofs::circuit::sapling::Spend; const TREE_DEPTH: usize = 32; fn main() { - let jubjub_params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -20,7 +19,6 @@ fn main() { println!("Creating sample parameters..."); let groth_params = generate_random_parameters::( Spend { - params: jubjub_params, value_commitment: None, proof_generation_key: None, payment_address: None, @@ -39,18 +37,18 @@ fn main() { for _ in 0..SAMPLES { let value_commitment = ValueCommitment { value: 1, - randomness: fs::Fs::random(rng), + randomness: jubjub::Fr::random(rng), }; - let nsk = fs::Fs::random(rng); - let ak = edwards::Point::rand(rng, jubjub_params).mul_by_cofactor(jubjub_params); + let nsk = jubjub::Fr::random(rng); + let ak = jubjub::SubgroupPoint::random(rng); let proof_generation_key = ProofGenerationKey { ak: ak.clone(), nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.to_viewing_key(jubjub_params); + let viewing_key = proof_generation_key.to_viewing_key(); let payment_address; @@ -61,21 +59,21 @@ fn main() { Diversifier(d) }; - if let Some(p) = viewing_key.to_payment_address(diversifier, jubjub_params) { + if let Some(p) = viewing_key.to_payment_address(diversifier) { payment_address = p; break; } } - let commitment_randomness = fs::Fs::random(rng); - let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); TREE_DEPTH]; - let ar = fs::Fs::random(rng); - let anchor = Fr::random(rng); + let commitment_randomness = jubjub::Fr::random(rng); + let auth_path = + vec![Some((bls12_381::Scalar::random(rng), rng.next_u32() % 2 != 0)); TREE_DEPTH]; + let ar = jubjub::Fr::random(rng); + let anchor = bls12_381::Scalar::random(rng); 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), diff --git a/zcash_proofs/examples/download-params.rs b/zcash_proofs/examples/download-params.rs new file mode 100644 index 0000000000..b23cb75fae --- /dev/null +++ b/zcash_proofs/examples/download-params.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), minreq::Error> { + zcash_proofs::download_parameters() +} diff --git a/zcash_proofs/examples/get-params-path.rs b/zcash_proofs/examples/get-params-path.rs new file mode 100644 index 0000000000..224b2f5ead --- /dev/null +++ b/zcash_proofs/examples/get-params-path.rs @@ -0,0 +1,7 @@ +fn main() { + if let Some(path) = zcash_proofs::default_params_folder() { + if let Some(path) = path.to_str() { + println!("{}", path); + } + } +} diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 05baf8b607..61667e1f39 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -1,7 +1,5 @@ //! Gadgets implementing Jubjub elliptic curve operations. -use ff::Field; -use pairing::Engine; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use bellman::{ConstraintSystem, SynthesisError}; @@ -10,38 +8,34 @@ use bellman::gadgets::Assignment; use bellman::gadgets::num::{AllocatedNum, Num}; -use zcash_primitives::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams}; - use bellman::gadgets::lookup::lookup3_xy; use bellman::gadgets::boolean::Boolean; +use group::Curve; + +use crate::constants::{FixedGenerator, EDWARDS_D, MONTGOMERY_A, MONTGOMERY_SCALE}; + #[derive(Clone)] -pub struct EdwardsPoint { - x: AllocatedNum, - y: AllocatedNum, +pub struct EdwardsPoint { + u: AllocatedNum, + v: AllocatedNum, } /// Perform a fixed-base scalar multiplication with /// `by` being in little-endian bit order. -pub fn fixed_base_multiplication( +pub fn fixed_base_multiplication( mut cs: CS, - base: FixedGenerators, + base: FixedGenerator, by: &[Boolean], - params: &E::Params, -) -> Result, SynthesisError> +) -> Result where - CS: ConstraintSystem, - E: JubjubEngine, + CS: ConstraintSystem, { // Represents the result of the multiplication let mut result = None; - for (i, (chunk, window)) in by - .chunks(3) - .zip(params.circuit_generators(base).iter()) - .enumerate() - { + for (i, (chunk, window)) in by.chunks(3).zip(base.iter()).enumerate() { let chunk_a = chunk .get(0) .cloned() @@ -55,64 +49,61 @@ where .cloned() .unwrap_or_else(|| Boolean::constant(false)); - let (x, y) = lookup3_xy( + // TODO: rename to lookup3_uv + let (u, v) = lookup3_xy( cs.namespace(|| format!("window table lookup {}", i)), &[chunk_a, chunk_b, chunk_c], window, )?; - let p = EdwardsPoint { x, y }; + let p = EdwardsPoint { u, v }; if result.is_none() { result = Some(p); } else { - result = Some(result.unwrap().add( - cs.namespace(|| format!("addition {}", i)), - &p, - params, - )?); + result = Some( + result + .unwrap() + .add(cs.namespace(|| format!("addition {}", i)), &p)?, + ); } } Ok(result.get()?.clone()) } -impl EdwardsPoint { - pub fn get_x(&self) -> &AllocatedNum { - &self.x +impl EdwardsPoint { + pub fn get_u(&self) -> &AllocatedNum { + &self.u } - pub fn get_y(&self) -> &AllocatedNum { - &self.y + pub fn get_v(&self) -> &AllocatedNum { + &self.v } - pub fn assert_not_small_order( - &self, - mut cs: CS, - params: &E::Params, - ) -> Result<(), SynthesisError> + pub fn assert_not_small_order(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { - let tmp = self.double(cs.namespace(|| "first doubling"), params)?; - let tmp = tmp.double(cs.namespace(|| "second doubling"), params)?; - let tmp = tmp.double(cs.namespace(|| "third doubling"), params)?; + let tmp = self.double(cs.namespace(|| "first doubling"))?; + let tmp = tmp.double(cs.namespace(|| "second doubling"))?; + let tmp = tmp.double(cs.namespace(|| "third doubling"))?; // (0, -1) is a small order point, but won't ever appear here // because cofactor is 2^3, and we performed three doublings. - // (0, 1) is the neutral element, so checking if x is nonzero + // (0, 1) is the neutral element, so checking if u is nonzero // is sufficient to prevent small order points here. - tmp.x.assert_nonzero(cs.namespace(|| "check x != 0"))?; + tmp.u.assert_nonzero(cs.namespace(|| "check u != 0"))?; Ok(()) } pub fn inputize(&self, mut cs: CS) -> Result<(), SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { - self.x.inputize(cs.namespace(|| "x"))?; - self.y.inputize(cs.namespace(|| "y"))?; + self.u.inputize(cs.namespace(|| "u"))?; + self.v.inputize(cs.namespace(|| "v"))?; Ok(()) } @@ -120,39 +111,35 @@ impl EdwardsPoint { /// This converts the point into a representation. pub fn repr(&self, mut cs: CS) -> Result, SynthesisError> where - CS: ConstraintSystem, + CS: ConstraintSystem, { let mut tmp = vec![]; - let x = self.x.to_bits_le_strict(cs.namespace(|| "unpack x"))?; + let u = self.u.to_bits_le_strict(cs.namespace(|| "unpack u"))?; - let y = self.y.to_bits_le_strict(cs.namespace(|| "unpack y"))?; + let v = self.v.to_bits_le_strict(cs.namespace(|| "unpack v"))?; - tmp.extend(y); - tmp.push(x[0].clone()); + tmp.extend(v); + tmp.push(u[0].clone()); Ok(tmp) } /// This 'witnesses' a point inside the constraint system. /// It guarantees the point is on the curve. - pub fn witness( - mut cs: CS, - p: Option>, - params: &E::Params, - ) -> Result + pub fn witness(mut cs: CS, p: Option) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { - let p = p.map(|p| p.to_xy()); + let p = p.map(|p| p.to_affine()); - // Allocate x - let x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(p.get()?.0))?; + // Allocate u + let u = AllocatedNum::alloc(cs.namespace(|| "u"), || Ok(p.get()?.get_u()))?; - // Allocate y - let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(p.get()?.1))?; + // Allocate v + let v = AllocatedNum::alloc(cs.namespace(|| "v"), || Ok(p.get()?.get_v()))?; - Self::interpret(cs.namespace(|| "point interpretation"), &x, &y, params) + Self::interpret(cs.namespace(|| "point interpretation"), &u, &v) } /// Returns `self` if condition is true, and the neutral @@ -163,64 +150,59 @@ impl EdwardsPoint { condition: &Boolean, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { - // Compute x' = self.x if condition, and 0 otherwise - let x_prime = AllocatedNum::alloc(cs.namespace(|| "x'"), || { + // Compute u' = self.u if condition, and 0 otherwise + let u_prime = AllocatedNum::alloc(cs.namespace(|| "u'"), || { if *condition.get_value().get()? { - Ok(*self.x.get_value().get()?) + Ok(*self.u.get_value().get()?) } else { - Ok(E::Fr::zero()) + Ok(bls12_381::Scalar::zero()) } })?; - // condition * x = x' - // if condition is 0, x' must be 0 - // if condition is 1, x' must be x + // condition * u = u' + // if condition is 0, u' must be 0 + // if condition is 1, u' must be u let one = CS::one(); cs.enforce( - || "x' computation", - |lc| lc + self.x.get_variable(), - |_| condition.lc(one, E::Fr::one()), - |lc| lc + x_prime.get_variable(), + || "u' computation", + |lc| lc + self.u.get_variable(), + |_| condition.lc(one, bls12_381::Scalar::one()), + |lc| lc + u_prime.get_variable(), ); - // Compute y' = self.y if condition, and 1 otherwise - let y_prime = AllocatedNum::alloc(cs.namespace(|| "y'"), || { + // Compute v' = self.v if condition, and 1 otherwise + let v_prime = AllocatedNum::alloc(cs.namespace(|| "v'"), || { if *condition.get_value().get()? { - Ok(*self.y.get_value().get()?) + Ok(*self.v.get_value().get()?) } else { - Ok(E::Fr::one()) + Ok(bls12_381::Scalar::one()) } })?; - // condition * y = y' - (1 - condition) - // if condition is 0, y' must be 1 - // if condition is 1, y' must be y + // condition * v = v' - (1 - condition) + // if condition is 0, v' must be 1 + // if condition is 1, v' must be v cs.enforce( - || "y' computation", - |lc| lc + self.y.get_variable(), - |_| condition.lc(one, E::Fr::one()), - |lc| lc + y_prime.get_variable() - &condition.not().lc(one, E::Fr::one()), + || "v' computation", + |lc| lc + self.v.get_variable(), + |_| condition.lc(one, bls12_381::Scalar::one()), + |lc| lc + v_prime.get_variable() - &condition.not().lc(one, bls12_381::Scalar::one()), ); Ok(EdwardsPoint { - x: x_prime, - y: y_prime, + u: u_prime, + v: v_prime, }) } /// Performs a scalar multiplication of this twisted Edwards /// point by a scalar represented as a sequence of booleans /// in little-endian bit order. - pub fn mul( - &self, - mut cs: CS, - by: &[Boolean], - params: &E::Params, - ) -> Result + pub fn mul(&self, mut cs: CS, by: &[Boolean]) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Represents the current "magnitude" of the base // that we're operating over. Starts at self, @@ -238,7 +220,7 @@ impl EdwardsPoint { curbase = Some( curbase .unwrap() - .double(cs.namespace(|| format!("doubling {}", i)), params)?, + .double(cs.namespace(|| format!("doubling {}", i)))?, ); } @@ -254,11 +236,11 @@ impl EdwardsPoint { if result.is_none() { result = Some(thisbase); } else { - result = Some(result.unwrap().add( - cs.namespace(|| format!("addition {}", i)), - &thisbase, - params, - )?); + result = Some( + result + .unwrap() + .add(cs.namespace(|| format!("addition {}", i)), &thisbase)?, + ); } } @@ -267,44 +249,44 @@ impl EdwardsPoint { pub fn interpret( mut cs: CS, - x: &AllocatedNum, - y: &AllocatedNum, - params: &E::Params, + u: &AllocatedNum, + v: &AllocatedNum, ) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { - // -x^2 + y^2 = 1 + dx^2y^2 + // -u^2 + v^2 = 1 + du^2v^2 - let x2 = x.square(cs.namespace(|| "x^2"))?; - let y2 = y.square(cs.namespace(|| "y^2"))?; - let x2y2 = x2.mul(cs.namespace(|| "x^2 y^2"), &y2)?; + let u2 = u.square(cs.namespace(|| "u^2"))?; + let v2 = v.square(cs.namespace(|| "v^2"))?; + let u2v2 = u2.mul(cs.namespace(|| "u^2 v^2"), &v2)?; let one = CS::one(); cs.enforce( || "on curve check", - |lc| lc - x2.get_variable() + y2.get_variable(), + |lc| lc - u2.get_variable() + v2.get_variable(), |lc| lc + one, - |lc| lc + one + (*params.edwards_d(), x2y2.get_variable()), + |lc| lc + one + (EDWARDS_D, u2v2.get_variable()), ); Ok(EdwardsPoint { - x: x.clone(), - y: y.clone(), + u: u.clone(), + v: v.clone(), }) } - pub fn double(&self, mut cs: CS, params: &E::Params) -> Result + pub fn double(&self, mut cs: CS) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { - // Compute T = (x1 + y1) * (x1 + y1) + // Compute T = (u + v) * (v - EDWARDS_A*u) + // = (u + v) * (u + v) let t = AllocatedNum::alloc(cs.namespace(|| "T"), || { - let mut t0 = *self.x.get_value().get()?; - t0.add_assign(self.y.get_value().get()?); + let mut t0 = *self.u.get_value().get()?; + t0.add_assign(self.v.get_value().get()?); - let mut t1 = *self.x.get_value().get()?; - t1.add_assign(self.y.get_value().get()?); + let mut t1 = *self.u.get_value().get()?; + t1.add_assign(self.v.get_value().get()?); t0.mul_assign(&t1); @@ -313,35 +295,35 @@ impl EdwardsPoint { cs.enforce( || "T computation", - |lc| lc + self.x.get_variable() + self.y.get_variable(), - |lc| lc + self.x.get_variable() + self.y.get_variable(), + |lc| lc + self.u.get_variable() + self.v.get_variable(), + |lc| lc + self.u.get_variable() + self.v.get_variable(), |lc| lc + t.get_variable(), ); - // Compute A = x1 * y1 - let a = self.x.mul(cs.namespace(|| "A computation"), &self.y)?; + // Compute A = u * v + let a = self.u.mul(cs.namespace(|| "A computation"), &self.v)?; // Compute C = d*A*A let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { let mut t0 = a.get_value().get()?.square(); - t0.mul_assign(params.edwards_d()); + t0.mul_assign(EDWARDS_D); Ok(t0) })?; cs.enforce( || "C computation", - |lc| lc + (*params.edwards_d(), a.get_variable()), + |lc| lc + (EDWARDS_D, a.get_variable()), |lc| lc + a.get_variable(), |lc| lc + c.get_variable(), ); - // Compute x3 = (2.A) / (1 + C) - let x3 = AllocatedNum::alloc(cs.namespace(|| "x3"), || { + // Compute u3 = (2.A) / (1 + C) + let u3 = AllocatedNum::alloc(cs.namespace(|| "u3"), || { let mut t0 = *a.get_value().get()?; t0 = t0.double(); - let mut t1 = E::Fr::one(); + let mut t1 = bls12_381::Scalar::one(); t1.add_assign(c.get_value().get()?); let res = t1.invert().map(|t1| t0 * &t1); @@ -354,19 +336,20 @@ impl EdwardsPoint { let one = CS::one(); cs.enforce( - || "x3 computation", + || "u3 computation", |lc| lc + one + c.get_variable(), - |lc| lc + x3.get_variable(), + |lc| lc + u3.get_variable(), |lc| lc + a.get_variable() + a.get_variable(), ); - // Compute y3 = (U - 2.A) / (1 - C) - let y3 = AllocatedNum::alloc(cs.namespace(|| "y3"), || { + // Compute v3 = (T + (EDWARDS_A-1)*A) / (1 - C) + // = (T - 2.A) / (1 - C) + let v3 = AllocatedNum::alloc(cs.namespace(|| "v3"), || { let mut t0 = *a.get_value().get()?; t0 = t0.double().neg(); t0.add_assign(t.get_value().get()?); - let mut t1 = E::Fr::one(); + let mut t1 = bls12_381::Scalar::one(); t1.sub_assign(c.get_value().get()?); let res = t1.invert().map(|t1| t0 * &t1); @@ -378,32 +361,29 @@ impl EdwardsPoint { })?; cs.enforce( - || "y3 computation", + || "v3 computation", |lc| lc + one - c.get_variable(), - |lc| lc + y3.get_variable(), + |lc| lc + v3.get_variable(), |lc| lc + t.get_variable() - a.get_variable() - a.get_variable(), ); - Ok(EdwardsPoint { x: x3, y: y3 }) + Ok(EdwardsPoint { u: u3, v: v3 }) } /// Perform addition between any two points - pub fn add( - &self, - mut cs: CS, - other: &Self, - params: &E::Params, - ) -> Result + pub fn add(&self, mut cs: CS, other: &Self) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { - // Compute U = (x1 + y1) * (x2 + y2) - let u = AllocatedNum::alloc(cs.namespace(|| "U"), || { - let mut t0 = *self.x.get_value().get()?; - t0.add_assign(self.y.get_value().get()?); + // Compute U = (u1 + v1) * (v2 - EDWARDS_A*u2) + // = (u1 + v1) * (u2 + v2) + // (In hindsight, U was a poor choice of name.) + let uppercase_u = AllocatedNum::alloc(cs.namespace(|| "U"), || { + let mut t0 = *self.u.get_value().get()?; + t0.add_assign(self.v.get_value().get()?); - let mut t1 = *other.x.get_value().get()?; - t1.add_assign(other.y.get_value().get()?); + let mut t1 = *other.u.get_value().get()?; + t1.add_assign(other.v.get_value().get()?); t0.mul_assign(&t1); @@ -412,39 +392,39 @@ impl EdwardsPoint { cs.enforce( || "U computation", - |lc| lc + self.x.get_variable() + self.y.get_variable(), - |lc| lc + other.x.get_variable() + other.y.get_variable(), - |lc| lc + u.get_variable(), + |lc| lc + self.u.get_variable() + self.v.get_variable(), + |lc| lc + other.u.get_variable() + other.v.get_variable(), + |lc| lc + uppercase_u.get_variable(), ); - // Compute A = y2 * x1 - let a = other.y.mul(cs.namespace(|| "A computation"), &self.x)?; + // Compute A = v2 * u1 + let a = other.v.mul(cs.namespace(|| "A computation"), &self.u)?; - // Compute B = x2 * y1 - let b = other.x.mul(cs.namespace(|| "B computation"), &self.y)?; + // Compute B = u2 * v1 + let b = other.u.mul(cs.namespace(|| "B computation"), &self.v)?; // Compute C = d*A*B let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { let mut t0 = *a.get_value().get()?; t0.mul_assign(b.get_value().get()?); - t0.mul_assign(params.edwards_d()); + t0.mul_assign(EDWARDS_D); Ok(t0) })?; cs.enforce( || "C computation", - |lc| lc + (*params.edwards_d(), a.get_variable()), + |lc| lc + (EDWARDS_D, a.get_variable()), |lc| lc + b.get_variable(), |lc| lc + c.get_variable(), ); - // Compute x3 = (A + B) / (1 + C) - let x3 = AllocatedNum::alloc(cs.namespace(|| "x3"), || { + // Compute u3 = (A + B) / (1 + C) + let u3 = AllocatedNum::alloc(cs.namespace(|| "u3"), || { let mut t0 = *a.get_value().get()?; t0.add_assign(b.get_value().get()?); - let mut t1 = E::Fr::one(); + let mut t1 = bls12_381::Scalar::one(); t1.add_assign(c.get_value().get()?); let ret = t1.invert().map(|t1| t0 * &t1); @@ -457,19 +437,19 @@ impl EdwardsPoint { let one = CS::one(); cs.enforce( - || "x3 computation", + || "u3 computation", |lc| lc + one + c.get_variable(), - |lc| lc + x3.get_variable(), + |lc| lc + u3.get_variable(), |lc| lc + a.get_variable() + b.get_variable(), ); - // Compute y3 = (U - A - B) / (1 - C) - let y3 = AllocatedNum::alloc(cs.namespace(|| "y3"), || { - let mut t0 = *u.get_value().get()?; + // Compute v3 = (U - A - B) / (1 - C) + let v3 = AllocatedNum::alloc(cs.namespace(|| "v3"), || { + let mut t0 = *uppercase_u.get_value().get()?; t0.sub_assign(a.get_value().get()?); t0.sub_assign(b.get_value().get()?); - let mut t1 = E::Fr::one(); + let mut t1 = bls12_381::Scalar::one(); t1.sub_assign(c.get_value().get()?); let ret = t1.invert().map(|t1| t0 * &t1); @@ -481,37 +461,33 @@ impl EdwardsPoint { })?; cs.enforce( - || "y3 computation", + || "v3 computation", |lc| lc + one - c.get_variable(), - |lc| lc + y3.get_variable(), - |lc| lc + u.get_variable() - a.get_variable() - b.get_variable(), + |lc| lc + v3.get_variable(), + |lc| lc + uppercase_u.get_variable() - a.get_variable() - b.get_variable(), ); - Ok(EdwardsPoint { x: x3, y: y3 }) + Ok(EdwardsPoint { u: u3, v: v3 }) } } -pub struct MontgomeryPoint { - x: Num, - y: Num, +pub struct MontgomeryPoint { + x: Num, + y: Num, } -impl MontgomeryPoint { +impl MontgomeryPoint { /// Converts an element in the prime order subgroup into /// a point in the birationally equivalent twisted /// Edwards curve. - pub fn into_edwards( - self, - mut cs: CS, - params: &E::Params, - ) -> Result, SynthesisError> + pub fn into_edwards(self, mut cs: CS) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute u = (scale*x) / y let u = AllocatedNum::alloc(cs.namespace(|| "u"), || { let mut t0 = *self.x.get_value().get()?; - t0.mul_assign(params.scale()); + t0.mul_assign(MONTGOMERY_SCALE); let ret = self.y.get_value().get()?.invert().map(|invy| t0 * &invy); if bool::from(ret.is_some()) { @@ -523,17 +499,17 @@ impl MontgomeryPoint { cs.enforce( || "u computation", - |lc| lc + &self.y.lc(E::Fr::one()), + |lc| lc + &self.y.lc(bls12_381::Scalar::one()), |lc| lc + u.get_variable(), - |lc| lc + &self.x.lc(*params.scale()), + |lc| lc + &self.x.lc(MONTGOMERY_SCALE), ); // Compute v = (x - 1) / (x + 1) let v = AllocatedNum::alloc(cs.namespace(|| "v"), || { let mut t0 = *self.x.get_value().get()?; let mut t1 = t0; - t0.sub_assign(&E::Fr::one()); - t1.add_assign(&E::Fr::one()); + t0.sub_assign(&bls12_381::Scalar::one()); + t1.add_assign(&bls12_381::Scalar::one()); let ret = t1.invert().map(|t1| t0 * &t1); if bool::from(ret.is_some()) { @@ -546,32 +522,27 @@ impl MontgomeryPoint { let one = CS::one(); cs.enforce( || "v computation", - |lc| lc + &self.x.lc(E::Fr::one()) + one, + |lc| lc + &self.x.lc(bls12_381::Scalar::one()) + one, |lc| lc + v.get_variable(), - |lc| lc + &self.x.lc(E::Fr::one()) - one, + |lc| lc + &self.x.lc(bls12_381::Scalar::one()) - one, ); - Ok(EdwardsPoint { x: u, y: v }) + Ok(EdwardsPoint { u, v }) } /// Interprets an (x, y) pair as a point /// in Montgomery, does not check that it's /// on the curve. Useful for constants and /// window table lookups. - pub fn interpret_unchecked(x: Num, y: Num) -> Self { + pub fn interpret_unchecked(x: Num, y: Num) -> Self { MontgomeryPoint { x, y } } /// Performs an affine point addition, not defined for - /// coincident points. - pub fn add( - &self, - mut cs: CS, - other: &Self, - params: &E::Params, - ) -> Result + /// points with the same x-coordinate. + pub fn add(&self, mut cs: CS, other: &Self) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { // Compute lambda = (y' - y) / (x' - x) let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { @@ -591,15 +562,15 @@ impl MontgomeryPoint { cs.enforce( || "evaluate lambda", - |lc| lc + &other.x.lc(E::Fr::one()) - &self.x.lc(E::Fr::one()), + |lc| lc + &other.x.lc(bls12_381::Scalar::one()) - &self.x.lc(bls12_381::Scalar::one()), |lc| lc + lambda.get_variable(), - |lc| lc + &other.y.lc(E::Fr::one()) - &self.y.lc(E::Fr::one()), + |lc| lc + &other.y.lc(bls12_381::Scalar::one()) - &self.y.lc(bls12_381::Scalar::one()), ); // Compute x'' = lambda^2 - A - x - x' let xprime = AllocatedNum::alloc(cs.namespace(|| "xprime"), || { let mut t0 = lambda.get_value().get()?.square(); - t0.sub_assign(params.montgomery_a()); + t0.sub_assign(MONTGOMERY_A); t0.sub_assign(self.x.get_value().get()?); t0.sub_assign(other.x.get_value().get()?); @@ -613,9 +584,9 @@ impl MontgomeryPoint { |lc| lc + lambda.get_variable(), |lc| lc + lambda.get_variable(), |lc| { - lc + (*params.montgomery_a(), one) - + &self.x.lc(E::Fr::one()) - + &other.x.lc(E::Fr::one()) + lc + (MONTGOMERY_A, one) + + &self.x.lc(bls12_381::Scalar::one()) + + &other.x.lc(bls12_381::Scalar::one()) + xprime.get_variable() }, ); @@ -634,9 +605,9 @@ impl MontgomeryPoint { // y' + y = lambda(x - x') cs.enforce( || "evaluate yprime", - |lc| lc + &self.x.lc(E::Fr::one()) - xprime.get_variable(), + |lc| lc + &self.x.lc(bls12_381::Scalar::one()) - xprime.get_variable(), |lc| lc + lambda.get_variable(), - |lc| lc + yprime.get_variable() + &self.y.lc(E::Fr::one()), + |lc| lc + yprime.get_variable() + &self.y.lc(bls12_381::Scalar::one()), ); Ok(MontgomeryPoint { @@ -650,52 +621,48 @@ impl MontgomeryPoint { mod test { use bellman::ConstraintSystem; use ff::{BitIterator, Field, PrimeField}; - use pairing::bls12_381::{Bls12, Fr}; + use group::{Curve, Group}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; - use std::ops::SubAssign; use bellman::gadgets::test::*; - use zcash_primitives::jubjub::fs::Fs; - use zcash_primitives::jubjub::{ - edwards, montgomery, FixedGenerators, JubjubBls12, JubjubParams, - }; use super::{fixed_base_multiplication, AllocatedNum, EdwardsPoint, MontgomeryPoint}; + use crate::constants::{to_montgomery_coords, NOTE_COMMITMENT_RANDOMNESS_GENERATOR}; use bellman::gadgets::boolean::{AllocatedBit, Boolean}; #[test] fn test_into_edwards() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); - let p = montgomery::Point::::rand(rng, params); - let (u, v) = edwards::Point::from_montgomery(&p, params).to_xy(); - let (x, y) = p.to_xy().unwrap(); + let p = jubjub::ExtendedPoint::random(rng); + let (x, y) = to_montgomery_coords(p).unwrap(); + let p = p.to_affine(); + let (u, v) = (p.get_u(), p.get_v()); let numx = AllocatedNum::alloc(cs.namespace(|| "mont x"), || Ok(x)).unwrap(); let numy = AllocatedNum::alloc(cs.namespace(|| "mont y"), || Ok(y)).unwrap(); let p = MontgomeryPoint::interpret_unchecked(numx.into(), numy.into()); - let q = p.into_edwards(&mut cs, params).unwrap(); + let q = p.into_edwards(&mut cs).unwrap(); assert!(cs.is_satisfied()); - assert!(q.x.get_value().unwrap() == u); - assert!(q.y.get_value().unwrap() == v); + assert!(q.u.get_value().unwrap() == u); + assert!(q.v.get_value().unwrap() == v); - cs.set("u/num", Fr::random(rng)); + cs.set("u/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied().unwrap(), "u computation"); cs.set("u/num", u); assert!(cs.is_satisfied()); - cs.set("v/num", Fr::random(rng)); + cs.set("v/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied().unwrap(), "v computation"); cs.set("v/num", v); assert!(cs.is_satisfied()); @@ -704,50 +671,49 @@ mod test { #[test] fn test_interpret() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let p = edwards::Point::::rand(rng, ¶ms); + let p = jubjub::ExtendedPoint::random(rng); - let mut cs = TestConstraintSystem::::new(); - let q = EdwardsPoint::witness(&mut cs, Some(p.clone()), ¶ms).unwrap(); + let mut cs = TestConstraintSystem::new(); + let q = EdwardsPoint::witness(&mut cs, Some(p.clone())).unwrap(); - let p = p.to_xy(); + let p = p.to_affine(); assert!(cs.is_satisfied()); - assert_eq!(q.x.get_value().unwrap(), p.0); - assert_eq!(q.y.get_value().unwrap(), p.1); + assert_eq!(q.u.get_value().unwrap(), p.get_u()); + assert_eq!(q.v.get_value().unwrap(), p.get_v()); } for _ in 0..100 { - let p = edwards::Point::::rand(rng, ¶ms); - let (x, y) = p.to_xy(); + let p = jubjub::ExtendedPoint::random(rng).to_affine(); + let (u, v) = (p.get_u(), p.get_v()); - let mut cs = TestConstraintSystem::::new(); - let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x)).unwrap(); - let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y)).unwrap(); + let mut cs = TestConstraintSystem::new(); + let numu = AllocatedNum::alloc(cs.namespace(|| "u"), || Ok(u)).unwrap(); + let numv = AllocatedNum::alloc(cs.namespace(|| "v"), || Ok(v)).unwrap(); - let p = EdwardsPoint::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + let p = EdwardsPoint::interpret(&mut cs, &numu, &numv).unwrap(); assert!(cs.is_satisfied()); - assert_eq!(p.x.get_value().unwrap(), x); - assert_eq!(p.y.get_value().unwrap(), y); + assert_eq!(p.u.get_value().unwrap(), u); + assert_eq!(p.v.get_value().unwrap(), v); } - // Random (x, y) are unlikely to be on the curve. + // Random (u, v) are unlikely to be on the curve. for _ in 0..100 { - let x = Fr::random(rng); - let y = Fr::random(rng); + let u = bls12_381::Scalar::random(rng); + let v = bls12_381::Scalar::random(rng); - let mut cs = TestConstraintSystem::::new(); - let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x)).unwrap(); - let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y)).unwrap(); + let mut cs = TestConstraintSystem::new(); + let numu = AllocatedNum::alloc(cs.namespace(|| "u"), || Ok(u)).unwrap(); + let numv = AllocatedNum::alloc(cs.namespace(|| "v"), || Ok(v)).unwrap(); - EdwardsPoint::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + EdwardsPoint::interpret(&mut cs, &numu, &numv).unwrap(); assert_eq!(cs.which_is_unsatisfied().unwrap(), "on curve check"); } @@ -755,23 +721,22 @@ mod test { #[test] fn test_edwards_fixed_base_multiplication() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); - let p = params.generator(FixedGenerators::NoteCommitmentRandomness); - let s = Fs::random(rng); - let q = p.mul(s, params); - let (x1, y1) = q.to_xy(); + let p = zcash_primitives::constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR; + let s = jubjub::Fr::random(rng); + let q = jubjub::ExtendedPoint::from(p * s).to_affine(); + let (u1, v1) = (q.get_u(), q.get_v()); - let mut s_bits = BitIterator::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); s_bits.reverse(); - s_bits.truncate(Fs::NUM_BITS as usize); + s_bits.truncate(jubjub::Fr::NUM_BITS as usize); let s_bits = s_bits .into_iter() @@ -785,46 +750,45 @@ mod test { let q = fixed_base_multiplication( cs.namespace(|| "multiplication"), - FixedGenerators::NoteCommitmentRandomness, + &NOTE_COMMITMENT_RANDOMNESS_GENERATOR, &s_bits, - params, ) .unwrap(); - assert_eq!(q.x.get_value().unwrap(), x1); - assert_eq!(q.y.get_value().unwrap(), y1); + assert_eq!(q.u.get_value().unwrap(), u1); + assert_eq!(q.v.get_value().unwrap(), v1); } } #[test] fn test_edwards_multiplication() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); - let p = edwards::Point::::rand(rng, params); - let s = Fs::random(rng); - let q = p.mul(s, params); + let p = jubjub::ExtendedPoint::random(rng); + let s = jubjub::Fr::random(rng); + let q = (p * s).to_affine(); + let p = p.to_affine(); - let (x0, y0) = p.to_xy(); - let (x1, y1) = q.to_xy(); + let (u0, v0) = (p.get_u(), p.get_v()); + let (u1, v1) = (q.get_u(), q.get_v()); - let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); - let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); + let num_u0 = AllocatedNum::alloc(cs.namespace(|| "u0"), || Ok(u0)).unwrap(); + let num_v0 = AllocatedNum::alloc(cs.namespace(|| "v0"), || Ok(v0)).unwrap(); let p = EdwardsPoint { - x: num_x0, - y: num_y0, + u: num_u0, + v: num_v0, }; - let mut s_bits = BitIterator::new(s.into_repr()).collect::>(); + let mut s_bits = BitIterator::::new(s.to_repr()).collect::>(); s_bits.reverse(); - s_bits.truncate(Fs::NUM_BITS as usize); + s_bits.truncate(jubjub::Fr::NUM_BITS as usize); let s_bits = s_bits .into_iter() @@ -836,39 +800,36 @@ mod test { .map(|v| Boolean::from(v)) .collect::>(); - let q = p - .mul(cs.namespace(|| "scalar mul"), &s_bits, params) - .unwrap(); + let q = p.mul(cs.namespace(|| "scalar mul"), &s_bits).unwrap(); assert!(cs.is_satisfied()); - assert_eq!(q.x.get_value().unwrap(), x1); + assert_eq!(q.u.get_value().unwrap(), u1); - assert_eq!(q.y.get_value().unwrap(), y1); + assert_eq!(q.v.get_value().unwrap(), v1); } } #[test] fn test_conditionally_select() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..1000 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); - let p = edwards::Point::::rand(rng, params); + let p = jubjub::ExtendedPoint::random(rng).to_affine(); - let (x0, y0) = p.to_xy(); + let (u0, v0) = (p.get_u(), p.get_v()); - let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); - let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); + let num_u0 = AllocatedNum::alloc(cs.namespace(|| "u0"), || Ok(u0)).unwrap(); + let num_v0 = AllocatedNum::alloc(cs.namespace(|| "v0"), || Ok(v0)).unwrap(); let p = EdwardsPoint { - x: num_x0, - y: num_y0, + u: num_u0, + v: num_v0, }; let mut should_we_select = rng.next_u32() % 2 != 0; @@ -896,158 +857,144 @@ mod test { assert!(cs.is_satisfied()); if should_we_select { - assert_eq!(q.x.get_value().unwrap(), x0); - assert_eq!(q.y.get_value().unwrap(), y0); + assert_eq!(q.u.get_value().unwrap(), u0); + assert_eq!(q.v.get_value().unwrap(), v0); - cs.set("select/y'/num", Fr::one()); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/y' computation"); - cs.set("select/x'/num", Fr::zero()); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/x' computation"); + cs.set("select/v'/num", bls12_381::Scalar::one()); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/v' computation"); + cs.set("select/u'/num", bls12_381::Scalar::zero()); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/u' computation"); } else { - assert_eq!(q.x.get_value().unwrap(), Fr::zero()); - assert_eq!(q.y.get_value().unwrap(), Fr::one()); + assert_eq!(q.u.get_value().unwrap(), bls12_381::Scalar::zero()); + assert_eq!(q.v.get_value().unwrap(), bls12_381::Scalar::one()); - cs.set("select/y'/num", x0); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/y' computation"); - cs.set("select/x'/num", y0); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/x' computation"); + cs.set("select/v'/num", u0); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/v' computation"); + cs.set("select/u'/num", v0); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/u' computation"); } } } #[test] fn test_edwards_addition() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let p1 = edwards::Point::::rand(rng, params); - let p2 = edwards::Point::::rand(rng, params); + let p1 = jubjub::ExtendedPoint::random(rng); + let p2 = jubjub::ExtendedPoint::random(rng); - let p3 = p1.add(&p2, params); + let p3 = p1 + p2; - let (x0, y0) = p1.to_xy(); - let (x1, y1) = p2.to_xy(); - let (x2, y2) = p3.to_xy(); + let p1 = p1.to_affine(); + let p2 = p2.to_affine(); + let p3 = p3.to_affine(); - let mut cs = TestConstraintSystem::::new(); + let (u0, v0) = (p1.get_u(), p1.get_v()); + let (u1, v1) = (p2.get_u(), p2.get_v()); + let (u2, v2) = (p3.get_u(), p3.get_v()); - let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); - let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); + let mut cs = TestConstraintSystem::new(); - let num_x1 = AllocatedNum::alloc(cs.namespace(|| "x1"), || Ok(x1)).unwrap(); - let num_y1 = AllocatedNum::alloc(cs.namespace(|| "y1"), || Ok(y1)).unwrap(); + let num_u0 = AllocatedNum::alloc(cs.namespace(|| "u0"), || Ok(u0)).unwrap(); + let num_v0 = AllocatedNum::alloc(cs.namespace(|| "v0"), || Ok(v0)).unwrap(); + + let num_u1 = AllocatedNum::alloc(cs.namespace(|| "u1"), || Ok(u1)).unwrap(); + let num_v1 = AllocatedNum::alloc(cs.namespace(|| "v1"), || Ok(v1)).unwrap(); let p1 = EdwardsPoint { - x: num_x0, - y: num_y0, + u: num_u0, + v: num_v0, }; let p2 = EdwardsPoint { - x: num_x1, - y: num_y1, + u: num_u1, + v: num_v1, }; - let p3 = p1.add(cs.namespace(|| "addition"), &p2, params).unwrap(); + let p3 = p1.add(cs.namespace(|| "addition"), &p2).unwrap(); assert!(cs.is_satisfied()); - assert!(p3.x.get_value().unwrap() == x2); - assert!(p3.y.get_value().unwrap() == y2); + assert!(p3.u.get_value().unwrap() == u2); + assert!(p3.v.get_value().unwrap() == v2); - let u = cs.get("addition/U/num"); - cs.set("addition/U/num", Fr::random(rng)); + let uppercase_u = cs.get("addition/U/num"); + cs.set("addition/U/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/U computation")); - cs.set("addition/U/num", u); + cs.set("addition/U/num", uppercase_u); assert!(cs.is_satisfied()); - let x3 = cs.get("addition/x3/num"); - cs.set("addition/x3/num", Fr::random(rng)); - assert_eq!(cs.which_is_unsatisfied(), Some("addition/x3 computation")); - cs.set("addition/x3/num", x3); + let u3 = cs.get("addition/u3/num"); + cs.set("addition/u3/num", bls12_381::Scalar::random(rng)); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/u3 computation")); + cs.set("addition/u3/num", u3); assert!(cs.is_satisfied()); - let y3 = cs.get("addition/y3/num"); - cs.set("addition/y3/num", Fr::random(rng)); - assert_eq!(cs.which_is_unsatisfied(), Some("addition/y3 computation")); - cs.set("addition/y3/num", y3); + let v3 = cs.get("addition/v3/num"); + cs.set("addition/v3/num", bls12_381::Scalar::random(rng)); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/v3 computation")); + cs.set("addition/v3/num", v3); assert!(cs.is_satisfied()); } } #[test] fn test_edwards_doubling() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let p1 = edwards::Point::::rand(rng, params); - let p2 = p1.double(params); + let p1 = jubjub::ExtendedPoint::random(rng); + let p2 = p1.double(); - let (x0, y0) = p1.to_xy(); - let (x1, y1) = p2.to_xy(); + let p1 = p1.to_affine(); + let p2 = p2.to_affine(); - let mut cs = TestConstraintSystem::::new(); + let (u0, v0) = (p1.get_u(), p1.get_v()); + let (u1, v1) = (p2.get_u(), p2.get_v()); - let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); - let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); + let mut cs = TestConstraintSystem::new(); + + let num_u0 = AllocatedNum::alloc(cs.namespace(|| "u0"), || Ok(u0)).unwrap(); + let num_v0 = AllocatedNum::alloc(cs.namespace(|| "v0"), || Ok(v0)).unwrap(); let p1 = EdwardsPoint { - x: num_x0, - y: num_y0, + u: num_u0, + v: num_v0, }; - let p2 = p1.double(cs.namespace(|| "doubling"), params).unwrap(); + let p2 = p1.double(cs.namespace(|| "doubling")).unwrap(); assert!(cs.is_satisfied()); - assert!(p2.x.get_value().unwrap() == x1); - assert!(p2.y.get_value().unwrap() == y1); + assert!(p2.u.get_value().unwrap() == u1); + assert!(p2.v.get_value().unwrap() == v1); } } #[test] fn test_montgomery_addition() { - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); for _ in 0..100 { - let p1 = loop { - let x = Fr::random(rng); - let s: bool = rng.next_u32() % 2 != 0; - - let p = montgomery::Point::::get_for_x(x, s, params); - if p.is_some().into() { - break p.unwrap(); - } - }; + let p1 = jubjub::ExtendedPoint::random(rng); + let p2 = jubjub::ExtendedPoint::random(rng); + let p3 = p1 + p2; - let p2 = loop { - let x = Fr::random(rng); - let s: bool = rng.next_u32() % 2 != 0; + let (x0, y0) = to_montgomery_coords(p1).unwrap(); + let (x1, y1) = to_montgomery_coords(p2).unwrap(); + let (x2, y2) = to_montgomery_coords(p3).unwrap(); - let p = montgomery::Point::::get_for_x(x, s, params); - if p.is_some().into() { - break p.unwrap(); - } - }; - - let p3 = p1.add(&p2, params); - - let (x0, y0) = p1.to_xy().unwrap(); - let (x1, y1) = p2.to_xy().unwrap(); - let (x2, y2) = p3.to_xy().unwrap(); - - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || Ok(x0)).unwrap(); let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || Ok(y0)).unwrap(); @@ -1065,105 +1012,101 @@ mod test { y: num_y1.into(), }; - let p3 = p1.add(cs.namespace(|| "addition"), &p2, params).unwrap(); + let p3 = p1.add(cs.namespace(|| "addition"), &p2).unwrap(); assert!(cs.is_satisfied()); assert!(p3.x.get_value().unwrap() == x2); assert!(p3.y.get_value().unwrap() == y2); - cs.set("addition/yprime/num", Fr::random(rng)); + cs.set("addition/yprime/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate yprime")); cs.set("addition/yprime/num", y2); assert!(cs.is_satisfied()); - cs.set("addition/xprime/num", Fr::random(rng)); + cs.set("addition/xprime/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate xprime")); cs.set("addition/xprime/num", x2); assert!(cs.is_satisfied()); - cs.set("addition/lambda/num", Fr::random(rng)); + cs.set("addition/lambda/num", bls12_381::Scalar::random(rng)); assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate lambda")); } } #[test] fn test_assert_not_small_order() { - let params = &JubjubBls12::new(); - - let check_small_order_from_p = |p: edwards::Point, is_small_order| { - let mut cs = TestConstraintSystem::::new(); + let check_small_order_from_p = |p: jubjub::ExtendedPoint, is_small_order| { + let mut cs = TestConstraintSystem::new(); - let p = EdwardsPoint::witness(&mut cs, Some(p), params).unwrap(); + let p = EdwardsPoint::witness(&mut cs, Some(p)).unwrap(); assert!(cs.is_satisfied()); - assert!(p.assert_not_small_order(&mut cs, params).is_err() == is_small_order); + assert!(p.assert_not_small_order(&mut cs).is_err() == is_small_order); }; - let check_small_order_from_strs = |x, y| { - //let (x,y) = (Fr::from_str("14080418777298869350588389379361252092475090129841789940098060767181937064268").unwrap(), Fr::from_str("4408371274642418797323679050836535851651768103477128764103246588657558662748").unwrap()); - let (x, y) = (Fr::from_str(x).unwrap(), Fr::from_str(y).unwrap()); - let p = edwards::Point::::get_for_y(y, false, params).unwrap(); - assert_eq!(x, p.to_xy().0); + let check_small_order_from_strs = |u, v| { + let (u, v) = ( + bls12_381::Scalar::from_str(u).unwrap(), + bls12_381::Scalar::from_str(v).unwrap(), + ); + let p = jubjub::AffinePoint::from_raw_unchecked(u, v); - check_small_order_from_p(p, true); + check_small_order_from_p(p.into(), true); }; // zero has low order check_small_order_from_strs("0", "1"); // prime subgroup order - let prime_subgroup_order = Fs::from_str( + let prime_subgroup_order = jubjub::Fr::from_str( "6554484396890773809930967563523245729705921265872317281365359162392183254199", ) .unwrap(); - let largest_small_subgroup_order = Fs::from_str("8").unwrap(); + let largest_small_subgroup_order = jubjub::Fr::from_str("8").unwrap(); - let (zero_x, zero_y) = (Fr::from_str("0").unwrap(), Fr::from_str("1").unwrap()); + let (zero_u, zero_v) = (bls12_381::Scalar::zero(), bls12_381::Scalar::one()); // generator for jubjub - let (x, y) = ( - Fr::from_str( + let (u, v) = ( + bls12_381::Scalar::from_str( "11076627216317271660298050606127911965867021807910416450833192264015104452986", ) .unwrap(), - Fr::from_str( + bls12_381::Scalar::from_str( "44412834903739585386157632289020980010620626017712148233229312325549216099227", ) .unwrap(), ); - let g = edwards::Point::::get_for_y(y, false, params).unwrap(); - assert_eq!(x, g.to_xy().0); - check_small_order_from_p(g.clone(), false); + let g = jubjub::AffinePoint::from_raw_unchecked(u, v).into(); + check_small_order_from_p(g, false); // generator for the prime subgroup - let g_prime = g.mul(largest_small_subgroup_order, params); + let g_prime = g * largest_small_subgroup_order; check_small_order_from_p(g_prime.clone(), false); - let mut prime_subgroup_order_minus_1 = prime_subgroup_order.clone(); - prime_subgroup_order_minus_1.sub_assign(&Fs::from_str("1").unwrap()); + let prime_subgroup_order_minus_1 = prime_subgroup_order - jubjub::Fr::one(); - let should_not_be_zero = g_prime.mul(prime_subgroup_order_minus_1, params); - assert_ne!(zero_x, should_not_be_zero.to_xy().0); - assert_ne!(zero_y, should_not_be_zero.to_xy().1); - let should_be_zero = should_not_be_zero.add(&g_prime, params); - assert_eq!(zero_x, should_be_zero.to_xy().0); - assert_eq!(zero_y, should_be_zero.to_xy().1); + let should_not_be_zero = g_prime * prime_subgroup_order_minus_1; + assert_ne!(zero_u, should_not_be_zero.to_affine().get_u()); + assert_ne!(zero_v, should_not_be_zero.to_affine().get_v()); + let should_be_zero = should_not_be_zero + g_prime; + assert_eq!(zero_u, should_be_zero.to_affine().get_u()); + assert_eq!(zero_v, should_be_zero.to_affine().get_v()); // generator for the small order subgroup - let g_small = g.mul(prime_subgroup_order_minus_1, params); - let g_small = g_small.add(&g, params); + let g_small = g * prime_subgroup_order_minus_1; + let g_small = g_small + g; check_small_order_from_p(g_small.clone(), true); // g_small does have order 8 - let mut largest_small_subgroup_order_minus_1 = largest_small_subgroup_order.clone(); - largest_small_subgroup_order_minus_1.sub_assign(&Fs::from_str("1").unwrap()); + let largest_small_subgroup_order_minus_1 = largest_small_subgroup_order - jubjub::Fr::one(); - let should_not_be_zero = g_small.mul(largest_small_subgroup_order_minus_1, params); - assert_ne!(zero_x, should_not_be_zero.to_xy().0); - assert_ne!(zero_y, should_not_be_zero.to_xy().1); + let should_not_be_zero = g_small * largest_small_subgroup_order_minus_1; + assert_ne!(zero_u, should_not_be_zero.to_affine().get_u()); + assert_ne!(zero_v, should_not_be_zero.to_affine().get_v()); - let should_be_zero = should_not_be_zero.add(&g_small, params); - assert_eq!(zero_x, should_be_zero.to_xy().0); - assert_eq!(zero_y, should_be_zero.to_xy().1); + let should_be_zero = should_not_be_zero + g_small; + assert_eq!(zero_u, should_be_zero.to_affine().get_u()); + assert_eq!(zero_v, should_be_zero.to_affine().get_v()); // take all the points from the script // assert should be different than multiplying by cofactor, which is the solution diff --git a/zcash_proofs/src/circuit/pedersen_hash.rs b/zcash_proofs/src/circuit/pedersen_hash.rs index 18c2aae1cf..2ba02f9691 100644 --- a/zcash_proofs/src/circuit/pedersen_hash.rs +++ b/zcash_proofs/src/circuit/pedersen_hash.rs @@ -4,9 +4,10 @@ use super::ecc::{EdwardsPoint, MontgomeryPoint}; use bellman::gadgets::boolean::Boolean; use bellman::gadgets::lookup::*; use bellman::{ConstraintSystem, SynthesisError}; -use zcash_primitives::jubjub::*; pub use zcash_primitives::pedersen_hash::Personalization; +use crate::constants::PEDERSEN_CIRCUIT_GENERATORS; + fn get_constant_bools(person: &Personalization) -> Vec { person .get_bits() @@ -15,21 +16,20 @@ fn get_constant_bools(person: &Personalization) -> Vec { .collect() } -pub fn pedersen_hash( +pub fn pedersen_hash( mut cs: CS, personalization: Personalization, bits: &[Boolean], - params: &E::Params, -) -> Result, SynthesisError> +) -> Result where - CS: ConstraintSystem, + CS: ConstraintSystem, { let personalization = get_constant_bools(&personalization); assert_eq!(personalization.len(), 6); let mut edwards_result = None; let mut bits = personalization.iter().chain(bits.iter()).peekable(); - let mut segment_generators = params.pedersen_circuit_generators().iter(); + let mut segment_generators = PEDERSEN_CIRCUIT_GENERATORS.iter(); let boolean_false = Boolean::constant(false); let mut segment_i = 0; @@ -60,7 +60,6 @@ where format!("addition of segment {}, window {}", segment_i, window_i) }), segment_result, - params, )?; } } @@ -83,7 +82,6 @@ where // 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 { @@ -91,7 +89,6 @@ where *edwards_result = segment_result.add( cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)), edwards_result, - params, )?; } None => { @@ -111,7 +108,7 @@ mod test { use bellman::gadgets::boolean::{AllocatedBit, Boolean}; use bellman::gadgets::test::*; use ff::PrimeField; - use pairing::bls12_381::{Bls12, Fr}; + use group::Curve; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use zcash_primitives::pedersen_hash; @@ -147,7 +144,6 @@ mod test { 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); let leaves_len = 2 * 255; let note_len = 64 + 256 + 256; @@ -162,7 +158,7 @@ mod test { ] .iter() { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let input: Vec = (0..n_bits).map(|_| rng.next_u32() % 2 != 0).collect(); @@ -181,7 +177,6 @@ mod test { cs.namespace(|| "pedersen hash"), Personalization::NoteCommitment, &input_bools, - params, ) .unwrap(); @@ -206,13 +201,12 @@ mod test { 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); for length in 0..751 { for _ in 0..5 { let input: Vec = (0..length).map(|_| rng.next_u32() % 2 != 0).collect(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let input_bools: Vec = input .iter() @@ -229,32 +223,29 @@ mod test { cs.namespace(|| "pedersen hash"), Personalization::MerkleTree(1), &input_bools, - params, ) .unwrap(); assert!(cs.is_satisfied()); - let expected = pedersen_hash::pedersen_hash::( + let expected = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( Personalization::MerkleTree(1), input.clone().into_iter(), - params, - ) - .to_xy(); + )) + .to_affine(); - assert_eq!(res.get_x().get_value().unwrap(), expected.0); - assert_eq!(res.get_y().get_value().unwrap(), expected.1); + assert_eq!(res.get_u().get_value().unwrap(), expected.get_u()); + assert_eq!(res.get_v().get_value().unwrap(), expected.get_v()); // Test against the output of a different personalization - let unexpected = pedersen_hash::pedersen_hash::( + let unexpected = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( Personalization::MerkleTree(0), input.into_iter(), - params, - ) - .to_xy(); + )) + .to_affine(); - assert!(res.get_x().get_value().unwrap() != unexpected.0); - assert!(res.get_y().get_value().unwrap() != unexpected.1); + assert!(res.get_u().get_value().unwrap() != unexpected.get_u()); + assert!(res.get_v().get_value().unwrap() != unexpected.get_v()); } } } @@ -265,20 +256,19 @@ mod test { 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); - let params = &JubjubBls12::new(); - let expected_xs = [ + let expected_us = [ "28161926966428986673895580777285905189725480206811328272001879986576840909576", "39669831794597628158501766225645040955899576179071014703006420393381978263045", ]; - let expected_ys = [ + let expected_vs = [ "26869991781071974894722407757894142583682396277979904369818887810555917099932", "2112827187110048608327330788910224944044097981650120385961435904443901436107", ]; for length in 300..302 { let input: Vec = (0..length).map(|_| rng.next_u32() % 2 != 0).collect(); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let input_bools: Vec = input .iter() @@ -295,19 +285,18 @@ mod test { cs.namespace(|| "pedersen hash"), Personalization::MerkleTree(1), &input_bools, - params, ) .unwrap(); assert!(cs.is_satisfied()); assert_eq!( - res.get_x().get_value().unwrap(), - Fr::from_str(expected_xs[length - 300]).unwrap() + res.get_u().get_value().unwrap(), + bls12_381::Scalar::from_str(expected_us[length - 300]).unwrap() ); assert_eq!( - res.get_y().get_value().unwrap(), - Fr::from_str(expected_ys[length - 300]).unwrap() + res.get_v().get_value().unwrap(), + bls12_381::Scalar::from_str(expected_vs[length - 300]).unwrap() ); } } diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index 9782a4f965..93e3e97e9a 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -1,17 +1,21 @@ //! The Sapling circuits. -use ff::{Field, PrimeField, PrimeFieldRepr}; +use ff::PrimeField; +use group::Curve; use bellman::{Circuit, ConstraintSystem, SynthesisError}; -use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine}; - use zcash_primitives::constants; use zcash_primitives::primitives::{PaymentAddress, ProofGenerationKey, ValueCommitment}; use super::ecc; use super::pedersen_hash; +use crate::constants::{ + NOTE_COMMITMENT_RANDOMNESS_GENERATOR, NULLIFIER_POSITION_GENERATOR, + PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR, + VALUE_COMMITMENT_VALUE_GENERATOR, +}; use bellman::gadgets::blake2s; use bellman::gadgets::boolean; use bellman::gadgets::multipack; @@ -21,60 +25,54 @@ use bellman::gadgets::Assignment; pub const TREE_DEPTH: usize = zcash_primitives::sapling::SAPLING_COMMITMENT_TREE_DEPTH; /// This is an instance of the `Spend` circuit. -pub struct Spend<'a, E: JubjubEngine> { - pub params: &'a E::Params, - +pub struct Spend { /// Pedersen commitment to the value being spent - pub value_commitment: Option>, + pub value_commitment: Option, /// Key required to construct proofs for spending notes /// for a particular spending key - pub proof_generation_key: Option>, + pub proof_generation_key: Option, /// The payment address associated with the note - pub payment_address: Option>, + pub payment_address: Option, /// The randomness of the note commitment - pub commitment_randomness: Option, + pub commitment_randomness: Option, /// Re-randomization of the public key - pub ar: Option, + pub ar: Option, /// The authentication path of the commitment in the tree - pub auth_path: Vec>, + pub auth_path: Vec>, /// The anchor; the root of the tree. If the note being /// spent is zero-value, this can be anything. - pub anchor: Option, + pub anchor: Option, } /// This is an output circuit instance. -pub struct Output<'a, E: JubjubEngine> { - pub params: &'a E::Params, - +pub struct Output { /// Pedersen commitment to the value being spent - pub value_commitment: Option>, + pub value_commitment: Option, /// The payment address of the recipient - pub payment_address: Option>, + pub payment_address: Option, /// The randomness used to hide the note commitment data - pub commitment_randomness: Option, + pub commitment_randomness: Option, /// The ephemeral secret key for DH with recipient - pub esk: Option, + pub esk: Option, } /// Exposes a Pedersen commitment to the value as an /// input to the circuit -fn expose_value_commitment( +fn expose_value_commitment( mut cs: CS, - value_commitment: Option>, - params: &E::Params, + value_commitment: Option, ) -> Result, SynthesisError> where - E: JubjubEngine, - CS: ConstraintSystem, + CS: ConstraintSystem, { // Booleanize the value into little-endian bit order let value_bits = boolean::u64_into_boolean_vec_le( @@ -85,9 +83,8 @@ where // Compute the note value in the exponent let value = ecc::fixed_base_multiplication( cs.namespace(|| "compute the value in the exponent"), - FixedGenerators::ValueCommitmentValue, + &VALUE_COMMITMENT_VALUE_GENERATOR, &value_bits, - params, )?; // Booleanize the randomness. This does not ensure @@ -101,13 +98,12 @@ where // Compute the randomness in the exponent let rcv = ecc::fixed_base_multiplication( cs.namespace(|| "computation of rcv"), - FixedGenerators::ValueCommitmentRandomness, + &VALUE_COMMITMENT_RANDOMNESS_GENERATOR, &rcv, - params, )?; // Compute the Pedersen commitment to the value - let cv = value.add(cs.namespace(|| "computation of cv"), &rcv, params)?; + let cv = value.add(cs.namespace(|| "computation of cv"), &rcv)?; // Expose the commitment as an input to the circuit cv.inputize(cs.namespace(|| "commitment point"))?; @@ -115,19 +111,21 @@ where Ok(value_bits) } -impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl Circuit for Spend { + fn synthesize>( + self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { // Prover witnesses ak (ensures that it's on the curve) let ak = ecc::EdwardsPoint::witness( cs.namespace(|| "ak"), - self.proof_generation_key.as_ref().map(|k| k.ak.clone()), - self.params, + self.proof_generation_key.as_ref().map(|k| k.ak.into()), )?; // There are no sensible attacks on small order points // of ak (that we're aware of!) but it's a cheap check, // so we do it. - ak.assert_not_small_order(cs.namespace(|| "ak not small order"), self.params)?; + ak.assert_not_small_order(cs.namespace(|| "ak not small order"))?; // Rerandomize ak and expose it as an input to the circuit { @@ -136,12 +134,11 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Compute the randomness in the exponent let ar = ecc::fixed_base_multiplication( cs.namespace(|| "computation of randomization for the signing key"), - FixedGenerators::SpendingKeyGenerator, + &SPENDING_KEY_GENERATOR, &ar, - self.params, )?; - let rk = ak.add(cs.namespace(|| "computation of rk"), &ar, self.params)?; + let rk = ak.add(cs.namespace(|| "computation of rk"), &ar)?; rk.inputize(cs.namespace(|| "rk"))?; } @@ -156,16 +153,15 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; // NB: We don't ensure that the bit representation of nsk - // is "in the field" (Fs) because it's not used except to - // demonstrate the prover knows it. If they know a - // congruency then that's equivalent. + // is "in the field" (jubjub::Fr) because it's not used + // except to demonstrate the prover knows it. If they know + // a congruency then that's equivalent. // Compute nk = [nsk] ProvingPublicKey nk = ecc::fixed_base_multiplication( cs.namespace(|| "computation of nk"), - FixedGenerators::ProofGenerationKey, + &PROOF_GENERATION_KEY_GENERATOR, &nsk, - self.params, )?; } @@ -198,21 +194,15 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; // drop_5 to ensure it's in the field - ivk.truncate(E::Fs::CAPACITY as usize); + ivk.truncate(jubjub::Fr::CAPACITY as usize); // Witness g_d, checking that it's on the curve. let g_d = { - // This binding is to avoid a weird edge case in Rust's - // ownership/borrowing rules. self is partially moved - // above, but the closure for and_then will have to - // move self (or a reference to self) to reference - // self.params, so we have to copy self.params here. - let params = self.params; - ecc::EdwardsPoint::witness( cs.namespace(|| "witness g_d"), - self.payment_address.as_ref().and_then(|a| a.g_d(params)), - self.params, + self.payment_address + .as_ref() + .and_then(|a| a.g_d().map(jubjub::ExtendedPoint::from)), )? }; @@ -220,10 +210,10 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // is already done in the Output circuit, and this proof ensures // g_d is bound to a product of that check, but for defense in // depth let's check it anyway. It's cheap. - g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?; + g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"))?; // Compute pk_d = g_d^ivk - let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk, self.params)?; + let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk)?; // Compute note contents: // value (in big endian) followed by g_d and pk_d @@ -237,12 +227,11 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let value_bits = expose_value_commitment( cs.namespace(|| "value commitment"), self.value_commitment, - self.params, )?; // Compute the note's value as a linear combination // of the bits. - let mut coeff = E::Fr::one(); + let mut coeff = bls12_381::Scalar::one(); for bit in &value_bits { value_num = value_num.add_bool_with_coeff(CS::one(), bit, coeff); coeff = coeff.double(); @@ -270,7 +259,6 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { cs.namespace(|| "note content hash"), pedersen_hash::Personalization::NoteCommitment, ¬e_contents, - self.params, )?; { @@ -283,18 +271,13 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Compute the note commitment randomness in the exponent let rcm = ecc::fixed_base_multiplication( cs.namespace(|| "computation of commitment randomness"), - FixedGenerators::NoteCommitmentRandomness, + &NOTE_COMMITMENT_RANDOMNESS_GENERATOR, &rcm, - self.params, )?; // Randomize the note commitment. Pedersen hashes are not // themselves hiding commitments. - cm = cm.add( - cs.namespace(|| "randomization of note commitment"), - &rcm, - self.params, - )?; + cm = cm.add(cs.namespace(|| "randomization of note commitment"), &rcm)?; } // This will store (least significant bit first) @@ -304,7 +287,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // This is an injective encoding, as cur is a // point in the prime order subgroup. - let mut cur = cm.get_x().clone(); + let mut cur = cm.get_u().clone(); // Ascend the merkle tree authentication path for (i, e) in self.auth_path.into_iter().enumerate() { @@ -326,7 +309,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { num::AllocatedNum::alloc(cs.namespace(|| "path element"), || Ok(e.get()?.0))?; // Swap the two if the current subtree is on the right - let (xl, xr) = num::AllocatedNum::conditionally_reverse( + let (ul, ur) = num::AllocatedNum::conditionally_reverse( cs.namespace(|| "conditional reversal of preimage"), &cur, &path_element, @@ -338,17 +321,16 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // they will be unable to find an authentication path in the // tree with high probability. let mut preimage = vec![]; - preimage.extend(xl.to_bits_le(cs.namespace(|| "xl into bits"))?); - preimage.extend(xr.to_bits_le(cs.namespace(|| "xr into bits"))?); + preimage.extend(ul.to_bits_le(cs.namespace(|| "ul into bits"))?); + preimage.extend(ur.to_bits_le(cs.namespace(|| "ur into bits"))?); // Compute the new subtree value cur = pedersen_hash::pedersen_hash( cs.namespace(|| "computation of pedersen hash"), pedersen_hash::Personalization::MerkleTree(i), &preimage, - self.params, )? - .get_x() + .get_u() .clone(); // Injective encoding } @@ -366,7 +348,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { cs.enforce( || "conditionally enforce correct root", |lc| lc + cur.get_variable() - rt.get_variable(), - |lc| lc + &value_num.lc(E::Fr::one()), + |lc| lc + &value_num.lc(bls12_381::Scalar::one()), |lc| lc, ); @@ -381,17 +363,12 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Compute the position in the exponent let position = ecc::fixed_base_multiplication( cs.namespace(|| "g^position"), - FixedGenerators::NullifierPosition, + &NULLIFIER_POSITION_GENERATOR, &position_bits, - self.params, )?; // Add the position to the commitment - rho = rho.add( - cs.namespace(|| "faerie gold prevention"), - &position, - self.params, - )?; + rho = rho.add(cs.namespace(|| "faerie gold prevention"), &position)?; } // Let's compute nf = BLAKE2s(nk || rho) @@ -410,8 +387,11 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { } } -impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl Circuit for Output { + fn synthesize>( + self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { // Let's start to construct our note, which contains // value (big endian) let mut note_contents = vec![]; @@ -421,19 +401,17 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { note_contents.extend(expose_value_commitment( cs.namespace(|| "value commitment"), self.value_commitment, - self.params, )?); // Let's deal with g_d { - let params = self.params; - // Prover witnesses g_d, ensuring it's on the // curve. let g_d = ecc::EdwardsPoint::witness( cs.namespace(|| "witness g_d"), - self.payment_address.as_ref().and_then(|a| a.g_d(params)), - self.params, + self.payment_address + .as_ref() + .and_then(|a| a.g_d().map(jubjub::ExtendedPoint::from)), )?; // g_d is ensured to be large order. The relationship @@ -445,7 +423,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // // Further, if it were small order, epk would be // small order too! - g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?; + g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"))?; // Extend our note contents with the representation of // g_d. @@ -455,7 +433,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { let esk = boolean::field_into_boolean_vec_le(cs.namespace(|| "esk"), self.esk)?; // Create the ephemeral public key from g_d. - let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk, self.params)?; + let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk)?; // Expose epk publicly. epk.inputize(cs.namespace(|| "epk"))?; @@ -466,23 +444,26 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // they would like. { // Just grab pk_d from the witness - let pk_d = self.payment_address.as_ref().map(|e| e.pk_d().to_xy()); + let pk_d = self + .payment_address + .as_ref() + .map(|e| jubjub::ExtendedPoint::from(*e.pk_d()).to_affine()); - // Witness the y-coordinate, encoded as little + // Witness the v-coordinate, encoded as little // endian bits (to match the representation) - let y_contents = boolean::field_into_boolean_vec_le( - cs.namespace(|| "pk_d bits of y"), - pk_d.map(|e| e.1), + let v_contents = boolean::field_into_boolean_vec_le( + cs.namespace(|| "pk_d bits of v"), + pk_d.map(|e| e.get_v()), )?; // Witness the sign bit let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( - cs.namespace(|| "pk_d bit of x"), - pk_d.map(|e| e.0.into_repr().is_odd()), + cs.namespace(|| "pk_d bit of u"), + pk_d.map(|e| e.get_u().is_odd()), )?); // Extend the note with pk_d representation - note_contents.extend(y_contents); + note_contents.extend(v_contents); note_contents.push(sign_bit); } @@ -498,7 +479,6 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { cs.namespace(|| "note content hash"), pedersen_hash::Personalization::NoteCommitment, ¬e_contents, - self.params, )?; { @@ -511,24 +491,19 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // Compute the note commitment randomness in the exponent let rcm = ecc::fixed_base_multiplication( cs.namespace(|| "computation of commitment randomness"), - FixedGenerators::NoteCommitmentRandomness, + &NOTE_COMMITMENT_RANDOMNESS_GENERATOR, &rcm, - self.params, )?; // Randomize our note commitment - cm = cm.add( - cs.namespace(|| "randomization of note commitment"), - &rcm, - self.params, - )?; + cm = cm.add(cs.namespace(|| "randomization of note commitment"), &rcm)?; } - // Only the x-coordinate of the output is revealed, + // Only the u-coordinate of the output is revealed, // since we know it is prime order, and we know that - // the x-coordinate is an injective encoding for - // prime-order elements. - cm.get_x().inputize(cs.namespace(|| "commitment"))?; + // the u-coordinate is an injective encoding for + // elements in the prime-order subgroup. + cm.get_u().inputize(cs.namespace(|| "commitment"))?; Ok(()) } @@ -538,16 +513,14 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { fn test_input_circuit_with_bls12_381() { use bellman::gadgets::test::*; use ff::{BitIterator, Field}; - use pairing::bls12_381::*; + use group::Group; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use zcash_primitives::{ - jubjub::{edwards, fs, JubjubBls12}, pedersen_hash, - primitives::{Diversifier, Note, ProofGenerationKey}, + primitives::{Diversifier, Note, ProofGenerationKey, Rseed}, }; - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -558,18 +531,18 @@ fn test_input_circuit_with_bls12_381() { for _ in 0..10 { let value_commitment = ValueCommitment { value: rng.next_u64(), - randomness: fs::Fs::random(rng), + randomness: jubjub::Fr::random(rng), }; - let nsk = fs::Fs::random(rng); - let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + let nsk = jubjub::Fr::random(rng); + let ak = jubjub::SubgroupPoint::random(rng); let proof_generation_key = ProofGenerationKey { ak: ak.clone(), nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.to_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(); let payment_address; @@ -580,30 +553,32 @@ fn test_input_circuit_with_bls12_381() { Diversifier(d) }; - if let Some(p) = viewing_key.to_payment_address(diversifier, params) { + if let Some(p) = viewing_key.to_payment_address(diversifier) { payment_address = p; break; } } - let g_d = payment_address.diversifier().g_d(params).unwrap(); - let commitment_randomness = fs::Fs::random(rng); - let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; - let ar = fs::Fs::random(rng); + let g_d = payment_address.diversifier().g_d().unwrap(); + let commitment_randomness = jubjub::Fr::random(rng); + let auth_path = + vec![Some((bls12_381::Scalar::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; + let ar = jubjub::Fr::random(rng); { - let rk = viewing_key.rk(ar, params).to_xy(); - let expected_value_cm = value_commitment.cm(params).to_xy(); + let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine(); + let expected_value_commitment = + jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); let note = Note { value: value_commitment.value, g_d: g_d.clone(), pk_d: payment_address.pk_d().clone(), - r: commitment_randomness.clone(), + rseed: Rseed::BeforeZip212(commitment_randomness.clone()), }; let mut position = 0u64; - let cm: Fr = note.cm(params); - let mut cur = cm.clone(); + let cmu = note.cmu(); + let mut cur = cmu.clone(); for (i, val) in auth_path.clone().into_iter().enumerate() { let (uncle, b) = val.unwrap(); @@ -615,36 +590,34 @@ fn test_input_circuit_with_bls12_381() { ::std::mem::swap(&mut lhs, &mut rhs); } - let mut lhs: Vec = BitIterator::new(lhs.into_repr()).collect(); - let mut rhs: Vec = BitIterator::new(rhs.into_repr()).collect(); + let mut lhs: Vec = BitIterator::::new(lhs.to_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.to_repr()).collect(); lhs.reverse(); rhs.reverse(); - cur = pedersen_hash::pedersen_hash::( + cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( pedersen_hash::Personalization::MerkleTree(i), lhs.into_iter() - .take(Fr::NUM_BITS as usize) - .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), - params, - ) - .to_xy() - .0; + .take(bls12_381::Scalar::NUM_BITS as usize) + .chain(rhs.into_iter().take(bls12_381::Scalar::NUM_BITS as usize)), + )) + .to_affine() + .get_u(); if b { position |= 1 << i; } } - let expected_nf = note.nf(&viewing_key, position, params); + let expected_nf = note.nf(&viewing_key, position); let expected_nf = multipack::bytes_to_bits_le(&expected_nf); - let expected_nf = multipack::compute_multipacking::(&expected_nf); + let expected_nf = multipack::compute_multipacking(&expected_nf); assert_eq!(expected_nf.len(), 2); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let instance = Spend { - params, value_commitment: Some(value_commitment.clone()), proof_generation_key: Some(proof_generation_key.clone()), payment_address: Some(payment_address.clone()), @@ -663,19 +636,19 @@ fn test_input_circuit_with_bls12_381() { "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89" ); - assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); + assert_eq!(cs.get("randomization of note commitment/u3/num"), cmu); assert_eq!(cs.num_inputs(), 8); - assert_eq!(cs.get_input(0, "ONE"), Fr::one()); - assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); - assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); + assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); + assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u()); + assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v()); assert_eq!( - cs.get_input(3, "value commitment/commitment point/x/input variable"), - expected_value_cm.0 + cs.get_input(3, "value commitment/commitment point/u/input variable"), + expected_value_commitment.get_u() ); assert_eq!( - cs.get_input(4, "value commitment/commitment point/y/input variable"), - expected_value_cm.1 + cs.get_input(4, "value commitment/commitment point/v/input variable"), + expected_value_commitment.get_v() ); assert_eq!(cs.get_input(5, "anchor/input variable"), cur); assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); @@ -688,16 +661,14 @@ fn test_input_circuit_with_bls12_381() { fn test_input_circuit_with_bls12_381_external_test_vectors() { use bellman::gadgets::test::*; use ff::{BitIterator, Field}; - use pairing::bls12_381::*; + use group::Group; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use zcash_primitives::{ - jubjub::{edwards, fs, JubjubBls12}, pedersen_hash, - primitives::{Diversifier, Note, ProofGenerationKey}, + primitives::{Diversifier, Note, ProofGenerationKey, Rseed}, }; - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -705,7 +676,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { let tree_depth = 32; - let expected_cm_xs = vec![ + let expected_commitment_us = vec![ "43821661663052659750276289184181083197337192946256245809816728673021647664276", "7220807656052227578299730541645543434083158611414003423211850718229633594616", "13239753550660714843257636471668037031928211668773449453628093339627668081697", @@ -718,7 +689,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { "18269767207277008186871145355531741929166733260352590789136389380124992250945", ]; - let expected_cm_ys = vec![ + let expected_commitment_vs = vec![ "27630722367128086497290371604583225252915685718989450292520883698391703910", "23310648738313092772044712773481584369462075017189681529702825235349449805260", "25709635353183537915646348052945798827495141780341329896098121888376871589480", @@ -734,18 +705,18 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { for i in 0..10 { let value_commitment = ValueCommitment { value: i, - randomness: fs::Fs::from_str(&(1000 * (i + 1)).to_string()).unwrap(), + randomness: jubjub::Fr::from_str(&(1000 * (i + 1)).to_string()).unwrap(), }; - let nsk = fs::Fs::random(rng); - let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + let nsk = jubjub::Fr::random(rng); + let ak = jubjub::SubgroupPoint::random(rng); let proof_generation_key = ProofGenerationKey { ak: ak.clone(), nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.to_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(); let payment_address; @@ -756,38 +727,40 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { Diversifier(d) }; - if let Some(p) = viewing_key.to_payment_address(diversifier, params) { + if let Some(p) = viewing_key.to_payment_address(diversifier) { payment_address = p; break; } } - let g_d = payment_address.diversifier().g_d(params).unwrap(); - let commitment_randomness = fs::Fs::random(rng); - let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; - let ar = fs::Fs::random(rng); + let g_d = payment_address.diversifier().g_d().unwrap(); + let commitment_randomness = jubjub::Fr::random(rng); + let auth_path = + vec![Some((bls12_381::Scalar::random(rng), rng.next_u32() % 2 != 0)); tree_depth]; + let ar = jubjub::Fr::random(rng); { - let rk = viewing_key.rk(ar, params).to_xy(); - let expected_value_cm = value_commitment.cm(params).to_xy(); + let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine(); + let expected_value_commitment = + jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); assert_eq!( - expected_value_cm.0, - Fr::from_str(&expected_cm_xs[i as usize]).unwrap() + expected_value_commitment.get_u(), + bls12_381::Scalar::from_str(&expected_commitment_us[i as usize]).unwrap() ); assert_eq!( - expected_value_cm.1, - Fr::from_str(&expected_cm_ys[i as usize]).unwrap() + expected_value_commitment.get_v(), + bls12_381::Scalar::from_str(&expected_commitment_vs[i as usize]).unwrap() ); let note = Note { value: value_commitment.value, g_d: g_d.clone(), pk_d: payment_address.pk_d().clone(), - r: commitment_randomness.clone(), + rseed: Rseed::BeforeZip212(commitment_randomness.clone()), }; let mut position = 0u64; - let cm: Fr = note.cm(params); - let mut cur = cm.clone(); + let cmu = note.cmu(); + let mut cur = cmu.clone(); for (i, val) in auth_path.clone().into_iter().enumerate() { let (uncle, b) = val.unwrap(); @@ -799,36 +772,34 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { ::std::mem::swap(&mut lhs, &mut rhs); } - let mut lhs: Vec = BitIterator::new(lhs.into_repr()).collect(); - let mut rhs: Vec = BitIterator::new(rhs.into_repr()).collect(); + let mut lhs: Vec = BitIterator::::new(lhs.to_repr()).collect(); + let mut rhs: Vec = BitIterator::::new(rhs.to_repr()).collect(); lhs.reverse(); rhs.reverse(); - cur = pedersen_hash::pedersen_hash::( + cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash( pedersen_hash::Personalization::MerkleTree(i), lhs.into_iter() - .take(Fr::NUM_BITS as usize) - .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), - params, - ) - .to_xy() - .0; + .take(bls12_381::Scalar::NUM_BITS as usize) + .chain(rhs.into_iter().take(bls12_381::Scalar::NUM_BITS as usize)), + )) + .to_affine() + .get_u(); if b { position |= 1 << i; } } - let expected_nf = note.nf(&viewing_key, position, params); + let expected_nf = note.nf(&viewing_key, position); let expected_nf = multipack::bytes_to_bits_le(&expected_nf); - let expected_nf = multipack::compute_multipacking::(&expected_nf); + let expected_nf = multipack::compute_multipacking(&expected_nf); assert_eq!(expected_nf.len(), 2); - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let instance = Spend { - params: params, value_commitment: Some(value_commitment.clone()), proof_generation_key: Some(proof_generation_key.clone()), payment_address: Some(payment_address.clone()), @@ -847,19 +818,19 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89" ); - assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); + assert_eq!(cs.get("randomization of note commitment/u3/num"), cmu); assert_eq!(cs.num_inputs(), 8); - assert_eq!(cs.get_input(0, "ONE"), Fr::one()); - assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); - assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); + assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); + assert_eq!(cs.get_input(1, "rk/u/input variable"), rk.get_u()); + assert_eq!(cs.get_input(2, "rk/v/input variable"), rk.get_v()); assert_eq!( - cs.get_input(3, "value commitment/commitment point/x/input variable"), - expected_value_cm.0 + cs.get_input(3, "value commitment/commitment point/u/input variable"), + expected_value_commitment.get_u() ); assert_eq!( - cs.get_input(4, "value commitment/commitment point/y/input variable"), - expected_value_cm.1 + cs.get_input(4, "value commitment/commitment point/v/input variable"), + expected_value_commitment.get_v() ); assert_eq!(cs.get_input(5, "anchor/input variable"), cur); assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); @@ -872,15 +843,11 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { fn test_output_circuit_with_bls12_381() { use bellman::gadgets::test::*; use ff::Field; - use pairing::bls12_381::*; + use group::Group; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; - use zcash_primitives::{ - jubjub::{edwards, fs, JubjubBls12}, - primitives::{Diversifier, ProofGenerationKey}, - }; + use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, Rseed}; - let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([ 0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -889,18 +856,18 @@ fn test_output_circuit_with_bls12_381() { for _ in 0..100 { let value_commitment = ValueCommitment { value: rng.next_u64(), - randomness: fs::Fs::random(rng), + randomness: jubjub::Fr::random(rng), }; - let nsk = fs::Fs::random(rng); - let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + let nsk = jubjub::Fr::random(rng); + let ak = jubjub::SubgroupPoint::random(rng); let proof_generation_key = ProofGenerationKey { ak: ak.clone(), nsk: nsk.clone(), }; - let viewing_key = proof_generation_key.to_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(); let payment_address; @@ -911,20 +878,19 @@ fn test_output_circuit_with_bls12_381() { Diversifier(d) }; - if let Some(p) = viewing_key.to_payment_address(diversifier, params) { + if let Some(p) = viewing_key.to_payment_address(diversifier) { payment_address = p; break; } } - let commitment_randomness = fs::Fs::random(rng); - let esk = fs::Fs::random(rng); + let commitment_randomness = jubjub::Fr::random(rng); + let esk = jubjub::Fr::random(rng); { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::new(); let instance = Output { - params, value_commitment: Some(value_commitment.clone()), payment_address: Some(payment_address.clone()), commitment_randomness: Some(commitment_randomness), @@ -940,32 +906,40 @@ fn test_output_circuit_with_bls12_381() { "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee" ); - let expected_cm = payment_address - .create_note(value_commitment.value, commitment_randomness, params) + let expected_cmu = payment_address + .create_note( + value_commitment.value, + Rseed::BeforeZip212(commitment_randomness), + ) .expect("should be valid") - .cm(params); + .cmu(); - let expected_value_cm = value_commitment.cm(params).to_xy(); + let expected_value_commitment = + jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); - let expected_epk = payment_address - .g_d(params) - .expect("should be valid") - .mul(esk, params); - let expected_epk_xy = expected_epk.to_xy(); + let expected_epk = + jubjub::ExtendedPoint::from(payment_address.g_d().expect("should be valid") * esk) + .to_affine(); assert_eq!(cs.num_inputs(), 6); - assert_eq!(cs.get_input(0, "ONE"), Fr::one()); + assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one()); + assert_eq!( + cs.get_input(1, "value commitment/commitment point/u/input variable"), + expected_value_commitment.get_u() + ); + assert_eq!( + cs.get_input(2, "value commitment/commitment point/v/input variable"), + expected_value_commitment.get_v() + ); assert_eq!( - cs.get_input(1, "value commitment/commitment point/x/input variable"), - expected_value_cm.0 + cs.get_input(3, "epk/u/input variable"), + expected_epk.get_u() ); assert_eq!( - cs.get_input(2, "value commitment/commitment point/y/input variable"), - expected_value_cm.1 + cs.get_input(4, "epk/v/input variable"), + expected_epk.get_v() ); - assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0); - assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1); - assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); + assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cmu); } } } diff --git a/zcash_proofs/src/circuit/sprout/commitment.rs b/zcash_proofs/src/circuit/sprout/commitment.rs index fba1217389..367be9caf2 100644 --- a/zcash_proofs/src/circuit/sprout/commitment.rs +++ b/zcash_proofs/src/circuit/sprout/commitment.rs @@ -1,9 +1,9 @@ use bellman::gadgets::boolean::Boolean; use bellman::gadgets::sha256::sha256; use bellman::{ConstraintSystem, SynthesisError}; -use pairing::Engine; +use ff::PrimeField; -pub fn note_comm( +pub fn note_comm( cs: CS, a_pk: &[Boolean], value: &[Boolean], @@ -11,8 +11,8 @@ pub fn note_comm( r: &[Boolean], ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert_eq!(a_pk.len(), 256); assert_eq!(value.len(), 64); diff --git a/zcash_proofs/src/circuit/sprout/input.rs b/zcash_proofs/src/circuit/sprout/input.rs index a2726d41a2..b2e0d6ced8 100644 --- a/zcash_proofs/src/circuit/sprout/input.rs +++ b/zcash_proofs/src/circuit/sprout/input.rs @@ -1,7 +1,7 @@ use bellman::gadgets::boolean::{AllocatedBit, Boolean}; use bellman::gadgets::sha256::sha256_block_no_padding; use bellman::{ConstraintSystem, SynthesisError}; -use pairing::Engine; +use ff::PrimeField; use super::commitment::note_comm; use super::prfs::*; @@ -13,7 +13,7 @@ pub struct InputNote { } impl InputNote { - pub fn compute( + pub fn compute( mut cs: CS, a_sk: Option, rho: Option, @@ -25,8 +25,8 @@ impl InputNote { rt: &[Boolean], ) -> Result where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let a_sk = witness_u252( cs.namespace(|| "a_sk"), @@ -106,7 +106,7 @@ impl InputNote { // if enforce is one, they must be equal cs.enforce( || format!("conditionally enforce correct root for bit {}", i), - |_| cur.lc(CS::one(), E::Fr::one()) - &rt.lc(CS::one(), E::Fr::one()), + |_| cur.lc(CS::one(), Scalar::one()) - &rt.lc(CS::one(), Scalar::one()), |lc| lc + enforce.get_variable(), |lc| lc, ); @@ -118,15 +118,15 @@ impl InputNote { /// Swaps two 256-bit blobs conditionally, returning the /// 512-bit concatenation. -pub fn conditionally_swap_u256( +pub fn conditionally_swap_u256( mut cs: CS, lhs: &[Boolean], rhs: &[Boolean], condition: &AllocatedBit, ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert_eq!(lhs.len(), 256); assert_eq!(rhs.len(), 256); @@ -155,9 +155,9 @@ where // x = rhs cs.enforce( || "conditional swap for x", - |lc| lc + &rhs.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()), + |lc| lc + &rhs.lc(CS::one(), Scalar::one()) - &lhs.lc(CS::one(), Scalar::one()), |lc| lc + condition.get_variable(), - |lc| lc + &x.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()), + |lc| lc + &x.lc(CS::one(), Scalar::one()) - &lhs.lc(CS::one(), Scalar::one()), ); let y = Boolean::from(AllocatedBit::alloc( @@ -171,9 +171,9 @@ where // y - rhs = condition (lhs - rhs) cs.enforce( || "conditional swap for y", - |lc| lc + &lhs.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()), + |lc| lc + &lhs.lc(CS::one(), Scalar::one()) - &rhs.lc(CS::one(), Scalar::one()), |lc| lc + condition.get_variable(), - |lc| lc + &y.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()), + |lc| lc + &y.lc(CS::one(), Scalar::one()) - &rhs.lc(CS::one(), Scalar::one()), ); new_lhs.push(x); diff --git a/zcash_proofs/src/circuit/sprout/mod.rs b/zcash_proofs/src/circuit/sprout/mod.rs index 6afe677938..ddb55deb92 100644 --- a/zcash_proofs/src/circuit/sprout/mod.rs +++ b/zcash_proofs/src/circuit/sprout/mod.rs @@ -13,8 +13,7 @@ use bellman::gadgets::boolean::{AllocatedBit, Boolean}; use bellman::gadgets::multipack::pack_into_inputs; use bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError}; -use ff::Field; -use pairing::Engine; +use ff::PrimeField; mod commitment; mod input; @@ -55,8 +54,8 @@ pub struct JSOutput { pub r: Option, } -impl Circuit for JoinSplit { - fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { +impl Circuit for JoinSplit { + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { assert_eq!(self.inputs.len(), 2); assert_eq!(self.outputs.len(), 2); @@ -219,10 +218,10 @@ pub struct NoteValue { } impl NoteValue { - fn new(mut cs: CS, value: Option) -> Result + fn new(mut cs: CS, value: Option) -> Result where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let mut values; match value { @@ -262,10 +261,10 @@ impl NoteValue { /// Computes this value as a linear combination of /// its bits. - fn lc(&self) -> LinearCombination { + fn lc(&self) -> LinearCombination { let mut tmp = LinearCombination::zero(); - let mut coeff = E::Fr::one(); + let mut coeff = Scalar::one(); for b in &self.bits { tmp = tmp + (coeff, b.get_variable()); coeff = coeff.double(); @@ -281,15 +280,15 @@ impl NoteValue { /// Witnesses some bytes in the constraint system, /// skipping the first `skip_bits`. -fn witness_bits( +fn witness_bits( mut cs: CS, value: Option<&[u8]>, num_bits: usize, skip_bits: usize, ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let bit_values = if let Some(value) = value { let mut tmp = vec![]; @@ -318,18 +317,18 @@ where Ok(bits) } -fn witness_u256(cs: CS, value: Option<&[u8]>) -> Result, SynthesisError> +fn witness_u256(cs: CS, value: Option<&[u8]>) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { witness_bits(cs, value, 256, 0) } -fn witness_u252(cs: CS, value: Option<&[u8]>) -> Result, SynthesisError> +fn witness_u252(cs: CS, value: Option<&[u8]>) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { witness_bits(cs, value, 252, 4) } @@ -338,7 +337,7 @@ where #[ignore] fn test_sprout_constraints() { use bellman::gadgets::test::*; - use pairing::bls12_381::Bls12; + use bls12_381::Scalar; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; @@ -356,7 +355,7 @@ fn test_sprout_constraints() { } while test_vector.len() != 0 { - let mut cs = TestConstraintSystem::::new(); + let mut cs = TestConstraintSystem::::new(); let phi = Some(get_u256(&mut test_vector)); let rt = Some(get_u256(&mut test_vector)); @@ -462,7 +461,7 @@ fn test_sprout_constraints() { use bellman::gadgets::multipack; let expected_inputs = multipack::bytes_to_bits(&expected_inputs); - let expected_inputs = multipack::compute_multipacking::(&expected_inputs); + let expected_inputs = multipack::compute_multipacking(&expected_inputs); assert!(cs.verify(&expected_inputs)); } diff --git a/zcash_proofs/src/circuit/sprout/output.rs b/zcash_proofs/src/circuit/sprout/output.rs index 73a98514ac..6360f41223 100644 --- a/zcash_proofs/src/circuit/sprout/output.rs +++ b/zcash_proofs/src/circuit/sprout/output.rs @@ -1,6 +1,6 @@ use bellman::gadgets::boolean::Boolean; use bellman::{ConstraintSystem, SynthesisError}; -use pairing::Engine; +use ff::PrimeField; use super::commitment::note_comm; use super::prfs::*; @@ -11,7 +11,7 @@ pub struct OutputNote { } impl OutputNote { - pub fn compute( + pub fn compute( mut cs: CS, a_pk: Option, value: &NoteValue, @@ -21,8 +21,8 @@ impl OutputNote { nonce: bool, ) -> Result where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { let rho = prf_rho(cs.namespace(|| "rho"), phi, h_sig, nonce)?; diff --git a/zcash_proofs/src/circuit/sprout/prfs.rs b/zcash_proofs/src/circuit/sprout/prfs.rs index ea87b08d45..240538a9fe 100644 --- a/zcash_proofs/src/circuit/sprout/prfs.rs +++ b/zcash_proofs/src/circuit/sprout/prfs.rs @@ -1,9 +1,9 @@ use bellman::gadgets::boolean::Boolean; use bellman::gadgets::sha256::sha256_block_no_padding; use bellman::{ConstraintSystem, SynthesisError}; -use pairing::Engine; +use ff::PrimeField; -fn prf( +fn prf( cs: CS, a: bool, b: bool, @@ -13,8 +13,8 @@ fn prf( y: &[Boolean], ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { assert_eq!(x.len(), 252); assert_eq!(y.len(), 256); @@ -32,10 +32,10 @@ where sha256_block_no_padding(cs, &image) } -pub fn prf_a_pk(cs: CS, a_sk: &[Boolean]) -> Result, SynthesisError> +pub fn prf_a_pk(cs: CS, a_sk: &[Boolean]) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { prf( cs, @@ -50,40 +50,40 @@ where ) } -pub fn prf_nf( +pub fn prf_nf( cs: CS, a_sk: &[Boolean], rho: &[Boolean], ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { prf(cs, true, true, true, false, a_sk, rho) } -pub fn prf_pk( +pub fn prf_pk( cs: CS, a_sk: &[Boolean], h_sig: &[Boolean], nonce: bool, ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { prf(cs, false, nonce, false, false, a_sk, h_sig) } -pub fn prf_rho( +pub fn prf_rho( cs: CS, phi: &[Boolean], h_sig: &[Boolean], nonce: bool, ) -> Result, SynthesisError> where - E: Engine, - CS: ConstraintSystem, + Scalar: PrimeField, + CS: ConstraintSystem, { prf(cs, false, nonce, true, false, phi, h_sig) } diff --git a/zcash_proofs/src/constants.rs b/zcash_proofs/src/constants.rs new file mode 100644 index 0000000000..5727ae385d --- /dev/null +++ b/zcash_proofs/src/constants.rs @@ -0,0 +1,198 @@ +//! Various constants used for the Zcash proofs. + +use bls12_381::Scalar; +use ff::Field; +use group::{Curve, Group}; +use jubjub::ExtendedPoint; +use lazy_static::lazy_static; +use zcash_primitives::constants::{PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_GENERATORS}; + +/// The `d` constant of the twisted Edwards curve. +pub const EDWARDS_D: Scalar = Scalar::from_raw([ + 0x0106_5fd6_d634_3eb1, + 0x292d_7f6d_3757_9d26, + 0xf5fd_9207_e6bd_7fd4, + 0x2a93_18e7_4bfa_2b48, +]); + +/// The `A` constant of the birationally equivalent Montgomery curve. +pub const MONTGOMERY_A: Scalar = Scalar::from_raw([ + 0x0000_0000_0000_a002, + 0x0000_0000_0000_0000, + 0x0000_0000_0000_0000, + 0x0000_0000_0000_0000, +]); + +/// The scaling factor used for conversion to and from the Montgomery form. +pub const MONTGOMERY_SCALE: Scalar = Scalar::from_raw([ + 0x8f45_35f7_cf82_b8d9, + 0xce40_6970_3da8_8abd, + 0x31de_341e_77d7_64e5, + 0x2762_de61_e862_645e, +]); + +/// The number of chunks needed to represent a full scalar during fixed-base +/// exponentiation. +const FIXED_BASE_CHUNKS_PER_GENERATOR: usize = 84; + +/// Reference to a circuit version of a generator for fixed-base salar multiplication. +pub type FixedGenerator = &'static [Vec<(Scalar, Scalar)>]; + +/// Circuit version of a generator for fixed-base salar multiplication. +pub type FixedGeneratorOwned = Vec>; + +lazy_static! { + pub static ref PROOF_GENERATION_KEY_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::PROOF_GENERATION_KEY_GENERATOR); + + pub static ref NOTE_COMMITMENT_RANDOMNESS_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR); + + pub static ref NULLIFIER_POSITION_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::NULLIFIER_POSITION_GENERATOR); + + pub static ref VALUE_COMMITMENT_VALUE_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR); + + pub static ref VALUE_COMMITMENT_RANDOMNESS_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR); + + pub static ref SPENDING_KEY_GENERATOR: FixedGeneratorOwned = + generate_circuit_generator(zcash_primitives::constants::SPENDING_KEY_GENERATOR); + + /// The pre-computed window tables `[-4, 3, 2, 1, 1, 2, 3, 4]` of different magnitudes + /// of the Pedersen hash segment generators. + pub static ref PEDERSEN_CIRCUIT_GENERATORS: Vec>> = + generate_pedersen_circuit_generators(); +} + +/// Creates the 3-bit window table `[0, 1, ..., 8]` for different magnitudes of a fixed +/// generator. +fn generate_circuit_generator(mut gen: jubjub::SubgroupPoint) -> FixedGeneratorOwned { + let mut windows = vec![]; + + for _ in 0..FIXED_BASE_CHUNKS_PER_GENERATOR { + let mut coeffs = vec![(Scalar::zero(), Scalar::one())]; + let mut g = gen.clone(); + for _ in 0..7 { + let g_affine = jubjub::ExtendedPoint::from(g).to_affine(); + coeffs.push((g_affine.get_u(), g_affine.get_v())); + g += gen; + } + windows.push(coeffs); + + // gen = gen * 8 + gen = g; + } + + windows +} + +/// Returns the coordinates of this point's Montgomery curve representation, or `None` if +/// it is the point at infinity. +pub(crate) fn to_montgomery_coords(g: ExtendedPoint) -> Option<(Scalar, Scalar)> { + let g = g.to_affine(); + let (x, y) = (g.get_u(), g.get_v()); + + if y == Scalar::one() { + // The only solution for y = 1 is x = 0. (0, 1) is the neutral element, so we map + // this to the point at infinity. + None + } else { + // The map from a twisted Edwards curve is defined as + // (x, y) -> (u, v) where + // u = (1 + y) / (1 - y) + // v = u / x + // + // This mapping is not defined for y = 1 and for x = 0. + // + // We have that y != 1 above. If x = 0, the only + // solutions for y are 1 (contradiction) or -1. + if x.is_zero() { + // (0, -1) is the point of order two which is not + // the neutral element, so we map it to (0, 0) which is + // the only affine point of order 2. + Some((Scalar::zero(), Scalar::zero())) + } else { + // The mapping is defined as above. + // + // (x, y) -> (u, v) where + // u = (1 + y) / (1 - y) + // v = u / x + + let u = (Scalar::one() + y) * (Scalar::one() - y).invert().unwrap(); + let v = u * x.invert().unwrap(); + + // Scale it into the correct curve constants + // scaling factor = sqrt(4 / (a - d)) + Some((u, v * MONTGOMERY_SCALE)) + } + } +} + +/// Creates the 2-bit window table lookups for each 4-bit "chunk" in each segment of the +/// Pedersen hash. +fn generate_pedersen_circuit_generators() -> Vec>> { + // Process each segment + PEDERSEN_HASH_GENERATORS + .iter() + .cloned() + .map(|mut gen| { + let mut windows = vec![]; + + for _ in 0..PEDERSEN_HASH_CHUNKS_PER_GENERATOR { + // Create (x, y) coeffs for this chunk + let mut coeffs = vec![]; + let mut g = gen.clone(); + + // coeffs = g, g*2, g*3, g*4 + for _ in 0..4 { + coeffs.push( + to_montgomery_coords(g.into()) + .expect("we never encounter the point at infinity"), + ); + g += gen; + } + windows.push(coeffs); + + // Our chunks are separated by 2 bits to prevent overlap. + for _ in 0..4 { + gen = gen.double(); + } + } + + windows + }) + .collect() +} + +#[cfg(test)] +mod tests { + use ff::PrimeField; + + use super::*; + + #[test] + fn edwards_d() { + // d = -(10240/10241) + assert_eq!( + -Scalar::from_str("10240").unwrap() + * Scalar::from_str("10241").unwrap().invert().unwrap(), + EDWARDS_D + ); + } + + #[test] + fn montgomery_a() { + assert_eq!(Scalar::from_str("40962").unwrap(), MONTGOMERY_A); + } + + #[test] + fn montgomery_scale() { + // scaling factor = sqrt(4 / (a - d)) + assert_eq!( + MONTGOMERY_SCALE.square() * (-Scalar::one() - EDWARDS_D), + Scalar::from(4), + ); + } +} diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 1e8ceb2fe0..6d0476c578 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -3,30 +3,118 @@ //! `zcash_proofs` contains the zk-SNARK circuits used by Zcash, and the APIs for creating //! and verifying proofs. +#![cfg_attr(docsrs, feature(doc_cfg))] // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] use bellman::groth16::{prepare_verifying_key, Parameters, PreparedVerifyingKey, VerifyingKey}; -use pairing::bls12_381::Bls12; +use bls12_381::Bls12; use std::fs::File; use std::io::{self, BufReader}; use std::path::Path; +#[cfg(feature = "directories")] +use directories::BaseDirs; +#[cfg(feature = "directories")] +use std::path::PathBuf; + pub mod circuit; +mod constants; mod hashreader; pub mod sapling; pub mod sprout; -#[cfg(feature = "local-prover")] +#[cfg(any(feature = "local-prover", feature = "bundled-prover"))] +#[cfg_attr( + docsrs, + doc(cfg(any(feature = "local-prover", feature = "bundled-prover"))) +)] pub mod prover; +// Circuit names +#[cfg(feature = "local-prover")] +const SAPLING_SPEND_NAME: &str = "sapling-spend.params"; +#[cfg(feature = "local-prover")] +const SAPLING_OUTPUT_NAME: &str = "sapling-output.params"; + +// Circuit hashes +const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; +const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; +const SPROUT_HASH: &str = "e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a"; + +#[cfg(feature = "download-params")] +const DOWNLOAD_URL: &str = "https://download.z.cash/downloads"; + +/// Returns the default folder that the Zcash proving parameters are located in. +#[cfg(feature = "directories")] +#[cfg_attr(docsrs, doc(cfg(feature = "directories")))] +pub fn default_params_folder() -> Option { + BaseDirs::new().map(|base_dirs| { + if cfg!(any(windows, target_os = "macos")) { + base_dirs.data_dir().join("ZcashParams") + } else { + base_dirs.home_dir().join(".zcash-params") + } + }) +} + +/// Download the Zcash Sapling parameters, storing them in the default location. +/// +/// This mirrors the behaviour of the `fetch-params.sh` script from `zcashd`. +#[cfg(feature = "download-params")] +#[cfg_attr(docsrs, doc(cfg(feature = "download-params")))] +pub fn download_parameters() -> Result<(), minreq::Error> { + // Ensure that the default Zcash parameters location exists. + let params_dir = default_params_folder().ok_or(io::Error::new( + io::ErrorKind::Other, + "Could not load default params folder", + ))?; + std::fs::create_dir_all(¶ms_dir)?; + + let fetch_params = |name: &str, expected_hash: &str| -> Result<(), minreq::Error> { + use std::io::Write; + + // Download the parts directly (Sapling parameters are small enough for this). + let part_1 = minreq::get(format!("{}/{}.part.1", DOWNLOAD_URL, name)).send()?; + let part_2 = minreq::get(format!("{}/{}.part.2", DOWNLOAD_URL, name)).send()?; + + // Verify parameter file hash. + let hash = blake2b_simd::State::new() + .update(part_1.as_bytes()) + .update(part_2.as_bytes()) + .finalize() + .to_hex(); + if &hash != expected_hash { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!( + "{} failed validation (expected: {}, actual: {}, fetched {} bytes)", + name, + expected_hash, + hash, + part_1.as_bytes().len() + part_2.as_bytes().len() + ), + ) + .into()); + } + + // Write parameter file. + let mut f = File::create(params_dir.join(name))?; + f.write_all(part_1.as_bytes())?; + f.write_all(part_2.as_bytes())?; + Ok(()) + }; + + fetch_params(SAPLING_SPEND_NAME, SAPLING_SPEND_HASH)?; + fetch_params(SAPLING_OUTPUT_NAME, SAPLING_OUTPUT_HASH)?; + + Ok(()) +} + pub fn load_parameters( spend_path: &Path, - spend_hash: &str, output_path: &Path, - output_hash: &str, sprout_path: Option<&Path>, - sprout_hash: Option<&str>, ) -> ( Parameters, PreparedVerifyingKey, @@ -40,11 +128,27 @@ pub fn load_parameters( let sprout_fs = sprout_path.map(|p| File::open(p).expect("couldn't load Sprout groth16 parameters file")); - let mut spend_fs = hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, spend_fs)); - let mut output_fs = - hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, output_fs)); - let mut sprout_fs = - sprout_fs.map(|fs| hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, fs))); + parse_parameters( + BufReader::with_capacity(1024 * 1024, spend_fs), + BufReader::with_capacity(1024 * 1024, output_fs), + sprout_fs.map(|fs| BufReader::with_capacity(1024 * 1024, fs)), + ) +} + +fn parse_parameters( + spend_fs: R, + output_fs: R, + sprout_fs: Option, +) -> ( + Parameters, + PreparedVerifyingKey, + Parameters, + PreparedVerifyingKey, + Option>, +) { + let mut spend_fs = hashreader::HashReader::new(spend_fs); + let mut output_fs = hashreader::HashReader::new(output_fs); + let mut sprout_fs = sprout_fs.map(|fs| hashreader::HashReader::new(fs)); // Deserialize params let spend_params = Parameters::::read(&mut spend_fs, false) @@ -74,15 +178,18 @@ pub fn load_parameters( .expect("couldn't finish reading Sprout groth16 parameter file"); } - if spend_fs.into_hash() != spend_hash { + if spend_fs.into_hash() != SAPLING_SPEND_HASH { panic!("Sapling spend parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } - if output_fs.into_hash() != output_hash { + if output_fs.into_hash() != SAPLING_OUTPUT_HASH { panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } - if sprout_fs.map(|fs| fs.into_hash()) != sprout_hash.map(|h| h.to_owned()) { + if sprout_fs + .map(|fs| fs.into_hash() != SPROUT_HASH) + .unwrap_or(false) + { panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); } diff --git a/zcash_proofs/src/prover.rs b/zcash_proofs/src/prover.rs index c4608f7030..f84b22279a 100644 --- a/zcash_proofs/src/prover.rs +++ b/zcash_proofs/src/prover.rs @@ -1,26 +1,25 @@ //! Abstractions over the proving system and parameters for ease of use. use bellman::groth16::{Parameters, PreparedVerifyingKey}; -use directories::BaseDirs; -use pairing::bls12_381::{Bls12, Fr}; -use std::path::Path; -use zcash_primitives::{ - jubjub::{edwards, fs::Fs, Unknown}, - primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, -}; +use bls12_381::Bls12; use zcash_primitives::{ merkle_tree::MerklePath, + primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed}, prover::TxProver, redjubjub::{PublicKey, Signature}, sapling::Node, transaction::components::{Amount, GROTH_PROOF_SIZE}, - JUBJUB, }; -use crate::{load_parameters, sapling::SaplingProvingContext}; +use crate::sapling::SaplingProvingContext; + +#[cfg(feature = "local-prover")] +use crate::{default_params_folder, load_parameters, SAPLING_OUTPUT_NAME, SAPLING_SPEND_NAME}; +#[cfg(feature = "local-prover")] +use std::path::Path; -const SAPLING_SPEND_HASH: &str = "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c"; -const SAPLING_OUTPUT_HASH: &str = "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028"; +#[cfg(feature = "bundled-prover")] +use crate::parse_parameters; /// An implementation of [`TxProver`] using Sapling Spend and Output parameters from /// locally-accessible paths. @@ -49,15 +48,11 @@ impl LocalTxProver { /// /// This function will panic if the paths do not point to valid parameter files with /// the expected hashes. + #[cfg(feature = "local-prover")] + #[cfg_attr(docsrs, doc(cfg(feature = "local-prover")))] pub fn new(spend_path: &Path, output_path: &Path) -> Self { - let (spend_params, spend_vk, output_params, _, _) = load_parameters( - spend_path, - SAPLING_SPEND_HASH, - output_path, - SAPLING_OUTPUT_HASH, - None, - None, - ); + let (spend_params, spend_vk, output_params, _, _) = + load_parameters(spend_path, output_path, None); LocalTxProver { spend_params, spend_vk, @@ -86,19 +81,14 @@ impl LocalTxProver { /// /// This function will panic if the parameters in the default local location do not /// have the expected hashes. + #[cfg(feature = "local-prover")] + #[cfg_attr(docsrs, doc(cfg(feature = "local-prover")))] pub fn with_default_location() -> Option { - let base_dirs = BaseDirs::new()?; - let unix_params_dir = base_dirs.home_dir().join(".zcash-params"); - let win_osx_params_dir = base_dirs.data_dir().join("ZcashParams"); - let (spend_path, output_path) = if unix_params_dir.exists() { + let params_dir = default_params_folder()?; + let (spend_path, output_path) = if params_dir.exists() { ( - unix_params_dir.join("sapling-spend.params"), - unix_params_dir.join("sapling-output.params"), - ) - } else if win_osx_params_dir.exists() { - ( - win_osx_params_dir.join("sapling-spend.params"), - win_osx_params_dir.join("sapling-output.params"), + params_dir.join(SAPLING_SPEND_NAME), + params_dir.join(SAPLING_OUTPUT_NAME), ) } else { return None; @@ -109,6 +99,24 @@ impl LocalTxProver { Some(LocalTxProver::new(&spend_path, &output_path)) } + + /// Creates a `LocalTxProver` using Sapling parameters bundled inside the binary. + /// + /// This requires the `bundled-prover` feature, which will increase the binary size by + /// around 50 MiB. + #[cfg(feature = "bundled-prover")] + #[cfg_attr(docsrs, doc(cfg(feature = "bundled-prover")))] + pub fn bundled() -> Self { + let (spend_buf, output_buf) = wagyu_zcash_parameters::load_sapling_parameters(); + let (spend_params, spend_vk, output_params, _, _) = + parse_parameters(&spend_buf[..], &output_buf[..], None); + + LocalTxProver { + spend_params, + spend_vk, + output_params, + } + } } impl TxProver for LocalTxProver { @@ -121,32 +129,24 @@ impl TxProver for LocalTxProver { fn spend_proof( &self, ctx: &mut Self::SaplingProvingContext, - proof_generation_key: ProofGenerationKey, + proof_generation_key: ProofGenerationKey, diversifier: Diversifier, - rcm: Fs, - ar: Fs, + rseed: Rseed, + ar: jubjub::Fr, value: u64, - anchor: Fr, + anchor: bls12_381::Scalar, merkle_path: MerklePath, - ) -> Result< - ( - [u8; GROTH_PROOF_SIZE], - edwards::Point, - PublicKey, - ), - (), - > { + ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()> { let (proof, cv, rk) = ctx.spend_proof( proof_generation_key, diversifier, - rcm, + rseed, ar, value, anchor, merkle_path, &self.spend_params, &self.spend_vk, - &JUBJUB, )?; let mut zkproof = [0u8; GROTH_PROOF_SIZE]; @@ -160,19 +160,12 @@ impl TxProver for LocalTxProver { fn output_proof( &self, ctx: &mut Self::SaplingProvingContext, - esk: Fs, - payment_address: PaymentAddress, - rcm: Fs, + esk: jubjub::Fr, + payment_address: PaymentAddress, + rcm: jubjub::Fr, value: u64, - ) -> ([u8; GROTH_PROOF_SIZE], edwards::Point) { - let (proof, cv) = ctx.output_proof( - esk, - payment_address, - rcm, - value, - &self.output_params, - &JUBJUB, - ); + ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint) { + let (proof, cv) = ctx.output_proof(esk, payment_address, rcm, value, &self.output_params); let mut zkproof = [0u8; GROTH_PROOF_SIZE]; proof @@ -188,6 +181,6 @@ impl TxProver for LocalTxProver { value_balance: Amount, sighash: &[u8; 32], ) -> Result { - ctx.binding_sig(value_balance, sighash, &JUBJUB) + ctx.binding_sig(value_balance, sighash) } } diff --git a/zcash_proofs/src/sapling/mod.rs b/zcash_proofs/src/sapling/mod.rs index 60ffd9bdec..ad28252fe7 100644 --- a/zcash_proofs/src/sapling/mod.rs +++ b/zcash_proofs/src/sapling/mod.rs @@ -1,10 +1,8 @@ //! Helpers for creating Sapling proofs. -use pairing::bls12_381::Bls12; -use zcash_primitives::jubjub::{ - edwards, fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown, +use zcash_primitives::{ + constants::VALUE_COMMITMENT_VALUE_GENERATOR, transaction::components::Amount, }; -use zcash_primitives::transaction::components::Amount; mod prover; mod verifier; @@ -13,10 +11,7 @@ pub use self::prover::SaplingProvingContext; pub use self::verifier::SaplingVerificationContext; // This function computes `value` in the exponent of the value commitment base -fn compute_value_balance( - value: Amount, - params: &JubjubBls12, -) -> Option> { +fn compute_value_balance(value: Amount) -> Option { // Compute the absolute value (failing if -i64::MAX is // the value) let abs = match i64::from(value).checked_abs() { @@ -28,13 +23,11 @@ fn compute_value_balance( let is_negative = value.is_negative(); // Compute it in the exponent - let mut value_balance = params - .generator(FixedGenerators::ValueCommitmentValue) - .mul(FsRepr::from(abs), params); + let mut value_balance = VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Fr::from(abs); // Negate if necessary if is_negative { - value_balance = value_balance.negate(); + value_balance = -value_balance; } // Convert to unknown order point diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index f7b0373433..e41e5ddcfd 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -2,16 +2,15 @@ use bellman::{ gadgets::multipack, groth16::{create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof}, }; +use bls12_381::Bls12; use ff::Field; -use pairing::bls12_381::{Bls12, Fr}; +use group::{Curve, GroupEncoding}; use rand_core::OsRng; use std::ops::{AddAssign, Neg}; use zcash_primitives::{ - jubjub::{edwards, fs::Fs, FixedGenerators, JubjubBls12, Unknown}, - primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ValueCommitment}, -}; -use zcash_primitives::{ + constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, merkle_tree::MerklePath, + primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment}, redjubjub::{PrivateKey, PublicKey, Signature}, sapling::Node, transaction::components::Amount, @@ -22,17 +21,17 @@ use crate::circuit::sapling::{Output, Spend}; /// A context object for creating the Sapling components of a Zcash transaction. pub struct SaplingProvingContext { - bsk: Fs, + bsk: jubjub::Fr, // (sum of the Spend value commitments) - (sum of the Output value commitments) - cv_sum: edwards::Point, + cv_sum: jubjub::ExtendedPoint, } impl SaplingProvingContext { /// Construct a new context to be used with a single transaction. pub fn new() -> Self { SaplingProvingContext { - bsk: Fs::zero(), - cv_sum: edwards::Point::zero(), + bsk: jubjub::Fr::zero(), + cv_sum: jubjub::ExtendedPoint::identity(), } } @@ -41,29 +40,21 @@ impl SaplingProvingContext { /// inside the context for later use. pub fn spend_proof( &mut self, - proof_generation_key: ProofGenerationKey, + proof_generation_key: ProofGenerationKey, diversifier: Diversifier, - rcm: Fs, - ar: Fs, + rseed: Rseed, + ar: jubjub::Fr, value: u64, - anchor: Fr, + anchor: bls12_381::Scalar, merkle_path: MerklePath, proving_key: &Parameters, verifying_key: &PreparedVerifyingKey, - params: &JubjubBls12, - ) -> Result< - ( - Proof, - edwards::Point, - PublicKey, - ), - (), - > { + ) -> Result<(Proof, jubjub::ExtendedPoint, PublicKey), ()> { // Initialize secure RNG let mut rng = OsRng; // We create the randomness of the value commitment - let rcv = Fs::random(&mut rng); + let rcv = jubjub::Fr::random(&mut rng); // Accumulate the value commitment randomness in the context { @@ -75,45 +66,37 @@ impl SaplingProvingContext { } // Construct the value commitment - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value, randomness: rcv, }; // Construct the viewing key - let viewing_key = proof_generation_key.to_viewing_key(params); + let viewing_key = proof_generation_key.to_viewing_key(); // Construct the payment address with the viewing key / diversifier - let payment_address = viewing_key - .to_payment_address(diversifier, params) - .ok_or(())?; + let payment_address = viewing_key.to_payment_address(diversifier).ok_or(())?; // This is the result of the re-randomization, we compute it for the caller - let rk = PublicKey::(proof_generation_key.ak.clone().into()).randomize( - ar, - FixedGenerators::SpendingKeyGenerator, - params, - ); + let rk = + PublicKey(proof_generation_key.ak.clone().into()).randomize(ar, SPENDING_KEY_GENERATOR); // Let's compute the nullifier while we have the position let note = Note { value, - g_d: diversifier - .g_d::(params) - .expect("was a valid diversifier before"), + g_d: diversifier.g_d().expect("was a valid diversifier before"), pk_d: payment_address.pk_d().clone(), - r: rcm, + rseed, }; - let nullifier = note.nf(&viewing_key, merkle_path.position, params); + let nullifier = note.nf(&viewing_key, merkle_path.position); // We now have the full witness for our circuit let instance = Spend { - params, value_commitment: Some(value_commitment.clone()), proof_generation_key: Some(proof_generation_key), payment_address: Some(payment_address), - commitment_randomness: Some(rcm), + commitment_randomness: Some(note.rcm()), ar: Some(ar), auth_path: merkle_path .auth_path @@ -129,23 +112,25 @@ impl SaplingProvingContext { // Try to verify the proof: // Construct public input for circuit - let mut public_input = [Fr::zero(); 7]; + let mut public_input = [bls12_381::Scalar::zero(); 7]; { - let (x, y) = rk.0.to_xy(); - public_input[0] = x; - public_input[1] = y; + let affine = rk.0.to_affine(); + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[0] = u; + public_input[1] = v; } { - let (x, y) = value_commitment.cm(params).to_xy(); - public_input[2] = x; - public_input[3] = y; + let affine = jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[2] = u; + public_input[3] = v; } public_input[4] = anchor; // Add the nullifier through multiscalar packing { let nullifier = multipack::bytes_to_bits_le(&nullifier); - let nullifier = multipack::compute_multipacking::(&nullifier); + let nullifier = multipack::compute_multipacking(&nullifier); assert_eq!(nullifier.len(), 2); @@ -154,27 +139,13 @@ impl SaplingProvingContext { } // Verify the proof - match verify_proof(verifying_key, &proof, &public_input[..]) { - // No error, and proof verification successful - Ok(true) => {} - - // Any other case - _ => { - return Err(()); - } - } + verify_proof(verifying_key, &proof, &public_input[..]).map_err(|_| ())?; // Compute value commitment - let value_commitment: edwards::Point = value_commitment.cm(params).into(); + let value_commitment: jubjub::ExtendedPoint = value_commitment.commitment().into(); // Accumulate the value commitment in the context - { - let mut tmp = value_commitment.clone(); - tmp = tmp.add(&self.cv_sum, params); - - // Update the context - self.cv_sum = tmp; - } + self.cv_sum += value_commitment; Ok((proof, value_commitment, rk)) } @@ -184,20 +155,19 @@ impl SaplingProvingContext { /// for later use. pub fn output_proof( &mut self, - esk: Fs, - payment_address: PaymentAddress, - rcm: Fs, + esk: jubjub::Fr, + payment_address: PaymentAddress, + rcm: jubjub::Fr, value: u64, proving_key: &Parameters, - params: &JubjubBls12, - ) -> (Proof, edwards::Point) { + ) -> (Proof, jubjub::ExtendedPoint) { // Initialize secure RNG let mut rng = OsRng; // We construct ephemeral randomness for the value commitment. This // randomness is not given back to the caller, but the synthetic // blinding factor `bsk` is accumulated in the context. - let rcv = Fs::random(&mut rng); + let rcv = jubjub::Fr::random(&mut rng); // Accumulate the value commitment randomness in the context { @@ -209,14 +179,13 @@ impl SaplingProvingContext { } // Construct the value commitment for the proof instance - let value_commitment = ValueCommitment:: { + let value_commitment = ValueCommitment { value, randomness: rcv, }; // We now have a full witness for the output proof. let instance = Output { - params, value_commitment: Some(value_commitment.clone()), payment_address: Some(payment_address.clone()), commitment_randomness: Some(rcm), @@ -228,69 +197,52 @@ impl SaplingProvingContext { create_random_proof(instance, proving_key, &mut rng).expect("proving should not fail"); // Compute the actual value commitment - let value_commitment: edwards::Point = value_commitment.cm(params).into(); + let value_commitment: jubjub::ExtendedPoint = value_commitment.commitment().into(); // Accumulate the value commitment in the context. We do this to check internal consistency. - { - let mut tmp = value_commitment.clone(); - tmp = tmp.negate(); // Outputs subtract from the total. - tmp = tmp.add(&self.cv_sum, params); - - // Update the context - self.cv_sum = tmp; - } + self.cv_sum -= value_commitment; // Outputs subtract from the total. (proof, value_commitment) } /// Create the bindingSig for a Sapling transaction. All calls to spend_proof() /// and output_proof() must be completed before calling this function. - pub fn binding_sig( - &self, - value_balance: Amount, - sighash: &[u8; 32], - params: &JubjubBls12, - ) -> Result { + pub fn binding_sig(&self, value_balance: Amount, sighash: &[u8; 32]) -> Result { // Initialize secure RNG let mut rng = OsRng; // Grab the current `bsk` from the context - let bsk = PrivateKey::(self.bsk); + let bsk = PrivateKey(self.bsk); // Grab the `bvk` using DerivePublic. - let bvk = PublicKey::from_private(&bsk, FixedGenerators::ValueCommitmentRandomness, params); + let bvk = PublicKey::from_private(&bsk, VALUE_COMMITMENT_RANDOMNESS_GENERATOR); // In order to check internal consistency, let's use the accumulated value // commitments (as the verifier would) and apply value_balance to compare // against our derived bvk. { // Compute value balance - let mut value_balance = compute_value_balance(value_balance, params).ok_or(())?; + let value_balance = compute_value_balance(value_balance).ok_or(())?; // Subtract value_balance from cv_sum to get final bvk - value_balance = value_balance.negate(); - let mut tmp = self.cv_sum.clone(); - tmp = tmp.add(&value_balance, params); + let final_bvk = self.cv_sum - value_balance; // The result should be the same, unless the provided valueBalance is wrong. - if bvk.0 != tmp { + if bvk.0 != final_bvk { return Err(()); } } // Construct signature message let mut data_to_be_signed = [0u8; 64]; - bvk.0 - .write(&mut data_to_be_signed[0..32]) - .expect("message buffer should be 32 bytes"); + data_to_be_signed[0..32].copy_from_slice(&bvk.0.to_bytes()); (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash[..]); // Sign Ok(bsk.sign( &data_to_be_signed, &mut rng, - FixedGenerators::ValueCommitmentRandomness, - params, + VALUE_COMMITMENT_RANDOMNESS_GENERATOR, )) } } diff --git a/zcash_proofs/src/sapling/verifier.rs b/zcash_proofs/src/sapling/verifier.rs index b8869129a9..fcbb86c596 100644 --- a/zcash_proofs/src/sapling/verifier.rs +++ b/zcash_proofs/src/sapling/verifier.rs @@ -2,31 +2,27 @@ use bellman::{ gadgets::multipack, groth16::{verify_proof, PreparedVerifyingKey, Proof}, }; -use ff::Field; -use pairing::bls12_381::{Bls12, Fr}; -use zcash_primitives::jubjub::{edwards, FixedGenerators, JubjubBls12, Unknown}; +use bls12_381::Bls12; +use group::{Curve, GroupEncoding}; use zcash_primitives::{ + constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, redjubjub::{PublicKey, Signature}, transaction::components::Amount, }; use super::compute_value_balance; -fn is_small_order(p: &edwards::Point, params: &JubjubBls12) -> bool { - p.double(params).double(params).double(params) == edwards::Point::zero() -} - /// A context object for verifying the Sapling components of a Zcash transaction. pub struct SaplingVerificationContext { // (sum of the Spend value commitments) - (sum of the Output value commitments) - cv_sum: edwards::Point, + cv_sum: jubjub::ExtendedPoint, } impl SaplingVerificationContext { /// Construct a new context to be used with a single transaction. pub fn new() -> Self { SaplingVerificationContext { - cv_sum: edwards::Point::zero(), + cv_sum: jubjub::ExtendedPoint::identity(), } } @@ -34,70 +30,55 @@ impl SaplingVerificationContext { /// accumulating its value commitment inside the context for later use. pub fn check_spend( &mut self, - cv: edwards::Point, - anchor: Fr, + cv: jubjub::ExtendedPoint, + anchor: bls12_381::Scalar, nullifier: &[u8; 32], - rk: PublicKey, + rk: PublicKey, sighash_value: &[u8; 32], spend_auth_sig: Signature, zkproof: Proof, verifying_key: &PreparedVerifyingKey, - params: &JubjubBls12, ) -> bool { - if is_small_order(&cv, params) { - return false; - } - - if is_small_order(&rk.0, params) { + if (cv.is_small_order() | rk.0.is_small_order()).into() { return false; } // Accumulate the value commitment in the context - { - let mut tmp = cv.clone(); - tmp = tmp.add(&self.cv_sum, params); - - // Update the context - self.cv_sum = tmp; - } + self.cv_sum += cv; // Grab the nullifier as a sequence of bytes let nullifier = &nullifier[..]; // Compute the signature's message for rk/spend_auth_sig let mut data_to_be_signed = [0u8; 64]; - rk.0.write(&mut data_to_be_signed[0..32]) - .expect("message buffer should be 32 bytes"); + data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes()); (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]); // Verify the spend_auth_sig - if !rk.verify( - &data_to_be_signed, - &spend_auth_sig, - FixedGenerators::SpendingKeyGenerator, - params, - ) { + if !rk.verify(&data_to_be_signed, &spend_auth_sig, SPENDING_KEY_GENERATOR) { return false; } // Construct public input for circuit - let mut public_input = [Fr::zero(); 7]; + let mut public_input = [bls12_381::Scalar::zero(); 7]; { - let (x, y) = rk.0.to_xy(); - public_input[0] = x; - public_input[1] = y; + let affine = rk.0.to_affine(); + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[0] = u; + public_input[1] = v; } { - let (x, y) = cv.to_xy(); - public_input[2] = x; - public_input[3] = y; + let affine = cv.to_affine(); + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[2] = u; + public_input[3] = v; } public_input[4] = anchor; // Add the nullifier through multiscalar packing { let nullifier = multipack::bytes_to_bits_le(nullifier); - let nullifier = multipack::compute_multipacking::(&nullifier); + let nullifier = multipack::compute_multipacking(&nullifier); assert_eq!(nullifier.len(), 2); @@ -106,66 +87,44 @@ impl SaplingVerificationContext { } // Verify the proof - match verify_proof(verifying_key, &zkproof, &public_input[..]) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } + verify_proof(verifying_key, &zkproof, &public_input[..]).is_ok() } /// Perform consensus checks on a Sapling OutputDescription, while /// accumulating its value commitment inside the context for later use. pub fn check_output( &mut self, - cv: edwards::Point, - cm: Fr, - epk: edwards::Point, + cv: jubjub::ExtendedPoint, + cmu: bls12_381::Scalar, + epk: jubjub::ExtendedPoint, zkproof: Proof, verifying_key: &PreparedVerifyingKey, - params: &JubjubBls12, ) -> bool { - if is_small_order(&cv, params) { - return false; - } - - if is_small_order(&epk, params) { + if (cv.is_small_order() | epk.is_small_order()).into() { return false; } // Accumulate the value commitment in the context - { - let mut tmp = cv.clone(); - tmp = tmp.negate(); // Outputs subtract from the total. - tmp = tmp.add(&self.cv_sum, params); - - // Update the context - self.cv_sum = tmp; - } + self.cv_sum -= cv; // Construct public input for circuit - let mut public_input = [Fr::zero(); 5]; + let mut public_input = [bls12_381::Scalar::zero(); 5]; { - let (x, y) = cv.to_xy(); - public_input[0] = x; - public_input[1] = y; + let affine = cv.to_affine(); + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[0] = u; + public_input[1] = v; } { - let (x, y) = epk.to_xy(); - public_input[2] = x; - public_input[3] = y; + let affine = epk.to_affine(); + let (u, v) = (affine.get_u(), affine.get_v()); + public_input[2] = u; + public_input[3] = v; } - public_input[4] = cm; + public_input[4] = cmu; // Verify the proof - match verify_proof(verifying_key, &zkproof, &public_input[..]) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } + verify_proof(verifying_key, &zkproof, &public_input[..]).is_ok() } /// Perform consensus checks on the valueBalance and bindingSig parts of a @@ -176,34 +135,29 @@ impl SaplingVerificationContext { value_balance: Amount, sighash_value: &[u8; 32], binding_sig: Signature, - params: &JubjubBls12, ) -> bool { // Obtain current cv_sum from the context let mut bvk = PublicKey(self.cv_sum.clone()); // Compute value balance - let mut value_balance = match compute_value_balance(value_balance, params) { + let value_balance = match compute_value_balance(value_balance) { Some(a) => a, None => return false, }; // Subtract value_balance from current cv_sum to get final bvk - value_balance = value_balance.negate(); - bvk.0 = bvk.0.add(&value_balance, params); + bvk.0 -= value_balance; // Compute the signature's message for bvk/binding_sig let mut data_to_be_signed = [0u8; 64]; - bvk.0 - .write(&mut data_to_be_signed[0..32]) - .expect("bvk is 32 bytes"); + data_to_be_signed[0..32].copy_from_slice(&bvk.0.to_bytes()); (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]); // Verify the binding_sig bvk.verify( &data_to_be_signed, &binding_sig, - FixedGenerators::ValueCommitmentRandomness, - params, + VALUE_COMMITMENT_RANDOMNESS_GENERATOR, ) } } diff --git a/zcash_proofs/src/sprout.rs b/zcash_proofs/src/sprout.rs index 30ec8217f1..475e2fb0ad 100644 --- a/zcash_proofs/src/sprout.rs +++ b/zcash_proofs/src/sprout.rs @@ -4,7 +4,7 @@ use bellman::{ gadgets::multipack, groth16::{self, create_random_proof, Parameters, PreparedVerifyingKey, Proof}, }; -use pairing::bls12_381::Bls12; +use bls12_381::Bls12; use rand_core::OsRng; use crate::circuit::sprout::*; @@ -161,7 +161,7 @@ pub fn verify_proof( public_input.extend(&vpub_new.to_le_bytes()); let public_input = multipack::bytes_to_bits(&public_input); - let public_input = multipack::compute_multipacking::(&public_input); + let public_input = multipack::compute_multipacking(&public_input); let proof = match Proof::read(&proof[..]) { Ok(p) => p, @@ -169,11 +169,5 @@ pub fn verify_proof( }; // Verify the proof - match groth16::verify_proof(verifying_key, &proof, &public_input[..]) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } + groth16::verify_proof(verifying_key, &proof, &public_input[..]).is_ok() }