diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f956c61f0..551aa7f9c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,21 @@ jobs: command: test args: --verbose --release --all --all-features + example: + name: Examples on ubuntu + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + override: false + - name: Run examples + run: + cargo run --example serialization + cargo run --example shuffle + cargo run --example shuffle_api + fmt: name: Rustfmt timeout-minutes: 30 diff --git a/.github/workflows/lints-stable.yml b/.github/workflows/lints-stable.yml index 7666dae1df..0add558c76 100644 --- a/.github/workflows/lints-stable.yml +++ b/.github/workflows/lints-stable.yml @@ -5,7 +5,7 @@ on: pull_request jobs: clippy: - name: Clippy (1.56.1) + name: Clippy (1.73.0) timeout-minutes: 30 runs-on: ubuntu-latest @@ -18,6 +18,6 @@ jobs: - name: Run clippy uses: actions-rs/clippy-check@v1 with: - name: Clippy (1.56.1) + name: Clippy (1.73.0) token: ${{ secrets.GITHUB_TOKEN }} args: --all-features --all-targets -- -D warnings diff --git a/Cargo.toml b/Cargo.toml index fb0ba23519..4f950a61da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,9 +2,8 @@ members = [ "halo2", "halo2_proofs", - "arithmetic/curves", - "primitives/poseidon" ] +resolver = "2" [profile.dev] opt-level = 3 diff --git a/arithmetic/curves/.github/workflows/ci.yml b/arithmetic/curves/.github/workflows/ci.yml deleted file mode 100644 index 7f3475823a..0000000000 --- a/arithmetic/curves/.github/workflows/ci.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: CI Check -on: - pull_request: - push: - branches: - - main - -jobs: - build: - if: github.event.pull_request.draft == false - name: Build - runs-on: ubuntu-latest - strategy: - matrix: - include: - - rust: 1.63.0 - feature: default - - rust: nightly - feature: asm - - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - override: true - toolchain: ${{ matrix.rust }} - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --features ${{ matrix.feature }} - - test: - if: github.event.pull_request.draft == false - name: Test - runs-on: ubuntu-latest - strategy: - matrix: - include: - - rust: 1.63.0 - feature: default - - rust: nightly - feature: asm - - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - override: true - toolchain: ${{ matrix.rust }} - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose --release --all --features ${{ matrix.feature }} - - fmt: - if: github.event.pull_request.draft == false - name: Rustfmt - timeout-minutes: 30 - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - - run: rustup component add rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - bench: - if: github.event.pull_request.draft == false - name: Bench - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - override: true - toolchain: nightly - - name: Bench arithmetic - uses: actions-rs/cargo@v1 - with: - command: test - args: --profile bench test_field -- --nocapture - - name: Bench assembly arithmetic - uses: actions-rs/cargo@v1 - with: - command: test - args: --profile bench test_field --features asm -- --nocapture diff --git a/arithmetic/curves/.gitignore b/arithmetic/curves/.gitignore deleted file mode 100644 index bceff729ff..0000000000 --- a/arithmetic/curves/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/target -Cargo.lock -**/*.rs.bk -.vscode -**/*.html \ No newline at end of file diff --git a/arithmetic/curves/Cargo.toml b/arithmetic/curves/Cargo.toml deleted file mode 100644 index 6d30863d8c..0000000000 --- a/arithmetic/curves/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "halo2curves" -version = "0.3.1" -authors = [ - "Sean Bowe ", - "Jack Grigg ", - "Alex Vlasov ", - "Alex Gluchowski " -] -license = "MIT/Apache-2.0" -edition = "2018" -readme = "README.md" -description = "Elliptic curve implementations and wrappers for halo2 library" - -[dev-dependencies] -criterion = { version = "0.3", features = ["html_reports"] } -rand_xorshift = "0.3" -ark-std = { version = "0.3", features = ["print-trace"] } - -[dependencies] -subtle = "2.4" -ff = "0.12" -group = "0.12" -pasta_curves = "0.4.1" -static_assertions = "1.1.0" -rand = "0.8" -rand_core = { version = "0.6", default-features = false } -lazy_static = { version = "1.4.0"} -num-bigint = "0.4.3" -num-traits = "0.2" -serde = { version = "1.0", default-features = false, features = ["derive"] } - -[features] -default = [] -asm = [] -prefetch = [] diff --git a/arithmetic/curves/LICENSE-APACHE b/arithmetic/curves/LICENSE-APACHE deleted file mode 100644 index f8e5e5ea03..0000000000 --- a/arithmetic/curves/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. \ No newline at end of file diff --git a/arithmetic/curves/LICENSE-MIT b/arithmetic/curves/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/arithmetic/curves/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/arithmetic/curves/README.md b/arithmetic/curves/README.md deleted file mode 100644 index bf431dac4c..0000000000 --- a/arithmetic/curves/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# BN256 pairing - -BN256 pairing library that implements original traits from `zkcrypto`, - -* [`zkcrypto/ff`](https://github.com/zkcrypto/ff) -* [`zkcrypto/group`](https://github.com/zkcrypto/group) -* [`zkcrypto/pairing`](https://github.com/zkcrypto/pairing) - -and plus - -`FieldExt`, `CurveExt` [traits](https://github.com/zcash/pasta_curves/tree/main/src/arithmetic) that are used in `halo2` library. - -This implementation is mostly ported from [matterlabs/pairing](https://github.com/matter-labs/pairing/tree/master/src/bn256) and [zkcrypto/bls12-381](https://github.com/zkcrypto/bls12_381). - -## Bench - -No assembly -``` -$ cargo test --profile bench test_field -- --nocapture -``` - -Assembly (returns rust nightly) -``` -$ cargo test --profile bench test_field --features asm -- --nocapture -``` diff --git a/arithmetic/curves/build.rs b/arithmetic/curves/build.rs deleted file mode 100644 index 7ec963436c..0000000000 --- a/arithmetic/curves/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - #[cfg(feature = "asm")] - if std::env::consts::ARCH != "x86_64" { - eprintln!("Currently feature `asm` can only be enabled on x86_64 arch."); - std::process::exit(1); - } -} diff --git a/arithmetic/curves/src/arithmetic.rs b/arithmetic/curves/src/arithmetic.rs deleted file mode 100644 index 388a422ba9..0000000000 --- a/arithmetic/curves/src/arithmetic.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! This module provides common utilities, traits and structures for group and -//! field arithmetic. -//! -//! This module is temporary, and the extension traits defined here are expected to be -//! upstreamed into the `ff` and `group` crates after some refactoring. - -use subtle::{Choice, ConditionallySelectable, CtOption}; - -pub trait CurveAffineExt: pasta_curves::arithmetic::CurveAffine { - fn batch_add( - points: &mut [Self], - output_indices: &[u32], - num_points: usize, - offset: usize, - bases: &[Self], - base_positions: &[u32], - ); - - /// Unlike the `Coordinates` trait, this just returns the raw affine coordinates without checking `is_on_curve` - fn into_coordinates(self) -> (Self::Base, Self::Base) { - // fallback implementation - let coordinates = self.coordinates().unwrap(); - (*coordinates.x(), *coordinates.y()) - } -} - -pub(crate) fn sqrt_tonelli_shanks>( - f: &F, - tm1d2: S, -) -> CtOption { - use subtle::ConstantTimeEq; - - // w = self^((t - 1) // 2) - let w = f.pow_vartime(tm1d2); - - let mut v = F::S; - let mut x = w * f; - let mut b = x * w; - - // Initialize z as the 2^S root of unity. - let mut z = F::root_of_unity(); - - for max_v in (1..=F::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(&F::one()); - let squared = F::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = F::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = F::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 = F::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * z; - x = F::conditional_select(&result, &x, b.ct_eq(&F::one())); - z = z.square(); - b *= z; - v = k; - } - - CtOption::new( - x, - (x * x).ct_eq(f), // Only return Some if it's the square root. - ) -} - -/// Compute a + b + carry, returning the result and the new carry over. -/// The carry input must be 0 or 1; otherwise the behavior is undefined. -/// The carryOut output is guaranteed to be 0 or 1. -#[inline(always)] -pub(crate) const fn adc(a: u64, b: u64, carry: bool) -> (u64, bool) { - a.carrying_add(b, carry) -} - -/// Compute a - b - borrow, returning the result and the new borrow. -#[inline(always)] -pub(crate) const fn sbb(a: u64, b: u64, borrow: bool) -> (u64, bool) { - a.borrowing_sub(b, borrow) -} - -/// Compute a + (b * c), returning the result and the new carry over. -// Alias madd1 -#[inline(always)] -pub(crate) const fn macx(a: u64, b: u64, c: u64) -> (u64, u64) { - b.carrying_mul(c, a) -} - -/// Compute a + (b * c) + carry, returning the result and the new carry over. -// Alias madd2 -#[inline(always)] -pub(crate) const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { - let (lo, hi) = b.carrying_mul(c, a); - let (lo, carry) = lo.overflowing_add(carry); - (lo, hi + carry as u64) -} - -/// Compute a * b, returning the result. -#[inline(always)] -pub(crate) fn mul_512(a: [u64; 4], b: [u64; 4]) -> [u64; 8] { - let (r0, carry) = macx(0, a[0], b[0]); - let (r1, carry) = macx(carry, a[0], b[1]); - let (r2, carry) = macx(carry, a[0], b[2]); - let (r3, carry_out) = macx(carry, a[0], b[3]); - - let (r1, carry) = macx(r1, a[1], b[0]); - let (r2, carry) = mac(r2, a[1], b[1], carry); - let (r3, carry) = mac(r3, a[1], b[2], carry); - let (r4, carry_out) = mac(carry_out, a[1], b[3], carry); - - let (r2, carry) = macx(r2, a[2], b[0]); - let (r3, carry) = mac(r3, a[2], b[1], carry); - let (r4, carry) = mac(r4, a[2], b[2], carry); - let (r5, carry_out) = mac(carry_out, a[2], b[3], carry); - - let (r3, carry) = macx(r3, a[3], b[0]); - let (r4, carry) = mac(r4, a[3], b[1], carry); - let (r5, carry) = mac(r5, a[3], b[2], carry); - let (r6, carry_out) = mac(carry_out, a[3], b[3], carry); - - [r0, r1, r2, r3, r4, r5, r6, carry_out] -} diff --git a/arithmetic/curves/src/bn256/assembly.rs b/arithmetic/curves/src/bn256/assembly.rs deleted file mode 100644 index c9294d9506..0000000000 --- a/arithmetic/curves/src/bn256/assembly.rs +++ /dev/null @@ -1,1378 +0,0 @@ -macro_rules! assembly_field { - ( - $field:ident, - $modulus:ident, - $inv:ident, - $modulus_str:ident, - $two_inv:ident, - $root_of_unity_inv:ident, - $delta:ident, - $zeta:ident, - $r:ident, - $r2:ident, - $r3:ident - ) => { - use std::arch::asm; - - impl $field { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> $field { - $field([0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> $field { - $r - } - - fn from_u512(limbs: [u64; 8]) -> $field { - // 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 multiplied 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 = $field([limbs[0], limbs[1], limbs[2], limbs[3]]); - let d1 = $field([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) `$field` representation. - pub const fn from_raw(val: [u64; 4]) -> Self { - // Multiplication - let (r0, carry) = mac(0, val[0], $r2.0[0], 0); - let (r1, carry) = mac(0, val[0], $r2.0[1], carry); - let (r2, carry) = mac(0, val[0], $r2.0[2], carry); - let (r3, r4) = mac(0, val[0], $r2.0[3], carry); - - let (r1, carry) = mac(r1, val[1], $r2.0[0], 0); - let (r2, carry) = mac(r2, val[1], $r2.0[1], carry); - let (r3, carry) = mac(r3, val[1], $r2.0[2], carry); - let (r4, r5) = mac(r4, val[1], $r2.0[3], carry); - - let (r2, carry) = mac(r2, val[2], $r2.0[0], 0); - let (r3, carry) = mac(r3, val[2], $r2.0[1], carry); - let (r4, carry) = mac(r4, val[2], $r2.0[2], carry); - let (r5, r6) = mac(r5, val[2], $r2.0[3], carry); - - let (r3, carry) = mac(r3, val[3], $r2.0[0], 0); - let (r4, carry) = mac(r4, val[3], $r2.0[1], carry); - let (r5, carry) = mac(r5, val[3], $r2.0[2], carry); - let (r6, r7) = mac(r6, val[3], $r2.0[3], carry); - - // Montgomery reduction (first part) - 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); - - // Montgomery reduction (sub part) - let (d0, borrow) = sbb(r4, $modulus.0[0], 0); - let (d1, borrow) = sbb(r5, $modulus.0[1], borrow); - let (d2, borrow) = sbb(r6, $modulus.0[2], borrow); - let (d3, borrow) = sbb(r7, $modulus.0[3], borrow); - - 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); - - $field([d0, d1, d2, d3]) - } - - /// Attempts to convert a little-endian byte representation of - /// a scalar into a `Fr`, failing if the input is not canonical. - pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<$field> { - ::from_repr(*bytes) - } - - /// Converts an element of `Fr` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 32] { - ::to_repr(self) - } - } - - impl Group for $field { - type Scalar = Self; - - fn group_zero() -> Self { - Self::zero() - } - fn group_add(&mut self, rhs: &Self) { - *self += *rhs; - } - fn group_sub(&mut self, rhs: &Self) { - *self -= *rhs; - } - fn group_scale(&mut self, by: &Self::Scalar) { - *self *= *by; - } - } - - impl fmt::Debug for $field { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_repr(); - write!(f, "0x")?; - for &b in tmp.iter().rev() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } - } - - impl Default for $field { - #[inline] - fn default() -> Self { - Self::zero() - } - } - - impl From for $field { - fn from(bit: bool) -> $field { - if bit { - $field::one() - } else { - $field::zero() - } - } - } - - impl From for $field { - fn from(val: u64) -> $field { - $field([val, 0, 0, 0]) * $r2 - } - } - - impl ConstantTimeEq for $field { - 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 core::cmp::Ord for $field { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - let left = self.to_repr(); - let right = other.to_repr(); - left.iter() - .zip(right.iter()) - .rev() - .find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) { - core::cmp::Ordering::Equal => None, - res => Some(res), - }) - .unwrap_or(core::cmp::Ordering::Equal) - } - } - - impl core::cmp::PartialOrd for $field { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - impl ConditionallySelectable for $field { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - $field([ - 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), - ]) - } - } - - impl<'a> Neg for &'a $field { - type Output = $field; - - #[inline] - fn neg(self) -> $field { - self.neg() - } - } - - impl Neg for $field { - type Output = $field; - - #[inline] - fn neg(self) -> $field { - -&self - } - } - - impl<'a, 'b> Sub<&'b $field> for &'a $field { - type Output = $field; - - #[inline] - fn sub(self, rhs: &'b $field) -> $field { - self.sub(rhs) - } - } - - impl<'a, 'b> Add<&'b $field> for &'a $field { - type Output = $field; - - #[inline] - fn add(self, rhs: &'b $field) -> $field { - self.add(rhs) - } - } - - impl<'a, 'b> Mul<&'b $field> for &'a $field { - type Output = $field; - - #[inline] - fn mul(self, rhs: &'b $field) -> $field { - self.mul(rhs) - } - } - - impl From<$field> for [u8; 32] { - fn from(value: $field) -> [u8; 32] { - value.to_repr() - } - } - - impl<'a> From<&'a $field> for [u8; 32] { - fn from(value: &'a $field) -> [u8; 32] { - value.to_repr() - } - } - - impl FieldExt for $field { - const MODULUS: &'static str = $modulus_str; - const TWO_INV: Self = $two_inv; - const ROOT_OF_UNITY_INV: Self = $root_of_unity_inv; - const DELTA: Self = $delta; - const ZETA: Self = $zeta; - - fn from_u128(v: u128) -> Self { - $field::from_raw([v as u64, (v >> 64) as u64, 0, 0]) - } - - /// Converts a 512-bit little endian integer into - /// a `$field` by reducing by the modulus. - fn from_bytes_wide(bytes: &[u8; 64]) -> $field { - $field::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 get_lower_128(&self) -> u128 { - let tmp = $field::montgomery_reduce(&[ - self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0, - ]); - - u128::from(tmp.0[0]) | (u128::from(tmp.0[1]) << 64) - } - } - - impl $crate::serde::SerdeObject for $field { - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { - debug_assert_eq!(bytes.len(), 32); - let inner = - [0, 8, 16, 24].map(|i| u64::from_le_bytes(bytes[i..i + 8].try_into().unwrap())); - Self(inner) - } - fn from_raw_bytes(bytes: &[u8]) -> Option { - if bytes.len() != 32 { - return None; - } - let elt = Self::from_raw_bytes_unchecked(bytes); - is_less_than(&elt.0, &$modulus.0).then(|| elt) - } - fn to_raw_bytes(&self) -> Vec { - let mut res = Vec::with_capacity(32); - for limb in self.0.iter() { - res.extend_from_slice(&limb.to_le_bytes()); - } - res - } - fn read_raw_unchecked(reader: &mut R) -> Self { - let inner = [(); 4].map(|_| { - let mut buf = [0; 8]; - reader.read_exact(&mut buf).unwrap(); - u64::from_le_bytes(buf) - }); - Self(inner) - } - fn read_raw(reader: &mut R) -> std::io::Result { - let mut inner = [0u64; 4]; - for limb in inner.iter_mut() { - let mut buf = [0; 8]; - reader.read_exact(&mut buf)?; - *limb = u64::from_le_bytes(buf); - } - let elt = Self(inner); - is_less_than(&elt.0, &$modulus.0) - .then(|| elt) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidData, - "input number is not less than field modulus", - ) - }) - } - fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { - for limb in self.0.iter() { - writer.write_all(&limb.to_le_bytes())?; - } - Ok(()) - } - } - - /// Lexicographic comparison of Montgomery forms. - #[inline(always)] - fn is_less_than(x: &[u64; 4], y: &[u64; 4]) -> bool { - match x[3].cmp(&y[3]) { - core::cmp::Ordering::Less => return true, - core::cmp::Ordering::Greater => return false, - _ => {} - } - match x[2].cmp(&y[2]) { - core::cmp::Ordering::Less => return true, - core::cmp::Ordering::Greater => return false, - _ => {} - } - match x[1].cmp(&y[1]) { - core::cmp::Ordering::Less => return true, - core::cmp::Ordering::Greater => return false, - _ => {} - } - x[0].lt(&y[0]) - } - - impl $field { - /// Doubles this field element. - #[inline] - pub fn double(&self) -> $field { - let mut r0: u64; - let mut r1: u64; - let mut r2: u64; - let mut r3: u64; - unsafe { - asm!( - // load a array to former registers - "mov r8, qword ptr [{a_ptr} + 0]", - "mov r9, qword ptr [{a_ptr} + 8]", - "mov r10, qword ptr [{a_ptr} + 16]", - "mov r11, qword ptr [{a_ptr} + 24]", - - // // add a array and b array with carry - "add r8, r8", - "adcx r9, r9", - "adcx r10, r10", - "adcx r11, r11", - - // copy result array to latter registers - "mov r12, r8", - "mov r13, r9", - "mov r14, r10", - "mov r15, r11", - - // mod reduction - "sub r12, qword ptr [{m_ptr} + 0]", - "sbb r13, qword ptr [{m_ptr} + 8]", - "sbb r14, qword ptr [{m_ptr} + 16]", - "sbb r15, qword ptr [{m_ptr} + 24]", - - // if carry copy former registers to out areas - "cmovc r12, r8", - "cmovc r13, r9", - "cmovc r14, r10", - "cmovc r15, r11", - - m_ptr = in(reg) $modulus.0.as_ptr(), - a_ptr = in(reg) self.0.as_ptr(), - out("r8") _, - out("r9") _, - out("r10") _, - out("r11") _, - out("r12") r0, - out("r13") r1, - out("r14") r2, - out("r15") r3, - options(pure, readonly, nostack) - ); - } - $field([r0, r1, r2, r3]) - } - - /// Squares this element. - #[inline] - pub fn square(&self) -> $field { - let mut r0: u64; - let mut r1: u64; - let mut r2: u64; - let mut r3: u64; - unsafe { - asm!( - // schoolbook multiplication - // * | a0 | a1 | a2 | a3 - // b0 | b0 * a0 | b0 * a1 | b0 * a2 | b0 * a3 - // b1 | b1 * a0 | b1 * a1 | b1 * a2 | b1 * a3 - // b2 | b2 * a0 | b2 * a1 | b2 * a2 | b2 * a3 - // b3 | b3 * a0 | b3 * a1 | b3 * a2 | b3 * a3 - - // load value to registers - "mov r13, qword ptr [{a_ptr} + 0]", - "mov r14, qword ptr [{a_ptr} + 8]", - "mov r15, qword ptr [{a_ptr} + 16]", - - // `a0` - "mov rdx, r13", - - // a0 * b0 - "mulx r9, r8, r13", - - // a0 * b1 - "mulx r10, rax, r14", - "add r9, rax", - - // a0 * b2 - "mulx r11, rax, r15", - "adcx r10, rax", - - // a0 * b3 - "mulx r12, rax, qword ptr [{a_ptr} + 24]", - "adcx r11, rax", - "adc r12, 0", - - // `a1` - "mov rdx, r14", - - // a1 * b0 - "mulx rcx, rax, r13", - "add r9, rax", - "adcx r10, rcx", - "adc r11, 0", - - // a1 * b1 - "mulx rcx, rax, r14", - "add r10, rax", - "adcx r11, rcx", - "adc r12, 0", - "xor r13, r13", - - // a1 * b2 - "mulx rcx, rax, r15", - "add r11, rax", - "adcx r12, rcx", - "adc r13, 0", - "xor r14, r14", - - // a1 * b3 - "mulx rcx, rax, qword ptr [{a_ptr} + 24]", - "add r12, rax", - "adcx r13, rcx", - "adc r14, 0", - - // `a2` - "mov rdx, r15", - - // a2 * b0 - "mulx rcx, rax, qword ptr [{a_ptr} + 0]", - "add r10, rax", - "adcx r11, rcx", - "adc r12, 0", - - // a2 * b1 - "mulx rcx, rax, qword ptr [{a_ptr} + 8]", - "add r11, rax", - "adcx r12, rcx", - "adc r13, 0", - - // a2 * b2 - "mulx rcx, rax, r15", - "add r12, rax", - "adcx r13, rcx", - "adc r14, 0", - "xor r15, r15", - - // a2 * b3 - "mulx rcx, rax, qword ptr [{a_ptr} + 24]", - "add r13, rax", - "adcx r14, rcx", - "adc r15, 0", - - // `a3` - "mov rdx, qword ptr [{a_ptr} + 24]", - - // a3 * b0 - "mulx rcx, rax, qword ptr [{a_ptr} + 0]", - "add r11, rax", - "adcx r12, rcx", - "adc r13, 0", - - // a3 * b1 - "mulx rcx, rax, qword ptr [{a_ptr} + 8]", - "add r12, rax", - "adcx r13, rcx", - "adc r14, 0", - - // a3 * b2 - "mulx rcx, rax, qword ptr [{a_ptr} + 16]", - "add r13, rax", - "adcx r14, rcx", - "adc r15, 0", - - // a3 * b3 - "mulx rcx, rax, qword ptr [{a_ptr} + 24]", - "add r14, rax", - "adc r15, rcx", - - // montgomery reduction - // r8 ~ r15 - - // `r8` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r8", - - // r8' * m0 - "mulx rcx, rax, qword ptr [{m_ptr} + 0]", - "add r8, rax", - "adcx r9, rcx", - "adc r10, 0", - - // r8' * m1 - "mulx rcx, rax, qword ptr [{m_ptr} + 8]", - "add r9, rax", - "adcx r10, rcx", - "adc r11, 0", - - // r8' * m2 - "mulx rcx, rax, qword ptr [{m_ptr} + 16]", - "add r10, rax", - "adcx r11, rcx", - "adc r12, 0", - - // r8' * m3 - "mulx rcx, rax, qword ptr [{m_ptr} + 24]", - "add r11, rax", - "adcx r12, rcx", - "adc r13, 0", - - // `r9` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r9", - - // r9' * m0 - "mulx rax, rcx, qword ptr [{m_ptr} + 0]", - "add r9, rcx", - "adcx r10, rax", - "adc r11, 0", - - // r9' * m1 - "mulx rax, rcx, qword ptr [{m_ptr} + 8]", - "add r10, rcx", - "adcx r11, rax", - "adc r12, 0", - - // r9' * m2 - "mulx rax, rcx, qword ptr [{m_ptr} + 16]", - "add r11, rcx", - "adcx r12, rax", - "adc r13, 0", - - // r9' * m3 - "mulx rax, rcx, qword ptr [{m_ptr} + 24]", - "add r12, rcx", - "adcx r13, rax", - "adc r14, 0", - - // `r10` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r10", - - // r10' * m0 - "mulx rax, rcx, qword ptr [{m_ptr} + 0]", - "add r10, rcx", - "adcx r11, rax", - "adc r12, 0", - - // r10' * m1 - "mulx rax, rcx, qword ptr [{m_ptr} + 8]", - "add r11, rcx", - "adcx r12, rax", - "adc r13, 0", - - // r10' * m2 - "mulx rax, rcx, qword ptr [{m_ptr} + 16]", - "add r12, rcx", - "adcx r13, rax", - "adc r14, 0", - - // r10' * m3 - "mulx rax, rcx, qword ptr [{m_ptr} + 24]", - "add r13, rcx", - "adcx r14, rax", - "adc r15, 0", - - // `r11` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r11", - - // r11' * m0 - "mulx rax, rcx, qword ptr [{m_ptr} + 0]", - "add r11, rcx", - "adcx r12, rax", - "adc r13, 0", - - // r11' * m1 - "mulx rax, rcx, qword ptr [{m_ptr} + 8]", - "add r12, rcx", - "adcx r13, rax", - "adc r14, 0", - - // r11' * m2 - "mulx rax, rcx, qword ptr [{m_ptr} + 16]", - "add r13, rcx", - "adcx r14, rax", - "adc r15, 0", - - // r11' * m3 - "mulx rax, rcx, qword ptr [{m_ptr} + 24]", - "add r14, rcx", - "adcx r15, rax", - - // reduction if limbs is greater then mod - "mov r8, r12", - "mov r9, r13", - "mov r10, r14", - "mov r11, r15", - - "sub r8, qword ptr [{m_ptr} + 0]", - "sbb r9, qword ptr [{m_ptr} + 8]", - "sbb r10, qword ptr [{m_ptr} + 16]", - "sbb r11, qword ptr [{m_ptr} + 24]", - - "cmovc r8, r12", - "cmovc r9, r13", - "cmovc r10, r14", - "cmovc r11, r15", - - "mov r12, r8", - "mov r13, r9", - "mov r14, r10", - "mov r15, r11", - - "sub r12, qword ptr [{m_ptr} + 0]", - "sbb r13, qword ptr [{m_ptr} + 8]", - "sbb r14, qword ptr [{m_ptr} + 16]", - "sbb r15, qword ptr [{m_ptr} + 24]", - - "cmovc r12, r8", - "cmovc r13, r9", - "cmovc r14, r10", - "cmovc r15, r11", - - a_ptr = in(reg) self.0.as_ptr(), - m_ptr = in(reg) $modulus.0.as_ptr(), - inv = const $inv, - out("rax") _, - out("rcx") _, - out("rdx") _, - out("r8") _, - out("r9") _, - out("r10") _, - out("r11") _, - out("r12") r0, - out("r13") r1, - out("r14") r2, - out("r15") r3, - options(pure, readonly, nostack) - ) - } - - $field([r0, r1, r2, r3]) - } - - #[inline(always)] - pub(crate) fn montgomery_reduce(a: &[u64; 8]) -> $field { - let mut r0: u64; - let mut r1: u64; - let mut r2: u64; - let mut r3: u64; - - unsafe { - asm!( - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - "mov r8, qword ptr [{a_ptr} + 0]", - "mov r9, qword ptr [{a_ptr} + 8]", - "mov r10, qword ptr [{a_ptr} + 16]", - "mov r11, qword ptr [{a_ptr} + 24]", - "mov r12, qword ptr [{a_ptr} + 32]", - "mov r13, qword ptr [{a_ptr} + 40]", - "mov r14, qword ptr [{a_ptr} + 48]", - "mov r15, qword ptr [{a_ptr} + 56]", - - // `r8` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r8", - - // r8' * m0 - "mulx rcx, rax, qword ptr [{m_ptr} + 0]", - "add r8, rax", - "adcx r9, rcx", - "adc r10, 0", - - // r8' * m1 - "mulx rcx, rax, qword ptr [{m_ptr} + 8]", - "add r9, rax", - "adcx r10, rcx", - "adc r11, 0", - - // // r8' * m2 - "mulx rcx, rax, qword ptr [{m_ptr} + 16]", - "add r10, rax", - "adcx r11, rcx", - "adc r12, 0", - - // // r8' * m3 - "mulx rcx, rax, qword ptr [{m_ptr} + 24]", - "add r11, rax", - "adcx r12, rcx", - "adc r13, 0", - - // `r9` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r9", - - // r9' * m0 - "mulx rax, rcx, qword ptr [{m_ptr} + 0]", - "add r9, rcx", - "adcx r10, rax", - "adc r11, 0", - - // r9' * m1 - "mulx rax, rcx, qword ptr [{m_ptr} + 8]", - "add r10, rcx", - "adcx r11, rax", - "adc r12, 0", - - // r9' * m2 - "mulx rax, rcx, qword ptr [{m_ptr} + 16]", - "add r11, rcx", - "adcx r12, rax", - "adc r13, 0", - - // r9' * m3 - "mulx rax, rcx, qword ptr [{m_ptr} + 24]", - "add r12, rcx", - "adcx r13, rax", - "adc r14, 0", - - // `r10` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r10", - - // r10' * m0 - "mulx rax, rcx, qword ptr [{m_ptr} + 0]", - "add r10, rcx", - "adcx r11, rax", - "adc r12, 0", - - // r10' * m1 - "mulx rax, rcx, qword ptr [{m_ptr} + 8]", - "add r11, rcx", - "adcx r12, rax", - "adc r13, 0", - - // r10' * m2 - "mulx rax, rcx, qword ptr [{m_ptr} + 16]", - "add r12, rcx", - "adcx r13, rax", - "adc r14, 0", - - // r10' * m3 - "mulx rax, rcx, qword ptr [{m_ptr} + 24]", - "add r13, rcx", - "adcx r14, rax", - "adc r15, 0", - - // `r11` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r11", - - // r11' * m0 - "mulx rax, rcx, qword ptr [{m_ptr} + 0]", - "add r11, rcx", - "adcx r12, rax", - "adc r13, 0", - - // r11' * m1 - "mulx rax, rcx, qword ptr [{m_ptr} + 8]", - "add r12, rcx", - "adcx r13, rax", - "adc r14, 0", - - // r11' * m2 - "mulx rax, rcx, qword ptr [{m_ptr} + 16]", - "add r13, rcx", - "adcx r14, rax", - "adc r15, 0", - - // r11' * m3 - "mulx rax, rcx, qword ptr [{m_ptr} + 24]", - "add r14, rcx", - "adcx r15, rax", - - // reduction if limbs is greater then mod - "mov r8, r12", - "mov r9, r13", - "mov r10, r14", - "mov r11, r15", - - "sub r8, qword ptr [{m_ptr} + 0]", - "sbb r9, qword ptr [{m_ptr} + 8]", - "sbb r10, qword ptr [{m_ptr} + 16]", - "sbb r11, qword ptr [{m_ptr} + 24]", - - "cmovc r8, r12", - "cmovc r9, r13", - "cmovc r10, r14", - "cmovc r11, r15", - - "mov r12, r8", - "mov r13, r9", - "mov r14, r10", - "mov r15, r11", - - "sub r12, qword ptr [{m_ptr} + 0]", - "sbb r13, qword ptr [{m_ptr} + 8]", - "sbb r14, qword ptr [{m_ptr} + 16]", - "sbb r15, qword ptr [{m_ptr} + 24]", - - "cmovc r12, r8", - "cmovc r13, r9", - "cmovc r14, r10", - "cmovc r15, r11", - - a_ptr = in(reg) a.as_ptr(), - m_ptr = in(reg) $modulus.0.as_ptr(), - inv = const $inv, - out("rax") _, - out("rcx") _, - out("rdx") _, - out("r8") _, - out("r9") _, - out("r10") _, - out("r11") _, - out("r12") r0, - out("r13") r1, - out("r14") r2, - out("r15") r3, - options(pure, readonly, nostack) - ) - } - - $field([r0, r1, r2, r3]) - } - - /// Multiplies `rhs` by `self`, returning the result. - #[inline] - pub fn mul(&self, rhs: &Self) -> $field { - let mut r0: u64; - let mut r1: u64; - let mut r2: u64; - let mut r3: u64; - unsafe { - asm!( - // schoolbook multiplication - // * | a0 | a1 | a2 | a3 - // b0 | b0 * a0 | b0 * a1 | b0 * a2 | b0 * a3 - // b1 | b1 * a0 | b1 * a1 | b1 * a2 | b1 * a3 - // b2 | b2 * a0 | b2 * a1 | b2 * a2 | b2 * a3 - // b3 | b3 * a0 | b3 * a1 | b3 * a2 | b3 * a3 - - // load value to registers - "mov r13, qword ptr [{b_ptr} + 0]", - "mov r14, qword ptr [{b_ptr} + 8]", - "mov r15, qword ptr [{b_ptr} + 16]", - - // `a0` - "mov rdx, qword ptr [{a_ptr} + 0]", - - // a0 * b0 - "mulx r9, r8, r13", - - // a0 * b1 - "mulx r10, rax, r14", - "add r9, rax", - - // a0 * b2 - "mulx r11, rax, r15", - "adcx r10, rax", - - // a0 * b3 - "mulx r12, rax, qword ptr [{b_ptr} + 24]", - "adcx r11, rax", - "adc r12, 0", - - // `a1` - "mov rdx, [{a_ptr} + 8]", - - // a1 * b0 - "mulx rcx, rax, r13", - "add r9, rax", - "adcx r10, rcx", - "adc r11, 0", - - // a1 * b1 - "mulx rcx, rax, r14", - "add r10, rax", - "adcx r11, rcx", - "adc r12, 0", - "xor r13, r13", - - // a1 * b2 - "mulx rcx, rax, r15", - "add r11, rax", - "adcx r12, rcx", - "adc r13, 0", - "xor r14, r14", - - // a1 * b3 - "mulx rcx, rax, qword ptr [{b_ptr} + 24]", - "add r12, rax", - "adcx r13, rcx", - "adc r14, 0", - - // `a2` - "mov rdx, [{a_ptr} + 16]", - - // a2 * b0 - "mulx rcx, rax, qword ptr [{b_ptr} + 0]", - "add r10, rax", - "adcx r11, rcx", - "adc r12, 0", - - // a2 * b1 - "mulx rcx, rax, qword ptr [{b_ptr} + 8]", - "add r11, rax", - "adcx r12, rcx", - "adc r13, 0", - - // a2 * b2 - "mulx rcx, rax, r15", - "add r12, rax", - "adcx r13, rcx", - "adc r14, 0", - "xor r15, r15", - - // a2 * b3 - "mulx rcx, rax, qword ptr [{b_ptr} + 24]", - "add r13, rax", - "adcx r14, rcx", - "adc r15, 0", - - // `a3` - "mov rdx, [{a_ptr} + 24]", - - // a3 * b0 - "mulx rcx, rax, qword ptr [{b_ptr} + 0]", - "add r11, rax", - "adcx r12, rcx", - "adc r13, 0", - - // a3 * b1 - "mulx rcx, rax, qword ptr [{b_ptr} + 8]", - "add r12, rax", - "adcx r13, rcx", - "adc r14, 0", - - // a3 * b2 - "mulx rcx, rax, qword ptr [{b_ptr} + 16]", - "add r13, rax", - "adcx r14, rcx", - "adc r15, 0", - - // a3 * b3 - "mulx rcx, rax, qword ptr [{b_ptr} + 24]", - "add r14, rax", - "adc r15, rcx", - - // montgomery reduction - // r8 ~ r15 - - // `r8` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r8", - - // r8' * m0 - "mulx rcx, rax, qword ptr [{m_ptr} + 0]", - "add r8, rax", - "adcx r9, rcx", - "adc r10, 0", - - // r8' * m1 - "mulx rcx, rax, qword ptr [{m_ptr} + 8]", - "add r9, rax", - "adcx r10, rcx", - "adc r11, 0", - - // // r8' * m2 - "mulx rcx, rax, qword ptr [{m_ptr} + 16]", - "add r10, rax", - "adcx r11, rcx", - "adc r12, 0", - - // // r8' * m3 - "mulx rcx, rax, qword ptr [{m_ptr} + 24]", - "add r11, rax", - "adcx r12, rcx", - "adc r13, 0", - - // `r9` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r9", - - // r9' * m0 - "mulx rax, rcx, qword ptr [{m_ptr} + 0]", - "add r9, rcx", - "adcx r10, rax", - "adc r11, 0", - - // r9' * m1 - "mulx rax, rcx, qword ptr [{m_ptr} + 8]", - "add r10, rcx", - "adcx r11, rax", - "adc r12, 0", - - // r9' * m2 - "mulx rax, rcx, qword ptr [{m_ptr} + 16]", - "add r11, rcx", - "adcx r12, rax", - "adc r13, 0", - - // r9' * m3 - "mulx rax, rcx, qword ptr [{m_ptr} + 24]", - "add r12, rcx", - "adcx r13, rax", - "adc r14, 0", - - // `r10` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r10", - - // r10' * m0 - "mulx rax, rcx, qword ptr [{m_ptr} + 0]", - "add r10, rcx", - "adcx r11, rax", - "adc r12, 0", - - // r10' * m1 - "mulx rax, rcx, qword ptr [{m_ptr} + 8]", - "add r11, rcx", - "adcx r12, rax", - "adc r13, 0", - - // r10' * m2 - "mulx rax, rcx, qword ptr [{m_ptr} + 16]", - "add r12, rcx", - "adcx r13, rax", - "adc r14, 0", - - // r10' * m3 - "mulx rax, rcx, qword ptr [{m_ptr} + 24]", - "add r13, rcx", - "adcx r14, rax", - "adc r15, 0", - - // `r11` -> 0 - "mov rdx, {inv}", - "mulx rax, rdx, r11", - - // r11' * m0 - "mulx rax, rcx, qword ptr [{m_ptr} + 0]", - "add r11, rcx", - "adcx r12, rax", - "adc r13, 0", - - // r11' * m1 - "mulx rax, rcx, qword ptr [{m_ptr} + 8]", - "add r12, rcx", - "adcx r13, rax", - "adc r14, 0", - - // r11' * m2 - "mulx rax, rcx, qword ptr [{m_ptr} + 16]", - "add r13, rcx", - "adcx r14, rax", - "adc r15, 0", - - // r11' * m3 - "mulx rax, rcx, qword ptr [{m_ptr} + 24]", - "add r14, rcx", - "adcx r15, rax", - - // reduction if limbs is greater then mod - "mov r8, r12", - "mov r9, r13", - "mov r10, r14", - "mov r11, r15", - - "sub r8, qword ptr [{m_ptr} + 0]", - "sbb r9, qword ptr [{m_ptr} + 8]", - "sbb r10, qword ptr [{m_ptr} + 16]", - "sbb r11, qword ptr [{m_ptr} + 24]", - - "cmovc r8, r12", - "cmovc r9, r13", - "cmovc r10, r14", - "cmovc r11, r15", - - "mov r12, r8", - "mov r13, r9", - "mov r14, r10", - "mov r15, r11", - - "sub r12, qword ptr [{m_ptr} + 0]", - "sbb r13, qword ptr [{m_ptr} + 8]", - "sbb r14, qword ptr [{m_ptr} + 16]", - "sbb r15, qword ptr [{m_ptr} + 24]", - - "cmovc r12, r8", - "cmovc r13, r9", - "cmovc r14, r10", - "cmovc r15, r11", - - m_ptr = in(reg) $modulus.0.as_ptr(), - a_ptr = in(reg) self.0.as_ptr(), - b_ptr = in(reg) rhs.0.as_ptr(), - inv = const $inv, - out("rax") _, - out("rcx") _, - out("rdx") _, - out("r8") _, - out("r9") _, - out("r10") _, - out("r11") _, - out("r12") r0, - out("r13") r1, - out("r14") r2, - out("r15") r3, - options(pure, readonly, nostack) - ) - } - - $field([r0, r1, r2, r3]) - } - - /// Subtracts `rhs` from `self`, returning the result. - #[inline] - pub fn sub(&self, rhs: &Self) -> $field { - let mut r0: u64; - let mut r1: u64; - let mut r2: u64; - let mut r3: u64; - unsafe { - asm!( - // init modulus area - "xor r12, r12", - "xor r13, r13", - "xor r14, r14", - "xor r15, r15", - - // load a array to former registers - "mov r8, qword ptr [{a_ptr} + 0]", - "mov r9, qword ptr [{a_ptr} + 8]", - "mov r10, qword ptr [{a_ptr} + 16]", - "mov r11, qword ptr [{a_ptr} + 24]", - - // sub a array and b array with borrow - "sub r8, qword ptr [{b_ptr} + 0]", - "sbb r9, qword ptr [{b_ptr} + 8]", - "sbb r10, qword ptr [{b_ptr} + 16]", - "sbb r11, qword ptr [{b_ptr} + 24]", - - // if carry copy modulus - "cmovc r12, qword ptr [{m_ptr} + 0]", - "cmovc r13, qword ptr [{m_ptr} + 8]", - "cmovc r14, qword ptr [{m_ptr} + 16]", - "cmovc r15, qword ptr [{m_ptr} + 24]", - - // mod addition - "add r12, r8", - "adcx r13, r9", - "adcx r14, r10", - "adcx r15, r11", - - m_ptr = in(reg) $modulus.0.as_ptr(), - a_ptr = in(reg) self.0.as_ptr(), - b_ptr = in(reg) rhs.0.as_ptr(), - out("r8") _, - out("r9") _, - out("r10") _, - out("r11") _, - out("r12") r0, - out("r13") r1, - out("r14") r2, - out("r15") r3, - options(pure, readonly, nostack) - ); - } - $field([r0, r1, r2, r3]) - } - - /// Adds `rhs` to `self`, returning the result. - #[inline] - pub fn add(&self, rhs: &Self) -> $field { - let mut r0: u64; - let mut r1: u64; - let mut r2: u64; - let mut r3: u64; - unsafe { - asm!( - // load a array to former registers - "mov r8, qword ptr [{a_ptr} + 0]", - "mov r9, qword ptr [{a_ptr} + 8]", - "mov r10, qword ptr [{a_ptr} + 16]", - "mov r11, qword ptr [{a_ptr} + 24]", - - // add a array and b array with carry - "add r8, qword ptr [{b_ptr} + 0]", - "adcx r9, qword ptr [{b_ptr} + 8]", - "adcx r10, qword ptr [{b_ptr} + 16]", - "adcx r11, qword ptr [{b_ptr} + 24]", - - // copy result array to latter registers - "mov r12, r8", - "mov r13, r9", - "mov r14, r10", - "mov r15, r11", - - // mod reduction - "sub r12, qword ptr [{m_ptr} + 0]", - "sbb r13, qword ptr [{m_ptr} + 8]", - "sbb r14, qword ptr [{m_ptr} + 16]", - "sbb r15, qword ptr [{m_ptr} + 24]", - - // if carry copy former registers to out areas - "cmovc r12, r8", - "cmovc r13, r9", - "cmovc r14, r10", - "cmovc r15, r11", - - m_ptr = in(reg) $modulus.0.as_ptr(), - a_ptr = in(reg) self.0.as_ptr(), - b_ptr = in(reg) rhs.0.as_ptr(), - out("r8") _, - out("r9") _, - out("r10") _, - out("r11") _, - out("r12") r0, - out("r13") r1, - out("r14") r2, - out("r15") r3, - options(pure, readonly, nostack) - ); - } - $field([r0, r1, r2, r3]) - } - - /// Negates `self`. - #[inline] - pub fn neg(&self) -> $field { - let mut r0: u64; - let mut r1: u64; - let mut r2: u64; - let mut r3: u64; - unsafe { - asm!( - // load a array to former registers - "mov r8, qword ptr [{m_ptr} + 0]", - "mov r9, qword ptr [{m_ptr} + 8]", - "mov r10, qword ptr [{m_ptr} + 16]", - "mov r11, qword ptr [{m_ptr} + 24]", - - "sub r8, qword ptr [{a_ptr} + 0]", - "sbb r9, qword ptr [{a_ptr} + 8]", - "sbb r10, qword ptr [{a_ptr} + 16]", - "sbb r11, qword ptr [{a_ptr} + 24]", - - "mov r12, qword ptr [{a_ptr} + 0]", - "mov r13, qword ptr [{a_ptr} + 8]", - "mov r14, qword ptr [{a_ptr} + 16]", - "mov r15, qword ptr [{a_ptr} + 24]", - - "or r12, r13", - "or r14, r15", - "or r12, r14", - - "mov r13, 0xffffffffffffffff", - "cmp r12, 0x0000000000000000", - "cmove r13, r12", - - "and r8, r13", - "and r9, r13", - "and r10, r13", - "and r11, r13", - - a_ptr = in(reg) self.0.as_ptr(), - m_ptr = in(reg) $modulus.0.as_ptr(), - out("r8") r0, - out("r9") r1, - out("r10") r2, - out("r11") r3, - out("r12") _, - out("r13") _, - out("r14") _, - out("r15") _, - options(pure, readonly, nostack) - ) - } - $field([r0, r1, r2, r3]) - } - } - }; -} - -pub(crate) use assembly_field; diff --git a/arithmetic/curves/src/bn256/curve.rs b/arithmetic/curves/src/bn256/curve.rs deleted file mode 100644 index 3450ed909c..0000000000 --- a/arithmetic/curves/src/bn256/curve.rs +++ /dev/null @@ -1,335 +0,0 @@ -use crate::arithmetic::mul_512; -use crate::bn256::Fq; -use crate::bn256::Fq2; -use crate::bn256::Fr; -use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt, Group}; -use core::cmp; -use core::fmt::Debug; -use core::iter::Sum; -use core::ops::{Add, Mul, Neg, Sub}; -use ff::{Field, PrimeField}; -use group::Curve; -use group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Group as _, GroupEncoding}; -use pasta_curves::arithmetic::FieldExt; -use rand::RngCore; -use serde::{Deserialize, Serialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::{ - batch_add, impl_add_binop_specify_output, impl_binops_additive, - impl_binops_additive_specify_output, impl_binops_multiplicative, - impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, new_curve_impl, -}; - -new_curve_impl!( - (pub), - G1, - G1Affine, - G1Compressed, - Fq::size(), - Fq, - Fr, - (G1_GENERATOR_X,G1_GENERATOR_Y), - G1_B, - "bn256_g1", -); - -new_curve_impl!( - (pub), - G2, - G2Affine, - G2Compressed, - Fq2::size(), - Fq2, - Fr, - (G2_GENERATOR_X, G2_GENERATOR_Y), - G2_B, - "bn256_g2", -); - -impl CurveAffineExt for G1Affine { - batch_add!(); - - fn into_coordinates(self) -> (Self::Base, Self::Base) { - (self.x, self.y) - } -} - -impl CurveAffineExt for G2Affine { - batch_add!(); - - fn into_coordinates(self) -> (Self::Base, Self::Base) { - (self.x, self.y) - } -} - -const G1_GENERATOR_X: Fq = Fq::one(); -const G1_GENERATOR_Y: Fq = Fq::from_raw([2, 0, 0, 0]); -const G1_B: Fq = Fq::from_raw([3, 0, 0, 0]); -const ENDO_G1: [u64; 4] = [ - 0x7a7bd9d4391eb18du64, - 0x4ccef014a773d2cfu64, - 0x0000000000000002u64, - 0u64, -]; -const ENDO_G2: [u64; 4] = [0xd91d232ec7e0b3d7u64, 0x0000000000000002u64, 0u64, 0u64]; -const ENDO_MINUS_B1: [u64; 4] = [0x8211bbeb7d4f1128u64, 0x6f4d8248eeb859fcu64, 0u64, 0u64]; -const ENDO_B2: [u64; 4] = [0x89d3256894d213e3u64, 0u64, 0u64, 0u64]; -const ENDO_BETA: Fr = Fr::from_raw([ - 0x8b17ea66b99c90ddu64, - 0x5bfc41088d8daaa7u64, - 0xb3c4d79d41a91758u64, - 0x0u64, -]); - -const G2_B: Fq2 = Fq2 { - c0: Fq::from_raw([ - 0x3267e6dc24a138e5, - 0xb5b4c5e559dbefa3, - 0x81be18991be06ac3, - 0x2b149d40ceb8aaae, - ]), - c1: Fq::from_raw([ - 0xe4a2bd0685c315d2, - 0xa74fa084e52d1852, - 0xcd2cafadeed8fdf4, - 0x009713b03af0fed4, - ]), -}; - -const G2_GENERATOR_X: Fq2 = Fq2 { - c0: Fq::from_raw([ - 0x46debd5cd992f6ed, - 0x674322d4f75edadd, - 0x426a00665e5c4479, - 0x1800deef121f1e76, - ]), - c1: Fq::from_raw([ - 0x97e485b7aef312c2, - 0xf1aa493335a9e712, - 0x7260bfb731fb5d25, - 0x198e9393920d483a, - ]), -}; - -const G2_GENERATOR_Y: Fq2 = Fq2 { - c0: Fq::from_raw([ - 0x4ce6cc0166fa7daa, - 0xe3d1e7690c43d37b, - 0x4aab71808dcb408f, - 0x12c85ea5db8c6deb, - ]), - - c1: Fq::from_raw([ - 0x55acdadcd122975b, - 0xbc4b313370b38ef3, - 0xec9e99ad690c3395, - 0x090689d0585ff075, - ]), -}; - -trait CurveEndo: CurveExt { - fn endomorphism_base(&self) -> Self; - fn endomorphism_scalars(k: &Self::ScalarExt) -> (u128, u128); -} - -impl CurveEndo for G1 { - fn endomorphism_base(&self) -> Self { - Self { - x: self.x * Self::Base::ZETA, - y: -self.y, - z: self.z, - } - } - - fn endomorphism_scalars(k: &Self::ScalarExt) -> (u128, u128) { - #[cfg(feature = "asm")] - let input = Fr::montgomery_reduce(&[k.0[0], k.0[1], k.0[2], k.0[3], 0, 0, 0, 0]).0; - - #[cfg(not(feature = "asm"))] - let input = Fr::montgomery_reduce(k.0[0], k.0[1], k.0[2], k.0[3], 0, 0, 0, 0).0; - - let c1_512 = mul_512(ENDO_G2, input); - let c2_512 = mul_512(ENDO_G1, input); - - let c1_hi = [c1_512[4], c1_512[5], c1_512[6], c1_512[7]]; - let c2_hi = [c2_512[4], c2_512[5], c2_512[6], c2_512[7]]; - - let q1_512 = mul_512(c1_hi, ENDO_MINUS_B1); - let q2_512 = mul_512(c2_hi, ENDO_B2); - - let q1_lo = Self::ScalarExt::from_raw([q1_512[0], q1_512[1], q1_512[2], q1_512[3]]); - let q2_lo = Self::ScalarExt::from_raw([q2_512[0], q2_512[1], q2_512[2], q2_512[3]]); - - let k1 = q2_lo - q1_lo; - let k2 = (k1 * ENDO_BETA) + k; - - (k2.get_lower_128(), k1.get_lower_128()) - } -} - -impl CurveEndo for G2 { - fn endomorphism_base(&self) -> Self { - unimplemented!(); - } - - fn endomorphism_scalars(_: &Self::ScalarExt) -> (u128, u128) { - unimplemented!(); - } -} - -impl group::cofactor::CofactorGroup for G1 { - type Subgroup = G1; - - fn clear_cofactor(&self) -> Self { - *self - } - - fn into_subgroup(self) -> CtOption { - CtOption::new(self, 1.into()) - } - - fn is_torsion_free(&self) -> Choice { - 1.into() - } -} - -impl CofactorGroup for G2 { - type Subgroup = G2; - - fn clear_cofactor(&self) -> Self { - // "0x30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d" - let e: [u8; 32] = [ - 0x30, 0x64, 0x4e, 0x72, 0xe1, 0x31, 0xa0, 0x29, 0xb8, 0x50, 0x45, 0xb6, 0x81, 0x81, - 0x58, 0x5e, 0x06, 0xce, 0xec, 0xda, 0x57, 0x2a, 0x24, 0x89, 0x34, 0x5f, 0x22, 0x99, - 0xc0, 0xf9, 0xfa, 0x8d, - ]; - - // self * COFACTOR_G2 - let mut acc = G2::identity(); - for bit in e - .iter() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = G2::conditional_select(&acc, &(acc + self), bit); - } - acc - } - - fn into_subgroup(self) -> CtOption { - unimplemented!(); - } - - fn is_torsion_free(&self) -> Choice { - // "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001" - let e: [u8; 32] = [ - 0x30, 0x64, 0x4e, 0x72, 0xe1, 0x31, 0xa0, 0x29, 0xb8, 0x50, 0x45, 0xb6, 0x81, 0x81, - 0x58, 0x5d, 0x28, 0x33, 0xe8, 0x48, 0x79, 0xb9, 0x70, 0x91, 0x43, 0xe1, 0xf5, 0x93, - 0xf0, 0x00, 0x00, 0x01, - ]; - - // self * GROUP_ORDER; - - let mut acc = G2::identity(); - for bit in e - .iter() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = G2::conditional_select(&acc, &(acc + self), bit); - } - acc.is_identity() - } -} - -#[cfg(test)] -mod tests { - - use crate::bn256::{ - curve::{CurveEndo, ENDO_BETA}, - Fr, G1Affine, G1, G2, - }; - use ff::Field; - use rand_core::OsRng; - - use crate::CurveExt; - - #[test] - fn test_curve() { - crate::tests::curve::curve_tests::(); - crate::tests::curve::curve_tests::(); - } - - #[test] - fn test_endo_consistency() { - let g = G1::generator(); - assert_eq!(g * (-ENDO_BETA), g.endo()); - } - - #[test] - fn test_endomorphism() { - use crate::FieldExt; - - let scalar = Fr::random(OsRng); - let point = G1Affine::random(OsRng); - - let expected = point * scalar; - - let (part1, part2) = G1::endomorphism_scalars(&scalar); - - let k1 = Fr::from_u128(part1); - let k2 = Fr::from_u128(part2); - - let t1 = point * k1; - let base = G1::endomorphism_base(&point.into()); - - let t2 = base * k2; - let result = t1 + t2; - - let res_affine: G1Affine = result.into(); - let exp_affine: G1Affine = expected.into(); - - assert_eq!(res_affine, exp_affine); - } - - #[test] - fn test_serialization() { - crate::tests::curve::random_serialization_test::(); - crate::tests::curve::random_serialization_test::(); - } -} - -impl group::UncompressedEncoding for G1Affine { - type Uncompressed = G1Compressed; - - fn from_uncompressed(_: &Self::Uncompressed) -> CtOption { - unimplemented!(); - } - - fn from_uncompressed_unchecked(_: &Self::Uncompressed) -> CtOption { - unimplemented!(); - } - - fn to_uncompressed(&self) -> Self::Uncompressed { - unimplemented!(); - } -} - -impl group::UncompressedEncoding for G2Affine { - type Uncompressed = G2Compressed; - - fn from_uncompressed(_: &Self::Uncompressed) -> CtOption { - unimplemented!(); - } - - fn from_uncompressed_unchecked(_: &Self::Uncompressed) -> CtOption { - unimplemented!(); - } - - fn to_uncompressed(&self) -> Self::Uncompressed { - unimplemented!(); - } -} diff --git a/arithmetic/curves/src/bn256/engine.rs b/arithmetic/curves/src/bn256/engine.rs deleted file mode 100644 index 87453c17be..0000000000 --- a/arithmetic/curves/src/bn256/engine.rs +++ /dev/null @@ -1,838 +0,0 @@ -#![allow(clippy::suspicious_arithmetic_impl)] -use crate::bn256::curve::*; -use crate::bn256::fq::*; -use crate::bn256::fq12::*; -use crate::bn256::fq2::*; -use crate::bn256::fq6::FROBENIUS_COEFF_FQ6_C1; -use crate::bn256::fr::*; -use crate::pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; -use core::borrow::Borrow; -use core::iter::Sum; -use core::ops::{Add, Mul, MulAssign, Neg, Sub}; -use ff::{Field, PrimeField}; -use group::cofactor::CofactorCurveAffine; -use group::Group; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; - -pub const BN_X: u64 = 4965661367192848881; - -// 6U+2 for in NAF form -pub const SIX_U_PLUS_2_NAF: [i8; 65] = [ - 0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, - 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, - 0, 1, 0, 1, 1, -]; - -pub const XI_TO_Q_MINUS_1_OVER_2: Fq2 = Fq2 { - c0: Fq([ - 0xe4bbdd0c2936b629, - 0xbb30f162e133bacb, - 0x31a9d1b6f9645366, - 0x253570bea500f8dd, - ]), - c1: Fq([ - 0xa1d77ce45ffe77c7, - 0x07affd117826d1db, - 0x6d16bd27bb7edc6b, - 0x2c87200285defecc, - ]), -}; - -impl PairingCurveAffine for G1Affine { - type Pair = G2Affine; - type PairingResult = Gt; - - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { - pairing(self, other) - } -} - -impl PairingCurveAffine for G2Affine { - type Pair = G1Affine; - type PairingResult = Gt; - - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { - pairing(other, self) - } -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct Gt(pub(crate) Fq12); - -impl std::fmt::Display for Gt { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - -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(Fq12::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(Fq12::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. - let mut u = self.0; - u.conjugate(); - Gt(u) - } -} - -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 Fr> for &'a Gt { - type Output = Gt; - - fn mul(self, other: &'b Fr) -> Self::Output { - let mut acc = Gt::identity(); - - for bit in other - .to_repr() - .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 - } -} - -use crate::{ - impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, - impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, -}; -impl_binops_additive!(Gt, Gt); -impl_binops_multiplicative!(Gt, Fr); - -impl Sum for Gt -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl Group for Gt { - type Scalar = Fr; - - fn random(_: impl RngCore) -> Self { - unimplemented!(); - } - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - unimplemented!(); - } - - fn is_identity(&self) -> Choice { - self.ct_eq(&Self::identity()) - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } -} - -#[derive(Clone, Debug)] -pub struct G2Prepared { - pub(crate) coeffs: Vec<(Fq2, Fq2, Fq2)>, - pub(crate) infinity: bool, -} - -impl G2Prepared { - pub fn is_zero(&self) -> bool { - self.infinity - } - - pub fn from_affine(q: G2Affine) -> Self { - if bool::from(q.is_identity()) { - return G2Prepared { - coeffs: vec![], - infinity: true, - }; - } - - fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) { - // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf - let mut tmp0 = r.x; - tmp0.square_assign(); - - let mut tmp1 = r.y; - tmp1.square_assign(); - - let mut tmp2 = tmp1; - tmp2.square_assign(); - - let mut tmp3 = tmp1; - tmp3 += &r.x; - tmp3.square_assign(); - tmp3 -= &tmp0; - tmp3 -= &tmp2; - tmp3.double_assign(); - - let mut tmp4 = tmp0; - tmp4.double_assign(); - tmp4 += &tmp0; - - let mut tmp6 = r.x; - tmp6 += &tmp4; - - let mut tmp5 = tmp4; - tmp5.square_assign(); - - let mut zsquared = r.z; - zsquared.square_assign(); - - r.x = tmp5; - r.x -= &tmp3; - r.x -= &tmp3; - - r.z += &r.y; - r.z.square_assign(); - r.z -= &tmp1; - r.z -= &zsquared; - - r.y = tmp3; - r.y -= &r.x; - r.y.mul_assign(&tmp4); - - tmp2.double_assign(); - tmp2.double_assign(); - tmp2.double_assign(); - - r.y -= &tmp2; - - // up to here everything was by algorith, line 11 - // use R instead of new T - - // tmp3 is the first part of line 12 - tmp3 = tmp4; - tmp3.mul_assign(&zsquared); - tmp3.double_assign(); - tmp3 = tmp3.neg(); - - // tmp6 is from line 14 - tmp6.square_assign(); - tmp6 -= &tmp0; - tmp6 -= &tmp5; - - tmp1.double_assign(); - tmp1.double_assign(); - - tmp6 -= &tmp1; - - // tmp0 is the first part of line 16 - tmp0 = r.z; - tmp0.mul_assign(&zsquared); - tmp0.double_assign(); - - (tmp0, tmp3, tmp6) - } - - fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) { - // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf - let mut zsquared = r.z; - zsquared.square_assign(); - - let mut ysquared = q.y; - ysquared.square_assign(); - - // t0 corresponds to line 1 - let mut t0 = zsquared; - t0.mul_assign(&q.x); - - // t1 corresponds to lines 2 and 3 - let mut t1 = q.y; - t1 += &r.z; - t1.square_assign(); - t1 -= &ysquared; - t1 -= &zsquared; - t1.mul_assign(&zsquared); - - // t2 corresponds to line 4 - let mut t2 = t0; - t2 -= &r.x; - - // t3 corresponds to line 5 - let mut t3 = t2; - t3.square_assign(); - - // t4 corresponds to line 6 - let mut t4 = t3; - t4.double_assign(); - t4.double_assign(); - - // t5 corresponds to line 7 - let mut t5 = t4; - t5.mul_assign(&t2); - - // t6 corresponds to line 8 - let mut t6 = t1; - t6 -= &r.y; - t6 -= &r.y; - - // t9 corresponds to line 9 - let mut t9 = t6; - t9.mul_assign(&q.x); - - // corresponds to line 10 - let mut t7 = t4; - t7.mul_assign(&r.x); - - // corresponds to line 11, but assigns to r.x instead of T.x - r.x = t6; - r.x.square_assign(); - r.x -= &t5; - r.x -= &t7; - r.x -= &t7; - - // corresponds to line 12, but assigns to r.z instead of T.z - r.z += &t2; - r.z.square_assign(); - r.z -= &zsquared; - r.z -= &t3; - - // corresponds to line 13 - let mut t10 = q.y; - t10 += &r.z; - - // corresponds to line 14 - let mut t8 = t7; - t8 -= &r.x; - t8.mul_assign(&t6); - - // corresponds to line 15 - t0 = r.y; - t0.mul_assign(&t5); - t0.double_assign(); - - // corresponds to line 12, but assigns to r.y instead of T.y - r.y = t8; - r.y -= &t0; - - // corresponds to line 17 - t10.square_assign(); - t10 -= &ysquared; - - let mut ztsquared = r.z; - ztsquared.square_assign(); - - t10 -= &ztsquared; - - // corresponds to line 18 - t9.double_assign(); - t9 -= &t10; - - // t10 = 2*Zt from Algo 27, line 19 - t10 = r.z; - t10.double_assign(); - - // t1 = first multiplicator of line 21 - t6 = t6.neg(); - - t1 = t6; - t1.double_assign(); - - // t9 corresponds to t9 from Algo 27 - (t10, t1, t9) - } - - let mut coeffs = vec![]; - let mut r: G2 = q.into(); - - let mut negq = q; - negq = -negq; - - for i in (1..SIX_U_PLUS_2_NAF.len()).rev() { - coeffs.push(doubling_step(&mut r)); - let x = SIX_U_PLUS_2_NAF[i - 1]; - match x { - 1 => { - coeffs.push(addition_step(&mut r, &q)); - } - -1 => { - coeffs.push(addition_step(&mut r, &negq)); - } - _ => continue, - } - } - - let mut q1 = q; - - q1.x.c1 = q1.x.c1.neg(); - q1.x.mul_assign(&FROBENIUS_COEFF_FQ6_C1[1]); - - q1.y.c1 = q1.y.c1.neg(); - q1.y.mul_assign(&XI_TO_Q_MINUS_1_OVER_2); - - coeffs.push(addition_step(&mut r, &q1)); - - let mut minusq2 = q; - minusq2.x.mul_assign(&FROBENIUS_COEFF_FQ6_C1[2]); - - coeffs.push(addition_step(&mut r, &minusq2)); - - G2Prepared { - coeffs, - infinity: false, - } - } -} - -impl From for G2Prepared { - fn from(q: G2Affine) -> G2Prepared { - G2Prepared::from_affine(q) - } -} - -impl MillerLoopResult for Gt { - type Gt = Self; - // pub fn final_exponentiation(r: &Fq12) -> CtOption { - fn final_exponentiation(&self) -> Gt { - fn exp_by_x(f: &mut Fq12) { - let x = BN_X; - let mut res = Fq12::one(); - for i in (0..64).rev() { - res.cyclotomic_square(); - if ((x >> i) & 1) == 1 { - res.mul_assign(f); - } - } - *f = res; - } - - let r = self.0; - let mut f1 = self.0; - f1.conjugate(); - - Gt(r.invert() - .map(|mut f2| { - let mut r = f1; - r.mul_assign(&f2); - f2 = r; - r.frobenius_map(2); - r.mul_assign(&f2); - - let mut fp = r; - fp.frobenius_map(1); - - let mut fp2 = r; - fp2.frobenius_map(2); - let mut fp3 = fp2; - fp3.frobenius_map(1); - - let mut fu = r; - exp_by_x(&mut fu); - - let mut fu2 = fu; - exp_by_x(&mut fu2); - - let mut fu3 = fu2; - exp_by_x(&mut fu3); - - let mut y3 = fu; - y3.frobenius_map(1); - - let mut fu2p = fu2; - fu2p.frobenius_map(1); - - let mut fu3p = fu3; - fu3p.frobenius_map(1); - - let mut y2 = fu2; - y2.frobenius_map(2); - - let mut y0 = fp; - y0.mul_assign(&fp2); - y0.mul_assign(&fp3); - - let mut y1 = r; - y1.conjugate(); - - let mut y5 = fu2; - y5.conjugate(); - - y3.conjugate(); - - let mut y4 = fu; - y4.mul_assign(&fu2p); - y4.conjugate(); - - let mut y6 = fu3; - y6.mul_assign(&fu3p); - y6.conjugate(); - - y6.cyclotomic_square(); - y6.mul_assign(&y4); - y6.mul_assign(&y5); - - let mut t1 = y3; - t1.mul_assign(&y5); - t1.mul_assign(&y6); - - y6.mul_assign(&y2); - - t1.cyclotomic_square(); - t1.mul_assign(&y6); - t1.cyclotomic_square(); - - let mut t0 = t1; - t0.mul_assign(&y1); - - t1.mul_assign(&y0); - - t0.cyclotomic_square(); - t0.mul_assign(&t1); - - t0 - }) - .unwrap()) - } -} - -pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> Gt { - let mut pairs = vec![]; - for &(p, q) in terms { - if !bool::from(p.is_identity()) && !q.is_zero() { - pairs.push((p, q.coeffs.iter())); - } - } - - // Final steps of the line function on prepared coefficients - 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_034(&c0, &c1, &coeffs.2); - } - - let mut f = Fq12::one(); - - for i in (1..SIX_U_PLUS_2_NAF.len()).rev() { - if i != SIX_U_PLUS_2_NAF.len() - 1 { - f.square_assign(); - } - for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), p); - } - let x = SIX_U_PLUS_2_NAF[i - 1]; - match x { - 1 => { - for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), p); - } - } - -1 => { - for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), p); - } - } - _ => continue, - } - } - - for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), p); - } - - for &mut (p, ref mut coeffs) in &mut pairs { - ell(&mut f, coeffs.next().unwrap(), p); - } - - for &mut (_p, ref mut coeffs) in &mut pairs { - assert_eq!(coeffs.next(), None); - } - - Gt(f) -} - -pub fn pairing(g1: &G1Affine, g2: &G2Affine) -> Gt { - let g2 = G2Prepared::from_affine(*g2); - let terms: &[(&G1Affine, &G2Prepared)] = &[(g1, &g2)]; - let u = multi_miller_loop(terms); - u.final_exponentiation() -} - -#[derive(Clone, Debug)] -pub struct Bn256; - -impl Engine for Bn256 { - type Scalar = Fr; - type G1 = G1; - type G1Affine = G1Affine; - type G2 = G2; - type G2Affine = G2Affine; - type Gt = Gt; - - fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { - pairing(p, q) - } -} - -impl MultiMillerLoop for Bn256 { - type G2Prepared = G2Prepared; - type Result = Gt; - - fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result { - multi_miller_loop(terms) - } -} - -#[cfg(test)] -use rand::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_pairing() { - let g1 = G1::generator(); - let mut g2 = G2::generator(); - g2 = g2.double(); - let pair12 = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2)); - - let mut g1 = G1::generator(); - let g2 = G2::generator(); - g1 = g1.double(); - let pair21 = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2)); - - assert_eq!(pair12, pair21); - - let g1 = G1::generator(); - let mut g2 = G2::generator(); - g2 = g2.double().double(); - let pair12 = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2)); - - let mut g1 = G1::generator(); - let mut g2 = G2::generator(); - g1 = g1.double(); - g2 = g2.double(); - let pair21 = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2)); - - assert_eq!(pair12, pair21); - - 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); - let b = Fr::random(&mut rng); - - let mut g1 = G1::generator(); - g1.mul_assign(a); - - let mut g2 = G2::generator(); - g1.mul_assign(b); - - let pair_ab = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2)); - - g1 = G1::generator(); - g1.mul_assign(b); - - g2 = G2::generator(); - g1.mul_assign(a); - - let pair_ba = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2)); - - assert_eq!(pair_ab, pair_ba); - } -} - -#[test] -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 mut a = G1::generator(); - let ka = Fr::random(&mut rng); - a.mul_assign(ka); - - let mut b = G2::generator(); - let kb = Fr::random(&mut rng); - b.mul_assign(kb); - - let c = Fr::random(&mut rng); - let d = 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 = Bn256::pairing(&G1Affine::from(ac), &G2Affine::from(bd)); - let adbc = Bn256::pairing(&G1Affine::from(ad), &G2Affine::from(bc)); - - let mut cd = c; - cd.mul_assign(&d); - - cd *= Fr([1, 0, 0, 0]); - - let abcd = Gt(Bn256::pairing(&G1Affine::from(a), &G2Affine::from(b)) - .0 - .pow_vartime(cd.0)); - - assert_eq!(acbd, adbc); - assert_eq!(acbd, abcd); - } -} - -#[test] -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 = G1Affine::from(G1::random(&mut rng)); - let b = G2Affine::from(G2::random(&mut rng)); - - assert!(a.pairing_with(&b) == b.pairing_with(&a)); - assert!(a.pairing_with(&b) == pairing(&a, &b)); - } - - for _ in 0..1000 { - let z1 = G1Affine::identity(); - let z2 = G2Prepared::from(G2Affine::identity()); - - let a = G1Affine::from(G1::random(&mut rng)); - let b = G2Prepared::from(G2Affine::from(G2::random(&mut rng))); - let c = G1Affine::from(G1::random(&mut rng)); - let d = G2Prepared::from(G2Affine::from(G2::random(&mut rng))); - - assert_eq!( - Fq12::one(), - multi_miller_loop(&[(&z1, &b)]).final_exponentiation().0, - ); - - assert_eq!( - Fq12::one(), - multi_miller_loop(&[(&a, &z2)]).final_exponentiation().0, - ); - - assert_eq!( - multi_miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(), - multi_miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation(), - ); - - assert_eq!( - multi_miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(), - multi_miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation(), - ); - } -} - -#[test] -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 a double miller loop - for _ in 0..1000 { - let a = G1Affine::from(G1::random(&mut rng)); - let b = G2Affine::from(G2::random(&mut rng)); - let c = G1Affine::from(G1::random(&mut rng)); - let d = G2Affine::from(G2::random(&mut rng)); - - let ab = pairing(&a, &b); - let cd = pairing(&c, &d); - - let mut abcd = ab; - abcd = Gt(abcd.0 * cd.0); - - let b = G2Prepared::from(b); - let d = G2Prepared::from(d); - - let abcd_with_double_loop = multi_miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); - - assert_eq!(abcd, abcd_with_double_loop); - } -} diff --git a/arithmetic/curves/src/bn256/fq.rs b/arithmetic/curves/src/bn256/fq.rs deleted file mode 100644 index b108e2ed9e..0000000000 --- a/arithmetic/curves/src/bn256/fq.rs +++ /dev/null @@ -1,362 +0,0 @@ -#[cfg(feature = "asm")] -use super::assembly::assembly_field; - -use super::LegendreSymbol; -use crate::arithmetic::{adc, mac, macx, sbb}; -use pasta_curves::arithmetic::{FieldExt, Group, SqrtRatio}; -use serde::{Deserialize, Serialize}; - -use core::convert::TryInto; -use core::fmt; -use core::ops::{Add, Mul, Neg, Sub}; -use ff::PrimeField; -use rand::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -/// This represents an element of $\mathbb{F}_q$ where -/// -/// `p = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47` -/// -/// is the base field of the BN254 curve. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. `Fq` values are always in -// Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct Fq(pub(crate) [u64; 4]); - -/// Constant representing the modulus -/// q = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 -pub const MODULUS: Fq = Fq([ - 0x3c208c16d87cfd47, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, -]); - -/// INV = -(q^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0x87d20782e4866389; - -/// R = 2^256 mod q -const R: Fq = Fq([ - 0xd35d438dc58f0d9d, - 0x0a78eb28f5c70b3d, - 0x666ea36f7879462c, - 0x0e0a77c19a07df2f, -]); - -/// R^2 = 2^512 mod q -const R2: Fq = Fq([ - 0xf32cfc5b538afa89, - 0xb5e71911d44501fb, - 0x47ab1eff0a417ff6, - 0x06d89f71cab8351f, -]); - -/// R^3 = 2^768 mod q -const R3: Fq = Fq([ - 0xb1cd6dafda1530df, - 0x62f210e6a7283db6, - 0xef7f0b0c0ada0afb, - 0x20fd6e902d592544, -]); - -pub const NEGATIVE_ONE: Fq = Fq([ - 0x68c3488912edefaa, - 0x8d087f6872aabf4f, - 0x51e1a24709081231, - 0x2259d6b14729c0fa, -]); - -const MODULUS_STR: &str = "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"; - -const TWO_INV: Fq = Fq::from_raw([ - 0x9e10460b6c3e7ea4, - 0xcbc0b548b438e546, - 0xdc2822db40c0ac2e, - 0x183227397098d014, -]); - -// Unused constant for base field -const ROOT_OF_UNITY_INV: Fq = Fq::zero(); - -// Unused constant for base field -const DELTA: Fq = Fq::zero(); - -/// `ZETA^3 = 1 mod r` where `ZETA^2 != 1 mod r` -const ZETA: Fq = Fq::from_raw([ - 0x5763473177fffffeu64, - 0xd4f263f1acdb5c4fu64, - 0x59e26bcea0d48bacu64, - 0x0u64, -]); - -use crate::{ - field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, - impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, - impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, -}; -impl_binops_additive!(Fq, Fq); -impl_binops_multiplicative!(Fq, Fq); -#[cfg(not(feature = "asm"))] -field_common!( - Fq, - MODULUS, - INV, - MODULUS_STR, - TWO_INV, - ROOT_OF_UNITY_INV, - DELTA, - ZETA, - R, - R2, - R3 -); -#[cfg(not(feature = "asm"))] -field_arithmetic!(Fq, MODULUS, INV, sparse); -#[cfg(feature = "asm")] -assembly_field!( - Fq, - MODULUS, - INV, - MODULUS_STR, - TWO_INV, - ROOT_OF_UNITY_INV, - DELTA, - ZETA, - R, - R2, - R3 -); - -impl Fq { - pub const fn size() -> usize { - 32 - } - - pub fn legendre(&self) -> LegendreSymbol { - // s = self^((modulus - 1) // 2) - // 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3 - let s = &[ - 0x9e10460b6c3e7ea3u64, - 0xcbc0b548b438e546u64, - 0xdc2822db40c0ac2eu64, - 0x183227397098d014u64, - ]; - let s = self.pow(s); - if s == Self::zero() { - LegendreSymbol::Zero - } else if s == Self::one() { - LegendreSymbol::QuadraticResidue - } else { - LegendreSymbol::QuadraticNonResidue - } - } -} - -impl ff::Field for Fq { - fn random(mut rng: impl RngCore) -> Self { - let mut random_bytes = [0; 64]; - rng.fill_bytes(&mut random_bytes[..]); - - Self::from_bytes_wide(&random_bytes) - } - - #[inline(always)] - fn zero() -> Self { - Self::zero() - } - - #[inline(always)] - fn one() -> Self { - Self::one() - } - - fn double(&self) -> Self { - self.double() - } - - #[inline(always)] - fn square(&self) -> Self { - self.square() - } - - /// Computes the square root of this element, if it exists. - fn sqrt(&self) -> CtOption { - let tmp = self.pow(&[ - 0x4f082305b61f3f52, - 0x65e05aa45a1c72a3, - 0x6e14116da0605617, - 0x0c19139cb84c680a, - ]); - - CtOption::new(tmp, tmp.square().ct_eq(self)) - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - fn invert(&self) -> CtOption { - let tmp = self.pow(&[ - 0x3c208c16d87cfd45, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, - ]); - - CtOption::new(tmp, !self.ct_eq(&Self::zero())) - } -} - -impl ff::PrimeField for Fq { - type Repr = [u8; 32]; - - const NUM_BITS: u32 = 254; - const CAPACITY: u32 = 253; - - const S: u32 = 0; - - fn from_repr(repr: Self::Repr) -> CtOption { - let mut tmp = Fq([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); - tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); - tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); - tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); - - // Try to subtract the modulus - let (_, borrow) = tmp.0[0].overflowing_sub(MODULUS.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)) - } - - fn to_repr(&self) -> Self::Repr { - // Turn into canonical form by computing - // (a.R) / R = a - #[cfg(feature = "asm")] - let tmp = - Self::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); - - #[cfg(not(feature = "asm"))] - let tmp = Self::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - - 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 - } - - fn is_odd(&self) -> Choice { - Choice::from(self.to_repr()[0] & 1) - } - - fn multiplicative_generator() -> Self { - unimplemented!() - } - - fn root_of_unity() -> Self { - unimplemented!() - } -} - -impl SqrtRatio for Fq { - const T_MINUS1_OVER2: [u64; 4] = [0, 0, 0, 0]; - - fn get_lower_32(&self) -> u32 { - #[cfg(not(feature = "asm"))] - let tmp = Fq::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - - #[cfg(feature = "asm")] - let tmp = Fq::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); - - tmp.0[0] as u32 - } -} - -#[cfg(test)] -mod test { - use super::*; - use ff::Field; - use rand_core::OsRng; - - #[test] - fn test_sqrt_fq() { - let v = (Fq::TWO_INV).square().sqrt().unwrap(); - assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV); - - for _ in 0..10000 { - let a = Fq::random(OsRng); - let mut b = a; - b = b.square(); - assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); - - let b = b.sqrt().unwrap(); - let mut negb = b; - negb = negb.neg(); - - assert!(a == b || a == negb); - } - - let mut c = Fq::one(); - for _ in 0..10000 { - let mut b = c; - b = b.square(); - assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); - - b = b.sqrt().unwrap(); - - if b != c { - b = b.neg(); - } - - assert_eq!(b, c); - - c += &Fq::one(); - } - } - - #[test] - fn test_from_u512() { - assert_eq!( - Fq::from_raw([ - 0x1f8905a172affa8a, - 0xde45ad177dcf3306, - 0xaaa7987907d73ae2, - 0x24d349431d468e30, - ]), - Fq::from_u512([ - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa - ]) - ); - } - - #[test] - fn test_field() { - crate::tests::field::random_field_tests::("fq".to_string()); - } - - #[test] - fn test_serialization() { - crate::tests::field::random_serialization_test::("fq".to_string()); - } -} diff --git a/arithmetic/curves/src/bn256/fq12.rs b/arithmetic/curves/src/bn256/fq12.rs deleted file mode 100644 index d98c7c0c9f..0000000000 --- a/arithmetic/curves/src/bn256/fq12.rs +++ /dev/null @@ -1,592 +0,0 @@ -use super::fq::Fq; -use super::fq2::Fq2; -use super::fq6::Fq6; -use core::ops::{Add, Mul, Neg, Sub}; -use ff::Field; -use rand::RngCore; -use serde::{Deserialize, Serialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] -pub struct Fq12 { - pub c0: Fq6, - pub c1: Fq6, -} - -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 ConstantTimeEq for Fq12 { - fn ct_eq(&self, other: &Self) -> Choice { - self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) - } -} - -impl Neg for Fq12 { - type Output = Fq12; - - #[inline] - fn neg(self) -> Fq12 { - -&self - } -} - -impl<'a> Neg for &'a Fq12 { - type Output = Fq12; - - #[inline] - fn neg(self) -> Fq12 { - self.neg() - } -} - -impl<'a, 'b> Sub<&'b Fq12> for &'a Fq12 { - type Output = Fq12; - - #[inline] - fn sub(self, rhs: &'b Fq12) -> Fq12 { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fq12> for &'a Fq12 { - type Output = Fq12; - - #[inline] - fn add(self, rhs: &'b Fq12) -> Fq12 { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fq12> for &'a Fq12 { - type Output = Fq12; - - #[inline] - fn mul(self, rhs: &'b Fq12) -> Fq12 { - self.mul(rhs) - } -} - -use crate::{ - impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, - impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, -}; -impl_binops_additive!(Fq12, Fq12); -impl_binops_multiplicative!(Fq12, Fq12); - -impl Fq12 { - pub fn mul_assign(&mut self, other: &Self) { - let t0 = self.c0 * other.c0; - let mut t1 = self.c1 * other.c1; - let t2 = other.c0 + other.c1; - - self.c1 += &self.c0; - self.c1 *= &t2; - self.c1 -= &t0; - self.c1 -= &t1; - - t1.mul_by_nonresidue(); - self.c0 = t0 + t1; - } - - pub fn square_assign(&mut self) { - let mut ab = self.c0 * self.c1; - - let c0c1 = self.c0 + self.c1; - - let mut c0 = self.c1; - c0.mul_by_nonresidue(); - c0 += &self.c0; - c0 *= &c0c1; - c0 -= &ab; - self.c1 = ab; - self.c1 += &ab; - ab.mul_by_nonresidue(); - c0 -= &ab; - self.c0 = c0; - } - - pub fn double(&self) -> Self { - Self { - c0: self.c0.double(), - c1: self.c1.double(), - } - } - - pub fn double_assign(&mut self) { - self.c0 = self.c0.double(); - self.c1 = self.c1.double(); - } - - pub fn add(&self, other: &Self) -> Self { - Self { - c0: self.c0 + other.c0, - c1: self.c1 + other.c1, - } - } - - pub fn sub(&self, other: &Self) -> Self { - Self { - c0: self.c0 - other.c0, - c1: self.c1 - other.c1, - } - } - - pub fn mul(&self, other: &Self) -> Self { - let mut t = *other; - t.mul_assign(self); - t - } - - pub fn square(&self) -> Self { - let mut t = *self; - t.square_assign(); - t - } - - #[inline(always)] - pub fn neg(&self) -> Self { - Self { - c0: -self.c0, - c1: -self.c1, - } - } - - #[inline(always)] - pub fn conjugate(&mut self) { - self.c1 = -self.c1; - } - - // pub fn conjugate(&self) -> Self { - // Self { - // c0: self.c0, - // c1: -self.c1, - // } - // } - - pub 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]); - } - - 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 o = c1 + c4; - self.c1 += &self.c0; - self.c1.mul_by_01(c0, &o); - self.c1 -= &aa; - self.c1 -= &bb; - self.c0 = bb; - self.c0.mul_by_nonresidue(); - self.c0 += &aa; - } - - pub fn mul_by_034(&mut self, c0: &Fq2, c3: &Fq2, c4: &Fq2) { - let t0 = Fq6 { - c0: self.c0.c0 * c0, - c1: self.c0.c1 * c0, - c2: self.c0.c2 * c0, - }; - let mut t1 = self.c1; - t1.mul_by_01(c3, c4); - let o = c0 + c3; - let mut t2 = self.c0 + self.c1; - t2.mul_by_01(&o, c4); - t2 -= t0; - self.c1 = t2 - t1; - t1.mul_by_nonresidue(); - self.c0 = t0 + t1; - } - - pub fn invert(&self) -> CtOption { - let mut c0s = self.c0; - c0s.square_assign(); - let mut c1s = self.c1; - c1s.square_assign(); - c1s.mul_by_nonresidue(); - c0s -= &c1s; - - c0s.invert().map(|t| { - let mut tmp = Fq12 { c0: t, c1: t }; - tmp.c0.mul_assign(&self.c0); - tmp.c1.mul_assign(&self.c1); - tmp.c1 = tmp.c1.neg(); - - tmp - }) - } - - pub fn cyclotomic_square(&mut self) { - fn fp4_square(c0: &mut Fq2, c1: &mut Fq2, a0: &Fq2, a1: &Fq2) { - let t0 = a0.square(); - let t1 = a1.square(); - let mut t2 = t1; - t2.mul_by_nonresidue(); - *c0 = t2 + t0; - t2 = a0 + a1; - t2.square_assign(); - t2 -= t0; - *c1 = t2 - t1; - } - - let mut t3 = Fq2::zero(); - let mut t4 = Fq2::zero(); - let mut t5 = Fq2::zero(); - let mut t6 = Fq2::zero(); - - fp4_square(&mut t3, &mut t4, &self.c0.c0, &self.c1.c1); - let mut t2 = t3 - self.c0.c0; - t2.double_assign(); - self.c0.c0 = t2 + t3; - - t2 = t4 + self.c1.c1; - t2.double_assign(); - self.c1.c1 = t2 + t4; - - fp4_square(&mut t3, &mut t4, &self.c1.c0, &self.c0.c2); - fp4_square(&mut t5, &mut t6, &self.c0.c1, &self.c1.c2); - - t2 = t3 - self.c0.c1; - t2.double_assign(); - self.c0.c1 = t2 + t3; - t2 = t4 + self.c1.c2; - t2.double_assign(); - self.c1.c2 = t2 + t4; - t3 = t6; - t3.mul_by_nonresidue(); - t2 = t3 + self.c1.c0; - t2.double_assign(); - self.c1.c0 = t2 + t3; - t2 = t5 - self.c0.c2; - t2.double_assign(); - self.c0.c2 = t2 + t5; - } -} - -impl Field for Fq12 { - fn random(mut rng: impl RngCore) -> Self { - Fq12 { - c0: Fq6::random(&mut rng), - c1: Fq6::random(&mut 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) -> Choice { - self.c0.is_zero() & self.c1.is_zero() - } - - fn square(&self) -> Self { - self.square() - } - - fn double(&self) -> Self { - self.double() - } - - fn sqrt(&self) -> CtOption { - unimplemented!() - } - - fn invert(&self) -> CtOption { - self.invert() - } -} - -// non_residue^((modulus^i-1)/6) for i=0,...,11 -pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ - // Fq2(u + 9)**(((q^0) - 1) / 6) - // Fq points are represented in Montgomery form with R = 2^256 - Fq2 { - c0: Fq([ - 0xd35d438dc58f0d9d, - 0x0a78eb28f5c70b3d, - 0x666ea36f7879462c, - 0x0e0a77c19a07df2f, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 9)**(((q^1) - 1) / 6) - Fq2 { - c0: Fq([ - 0xaf9ba69633144907, - 0xca6b1d7387afb78a, - 0x11bded5ef08a2087, - 0x02f34d751a1f3a7c, - ]), - c1: Fq([ - 0xa222ae234c492d72, - 0xd00f02a4565de15b, - 0xdc2ff3a253dfc926, - 0x10a75716b3899551, - ]), - }, - // Fq2(u + 9)**(((q^2) - 1) / 6) - Fq2 { - c0: Fq([ - 0xca8d800500fa1bf2, - 0xf0c5d61468b39769, - 0x0e201271ad0d4418, - 0x04290f65bad856e6, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 9)**(((q^3) - 1) / 6) - Fq2 { - c0: Fq([ - 0x365316184e46d97d, - 0x0af7129ed4c96d9f, - 0x659da72fca1009b5, - 0x08116d8983a20d23, - ]), - c1: Fq([ - 0xb1df4af7c39c1939, - 0x3d9f02878a73bf7f, - 0x9b2220928caf0ae0, - 0x26684515eff054a6, - ]), - }, - // Fq2(u + 9)**(((q^4) - 1) / 6) - Fq2 { - c0: Fq([ - 0x3350c88e13e80b9c, - 0x7dce557cdb5e56b9, - 0x6001b4b8b615564a, - 0x2682e617020217e0, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 9)**(((q^5) - 1) / 6) - Fq2 { - c0: Fq([ - 0x86b76f821b329076, - 0x408bf52b4d19b614, - 0x53dfb9d0d985e92d, - 0x051e20146982d2a7, - ]), - c1: Fq([ - 0x0fbc9cd47752ebc7, - 0x6d8fffe33415de24, - 0xbef22cf038cf41b9, - 0x15c0edff3c66bf54, - ]), - }, - // Fq2(u + 9)**(((q^6) - 1) / 6) - Fq2 { - c0: Fq([ - 0x68c3488912edefaa, - 0x8d087f6872aabf4f, - 0x51e1a24709081231, - 0x2259d6b14729c0fa, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 9)**(((q^7) - 1) / 6) - Fq2 { - c0: Fq([ - 0x8c84e580a568b440, - 0xcd164d1de0c21302, - 0xa692585790f737d5, - 0x2d7100fdc71265ad, - ]), - c1: Fq([ - 0x99fdddf38c33cfd5, - 0xc77267ed1213e931, - 0xdc2052142da18f36, - 0x1fbcf75c2da80ad7, - ]), - }, - // Fq2(u + 9)**(((q^8) - 1) / 6) - Fq2 { - c0: Fq([ - 0x71930c11d782e155, - 0xa6bb947cffbe3323, - 0xaa303344d4741444, - 0x2c3b3f0d26594943, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 9)**(((q^9) - 1) / 6) - Fq2 { - c0: Fq([ - 0x05cd75fe8a3623ca, - 0x8c8a57f293a85cee, - 0x52b29e86b7714ea8, - 0x2852e0e95d8f9306, - ]), - c1: Fq([ - 0x8a41411f14e0e40e, - 0x59e26809ddfe0b0d, - 0x1d2e2523f4d24d7d, - 0x09fc095cf1414b83, - ]), - }, - // Fq2(u + 9)**(((q^10) - 1) / 6) - Fq2 { - c0: Fq([ - 0x08cfc388c494f1ab, - 0x19b315148d1373d4, - 0x584e90fdcb6c0213, - 0x09e1685bdf2f8849, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 9)**(((q^11) - 1) / 6) - Fq2 { - c0: Fq([ - 0xb5691c94bd4a6cd1, - 0x56f575661b581478, - 0x64708be5a7fb6f30, - 0x2b462e5e77aecd82, - ]), - c1: Fq([ - 0x2c63ef42612a1180, - 0x29f16aae345bec69, - 0xf95e18c648b216a4, - 0x1aa36073a4cae0d4, - ]), - }, -]; - -#[cfg(test)] -use rand::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 test_fq12_mul_by_034() { - 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 c3 = Fq2::random(&mut rng); - let c4 = Fq2::random(&mut rng); - let mut a = Fq12::random(&mut rng); - let mut b = a; - - a.mul_by_034(&c0, &c3, &c4); - b.mul_assign(&Fq12 { - c0: Fq6 { - c0, - c1: Fq2::zero(), - c2: Fq2::zero(), - }, - c1: Fq6 { - c0: c3, - c1: c4, - c2: Fq2::zero(), - }, - }); - - assert_eq!(a, b); - } -} - -#[test] -fn test_squaring() { - 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 = Fq12::random(&mut rng); - let mut b = a; - b.mul_assign(&a); - a.square_assign(); - assert_eq!(a, b); - } -} - -#[test] -fn test_frobenius() { - 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..14 { - let mut a = Fq12::random(&mut rng); - let mut b = a; - - for _ in 0..i { - a = a.pow_vartime([ - 0x3c208c16d87cfd47, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, - ]); - } - b.frobenius_map(i); - - assert_eq!(a, b); - } - } -} - -#[test] -fn test_field() { - crate::tests::field::random_field_tests::("fq12".to_string()); -} diff --git a/arithmetic/curves/src/bn256/fq2.rs b/arithmetic/curves/src/bn256/fq2.rs deleted file mode 100644 index c362669da0..0000000000 --- a/arithmetic/curves/src/bn256/fq2.rs +++ /dev/null @@ -1,841 +0,0 @@ -use super::fq::{Fq, NEGATIVE_ONE}; -use super::LegendreSymbol; -use core::convert::TryInto; -use core::ops::{Add, Mul, Neg, Sub}; -use ff::Field; -use pasta_curves::arithmetic::{FieldExt, Group, SqrtRatio}; -use rand::RngCore; -use serde::{Deserialize, Serialize}; -use std::cmp::Ordering; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -/// An element of Fq2, represented by c0 + c1 * u. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct Fq2 { - pub c0: Fq, - pub c1: Fq, -} - -/// `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 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 ConstantTimeEq for Fq2 { - fn ct_eq(&self, other: &Self) -> Choice { - self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) - } -} - -impl Default for Fq2 { - #[inline] - fn default() -> Self { - Self::zero() - } -} - -impl From for [u8; 64] { - fn from(value: Fq2) -> [u8; 64] { - value.to_bytes() - } -} - -impl<'a> From<&'a Fq2> for [u8; 64] { - fn from(value: &'a Fq2) -> [u8; 64] { - value.to_bytes() - } -} - -impl Neg for Fq2 { - type Output = Fq2; - - #[inline] - fn neg(self) -> Fq2 { - -&self - } -} - -impl<'a> Neg for &'a Fq2 { - type Output = Fq2; - - #[inline] - fn neg(self) -> Fq2 { - self.neg() - } -} - -impl<'a, 'b> Sub<&'b Fq2> for &'a Fq2 { - type Output = Fq2; - - #[inline] - fn sub(self, rhs: &'b Fq2) -> Fq2 { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fq2> for &'a Fq2 { - type Output = Fq2; - - #[inline] - fn add(self, rhs: &'b Fq2) -> Fq2 { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fq2> for &'a Fq2 { - type Output = Fq2; - - #[inline] - fn mul(self, rhs: &'b Fq2) -> Fq2 { - self.mul(rhs) - } -} - -use crate::{ - impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, - impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, -}; -impl_binops_additive!(Fq2, Fq2); -impl_binops_multiplicative!(Fq2, Fq2); - -impl Fq2 { - pub const fn new(c0: Fq, c1: Fq) -> Self { - Fq2 { c0, c1 } - } - - pub const fn size() -> usize { - 64 - } - /// Attempts to convert a little-endian byte representation of - /// a scalar into a `Fq`, failing if the input is not canonical. - pub fn from_bytes(bytes: &[u8; 64]) -> CtOption { - let c0 = Fq::from_bytes(bytes[0..32].try_into().unwrap()); - let c1 = Fq::from_bytes(bytes[32..64].try_into().unwrap()); - CtOption::new( - Fq2 { - c0: c0.unwrap(), - c1: c1.unwrap(), - }, - c0.is_some() & c1.is_some(), - ) - } - - /// Converts an element of `Fq` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 64] { - let mut res = [0u8; 64]; - let c0_bytes = self.c0.to_bytes(); - let c1_bytes = self.c1.to_bytes(); - res[0..32].copy_from_slice(&c0_bytes[..]); - res[32..64].copy_from_slice(&c1_bytes[..]); - res - } - - pub fn legendre(&self) -> LegendreSymbol { - self.norm().legendre() - } - - pub fn mul_assign(&mut self, other: &Self) { - let mut t1 = self.c0 * other.c0; - let mut t0 = self.c0 + self.c1; - let t2 = self.c1 * other.c1; - self.c1 = other.c0 + other.c1; - self.c0 = t1 - t2; - t1 += t2; - t0 *= self.c1; - self.c1 = t0 - t1; - } - - pub fn square_assign(&mut self) { - let ab = self.c0 * self.c1; - let c0c1 = self.c0 + self.c1; - let mut c0 = -self.c1; - c0 += self.c0; - c0 *= c0c1; - c0 -= ab; - self.c1 = ab.double(); - self.c0 = c0 + ab; - } - - pub fn double(&self) -> Self { - Self { - c0: self.c0.double(), - c1: self.c1.double(), - } - } - - pub fn double_assign(&mut self) { - self.c0 = self.c0.double(); - self.c1 = self.c1.double(); - } - - pub fn add(&self, other: &Self) -> Self { - Self { - c0: self.c0.add(&other.c0), - c1: self.c1.add(&other.c1), - } - } - - pub fn sub(&self, other: &Self) -> Self { - Self { - c0: self.c0.sub(&other.c0), - c1: self.c1.sub(&other.c1), - } - } - - pub fn mul(&self, other: &Self) -> Self { - let mut t = *other; - t.mul_assign(self); - t - } - - pub fn square(&self) -> Self { - let mut t = *self; - t.square_assign(); - t - } - - pub fn neg(&self) -> Self { - Self { - c0: self.c0.neg(), - c1: self.c1.neg(), - } - } - - // conjucate by negating c1 - pub fn conjugate(&mut self) { - self.c1 = -self.c1; - } - - pub fn frobenius_map(&mut self, power: usize) { - self.c1 *= &FROBENIUS_COEFF_FQ2_C1[power % 2]; - } - - /// Multiply this element by quadratic nonresidue 9 + u. - pub fn mul_by_nonresidue(&mut self) { - // (xi+y)(i+9) = (9x+y)i+(9y-x) - let t0 = self.c0; - let t1 = self.c1; - - // 8*x*i + 8*y - self.double_assign(); - self.double_assign(); - self.double_assign(); - - // 9*y - self.c0 += &t0; - // (9*y - x) - self.c0 -= &t1; - - // (9*x)i - self.c1 += &t1; - // (9*x + y) - self.c1 += &t0; - } - - // Multiply this element by ξ where ξ=i+9 - pub fn mul_by_xi(&mut self) { - // (xi+y)(i+9) = (9x+y)i+(9y-x) - let t0 = self.c0; - let t1 = self.c1; - - // 8*x*i + 8*y - self.double_assign(); - self.double_assign(); - self.double_assign(); - - // 9*y - self.c0 += &t0; - // (9*y - x) - self.c0 -= &t1; - - // (9*x)i - self.c1 += &t1; - // (9*x + y) - self.c1 += &t0; - } - - /// Norm of Fq2 as extension field in i over Fq - pub fn norm(&self) -> Fq { - let mut t0 = self.c0; - let mut t1 = self.c1; - t0 = t0.square(); - t1 = t1.square(); - t1 + t0 - } - - pub fn invert(&self) -> CtOption { - let mut t1 = self.c1; - t1 = t1.square(); - let mut t0 = self.c0; - t0 = t0.square(); - t0 += &t1; - t0.invert().map(|t| { - let mut tmp = Fq2 { - c0: self.c0, - c1: self.c1, - }; - tmp.c0 *= &t; - tmp.c1 *= &t; - tmp.c1 = -tmp.c1; - - tmp - }) - } -} - -impl Field for Fq2 { - fn random(mut rng: impl RngCore) -> Self { - Fq2 { - c0: Fq::random(&mut rng), - c1: Fq::random(&mut 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) -> Choice { - self.c0.is_zero() & self.c1.is_zero() - } - - fn square(&self) -> Self { - self.square() - } - - fn double(&self) -> Self { - self.double() - } - - fn sqrt(&self) -> CtOption { - // Algorithm 9, https://eprint.iacr.org/2012/685.pdf - - if self.is_zero().into() { - CtOption::new(Self::zero(), Choice::from(1)) - } else { - // a1 = self^((q - 3) / 4) - // 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f51 - let u: [u64; 4] = [ - 0x4f082305b61f3f51, - 0x65e05aa45a1c72a3, - 0x6e14116da0605617, - 0x0c19139cb84c680a, - ]; - let mut a1 = self.pow(&u); - let mut alpha = a1; - - alpha.square_assign(); - 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(a0, Choice::from(0)) - } else { - a1.mul_assign(self); - - if alpha == neg1 { - a1.mul_assign(&Fq2 { - c0: Fq::zero(), - c1: Fq::one(), - }); - } else { - alpha += &Fq2::one(); - // alpha = alpha^((q - 1) / 2) - // 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3 - let u: [u64; 4] = [ - 0x9e10460b6c3e7ea3, - 0xcbc0b548b438e546, - 0xdc2822db40c0ac2e, - 0x183227397098d014, - ]; - alpha = alpha.pow(&u); - a1.mul_assign(&alpha); - } - CtOption::new(a1, Choice::from(1)) - } - } - } - - fn invert(&self) -> CtOption { - self.invert() - } -} - -impl From for Fq2 { - fn from(bit: bool) -> Fq2 { - if bit { - Fq2::one() - } else { - Fq2::zero() - } - } -} - -impl From for Fq2 { - fn from(val: u64) -> Self { - Fq2 { - c0: Fq::from(val), - c1: Fq::zero(), - } - } -} - -impl FieldExt for Fq2 { - const MODULUS: &'static str = - "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"; - - const ROOT_OF_UNITY_INV: Self = Fq2 { - c0: Fq::zero(), - c1: Fq::zero(), - }; - const DELTA: Self = Fq2 { - c0: Fq::zero(), - c1: Fq::zero(), - }; - const TWO_INV: Self = Fq2 { - c0: Fq::from_raw([ - 0x9e10460b6c3e7ea4, - 0xcbc0b548b438e546, - 0xdc2822db40c0ac2e, - 0x183227397098d014, - ]), - c1: Fq([0, 0, 0, 0]), - }; - const ZETA: Self = Fq2 { - c0: Fq::zero(), - c1: Fq::zero(), - }; - - /// Converts a 512-bit little endian integer into - /// a `Fq` by reducing by the modulus. - fn from_bytes_wide(bytes: &[u8; 64]) -> Self { - Self::new(Fq::from_bytes_wide(bytes), Fq::zero()) - } - - fn from_u128(v: u128) -> Self { - Fq2 { - c0: Fq::from_raw([v as u64, (v >> 64) as u64, 0, 0]), - c1: Fq::zero(), - } - } - - fn get_lower_128(&self) -> u128 { - self.c0.get_lower_128() - } - - // /// Writes this element in its normalized, little endian form into a buffer. - // fn write(&self, writer: &mut W) -> io::Result<()> { - // let compressed = self.to_bytes(); - // writer.write_all(&compressed[..]) - // } - - // /// Reads a normalized, little endian represented field element from a - // /// buffer. - // fn read(reader: &mut R) -> io::Result { - // let mut compressed = [0u8; 64]; - // reader.read_exact(&mut compressed[..])?; - // Option::from(Self::from_bytes(&compressed)) - // .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point encoding in proof")) - // } -} - -impl SqrtRatio for Fq2 { - const T_MINUS1_OVER2: [u64; 4] = [0, 0, 0, 0]; - - fn pow_by_t_minus1_over2(&self) -> Self { - unimplemented!(); - } - - fn get_lower_32(&self) -> u32 { - unimplemented!(); - } - - #[cfg(feature = "sqrt-table")] - fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { - unimplemented!(); - } - - #[cfg(feature = "sqrt-table")] - fn sqrt_alt(&self) -> (Choice, Self) { - unimplemented!(); - } -} - -impl Group for Fq2 { - type Scalar = Fq2; - - fn group_zero() -> Self { - Self::zero() - } - fn group_add(&mut self, rhs: &Self) { - *self += *rhs; - } - fn group_sub(&mut self, rhs: &Self) { - *self -= *rhs; - } - fn group_scale(&mut self, by: &Self::Scalar) { - *self *= *by; - } -} - -#[derive(Clone, Copy, Debug)] -pub struct Fq2Bytes([u8; 64]); - -impl Default for Fq2Bytes { - fn default() -> Self { - Self([0u8; 64]) - } -} - -impl AsMut<[u8]> for Fq2Bytes { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -impl AsRef<[u8]> for Fq2Bytes { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl ff::PrimeField for Fq2 { - type Repr = Fq2Bytes; - - const NUM_BITS: u32 = 254; - const CAPACITY: u32 = 253; - - const S: u32 = 0; - - fn from_repr(repr: Self::Repr) -> CtOption { - let c0 = Fq::from_bytes(&repr.0[..32].try_into().unwrap()); - let c1 = Fq::from_bytes(&repr.0[32..].try_into().unwrap()); - // Disallow overflow representation - CtOption::new(Fq2::new(c0.unwrap(), c1.unwrap()), Choice::from(1)) - } - - fn to_repr(&self) -> Self::Repr { - Fq2Bytes(self.to_bytes()) - } - - fn is_odd(&self) -> Choice { - Choice::from(self.to_repr().as_ref()[0] & 1) - } - - fn multiplicative_generator() -> Self { - unimplemented!() - } - - fn root_of_unity() -> Self { - unimplemented!() - } -} - -impl crate::serde::SerdeObject for Fq2 { - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { - debug_assert_eq!(bytes.len(), 64); - let [c0, c1] = [0, 32].map(|i| Fq::from_raw_bytes_unchecked(&bytes[i..i + 32])); - Self { c0, c1 } - } - fn from_raw_bytes(bytes: &[u8]) -> Option { - if bytes.len() != 64 { - return None; - } - let [c0, c1] = [0, 32].map(|i| Fq::from_raw_bytes(&bytes[i..i + 32])); - c0.zip(c1).map(|(c0, c1)| Self { c0, c1 }) - } - fn to_raw_bytes(&self) -> Vec { - let mut res = Vec::with_capacity(64); - for limb in self.c0.0.iter().chain(self.c1.0.iter()) { - res.extend_from_slice(&limb.to_le_bytes()); - } - res - } - fn read_raw_unchecked(reader: &mut R) -> Self { - let [c0, c1] = [(); 2].map(|_| Fq::read_raw_unchecked(reader)); - Self { c0, c1 } - } - fn read_raw(reader: &mut R) -> std::io::Result { - let c0 = Fq::read_raw(reader)?; - let c1 = Fq::read_raw(reader)?; - Ok(Self { c0, c1 }) - } - fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { - self.c0.write_raw(writer)?; - self.c1.write_raw(writer) - } -} - -pub const FROBENIUS_COEFF_FQ2_C1: [Fq; 2] = [ - // Fq(-1)**(((q^0) - 1) / 2) - // it's 1 in Montgommery form - Fq([ - 0xd35d438dc58f0d9d, - 0x0a78eb28f5c70b3d, - 0x666ea36f7879462c, - 0x0e0a77c19a07df2f, - ]), - // Fq(-1)**(((q^1) - 1) / 2) - Fq([ - 0x68c3488912edefaa, - 0x8d087f6872aabf4f, - 0x51e1a24709081231, - 0x2259d6b14729c0fa, - ]), -]; - -#[cfg(test)] -use rand::SeedableRng; -#[cfg(test)] -use rand_xorshift::XorShiftRng; - -#[test] -fn test_ser() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - let a0 = Fq2::random(&mut rng); - let a_bytes = a0.to_bytes(); - let a1 = Fq2::from_bytes(&a_bytes).unwrap(); - assert_eq!(a0, a1); -} - -#[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 += &Fq::one(); - assert!(a.cmp(&b) == Ordering::Less); - a.c0 += &Fq::one(); - assert!(a.cmp(&b) == Ordering::Equal); - b.c1 += &Fq::one(); - assert!(a.cmp(&b) == Ordering::Less); - a.c0 += &Fq::one(); - assert!(a.cmp(&b) == Ordering::Less); - a.c1 += &Fq::one(); - assert!(a.cmp(&b) == Ordering::Greater); - b.c0 += &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_eq!(Fq2::zero().is_zero().unwrap_u8(), 1); - assert_eq!(Fq2::one().is_zero().unwrap_u8(), 0); - assert_eq!( - Fq2 { - c0: Fq::zero(), - c1: Fq::one(), - } - .is_zero() - .unwrap_u8(), - 0 - ); -} - -#[test] -fn test_fq2_squaring() { - let mut a = Fq2 { - c0: Fq::one(), - c1: Fq::one(), - }; // u + 1 - a.square_assign(); - assert_eq!( - a, - Fq2 { - c0: Fq::zero(), - c1: Fq::one() + Fq::one(), - } - ); // 2u - - let mut a = Fq2 { - c0: Fq::zero(), - c1: Fq::one(), - }; // u - a.square_assign(); - assert_eq!(a, { - let neg1 = -Fq::one(); - Fq2 { - c0: neg1, - c1: Fq::zero(), - } - }); // -1 -} - -#[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 nine = Fq::one().double().double().double() + Fq::one(); - let nqr = Fq2 { - c0: nine, - 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 test_fq2_legendre() { - assert_eq!(LegendreSymbol::Zero, Fq2::zero().legendre()); - // i^2 = -1 - let mut m1 = Fq2::one(); - m1 = m1.neg(); - assert_eq!(LegendreSymbol::QuadraticResidue, m1.legendre()); - m1.mul_by_nonresidue(); - assert_eq!(LegendreSymbol::QuadraticNonResidue, m1.legendre()); -} - -#[test] -pub fn test_sqrt() { - 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 = Fq2::random(&mut rng); - if a.legendre() == LegendreSymbol::QuadraticNonResidue { - assert!(bool::from(a.sqrt().is_none())); - } - } - - for _ in 0..10000 { - let a = Fq2::random(&mut rng); - let mut b = a; - b.square_assign(); - assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); - - let b = b.sqrt().unwrap(); - let mut negb = b; - negb = negb.neg(); - - assert!(a == b || a == negb); - } - - let mut c = Fq2::one(); - for _ in 0..10000 { - let mut b = c; - b.square_assign(); - assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); - - b = b.sqrt().unwrap(); - - if b != c { - b = b.neg(); - } - - assert_eq!(b, c); - - c += &Fq2::one(); - } -} - -#[test] -fn test_frobenius() { - 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..14 { - let mut a = Fq2::random(&mut rng); - let mut b = a; - - for _ in 0..i { - a = a.pow(&[ - 0x3c208c16d87cfd47, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, - ]); - } - b.frobenius_map(i); - - assert_eq!(a, b); - } - } -} - -#[test] -fn test_field() { - crate::tests::field::random_field_tests::("fq2".to_string()); -} - -#[test] -fn test_serialization() { - crate::tests::field::random_serialization_test::("fq2".to_string()); -} diff --git a/arithmetic/curves/src/bn256/fq6.rs b/arithmetic/curves/src/bn256/fq6.rs deleted file mode 100644 index 036414177f..0000000000 --- a/arithmetic/curves/src/bn256/fq6.rs +++ /dev/null @@ -1,704 +0,0 @@ -use super::fq::Fq; -use super::fq2::Fq2; -use core::ops::{Add, Mul, Neg, Sub}; -use ff::Field; -use rand::RngCore; -use serde::{Deserialize, Serialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] -pub struct Fq6 { - pub c0: Fq2, - pub c1: Fq2, - pub c2: Fq2, -} - -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 ConstantTimeEq for Fq6 { - 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 Neg for Fq6 { - type Output = Fq6; - - #[inline] - fn neg(self) -> Fq6 { - -&self - } -} - -impl<'a> Neg for &'a Fq6 { - type Output = Fq6; - - #[inline] - fn neg(self) -> Fq6 { - self.neg() - } -} - -impl<'a, 'b> Sub<&'b Fq6> for &'a Fq6 { - type Output = Fq6; - - #[inline] - fn sub(self, rhs: &'b Fq6) -> Fq6 { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fq6> for &'a Fq6 { - type Output = Fq6; - - #[inline] - fn add(self, rhs: &'b Fq6) -> Fq6 { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fq6> for &'a Fq6 { - type Output = Fq6; - - #[inline] - fn mul(self, rhs: &'b Fq6) -> Fq6 { - self.mul(rhs) - } -} - -use crate::{ - impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, - impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, -}; -impl_binops_additive!(Fq6, Fq6); -impl_binops_multiplicative!(Fq6, Fq6); - -impl Fq6 { - pub 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 *= &other.c0; - b_b *= &other.c1; - c_c *= &other.c2; - - let mut t1 = other.c1; - t1 += &other.c2; - { - let mut tmp = self.c1; - tmp += &self.c2; - - t1 *= &tmp; - t1 -= &b_b; - t1 -= &c_c; - t1.mul_by_nonresidue(); - t1 += &a_a; - } - - let mut t3 = other.c0; - t3 += &other.c2; - { - let mut tmp = self.c0; - tmp += &self.c2; - - t3 *= &tmp; - t3 -= &a_a; - t3 += &b_b; - t3 -= &c_c; - } - - let mut t2 = other.c0; - t2 += &other.c1; - { - let mut tmp = self.c0; - tmp += &self.c1; - - t2 *= &tmp; - t2 -= &a_a; - t2 -= &b_b; - c_c.mul_by_nonresidue(); - t2 += &c_c; - } - - self.c0 = t1; - self.c1 = t2; - self.c2 = t3; - } - - pub fn square_assign(&mut self) { - // s0 = a^2 - let mut s0 = self.c0; - s0.square_assign(); - // s1 = 2ab - let mut ab = self.c0; - ab *= &self.c1; - let mut s1 = ab; - s1.double_assign(); - // s2 = (a - b + c)^2 - let mut s2 = self.c0; - s2 -= &self.c1; - s2 += &self.c2; - s2.square_assign(); - // bc - let mut bc = self.c1; - bc *= &self.c2; - // s3 = 2bc - let mut s3 = bc; - s3.double_assign(); - // s4 = c^2 - let mut s4 = self.c2; - s4.square_assign(); - - // new c0 = 2bc.mul_by_xi + a^2 - self.c0 = s3; - self.c0.mul_by_nonresidue(); - // self.c0.mul_by_xi(); - self.c0 += &s0; - - // new c1 = (c^2).mul_by_xi + 2ab - self.c1 = s4; - self.c1.mul_by_nonresidue(); - // self.c1.mul_by_xi(); - self.c1 += &s1; - - // new c2 = 2ab + (a - b + c)^2 + 2bc - a^2 - c^2 = b^2 + 2ac - self.c2 = s1; - self.c2 += &s2; - self.c2 += &s3; - self.c2 -= &s0; - self.c2 -= &s4; - } - - pub fn double(&self) -> Self { - Self { - c0: self.c0.double(), - c1: self.c1.double(), - c2: self.c2.double(), - } - } - - pub fn double_assign(&mut self) { - self.c0 = self.c0.double(); - self.c1 = self.c1.double(); - self.c2 = self.c2.double(); - } - - pub fn add(&self, other: &Self) -> Self { - Self { - c0: self.c0 + other.c0, - c1: self.c1 + other.c1, - c2: self.c2 + other.c2, - } - } - - pub fn sub(&self, other: &Self) -> Self { - Self { - c0: self.c0 - other.c0, - c1: self.c1 - other.c1, - c2: self.c2 - other.c2, - } - } - - pub fn mul(&self, other: &Self) -> Self { - let mut t = *other; - t.mul_assign(self); - t - } - - pub fn square(&self) -> Self { - let mut t = *self; - t.square_assign(); - t - } - - pub fn neg(&self) -> Self { - Self { - c0: -self.c0, - c1: -self.c1, - c2: -self.c2, - } - } - - pub 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]); - } - - /// Multiply by cubic 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); - // c0, c1, c2 -> c2, c0, c1 - self.c0.mul_by_nonresidue(); - } - - /// Multiply by cubic nonresidue v. - pub fn mul_by_v(&mut self) { - use std::mem::swap; - swap(&mut self.c0, &mut self.c1); - swap(&mut self.c0, &mut self.c2); - - self.c0.mul_by_xi(); - } - - pub fn mul_by_1(&mut self, c1: &Fq2) { - let mut b_b = self.c1; - b_b *= c1; - - let mut t1 = *c1; - { - let mut tmp = self.c1; - tmp += &self.c2; - - t1 *= &tmp; - t1 -= &b_b; - t1.mul_by_nonresidue(); - } - - let mut t2 = *c1; - { - let mut tmp = self.c0; - tmp += &self.c1; - - t2 *= &tmp; - t2 -= &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 *= c0; - b_b *= c1; - - let mut t1 = *c1; - { - let mut tmp = self.c1; - tmp += &self.c2; - - t1 *= &tmp; - t1 -= &b_b; - t1.mul_by_nonresidue(); - t1 += &a_a; - } - - let mut t3 = *c0; - { - let mut tmp = self.c0; - tmp += &self.c2; - - t3 *= &tmp; - t3 -= &a_a; - t3 += &b_b; - } - - let mut t2 = *c0; - t2 += c1; - { - let mut tmp = self.c0; - tmp += &self.c1; - - t2 *= &tmp; - t2 -= &a_a; - t2 -= &b_b; - } - - self.c0 = t1; - self.c1 = t2; - self.c2 = t3; - } - - fn invert(&self) -> CtOption { - let mut c0 = self.c2; - c0.mul_by_nonresidue(); - c0 *= &self.c1; - c0 = -c0; - { - let mut c0s = self.c0; - c0s.square_assign(); - c0 += &c0s; - } - let mut c1 = self.c2; - c1.square_assign(); - c1.mul_by_nonresidue(); - { - let mut c01 = self.c0; - c01 *= &self.c1; - c1 -= &c01; - } - let mut c2 = self.c1; - c2.square_assign(); - { - let mut c02 = self.c0; - c02 *= &self.c2; - c2 -= &c02; - } - - let mut tmp1 = self.c2; - tmp1 *= &c1; - let mut tmp2 = self.c1; - tmp2 *= &c2; - tmp1 += &tmp2; - tmp1.mul_by_nonresidue(); - tmp2 = self.c0; - tmp2 *= &c0; - tmp1 += &tmp2; - - tmp1.invert().map(|t| { - let mut tmp = Fq6 { - c0: t, - c1: t, - c2: t, - }; - tmp.c0 *= &c0; - tmp.c1 *= &c1; - tmp.c2 *= &c2; - - tmp - }) - } -} - -impl Field for Fq6 { - fn random(mut rng: impl RngCore) -> Self { - Fq6 { - c0: Fq2::random(&mut rng), - c1: Fq2::random(&mut rng), - c2: Fq2::random(&mut 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) -> Choice { - self.c0.is_zero() & self.c1.is_zero() - } - - fn square(&self) -> Self { - self.square() - } - - fn double(&self) -> Self { - self.double() - } - - fn sqrt(&self) -> CtOption { - unimplemented!() - } - - fn invert(&self) -> CtOption { - self.invert() - } -} - -pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ - // Fq2(u + 9)**(((q^0) - 1) / 3) - Fq2 { - c0: Fq([ - 0xd35d438dc58f0d9d, - 0x0a78eb28f5c70b3d, - 0x666ea36f7879462c, - 0x0e0a77c19a07df2f, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 9)**(((q^1) - 1) / 3) - // taken from go-ethereum and also re-calculated manually - Fq2 { - c0: Fq([ - 0xb5773b104563ab30, - 0x347f91c8a9aa6454, - 0x7a007127242e0991, - 0x1956bcd8118214ec, - ]), - c1: Fq([ - 0x6e849f1ea0aa4757, - 0xaa1c7b6d89f89141, - 0xb6e713cdfae0ca3a, - 0x26694fbb4e82ebc3, - ]), - }, - // Fq2(u + 9)**(((q^2) - 1) / 3) - // this one and other below are recalculated manually - Fq2 { - c0: Fq([ - 0x3350c88e13e80b9c, - 0x7dce557cdb5e56b9, - 0x6001b4b8b615564a, - 0x2682e617020217e0, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 9)**(((q^3) - 1) / 3) - Fq2 { - c0: Fq([ - 0xc9af22f716ad6bad, - 0xb311782a4aa662b2, - 0x19eeaf64e248c7f4, - 0x20273e77e3439f82, - ]), - c1: Fq([ - 0xacc02860f7ce93ac, - 0x3933d5817ba76b4c, - 0x69e6188b446c8467, - 0x0a46036d4417cc55, - ]), - }, - // Fq2(u + 9)**(((q^4) - 1) / 3) - Fq2 { - c0: Fq([ - 0x71930c11d782e155, - 0xa6bb947cffbe3323, - 0xaa303344d4741444, - 0x2c3b3f0d26594943, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 9)**(((q^5) - 1) / 3) - Fq2 { - c0: Fq([ - 0xf91aba2654e8e3b1, - 0x4771cb2fdc92ce12, - 0xdcb16ae0fc8bdf35, - 0x274aa195cd9d8be4, - ]), - c1: Fq([ - 0x5cfc50ae18811f8b, - 0x4bb28433cb43988c, - 0x4fd35f13c3b56219, - 0x301949bd2fc8883a, - ]), - }, -]; - -pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ - // Fq2(u + 1)**(((2q^0) - 2) / 3) - Fq2 { - c0: Fq([ - 0xd35d438dc58f0d9d, - 0x0a78eb28f5c70b3d, - 0x666ea36f7879462c, - 0x0e0a77c19a07df2f, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((2q^1) - 2) / 3) - Fq2 { - c0: Fq([ - 0x7361d77f843abe92, - 0xa5bb2bd3273411fb, - 0x9c941f314b3e2399, - 0x15df9cddbb9fd3ec, - ]), - c1: Fq([ - 0x5dddfd154bd8c949, - 0x62cb29a5a4445b60, - 0x37bc870a0c7dd2b9, - 0x24830a9d3171f0fd, - ]), - }, - // Fq2(u + 1)**(((2q^2) - 2) / 3) - Fq2 { - c0: Fq([ - 0x71930c11d782e155, - 0xa6bb947cffbe3323, - 0xaa303344d4741444, - 0x2c3b3f0d26594943, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((2q^3) - 2) / 3) - Fq2 { - c0: Fq([ - 0x448a93a57b6762df, - 0xbfd62df528fdeadf, - 0xd858f5d00e9bd47a, - 0x06b03d4d3476ec58, - ]), - c1: Fq([ - 0x2b19daf4bcc936d1, - 0xa1a54e7a56f4299f, - 0xb533eee05adeaef1, - 0x170c812b84dda0b2, - ]), - }, - // Fq2(u + 1)**(((2q^4) - 2) / 3) - Fq2 { - c0: Fq([ - 0x3350c88e13e80b9c, - 0x7dce557cdb5e56b9, - 0x6001b4b8b615564a, - 0x2682e617020217e0, - ]), - c1: Fq([0x0, 0x0, 0x0, 0x0]), - }, - // Fq2(u + 1)**(((2q^5) - 2) / 3) - Fq2 { - c0: Fq([ - 0x843420f1d8dadbd6, - 0x31f010c9183fcdb2, - 0x436330b527a76049, - 0x13d47447f11adfe4, - ]), - c1: Fq([ - 0xef494023a857fa74, - 0x2a925d02d5ab101a, - 0x83b015829ba62f10, - 0x2539111d0c13aea3, - ]), - }, -]; - -#[cfg(test)] -use rand::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 test_squaring() { - 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 = Fq6::random(&mut rng); - let mut b = a; - b.mul_assign(&a); - a.square_assign(); - assert_eq!(a, b); - } -} - -#[test] -fn test_frobenius() { - 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..14 { - let mut a = Fq6::random(&mut rng); - let mut b = a; - - for _ in 0..i { - a = a.pow_vartime([ - 0x3c208c16d87cfd47, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, - ]); - } - b.frobenius_map(i); - - assert_eq!(a, b); - } - } -} - -#[test] -fn test_field() { - crate::tests::field::random_field_tests::("fq6".to_string()); -} diff --git a/arithmetic/curves/src/bn256/fr.rs b/arithmetic/curves/src/bn256/fr.rs deleted file mode 100644 index 9bddcda35d..0000000000 --- a/arithmetic/curves/src/bn256/fr.rs +++ /dev/null @@ -1,373 +0,0 @@ -#[cfg(feature = "asm")] -use super::assembly::assembly_field; - -use crate::arithmetic::{adc, mac, macx, sbb}; -use core::convert::TryInto; -use core::fmt; -use core::ops::{Add, Mul, Neg, Sub}; -use ff::PrimeField; -use pasta_curves::arithmetic::{FieldExt, Group, SqrtRatio}; -use rand::RngCore; -use serde::{Deserialize, Serialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -/// This represents an element of $\mathbb{F}_r$ where -/// -/// `r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001` -/// -/// is the scalar field of the BN254 curve. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. `Fr` values are always in -// Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] -// PartialEq is derived for Hash, but if privacy perservation is needed, then constant time functions should be used: -// see NCC-E001151-003 in https://research.nccgroup.com/2021/11/02/public-report-zcash-nu5-cryptography-review/ -pub struct Fr(pub(crate) [u64; 4]); - -/// Constant representing the modulus -/// r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 -pub const MODULUS: Fr = Fr([ - 0x43e1f593f0000001, - 0x2833e84879b97091, - 0xb85045b68181585d, - 0x30644e72e131a029, -]); - -const MODULUS_STR: &str = "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"; - -/// INV = -(r^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0xc2e1f593efffffff; - -/// `R = 2^256 mod r` -/// `0xe0a77c19a07df2f666ea36f7879462e36fc76959f60cd29ac96341c4ffffffb` -const R: Fr = Fr([ - 0xac96341c4ffffffb, - 0x36fc76959f60cd29, - 0x666ea36f7879462e, - 0x0e0a77c19a07df2f, -]); - -/// `R^2 = 2^512 mod r` -/// `0x216d0b17f4e44a58c49833d53bb808553fe3ab1e35c59e31bb8e645ae216da7` -const R2: Fr = Fr([ - 0x1bb8e645ae216da7, - 0x53fe3ab1e35c59e3, - 0x8c49833d53bb8085, - 0x0216d0b17f4e44a5, -]); - -/// `R^3 = 2^768 mod r` -/// `0xcf8594b7fcc657c893cc664a19fcfed2a489cbe1cfbb6b85e94d8e1b4bf0040` -const R3: Fr = Fr([ - 0x5e94d8e1b4bf0040, - 0x2a489cbe1cfbb6b8, - 0x893cc664a19fcfed, - 0x0cf8594b7fcc657c, -]); - -/// `GENERATOR = 7 mod r` is a generator of the `r - 1` order multiplicative -/// subgroup, or in other words a primitive root of the field. -const GENERATOR: Fr = Fr::from_raw([0x07, 0x00, 0x00, 0x00]); - -const S: u32 = 28; - -/// GENERATOR^t where t * 2^s + 1 = r -/// with t odd. In other words, this -/// is a 2^s root of unity. -/// `0x3ddb9f5166d18b798865ea93dd31f743215cf6dd39329c8d34f1ed960c37c9c` -const ROOT_OF_UNITY: Fr = Fr::from_raw([ - 0xd34f1ed960c37c9c, - 0x3215cf6dd39329c8, - 0x98865ea93dd31f74, - 0x03ddb9f5166d18b7, -]); - -/// 1 / 2 mod r -const TWO_INV: Fr = Fr::from_raw([ - 0xa1f0fac9f8000001, - 0x9419f4243cdcb848, - 0xdc2822db40c0ac2e, - 0x183227397098d014, -]); - -/// 1 / ROOT_OF_UNITY mod r -const ROOT_OF_UNITY_INV: Fr = Fr::from_raw([ - 0x0ed3e50a414e6dba, - 0xb22625f59115aba7, - 0x1bbe587180f34361, - 0x048127174daabc26, -]); - -/// GENERATOR^{2^s} where t * 2^s + 1 = r -/// with t odd. In other words, this -/// is a t root of unity. -// 0x09226b6e22c6f0ca64ec26aad4c86e715b5f898e5e963f25870e56bbe533e9a2 -const DELTA: Fr = Fr::from_raw([ - 0x870e56bbe533e9a2, - 0x5b5f898e5e963f25, - 0x64ec26aad4c86e71, - 0x09226b6e22c6f0ca, -]); - -/// `ZETA^3 = 1 mod r` where `ZETA^2 != 1 mod r` -const ZETA: Fr = Fr::from_raw([ - 0xb8ca0b2d36636f23, - 0xcc37a73fec2bc5e9, - 0x048b6e193fd84104, - 0x30644e72e131a029, -]); - -use crate::{ - field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, - impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, - impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, -}; -impl_binops_additive!(Fr, Fr); -impl_binops_multiplicative!(Fr, Fr); -#[cfg(not(feature = "asm"))] -field_common!( - Fr, - MODULUS, - INV, - MODULUS_STR, - TWO_INV, - ROOT_OF_UNITY_INV, - DELTA, - ZETA, - R, - R2, - R3 -); -#[cfg(not(feature = "asm"))] -field_arithmetic!(Fr, MODULUS, INV, sparse); -#[cfg(feature = "asm")] -assembly_field!( - Fr, - MODULUS, - INV, - MODULUS_STR, - TWO_INV, - ROOT_OF_UNITY_INV, - DELTA, - ZETA, - R, - R2, - R3 -); - -impl ff::Field for Fr { - fn random(mut rng: impl RngCore) -> Self { - Self::from_u512([ - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - ]) - } - - fn zero() -> Self { - Self::zero() - } - - fn one() -> Self { - Self::one() - } - - fn double(&self) -> Self { - self.double() - } - - fn is_zero_vartime(&self) -> bool { - self == &Self::zero() - } - - #[inline(always)] - fn square(&self) -> Self { - self.square() - } - - /// Computes the square root of this element, if it exists. - fn sqrt(&self) -> CtOption { - crate::arithmetic::sqrt_tonelli_shanks(self, ::T_MINUS1_OVER2) - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - fn invert(&self) -> CtOption { - let tmp = self.pow(&[ - 0x43e1f593efffffff, - 0x2833e84879b97091, - 0xb85045b68181585d, - 0x30644e72e131a029, - ]); - - CtOption::new(tmp, !self.ct_eq(&Self::zero())) - } -} - -impl ff::PrimeField for Fr { - type Repr = [u8; 32]; - - const NUM_BITS: u32 = 254; - const CAPACITY: u32 = 253; - const S: u32 = S; - - fn from_repr(repr: Self::Repr) -> CtOption { - let mut tmp = Fr([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); - tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); - tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); - tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); - - // Try to subtract the modulus - let (_, borrow) = tmp.0[0].overflowing_sub(MODULUS.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)) - } - - fn to_repr(&self) -> Self::Repr { - // Turn into canonical form by computing - // (a.R) / R = a - #[cfg(feature = "asm")] - let tmp = Fr::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); - - #[cfg(not(feature = "asm"))] - let tmp = Fr::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - - 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 - } - - fn is_odd(&self) -> Choice { - Choice::from(self.to_repr()[0] & 1) - } - - fn multiplicative_generator() -> Self { - GENERATOR - } - - fn root_of_unity() -> Self { - ROOT_OF_UNITY - } -} - -impl SqrtRatio for Fr { - /// `(t - 1) // 2` where t * 2^s + 1 = p with t odd. - const T_MINUS1_OVER2: [u64; 4] = [ - 0xcdcb848a1f0fac9f, - 0x0c0ac2e9419f4243, - 0x098d014dc2822db4, - 0x0000000183227397, - ]; - - fn get_lower_32(&self) -> u32 { - #[cfg(not(feature = "asm"))] - let tmp = Fr::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - - #[cfg(feature = "asm")] - let tmp = Fr::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); - - tmp.0[0] as u32 - } -} - -#[cfg(test)] -mod test { - use super::*; - use ff::Field; - use rand_core::OsRng; - - #[test] - fn test_sqrt() { - let v = (Fr::TWO_INV).square().sqrt().unwrap(); - assert!(v == Fr::TWO_INV || (-v) == Fr::TWO_INV); - - for _ in 0..10000 { - let a = Fr::random(OsRng); - let mut b = a; - b = b.square(); - - let b = b.sqrt().unwrap(); - let mut negb = b; - negb = negb.neg(); - - assert!(a == b || a == negb); - } - } - - #[test] - fn test_root_of_unity() { - assert_eq!( - Fr::root_of_unity().pow_vartime([1 << Fr::S, 0, 0, 0]), - Fr::one() - ); - } - - #[test] - fn test_inv_root_of_unity() { - assert_eq!(Fr::ROOT_OF_UNITY_INV, Fr::root_of_unity().invert().unwrap()); - } - - #[test] - fn test_field() { - crate::tests::field::random_field_tests::("bn256 scalar".to_string()); - } - - #[test] - fn test_delta() { - assert_eq!(Fr::DELTA, GENERATOR.pow(&[1u64 << Fr::S, 0, 0, 0])); - assert_eq!( - Fr::DELTA, - Fr::multiplicative_generator().pow(&[1u64 << Fr::S, 0, 0, 0]) - ); - } - - #[test] - fn test_from_u512() { - assert_eq!( - Fr::from_raw([ - 0x7e7140b5196b9e6f, - 0x9abac9e4157b6172, - 0xf04bc41062fd7322, - 0x1185fa9c9fef6326, - ]), - Fr::from_u512([ - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa, - 0xaaaaaaaaaaaaaaaa - ]) - ); - } - - #[test] - fn test_serialization() { - crate::tests::field::random_serialization_test::("fr".to_string()); - } -} diff --git a/arithmetic/curves/src/bn256/mod.rs b/arithmetic/curves/src/bn256/mod.rs deleted file mode 100644 index 9cd0894620..0000000000 --- a/arithmetic/curves/src/bn256/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -mod curve; -mod engine; -mod fq; -mod fq12; -mod fq2; -mod fq6; -mod fr; - -#[cfg(feature = "asm")] -mod assembly; - -pub use curve::*; -pub use engine::*; -pub use fq::*; -pub use fq12::*; -pub use fq2::*; -pub use fq6::*; -pub use fr::*; - -#[derive(Debug, PartialEq, Eq)] -pub enum LegendreSymbol { - Zero = 0, - QuadraticResidue = 1, - QuadraticNonResidue = -1, -} diff --git a/arithmetic/curves/src/derive/curve.rs b/arithmetic/curves/src/derive/curve.rs deleted file mode 100644 index 1d51aeaa38..0000000000 --- a/arithmetic/curves/src/derive/curve.rs +++ /dev/null @@ -1,1043 +0,0 @@ -#[macro_export] -macro_rules! batch_add { - () => { - fn batch_add( - points: &mut [Self], - output_indices: &[u32], - num_points: usize, - offset: usize, - bases: &[Self], - base_positions: &[u32], - ) { - // assert!(Self::constant_a().is_zero()); - - let get_point = |point_data: u32| -> Self { - let negate = point_data & 0x80000000 != 0; - let base_idx = (point_data & 0x7FFFFFFF) as usize; - if negate { - bases[base_idx].neg() - } else { - bases[base_idx] - } - }; - - // Affine addition formula (P != Q): - // - lambda = (y_2 - y_1) / (x_2 - x_1) - // - x_3 = lambda^2 - (x_2 + x_1) - // - y_3 = lambda * (x_1 - x_3) - y_1 - - // Batch invert accumulator - let mut acc = Self::Base::one(); - - for i in (0..num_points).step_by(2) { - // Where that result of the point addition will be stored - let out_idx = output_indices[i >> 1] as usize - offset; - - #[cfg(all(feature = "prefetch", target_arch = "x86_64"))] - if i < num_points - 2 { - if LOAD_POINTS { - $crate::prefetch::(bases, base_positions[i + 2] as usize); - $crate::prefetch::(bases, base_positions[i + 3] as usize); - } - $crate::prefetch::( - points, - output_indices[(i >> 1) + 1] as usize - offset, - ); - } - if LOAD_POINTS { - points[i] = get_point(base_positions[i]); - points[i + 1] = get_point(base_positions[i + 1]); - } - - if COMPLETE { - // Nothing to do here if one of the points is zero - if (points[i].is_identity() | points[i + 1].is_identity()).into() { - continue; - } - - if points[i].x == points[i + 1].x { - if points[i].y == points[i + 1].y { - // Point doubling (P == Q) - // - s = (3 * x^2) / (2 * y) - // - x_2 = s^2 - (2 * x) - // - y_2 = s * (x - x_2) - y - - // (2 * x) - points[out_idx].x = points[i].x + points[i].x; - // x^2 - let xx = points[i].x.square(); - // (2 * y) - points[i + 1].x = points[i].y + points[i].y; - // (3 * x^2) * acc - points[i + 1].y = (xx + xx + xx) * acc; - // acc * (2 * y) - acc *= points[i + 1].x; - continue; - } else { - // Zero - points[i] = Self::identity(); - points[i + 1] = Self::identity(); - continue; - } - } - } - - // (x_2 + x_1) - points[out_idx].x = points[i].x + points[i + 1].x; - // (x_2 - x_1) - points[i + 1].x -= points[i].x; - // (y2 - y1) * acc - points[i + 1].y = (points[i + 1].y - points[i].y) * acc; - // acc * (x_2 - x_1) - acc *= points[i + 1].x; - } - - // Batch invert - if COMPLETE { - if (!acc.is_zero()).into() { - acc = acc.invert().unwrap(); - } - } else { - acc = acc.invert().unwrap(); - } - - for i in (0..num_points).step_by(2).rev() { - // Where that result of the point addition will be stored - let out_idx = output_indices[i >> 1] as usize - offset; - - #[cfg(all(feature = "prefetch", target_arch = "x86_64"))] - if i > 0 { - $crate::prefetch::( - points, - output_indices[(i >> 1) - 1] as usize - offset, - ); - } - - if COMPLETE { - // points[i] is zero so the sum is points[i + 1] - if points[i].is_identity().into() { - points[out_idx] = points[i + 1]; - continue; - } - // points[i + 1] is zero so the sum is points[i] - if points[i + 1].is_identity().into() { - points[out_idx] = points[i]; - continue; - } - } - - // lambda - points[i + 1].y *= acc; - // acc * (x_2 - x_1) - acc *= points[i + 1].x; - // x_3 = lambda^2 - (x_2 + x_1) - points[out_idx].x = points[i + 1].y.square() - points[out_idx].x; - // y_3 = lambda * (x_1 - x_3) - y_1 - points[out_idx].y = - points[i + 1].y * (points[i].x - points[out_idx].x) - points[i].y; - } - } - }; -} - -#[macro_export] -macro_rules! new_curve_impl { - (($($privacy:tt)*), - $name:ident, - $name_affine:ident, - $name_compressed:ident, - $compressed_size:expr, - $base:ident, - $scalar:ident, - $generator:expr, - $constant_b:expr, - $curve_id:literal, - ) => { - - #[derive(Copy, Clone, Debug, Serialize, Deserialize)] - $($privacy)* struct $name { - pub x: $base, - pub y: $base, - pub z: $base, - } - - #[derive(Copy, Clone, PartialEq, Hash, Serialize, Deserialize)] - $($privacy)* struct $name_affine { - pub x: $base, - pub y: $base, - } - - #[derive(Copy, Clone, Hash)] - $($privacy)* struct $name_compressed([u8; $compressed_size]); - - impl $name { - pub fn generator() -> Self { - let generator = $name_affine::generator(); - Self { - x: generator.x, - y: generator.y, - z: $base::one(), - } - } - - const fn curve_constant_b() -> $base { - $name_affine::curve_constant_b() - } - } - - impl $name_affine { - pub fn generator() -> Self { - Self { - x: $generator.0, - y: $generator.1, - } - } - - const fn curve_constant_b() -> $base { - $constant_b - } - - pub fn random(mut rng: impl RngCore) -> Self { - loop { - let x = $base::random(&mut rng); - let ysign = (rng.next_u32() % 2) as u8; - - let x3 = x.square() * x; - let y = (x3 + $name::curve_constant_b()).sqrt(); - if let Some(y) = Option::<$base>::from(y) { - let sign = y.to_bytes()[0] & 1; - let y = if ysign ^ sign == 0 { y } else { -y }; - - let p = $name_affine { - x, - y, - }; - - - use $crate::group::cofactor::CofactorGroup; - let p = p.to_curve(); - return p.clear_cofactor().to_affine() - } - } - } - } - - // Compressed - - impl std::fmt::Debug for $name_compressed { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - self.0[..].fmt(f) - } - } - - impl Default for $name_compressed { - fn default() -> Self { - $name_compressed([0; $compressed_size]) - } - } - - impl AsRef<[u8]> for $name_compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for $name_compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - - - // Jacobian implementations - - impl<'a> From<&'a $name_affine> for $name { - fn from(p: &'a $name_affine) -> $name { - p.to_curve() - } - } - - impl From<$name_affine> for $name { - fn from(p: $name_affine) -> $name { - p.to_curve() - } - } - - impl Default for $name { - fn default() -> $name { - $name::identity() - } - } - - impl subtle::ConstantTimeEq for $name { - 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.is_identity(); - let other_is_zero = other.is_identity(); - - (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 subtle::ConditionallySelectable for $name { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - $name { - x: $base::conditional_select(&a.x, &b.x, choice), - y: $base::conditional_select(&a.y, &b.y, choice), - z: $base::conditional_select(&a.z, &b.z, choice), - } - } - } - - impl PartialEq for $name { - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).into() - } - } - - impl cmp::Eq for $name {} - - impl CurveExt for $name { - - type ScalarExt = $scalar; - type Base = $base; - type AffineExt = $name_affine; - - const CURVE_ID: &'static str = $curve_id; - - fn endo(&self) -> Self { - self.endomorphism_base() - } - - fn jacobian_coordinates(&self) -> ($base, $base, $base) { - (self.x, self.y, self.z) - } - - - fn hash_to_curve<'a>(_: &'a str) -> Box Self + 'a> { - unimplemented!(); - } - - fn is_on_curve(&self) -> Choice { - - let z2 = self.z.square(); - let z4 = z2.square(); - let z6 = z4 * z2; - (self.y.square() - self.x.square() * self.x) - .ct_eq(&(z6 * $name::curve_constant_b())) - | self.z.is_zero() - } - - fn b() -> Self::Base { - $name::curve_constant_b() - } - - fn a() -> Self::Base { - Self::Base::zero() - } - - fn new_jacobian(x: Self::Base, y: Self::Base, z: Self::Base) -> CtOption { - let p = $name { x, y, z }; - CtOption::new(p, p.is_on_curve()) - } - } - - impl group::Curve for $name { - - type AffineRepr = $name_affine; - - fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { - assert_eq!(p.len(), q.len()); - - let mut acc = $base::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `x` field of $name_affine to store the product - // of previous z-coordinates seen. - q.x = acc; - - // We will end up skipping all identities in p - acc = $base::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 = $base::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 = $name_affine::conditional_select(&q, &$name_affine::identity(), skip); - } - } - - fn to_affine(&self) -> Self::AffineRepr { - let zinv = self.z.invert().unwrap_or($base::zero()); - let zinv2 = zinv.square(); - let x = self.x * zinv2; - let zinv3 = zinv2 * zinv; - let y = self.y * zinv3; - - let tmp = $name_affine { - x, - y, - }; - - $name_affine::conditional_select(&tmp, &$name_affine::identity(), zinv.is_zero()) - } - } - - impl group::Group for $name { - type Scalar = $scalar; - - fn random(mut rng: impl RngCore) -> Self { - $name_affine::random(&mut rng).to_curve() - } - - fn double(&self) -> Self { - 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 = $name { - x: x3, - y: y3, - z: z3, - }; - - $name::conditional_select(&tmp, &$name::identity(), self.is_identity()) - } - - fn generator() -> Self { - $name::generator() - } - - fn identity() -> Self { - Self { - x: $base::zero(), - y: $base::zero(), - z: $base::zero(), - } - } - - fn is_identity(&self) -> Choice { - self.z.is_zero() - } - } - - impl GroupEncoding for $name { - type Repr = $name_compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - $name_affine::from_bytes(bytes).map(Self::from) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - $name_affine::from_bytes(bytes).map(Self::from) - } - - fn to_bytes(&self) -> Self::Repr { - $name_affine::from(self).to_bytes() - } - } - - impl $crate::serde::SerdeObject for $name { - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { - debug_assert_eq!(bytes.len(), 3 * $base::size()); - let [x, y, z] = [0, 1, 2] - .map(|i| $base::from_raw_bytes_unchecked(&bytes[i * $base::size()..(i + 1) * $base::size()])); - Self { x, y, z } - } - fn from_raw_bytes(bytes: &[u8]) -> Option { - if bytes.len() != 3 * $base::size() { - return None; - } - let [x, y, z] = - [0, 1, 2].map(|i| $base::from_raw_bytes(&bytes[i * $base::size()..(i + 1) * $base::size()])); - x.zip(y).zip(z).and_then(|((x, y), z)| { - let res = Self { x, y, z }; - // Check that the point is on the curve. - bool::from(res.is_on_curve()).then(|| res) - }) - } - fn to_raw_bytes(&self) -> Vec { - let mut res = Vec::with_capacity(3 * $base::size()); - Self::write_raw(self, &mut res).unwrap(); - res - } - fn read_raw_unchecked(reader: &mut R) -> Self { - let [x, y, z] = [(); 3].map(|_| $base::read_raw_unchecked(reader)); - Self { x, y, z } - } - fn read_raw(reader: &mut R) -> std::io::Result { - let x = $base::read_raw(reader)?; - let y = $base::read_raw(reader)?; - let z = $base::read_raw(reader)?; - Ok(Self { x, y, z }) - } - fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { - self.x.write_raw(writer)?; - self.y.write_raw(writer)?; - self.z.write_raw(writer) - } - } - - impl group::prime::PrimeGroup for $name {} - - impl group::prime::PrimeCurve for $name { - type Affine = $name_affine; - } - - impl group::cofactor::CofactorCurve for $name { - type Affine = $name_affine; - } - - impl Group for $name { - type Scalar = $scalar; - - fn group_zero() -> Self { - Self::identity() - } - fn group_add(&mut self, rhs: &Self) { - *self += *rhs; - } - fn group_sub(&mut self, rhs: &Self) { - *self -= *rhs; - } - fn group_scale(&mut self, by: &Self::Scalar) { - *self *= *by; - } - } - - // Affine implementations - - impl std::fmt::Debug for $name_affine { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - if self.is_identity().into() { - write!(f, "Infinity") - } else { - write!(f, "({:?}, {:?})", self.x, self.y) - } - } - } - - impl<'a> From<&'a $name> for $name_affine { - fn from(p: &'a $name) -> $name_affine { - p.to_affine() - } - } - - impl From<$name> for $name_affine { - fn from(p: $name) -> $name_affine { - p.to_affine() - } - } - - impl Default for $name_affine { - fn default() -> $name_affine { - $name_affine::identity() - } - } - - impl subtle::ConstantTimeEq for $name_affine { - fn ct_eq(&self, other: &Self) -> Choice { - let z1 = self.is_identity(); - let z2 = other.is_identity(); - - (z1 & z2) | ((!z1) & (!z2) & (self.x.ct_eq(&other.x)) & (self.y.ct_eq(&other.y))) - } - } - - impl subtle::ConditionallySelectable for $name_affine { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - $name_affine { - x: $base::conditional_select(&a.x, &b.x, choice), - y: $base::conditional_select(&a.y, &b.y, choice), - } - } - } - - impl cmp::Eq for $name_affine {} - - impl group::GroupEncoding for $name_affine { - type Repr = $name_compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - let bytes = &bytes.0; - let mut tmp = *bytes; - let ysign = Choice::from(tmp[$compressed_size - 1] >> 7); - tmp[$compressed_size - 1] &= 0b0111_1111; - let mut xbytes = [0u8; $base::size()]; - xbytes.copy_from_slice(&tmp[ ..$base::size()]); - - $base::from_bytes(&xbytes).and_then(|x| { - CtOption::new(Self::identity(), x.is_zero() & (!ysign)).or_else(|| { - let x3 = x.square() * x; - (x3 + $name::curve_constant_b()).sqrt().and_then(|y| { - let sign = Choice::from(y.to_bytes()[0] & 1); - - let y = $base::conditional_select(&y, &-y, ysign ^ sign); - - CtOption::new( - $name_affine { - x, - y, - }, - Choice::from(1u8), - ) - }) - }) - }) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - Self::from_bytes(bytes) - } - - fn to_bytes(&self) -> Self::Repr { - if bool::from(self.is_identity()) { - $name_compressed::default() - } else { - let (x, y) = (self.x, self.y); - let sign = (y.to_bytes()[0] & 1) << 7; - let mut xbytes = [0u8; $compressed_size]; - xbytes[..$base::size()].copy_from_slice(&x.to_bytes()); - xbytes[$compressed_size - 1] |= sign; - $name_compressed(xbytes) - } - } - } - - impl $crate::serde::SerdeObject for $name_affine { - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { - debug_assert_eq!(bytes.len(), 2 * $base::size()); - let [x, y] = - [0, $base::size()].map(|i| $base::from_raw_bytes_unchecked(&bytes[i..i + $base::size()])); - Self { x, y } - } - fn from_raw_bytes(bytes: &[u8]) -> Option { - if bytes.len() != 2 * $base::size() { - return None; - } - let [x, y] = [0, $base::size()].map(|i| $base::from_raw_bytes(&bytes[i..i + $base::size()])); - x.zip(y).and_then(|(x, y)| { - let res = Self { x, y }; - // Check that the point is on the curve. - bool::from(res.is_on_curve()).then(|| res) - }) - } - fn to_raw_bytes(&self) -> Vec { - let mut res = Vec::with_capacity(2 * $base::size()); - Self::write_raw(self, &mut res).unwrap(); - res - } - fn read_raw_unchecked(reader: &mut R) -> Self { - let [x, y] = [(); 2].map(|_| $base::read_raw_unchecked(reader)); - Self { x, y } - } - fn read_raw(reader: &mut R) -> std::io::Result { - let x = $base::read_raw(reader)?; - let y = $base::read_raw(reader)?; - Ok(Self { x, y }) - } - fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { - self.x.write_raw(writer)?; - self.y.write_raw(writer) - } - } - - impl group::prime::PrimeCurveAffine for $name_affine { - type Curve = $name; - type Scalar = $scalar; - - - fn generator() -> Self { - $name_affine::generator() - } - - fn identity() -> Self { - Self { - x: $base::zero(), - y: $base::zero(), - } - } - - fn is_identity(&self) -> Choice { - self.x.is_zero() & self.y.is_zero() - } - - fn to_curve(&self) -> Self::Curve { - $name { - x: self.x, - y: self.y, - z: $base::conditional_select(&$base::one(), &$base::zero(), self.is_identity()), - } - } - } - - impl group::cofactor::CofactorCurveAffine for $name_affine { - type Curve = $name; - type Scalar = $scalar; - - fn identity() -> Self { - ::identity() - } - - fn generator() -> Self { - ::generator() - } - - fn is_identity(&self) -> Choice { - ::is_identity(self) - } - - fn to_curve(&self) -> Self::Curve { - ::to_curve(self) - } - } - - - impl CurveAffine for $name_affine { - type ScalarExt = $scalar; - type Base = $base; - type CurveExt = $name; - - fn is_on_curve(&self) -> Choice { - // y^2 - x^3 - ax ?= b - (self.y.square() - self.x.square() * self.x).ct_eq(&$name::curve_constant_b()) - | self.is_identity() - } - - fn coordinates(&self) -> CtOption> { - Coordinates::from_xy( self.x, self.y ) - } - - fn from_xy(x: Self::Base, y: Self::Base) -> CtOption { - let p = $name_affine { - x, y - }; - CtOption::new(p, p.is_on_curve()) - } - - fn a() -> Self::Base { - Self::Base::zero() - } - - fn b() -> Self::Base { - $name::curve_constant_b() - } - } - - - impl_binops_additive!($name, $name); - impl_binops_additive!($name, $name_affine); - impl_binops_additive_specify_output!($name_affine, $name_affine, $name); - impl_binops_additive_specify_output!($name_affine, $name, $name); - impl_binops_multiplicative!($name, $scalar); - impl_binops_multiplicative_mixed!($name_affine, $scalar, $name); - - impl<'a> Neg for &'a $name { - type Output = $name; - - fn neg(self) -> $name { - $name { - x: self.x, - y: -self.y, - z: self.z, - } - } - } - - impl Neg for $name { - type Output = $name; - - fn neg(self) -> $name { - -&self - } - } - - impl Sum for $name - where - T: core::borrow::Borrow<$name>, - { - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } - } - - impl<'a, 'b> Add<&'a $name> for &'b $name { - type Output = $name; - - fn add(self, rhs: &'a $name) -> $name { - if bool::from(self.is_identity()) { - *rhs - } else if bool::from(rhs.is_identity()) { - *self - } else { - let z1z1 = self.z.square(); - let z2z2 = rhs.z.square(); - let u1 = self.x * z2z2; - let u2 = rhs.x * z1z1; - let s1 = self.y * z2z2 * rhs.z; - let s2 = rhs.y * z1z1 * self.z; - - if u1 == u2 { - if s1 == s2 { - self.double() - } else { - $name::identity() - } - } else { - let h = u2 - u1; - let i = (h + h).square(); - let j = h * i; - let r = s2 - s1; - let r = r + r; - let v = u1 * i; - let x3 = r.square() - j - v - v; - let s1 = s1 * j; - let s1 = s1 + s1; - let y3 = r * (v - x3) - s1; - let z3 = (self.z + rhs.z).square() - z1z1 - z2z2; - let z3 = z3 * h; - - $name { - x: x3, y: y3, z: z3 - } - } - } - } - } - - impl<'a, 'b> Add<&'a $name_affine> for &'b $name { - type Output = $name; - - fn add(self, rhs: &'a $name_affine) -> $name { - if bool::from(self.is_identity()) { - rhs.to_curve() - } else if bool::from(rhs.is_identity()) { - *self - } else { - let z1z1 = self.z.square(); - let u2 = rhs.x * z1z1; - let s2 = rhs.y * z1z1 * self.z; - - if self.x == u2 { - if self.y == s2 { - self.double() - } else { - $name::identity() - } - } else { - let h = u2 - self.x; - let hh = h.square(); - let i = hh + hh; - let i = i + i; - let j = h * i; - let r = s2 - self.y; - let r = r + r; - let v = self.x * i; - let x3 = r.square() - j - v - v; - let j = self.y * j; - let j = j + j; - let y3 = r * (v - x3) - j; - let z3 = (self.z + h).square() - z1z1 - hh; - - $name { - x: x3, y: y3, z: z3 - } - } - } - } - } - - impl<'a, 'b> Sub<&'a $name> for &'b $name { - type Output = $name; - - fn sub(self, other: &'a $name) -> $name { - self + (-other) - } - } - - impl<'a, 'b> Sub<&'a $name_affine> for &'b $name { - type Output = $name; - - fn sub(self, other: &'a $name_affine) -> $name { - self + (-other) - } - } - - - - #[allow(clippy::suspicious_arithmetic_impl)] - impl<'a, 'b> Mul<&'b $scalar> for &'a $name { - type Output = $name; - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - - fn mul(self, other: &'b $scalar) -> Self::Output { - let mut acc = $name::identity(); - for bit in other - .to_repr() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - { - acc = acc.double(); - acc = $name::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - } - - impl<'a> Neg for &'a $name_affine { - type Output = $name_affine; - - fn neg(self) -> $name_affine { - $name_affine { - x: self.x, - y: -self.y, - } - } - } - - impl Neg for $name_affine { - type Output = $name_affine; - - fn neg(self) -> $name_affine { - -&self - } - } - - impl<'a, 'b> Add<&'a $name> for &'b $name_affine { - type Output = $name; - - fn add(self, rhs: &'a $name) -> $name { - rhs + self - } - } - - impl<'a, 'b> Add<&'a $name_affine> for &'b $name_affine { - type Output = $name; - - fn add(self, rhs: &'a $name_affine) -> $name { - if bool::from(self.is_identity()) { - rhs.to_curve() - } else if bool::from(rhs.is_identity()) { - self.to_curve() - } else { - if self.x == rhs.x { - if self.y == rhs.y { - self.to_curve().double() - } else { - $name::identity() - } - } else { - let h = rhs.x - self.x; - let hh = h.square(); - let i = hh + hh; - let i = i + i; - let j = h * i; - let r = rhs.y - self.y; - let r = r + r; - let v = self.x * i; - let x3 = r.square() - j - v - v; - let j = self.y * j; - let j = j + j; - let y3 = r * (v - x3) - j; - let z3 = h + h; - - $name { - x: x3, y: y3, z: z3 - } - } - } - } - } - - impl<'a, 'b> Sub<&'a $name_affine> for &'b $name_affine { - type Output = $name; - - fn sub(self, other: &'a $name_affine) -> $name { - self + (-other) - } - } - - impl<'a, 'b> Sub<&'a $name> for &'b $name_affine { - type Output = $name; - - fn sub(self, other: &'a $name) -> $name { - self + (-other) - } - } - - #[allow(clippy::suspicious_arithmetic_impl)] - impl<'a, 'b> Mul<&'b $scalar> for &'a $name_affine { - type Output = $name; - - fn mul(self, other: &'b $scalar) -> Self::Output { - let mut acc = $name::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - - for bit in other - .to_repr() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - { - acc = acc.double(); - acc = $name::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - } - }; -} diff --git a/arithmetic/curves/src/derive/field.rs b/arithmetic/curves/src/derive/field.rs deleted file mode 100644 index 4021e14322..0000000000 --- a/arithmetic/curves/src/derive/field.rs +++ /dev/null @@ -1,736 +0,0 @@ -#[macro_export] -macro_rules! field_common { - ( - $field:ident, - $modulus:ident, - $inv:ident, - $modulus_str:ident, - $two_inv:ident, - $root_of_unity_inv:ident, - $delta:ident, - $zeta:ident, - $r:ident, - $r2:ident, - $r3:ident - ) => { - impl $field { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> $field { - $field([0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> $field { - $r - } - - fn from_u512(limbs: [u64; 8]) -> $field { - // 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 multiplied 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 = $field([limbs[0], limbs[1], limbs[2], limbs[3]]); - let d1 = $field([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) `$field` representation. - pub const fn from_raw(val: [u64; 4]) -> Self { - (&$field(val)).mul(&$r2) - } - - /// Attempts to convert a little-endian byte representation of - /// a scalar into a `Fr`, failing if the input is not canonical. - pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<$field> { - ::from_repr(*bytes) - } - - /// Converts an element of `Fr` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 32] { - ::to_repr(self) - } - } - - impl Group for $field { - type Scalar = Self; - - fn group_zero() -> Self { - Self::zero() - } - fn group_add(&mut self, rhs: &Self) { - *self += *rhs; - } - fn group_sub(&mut self, rhs: &Self) { - *self -= *rhs; - } - fn group_scale(&mut self, by: &Self::Scalar) { - *self *= *by; - } - } - - impl fmt::Debug for $field { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_repr(); - write!(f, "0x")?; - for &b in tmp.iter().rev() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } - } - - impl Default for $field { - #[inline] - fn default() -> Self { - Self::zero() - } - } - - impl From for $field { - fn from(bit: bool) -> $field { - if bit { - $field::one() - } else { - $field::zero() - } - } - } - - impl From for $field { - fn from(val: u64) -> $field { - $field([val, 0, 0, 0]) * $r2 - } - } - - impl ConstantTimeEq for $field { - 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 core::cmp::Ord for $field { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - let left = self.to_repr(); - let right = other.to_repr(); - left.iter() - .zip(right.iter()) - .rev() - .find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) { - core::cmp::Ordering::Equal => None, - res => Some(res), - }) - .unwrap_or(core::cmp::Ordering::Equal) - } - } - - impl core::cmp::PartialOrd for $field { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - impl ConditionallySelectable for $field { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - $field([ - 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), - ]) - } - } - - impl<'a> Neg for &'a $field { - type Output = $field; - - #[inline] - fn neg(self) -> $field { - self.neg() - } - } - - impl Neg for $field { - type Output = $field; - - #[inline] - fn neg(self) -> $field { - -&self - } - } - - impl<'a, 'b> Sub<&'b $field> for &'a $field { - type Output = $field; - - #[inline] - fn sub(self, rhs: &'b $field) -> $field { - self.sub(rhs) - } - } - - impl<'a, 'b> Add<&'b $field> for &'a $field { - type Output = $field; - - #[inline] - fn add(self, rhs: &'b $field) -> $field { - self.add(rhs) - } - } - - impl<'a, 'b> Mul<&'b $field> for &'a $field { - type Output = $field; - - #[inline] - fn mul(self, rhs: &'b $field) -> $field { - self.mul(rhs) - } - } - - impl From<[u64; 4]> for $field { - fn from(digits: [u64; 4]) -> Self { - Self::from_raw(digits) - } - } - - impl From<$field> for [u64; 4] { - fn from(elt: $field) -> [u64; 4] { - // Turn into canonical form by computing - // (a.R) / R = a - #[cfg(feature = "asm")] - let tmp = $field::montgomery_reduce(&[ - elt.0[0], elt.0[1], elt.0[2], elt.0[3], 0, 0, 0, 0, - ]); - - #[cfg(not(feature = "asm"))] - let tmp = $field::montgomery_reduce_short(elt.0[0], elt.0[1], elt.0[2], elt.0[3]); - - tmp.0 - } - } - - impl From<$field> for [u8; 32] { - fn from(value: $field) -> [u8; 32] { - value.to_repr() - } - } - - impl<'a> From<&'a $field> for [u8; 32] { - fn from(value: &'a $field) -> [u8; 32] { - value.to_repr() - } - } - - impl From<$field> for i128 { - fn from(value: $field) -> i128 { - let tmp: [u64; 4] = value.into(); - if tmp[2] == 0 && tmp[3] == 0 { - i128::from(tmp[0]) | (i128::from(tmp[1]) << 64) - } else { - // modulus - tmp - let (a0, borrow) = $modulus.0[0].overflowing_sub(tmp[0]); - let (a1, _) = sbb($modulus.0[1], tmp[1], borrow); - - -(i128::from(a0) | (i128::from(a1) << 64)) - } - } - } - - impl FieldExt for $field { - const MODULUS: &'static str = $modulus_str; - const TWO_INV: Self = $two_inv; - const ROOT_OF_UNITY_INV: Self = $root_of_unity_inv; - const DELTA: Self = $delta; - const ZETA: Self = $zeta; - - fn from_u128(v: u128) -> Self { - $field::from_raw([v as u64, (v >> 64) as u64, 0, 0]) - } - - /// Converts a 512-bit little endian integer into - /// a `$field` by reducing by the modulus. - fn from_bytes_wide(bytes: &[u8; 64]) -> $field { - $field::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 get_lower_128(&self) -> u128 { - let tmp = - $field::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - - u128::from(tmp.0[0]) | (u128::from(tmp.0[1]) << 64) - } - } - - impl $crate::serde::SerdeObject for $field { - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { - debug_assert_eq!(bytes.len(), 32); - let inner = - [0, 8, 16, 24].map(|i| u64::from_le_bytes(bytes[i..i + 8].try_into().unwrap())); - Self(inner) - } - fn from_raw_bytes(bytes: &[u8]) -> Option { - if bytes.len() != 32 { - return None; - } - let elt = Self::from_raw_bytes_unchecked(bytes); - Self::is_less_than(&elt.0, &$modulus.0).then(|| elt) - } - fn to_raw_bytes(&self) -> Vec { - let mut res = Vec::with_capacity(32); - for limb in self.0.iter() { - res.extend_from_slice(&limb.to_le_bytes()); - } - res - } - fn read_raw_unchecked(reader: &mut R) -> Self { - let inner = [(); 4].map(|_| { - let mut buf = [0; 8]; - reader.read_exact(&mut buf).unwrap(); - u64::from_le_bytes(buf) - }); - Self(inner) - } - fn read_raw(reader: &mut R) -> std::io::Result { - let mut inner = [0u64; 4]; - for limb in inner.iter_mut() { - let mut buf = [0; 8]; - reader.read_exact(&mut buf)?; - *limb = u64::from_le_bytes(buf); - } - let elt = Self(inner); - Self::is_less_than(&elt.0, &$modulus.0) - .then(|| elt) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidData, - "input number is not less than field modulus", - ) - }) - } - fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { - for limb in self.0.iter() { - writer.write_all(&limb.to_le_bytes())?; - } - Ok(()) - } - } - }; -} - -#[macro_export] -macro_rules! field_arithmetic { - ($field:ident, $modulus:ident, $inv:ident, $field_type:ident) => { - field_specific!($field, $modulus, $inv, $field_type); - impl $field { - /// Doubles this field element. - #[inline] - pub const fn double(&self) -> $field { - self.add(self) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> $field { - let r0; - let mut r1; - let mut r2; - let mut r3; - let mut r4; - let mut r5; - let mut r6; - let mut r7; - let mut carry; - let mut carry2; - - (r1, carry) = self.0[0].widening_mul(self.0[1]); - (r2, carry) = self.0[0].carrying_mul(self.0[2], carry); - (r3, r4) = self.0[0].carrying_mul(self.0[3], carry); - - (r3, carry) = macx(r3, self.0[1], self.0[2]); - (r4, r5) = mac(r4, self.0[1], self.0[3], carry); - - (r5, r6) = macx(r5, self.0[2], self.0[3]); - - r7 = r6 >> 63; - r6 = (r6 << 1) | (r5 >> 63); - r5 = (r5 << 1) | (r4 >> 63); - r4 = (r4 << 1) | (r3 >> 63); - r3 = (r3 << 1) | (r2 >> 63); - r2 = (r2 << 1) | (r1 >> 63); - r1 <<= 1; - - (r0, carry) = self.0[0].widening_mul(self.0[0]); - (r1, carry2) = r1.overflowing_add(carry); - (r2, carry) = mac(r2, self.0[1], self.0[1], carry2 as u64); - (r3, carry2) = r3.overflowing_add(carry); - (r4, carry) = mac(r4, self.0[2], self.0[2], carry2 as u64); - (r5, carry2) = r5.overflowing_add(carry); - (r6, carry) = mac(r6, self.0[3], self.0[3], carry2 as u64); - r7 = r7.wrapping_add(carry); - - $field::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) = self.0[0].overflowing_sub(rhs.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); - - let borrow = 0u64.wrapping_sub(borrow as u64); - // 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) = d0.overflowing_add($modulus.0[0] & borrow); - 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); - $field([d0, d1, d2, d3]) - } - - /// Negates `self`. - #[inline] - pub const fn neg(&self) -> Self { - if self.0[0] == 0 && self.0[1] == 0 && self.0[2] == 0 && self.0[3] == 0 { - return $field([0, 0, 0, 0]); - } - // 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) = $modulus.0[0].overflowing_sub(self.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 = $modulus.0[3] - (self.0[3] + borrow as u64); - - $field([d0, d1, d2, d3]) - } - - /// Montgomery reduce where last 4 registers are 0 - #[inline(always)] - pub(crate) const fn montgomery_reduce_short( - mut r0: u64, - mut r1: u64, - mut r2: u64, - mut r3: u64, - ) -> $field { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - let mut k; - - k = r0.wrapping_mul($inv); - (_, r0) = macx(r0, k, $modulus.0[0]); - (r1, r0) = mac(r1, k, $modulus.0[1], r0); - (r2, r0) = mac(r2, k, $modulus.0[2], r0); - (r3, r0) = mac(r3, k, $modulus.0[3], r0); - - k = r1.wrapping_mul($inv); - (_, r1) = macx(r1, k, $modulus.0[0]); - (r2, r1) = mac(r2, k, $modulus.0[1], r1); - (r3, r1) = mac(r3, k, $modulus.0[2], r1); - (r0, r1) = mac(r0, k, $modulus.0[3], r1); - - k = r2.wrapping_mul($inv); - (_, r2) = macx(r2, k, $modulus.0[0]); - (r3, r2) = mac(r3, k, $modulus.0[1], r2); - (r0, r2) = mac(r0, k, $modulus.0[2], r2); - (r1, r2) = mac(r1, k, $modulus.0[3], r2); - - k = r3.wrapping_mul($inv); - (_, r3) = macx(r3, k, $modulus.0[0]); - (r0, r3) = mac(r0, k, $modulus.0[1], r3); - (r1, r3) = mac(r1, k, $modulus.0[2], r3); - (r2, r3) = mac(r2, k, $modulus.0[3], r3); - - // Result may be within MODULUS of the correct value - (&$field([r0, r1, r2, r3])).sub(&$modulus) - } - - #[inline(always)] - fn is_less_than(x: &[u64; 4], y: &[u64; 4]) -> bool { - let (_, borrow) = x[0].overflowing_sub(y[0]); - let (_, borrow) = x[1].borrowing_sub(y[1], borrow); - let (_, borrow) = x[2].borrowing_sub(y[2], borrow); - let (_, borrow) = x[3].borrowing_sub(y[3], borrow); - borrow - } - } - }; -} - -#[macro_export] -macro_rules! field_specific { - ($field:ident, $modulus:ident, $inv:ident, sparse) => { - impl $field { - /// Adds `rhs` to `self`, returning the result. - #[inline] - pub const fn add(&self, rhs: &Self) -> Self { - let (d0, carry) = self.0[0].overflowing_add(rhs.0[0]); - let (d1, carry) = self.0[1].carrying_add(rhs.0[1], carry); - let (d2, carry) = self.0[2].carrying_add(rhs.0[2], carry); - // sparse means that the sum won't overflow the top register - let d3 = self.0[3] + rhs.0[3] + carry as u64; - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&$field([d0, d1, d2, d3])).sub(&$modulus) - } - - /// Multiplies `rhs` by `self`, returning the result. - #[inline] - pub const fn mul(&self, rhs: &Self) -> $field { - // When the highest bit in the top register of the modulus is 0 and the rest of the bits are not all 1, we can use an optimization from the gnark team: https://hackmd.io/@gnark/modular_multiplication - - // I think this is exactly the same as the previous `mul` implementation with `montgomery_reduce` at the end (where `montgomery_reduce` is slightly cheaper in "sparse" setting) - // Maybe the use of mutable variables is slightly more efficient? - let mut r0; - let mut r1; - let mut t0; - let mut t1; - let mut t2; - let mut t3; - let mut k; - - (t0, r0) = self.0[0].widening_mul(rhs.0[0]); - k = t0.wrapping_mul($inv); - (_, r1) = macx(t0, k, $modulus.0[0]); - (t1, r0) = self.0[0].carrying_mul(rhs.0[1], r0); - (t0, r1) = mac(t1, k, $modulus.0[1], r1); - (t2, r0) = self.0[0].carrying_mul(rhs.0[2], r0); - (t1, r1) = mac(t2, k, $modulus.0[2], r1); - (t3, r0) = self.0[0].carrying_mul(rhs.0[3], r0); - (t2, r1) = mac(t3, k, $modulus.0[3], r1); - t3 = r0 + r1; - - (t0, r0) = macx(t0, self.0[1], rhs.0[0]); - k = t0.wrapping_mul($inv); - (_, r1) = macx(t0, k, $modulus.0[0]); - (t1, r0) = mac(t1, self.0[1], rhs.0[1], r0); - (t0, r1) = mac(t1, k, $modulus.0[1], r1); - (t2, r0) = mac(t2, self.0[1], rhs.0[2], r0); - (t1, r1) = mac(t2, k, $modulus.0[2], r1); - (t3, r0) = mac(t3, self.0[1], rhs.0[3], r0); - (t2, r1) = mac(t3, k, $modulus.0[3], r1); - t3 = r0 + r1; - - (t0, r0) = macx(t0, self.0[2], rhs.0[0]); - k = t0.wrapping_mul($inv); - (_, r1) = macx(t0, k, $modulus.0[0]); - (t1, r0) = mac(t1, self.0[2], rhs.0[1], r0); - (t0, r1) = mac(t1, k, $modulus.0[1], r1); - (t2, r0) = mac(t2, self.0[2], rhs.0[2], r0); - (t1, r1) = mac(t2, k, $modulus.0[2], r1); - (t3, r0) = mac(t3, self.0[2], rhs.0[3], r0); - (t2, r1) = mac(t3, k, $modulus.0[3], r1); - t3 = r0 + r1; - - (t0, r0) = macx(t0, self.0[3], rhs.0[0]); - k = t0.wrapping_mul($inv); - (_, r1) = macx(t0, k, $modulus.0[0]); - (t1, r0) = mac(t1, self.0[3], rhs.0[1], r0); - (t0, r1) = mac(t1, k, $modulus.0[1], r1); - (t2, r0) = mac(t2, self.0[3], rhs.0[2], r0); - (t1, r1) = mac(t2, k, $modulus.0[2], r1); - (t3, r0) = mac(t3, self.0[3], rhs.0[3], r0); - (t2, r1) = mac(t3, k, $modulus.0[3], r1); - t3 = r0 + r1; - - // Result may be within MODULUS of the correct value - (&$field([t0, t1, t2, t3])).sub(&$modulus) - } - - #[allow(clippy::too_many_arguments)] - #[inline(always)] - pub(crate) const fn montgomery_reduce( - r0: u64, - mut r1: u64, - mut r2: u64, - mut r3: u64, - mut r4: u64, - mut r5: u64, - mut r6: u64, - mut r7: u64, - ) -> $field { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - let mut k; - let mut carry; - let mut carry2; - - k = r0.wrapping_mul($inv); - (_, carry) = macx(r0, k, $modulus.0[0]); - (r1, carry) = mac(r1, k, $modulus.0[1], carry); - (r2, carry) = mac(r2, k, $modulus.0[2], carry); - (r3, carry) = mac(r3, k, $modulus.0[3], carry); - (r4, carry2) = r4.overflowing_add(carry); - - k = r1.wrapping_mul($inv); - (_, carry) = macx(r1, k, $modulus.0[0]); - (r2, carry) = mac(r2, k, $modulus.0[1], carry); - (r3, carry) = mac(r3, k, $modulus.0[2], carry); - (r4, carry) = mac(r4, k, $modulus.0[3], carry); - (r5, carry2) = adc(r5, carry, carry2); - - k = r2.wrapping_mul($inv); - (_, carry) = macx(r2, k, $modulus.0[0]); - (r3, carry) = mac(r3, k, $modulus.0[1], carry); - (r4, carry) = mac(r4, k, $modulus.0[2], carry); - (r5, carry) = mac(r5, k, $modulus.0[3], carry); - (r6, carry2) = adc(r6, carry, carry2); - - k = r3.wrapping_mul($inv); - (_, carry) = macx(r3, k, $modulus.0[0]); - (r4, carry) = mac(r4, k, $modulus.0[1], carry); - (r5, carry) = mac(r5, k, $modulus.0[2], carry); - (r6, carry) = mac(r6, k, $modulus.0[3], carry); - (r7, _) = adc(r7, carry, carry2); - - // Result may be within MODULUS of the correct value - (&$field([r4, r5, r6, r7])).sub(&$modulus) - } - } - }; - ($field:ident, $modulus:ident, $inv:ident, dense) => { - impl $field { - /// Adds `rhs` to `self`, returning the result. - #[inline] - pub const fn add(&self, rhs: &Self) -> Self { - let (d0, carry) = self.0[0].overflowing_add(rhs.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); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - let (d0, borrow) = d0.overflowing_sub($modulus.0[0]); - let (d1, borrow) = sbb(d1, $modulus.0[1], borrow); - let (d2, borrow) = sbb(d2, $modulus.0[2], borrow); - let (d3, borrow) = sbb(d3, $modulus.0[3], borrow); - let borrow = (carry as u64).wrapping_sub(borrow as u64); - - let (d0, carry) = d0.overflowing_add($modulus.0[0] & borrow); - 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); - - $field([d0, d1, d2, d3]) - } - - /// Multiplies `rhs` by `self`, returning the result. - #[inline] - pub const fn mul(&self, rhs: &Self) -> $field { - // 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); - - $field::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - #[allow(clippy::too_many_arguments)] - #[inline(always)] - pub(crate) const fn montgomery_reduce( - r0: u64, - mut r1: u64, - mut r2: u64, - mut r3: u64, - mut r4: u64, - mut r5: u64, - mut r6: u64, - mut r7: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - let mut k; - let mut carry; - let mut carry2; - - k = r0.wrapping_mul($inv); - (_, carry) = macx(r0, k, $modulus.0[0]); - (r1, carry) = mac(r1, k, $modulus.0[1], carry); - (r2, carry) = mac(r2, k, $modulus.0[2], carry); - (r3, carry) = mac(r3, k, $modulus.0[3], carry); - (r4, carry2) = r4.overflowing_add(carry); - - k = r1.wrapping_mul($inv); - (_, carry) = k.carrying_mul($modulus.0[0], r1); - (r2, carry) = mac(r2, k, $modulus.0[1], carry); - (r3, carry) = mac(r3, k, $modulus.0[2], carry); - (r4, carry) = mac(r4, k, $modulus.0[3], carry); - (r5, carry2) = adc(r5, carry, carry2); - - k = r2.wrapping_mul($inv); - (_, carry) = macx(r2, k, $modulus.0[0]); - (r3, carry) = mac(r3, k, $modulus.0[1], carry); - (r4, carry) = mac(r4, k, $modulus.0[2], carry); - (r5, carry) = mac(r5, k, $modulus.0[3], carry); - (r6, carry2) = adc(r6, carry, carry2); - - k = r3.wrapping_mul($inv); - (_, carry) = macx(r3, k, $modulus.0[0]); - (r4, carry) = mac(r4, k, $modulus.0[1], carry); - (r5, carry) = mac(r5, k, $modulus.0[2], carry); - (r6, carry) = mac(r6, k, $modulus.0[3], carry); - (r7, carry2) = adc(r7, carry, carry2); - - // Result may be within MODULUS of the correct value - let mut borrow; - (r4, borrow) = r4.overflowing_sub($modulus.0[0]); - (r5, borrow) = sbb(r5, $modulus.0[1], borrow); - (r6, borrow) = sbb(r6, $modulus.0[2], borrow); - (r7, borrow) = sbb(r7, $modulus.0[3], borrow); - let borrow = (carry2 as u64).wrapping_sub(borrow as u64); - - (r4, carry2) = r4.overflowing_add($modulus.0[0] & borrow); - (r5, carry2) = adc(r5, $modulus.0[1] & borrow, carry2); - (r6, carry2) = adc(r6, $modulus.0[2] & borrow, carry2); - (r7, _) = adc(r7, $modulus.0[3] & borrow, carry2); - $field([r4, r5, r6, r7]) - } - } - }; -} diff --git a/arithmetic/curves/src/derive/mod.rs b/arithmetic/curves/src/derive/mod.rs deleted file mode 100644 index de8bc4ccb8..0000000000 --- a/arithmetic/curves/src/derive/mod.rs +++ /dev/null @@ -1,164 +0,0 @@ -#[macro_use] -pub mod curve; -#[macro_use] -pub mod field; - -#[macro_export] -macro_rules! impl_add_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> ::core::ops::Add<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: &'b $rhs) -> $output { - &self + rhs - } - } - - impl<'a> ::core::ops::Add<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - self + &rhs - } - } - - impl ::core::ops::Add<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn add(self, rhs: $rhs) -> $output { - &self + &rhs - } - } - }; -} - -#[macro_export] -macro_rules! impl_sub_binop_specify_output { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> ::core::ops::Sub<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: &'b $rhs) -> $output { - &self - rhs - } - } - - impl<'a> ::core::ops::Sub<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - self - &rhs - } - } - - impl ::core::ops::Sub<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn sub(self, rhs: $rhs) -> $output { - &self - &rhs - } - } - }; -} - -#[macro_export] -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_export] -macro_rules! impl_binops_multiplicative_mixed { - ($lhs:ident, $rhs:ident, $output:ident) => { - impl<'b> ::core::ops::Mul<&'b $rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: &'b $rhs) -> $output { - &self * rhs - } - } - - impl<'a> ::core::ops::Mul<$rhs> for &'a $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - self * &rhs - } - } - - impl ::core::ops::Mul<$rhs> for $lhs { - type Output = $output; - - #[inline] - fn mul(self, rhs: $rhs) -> $output { - &self * &rhs - } - } - }; -} - -#[macro_export] -macro_rules! impl_binops_additive { - ($lhs:ident, $rhs:ident) => { - impl_binops_additive_specify_output!($lhs, $rhs, $lhs); - - impl ::core::ops::SubAssign<$rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: $rhs) { - *self = &*self - &rhs; - } - } - - impl ::core::ops::AddAssign<$rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: $rhs) { - *self = &*self + &rhs; - } - } - - impl<'b> ::core::ops::SubAssign<&'b $rhs> for $lhs { - #[inline] - fn sub_assign(&mut self, rhs: &'b $rhs) { - *self = &*self - rhs; - } - } - - impl<'b> ::core::ops::AddAssign<&'b $rhs> for $lhs { - #[inline] - fn add_assign(&mut self, rhs: &'b $rhs) { - *self = &*self + rhs; - } - } - }; -} - -#[macro_export] -macro_rules! impl_binops_multiplicative { - ($lhs:ident, $rhs:ident) => { - impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); - - impl ::core::ops::MulAssign<$rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: $rhs) { - *self = &*self * &rhs; - } - } - - impl<'b> ::core::ops::MulAssign<&'b $rhs> for $lhs { - #[inline] - fn mul_assign(&mut self, rhs: &'b $rhs) { - *self = &*self * rhs; - } - } - }; -} diff --git a/arithmetic/curves/src/lib.rs b/arithmetic/curves/src/lib.rs deleted file mode 100644 index abdf1e2c57..0000000000 --- a/arithmetic/curves/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![cfg_attr(feature = "asm", feature(asm_const))] -#![feature(bigint_helper_methods)] -#![feature(const_bigint_helper_methods)] - -mod arithmetic; - -pub mod bn256; -pub mod pairing; -pub mod pasta; -pub mod secp256k1; -pub mod serde; - -#[macro_use] -mod derive; - -pub use arithmetic::CurveAffineExt; -pub use pasta_curves::arithmetic::{Coordinates, CurveAffine, CurveExt, FieldExt, Group}; - -pub extern crate group; - -#[cfg(test)] -pub mod tests; - -#[cfg(all(feature = "prefetch", target_arch = "x86_64"))] -#[inline(always)] -pub fn prefetch(data: &[T], offset: usize) { - use core::arch::x86_64::_mm_prefetch; - unsafe { - _mm_prefetch( - data.as_ptr().offset(offset as isize) as *const i8, - core::arch::x86_64::_MM_HINT_T0, - ); - } -} diff --git a/arithmetic/curves/src/pairing.rs b/arithmetic/curves/src/pairing.rs deleted file mode 100644 index 55586e7c9e..0000000000 --- a/arithmetic/curves/src/pairing.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::{CurveAffine, FieldExt, Group as _Group}; -use core::ops::Mul; -use group::{ - prime::PrimeCurve, Group, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, - UncompressedEncoding, -}; - -pub trait Engine: Sized + 'static + Clone { - /// This is the scalar field of the engine's groups. - type Scalar: FieldExt; - - /// The projective representation of an element in G1. - type G1: PrimeCurve - + From - + GroupOps - + GroupOpsOwned - + ScalarMul - + ScalarMulOwned - + _Group; - - /// The affine representation of an element in G1. - type G1Affine: PairingCurveAffine< - ScalarExt = Self::Scalar, - CurveExt = Self::G1, - Pair = Self::G2Affine, - PairingResult = Self::Gt, - > + From - + Mul - + for<'a> Mul<&'a Self::Scalar, Output = Self::G1>; - - /// The projective representation of an element in G2. - type G2: PrimeCurve - + From - + GroupOps - + GroupOpsOwned - + ScalarMul - + ScalarMulOwned; - - /// The affine representation of an element in G2. - type G2Affine: PairingCurveAffine< - ScalarExt = Self::Scalar, - CurveExt = Self::G2, - Pair = Self::G1Affine, - PairingResult = Self::Gt, - > + From - + Mul - + for<'a> Mul<&'a Self::Scalar, Output = Self::G2>; - - /// The extension field that hosts the target group of the pairing. - type Gt: Group + ScalarMul + ScalarMulOwned; - - /// Invoke the pairing function `G1 x G2 -> Gt` without the use of precomputation and - /// other optimizations. - fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt; -} - -/// Affine representation of an elliptic curve point that can be used -/// to perform pairings. -pub trait PairingCurveAffine: CurveAffine + UncompressedEncoding { - type Pair: PairingCurveAffine; - type PairingResult: Group; - - /// Perform a pairing - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; -} - -/// An engine that can compute sums of pairings in an efficient way. -pub trait MultiMillerLoop: Engine { - /// The prepared form of `Self::G2Affine`. - type G2Prepared: Clone + Send + Sync + From; - - /// The type returned by `Engine::miller_loop`. - type Result: MillerLoopResult; - - /// 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).$$ - fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result; -} - -/// 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 -/// [`MillerLoopResult::final_exponentiation`] is called, which is also expensive. -pub trait MillerLoopResult { - /// The extension field that hosts the target group of the pairing. - type Gt: Group; - - /// This performs a "final exponentiation" routine to convert the result of a Miller - /// loop into an element of [`MillerLoopResult::Gt`], so that it can be compared with - /// other elements of `Gt`. - fn final_exponentiation(&self) -> Self::Gt; -} diff --git a/arithmetic/curves/src/pasta/mod.rs b/arithmetic/curves/src/pasta/mod.rs deleted file mode 100644 index ed94d11a47..0000000000 --- a/arithmetic/curves/src/pasta/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub use pasta_curves::{pallas, vesta, Ep, EpAffine, Eq, EqAffine, Fp, Fq}; diff --git a/arithmetic/curves/src/secp256k1/curve.rs b/arithmetic/curves/src/secp256k1/curve.rs deleted file mode 100644 index c3de22b935..0000000000 --- a/arithmetic/curves/src/secp256k1/curve.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::secp256k1::Fp; -use crate::secp256k1::Fq; -use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt, Group}; -use core::cmp; -use core::fmt::Debug; -use core::iter::Sum; -use core::ops::{Add, Mul, Neg, Sub}; -use ff::{Field, PrimeField}; -use group::Curve; -use group::{prime::PrimeCurveAffine, Group as _, GroupEncoding}; -use rand::RngCore; -use serde::{Deserialize, Serialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -impl Secp256k1 { - fn endomorphism_base(&self) -> Self { - unimplemented!(); - } -} - -impl group::cofactor::CofactorGroup for Secp256k1 { - type Subgroup = Secp256k1; - - fn clear_cofactor(&self) -> Self { - *self - } - - fn into_subgroup(self) -> CtOption { - CtOption::new(self, 1.into()) - } - - fn is_torsion_free(&self) -> Choice { - 1.into() - } -} - -// Reference: https://neuromancer.sk/std/secg/secp256k1 -const SECP_GENERATOR_X: Fp = Fp::from_raw([ - 0x59F2815B16F81798, - 0x029BFCDB2DCE28D9, - 0x55A06295CE870B07, - 0x79BE667EF9DCBBAC, -]); -const SECP_GENERATOR_Y: Fp = Fp::from_raw([ - 0x9C47D08FFB10D4B8, - 0xFD17B448A6855419, - 0x5DA4FBFC0E1108A8, - 0x483ADA7726A3C465, -]); -const SECP_B: Fp = Fp::from_raw([7, 0, 0, 0]); - -use crate::{ - batch_add, impl_add_binop_specify_output, impl_binops_additive, - impl_binops_additive_specify_output, impl_binops_multiplicative, - impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, new_curve_impl, -}; - -new_curve_impl!( - (pub), - Secp256k1, - Secp256k1Affine, - Secp256k1Compressed, - 33, - Fp, - Fq, - (SECP_GENERATOR_X,SECP_GENERATOR_Y), - SECP_B, - "secp256k1", -); - -impl CurveAffineExt for Secp256k1Affine { - batch_add!(); - - fn into_coordinates(self) -> (Self::Base, Self::Base) { - (self.x, self.y) - } -} - -#[test] -fn test_curve() { - crate::tests::curve::curve_tests::(); -} - -#[test] -fn test_serialization() { - crate::tests::curve::random_serialization_test::(); -} - -#[test] -fn ecdsa_example() { - use crate::group::Curve; - use crate::{CurveAffine, FieldExt}; - use rand_core::OsRng; - - fn mod_n(x: Fp) -> Fq { - let mut x_repr = [0u8; 32]; - x_repr.copy_from_slice(x.to_repr().as_ref()); - let mut x_bytes = [0u8; 64]; - x_bytes[..32].copy_from_slice(&x_repr[..]); - Fq::from_bytes_wide(&x_bytes) - } - - let g = Secp256k1::generator(); - - for _ in 0..1000 { - // Generate a key pair - let sk = Fq::random(OsRng); - let pk = (g * sk).to_affine(); - - // Generate a valid signature - // Suppose `m_hash` is the message hash - let msg_hash = Fq::random(OsRng); - - let (r, s) = { - // Draw arandomness - let k = Fq::random(OsRng); - let k_inv = k.invert().unwrap(); - - // Calculate `r` - let r_point = (g * k).to_affine().coordinates().unwrap(); - let x = r_point.x(); - let r = mod_n(*x); - - // Calculate `s` - let s = k_inv * (msg_hash + (r * sk)); - - (r, s) - }; - - { - // Verify - let s_inv = s.invert().unwrap(); - let u_1 = msg_hash * s_inv; - let u_2 = r * s_inv; - - let v_1 = g * u_1; - let v_2 = pk * u_2; - - let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); - let x_candidate = r_point.x(); - let r_candidate = mod_n(*x_candidate); - - assert_eq!(r, r_candidate); - } - } -} diff --git a/arithmetic/curves/src/secp256k1/fp.rs b/arithmetic/curves/src/secp256k1/fp.rs deleted file mode 100644 index 94ab7ac499..0000000000 --- a/arithmetic/curves/src/secp256k1/fp.rs +++ /dev/null @@ -1,283 +0,0 @@ -use core::convert::TryInto; -use core::fmt; -use core::ops::{Add, Mul, Neg, Sub}; - -use ff::PrimeField; -use rand::RngCore; -use serde::{Deserialize, Serialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use pasta_curves::arithmetic::{FieldExt, Group, SqrtRatio}; - -use crate::arithmetic::{adc, mac, macx, sbb}; - -/// This represents an element of $\mathbb{F}_p$ where -/// -/// `p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f` -/// -/// is the base field of the secp256k1 curve. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. `Fp` values are always in -// Montgomery form; i.e., Fp(a) = aR mod p, with R = 2^256. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct Fp(pub(crate) [u64; 4]); - -/// Constant representing the modulus -/// p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f -const MODULUS: Fp = Fp([ - 0xfffffffefffffc2f, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, -]); - -/// The modulus as u32 limbs. -#[cfg(not(target_pointer_width = "64"))] -const MODULUS_LIMBS_32: [u32; 8] = [ - 0xffff_fc2f, - 0xffff_fffe, - 0xffff_ffff, - 0xffff_ffff, - 0xffff_ffff, - 0xffff_ffff, - 0xffff_ffff, - 0xffff_ffff, -]; - -/// Constant representing the modolus as static str -const MODULUS_STR: &str = "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"; - -/// INV = -(p^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0xd838091dd2253531; - -/// R = 2^256 mod p -/// 0x1000003d1 -const R: Fp = Fp([0x1000003d1, 0, 0, 0]); - -/// R^2 = 2^512 mod p -/// 0x1000007a2000e90a1 -const R2: Fp = Fp([0x000007a2000e90a1, 0x1, 0, 0]); - -/// R^3 = 2^768 mod p -/// 0x100000b73002bb1e33795f671 -const R3: Fp = Fp([0x002bb1e33795f671, 0x100000b73, 0, 0]); - -/// 1 / 2 mod p -const TWO_INV: Fp = Fp::from_raw([ - 0xffffffff7ffffe18, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0x7fffffffffffffff, -]); - -const ZETA: Fp = Fp::zero(); -const DELTA: Fp = Fp::zero(); -const ROOT_OF_UNITY_INV: Fp = Fp::zero(); - -use crate::{ - field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, - impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, - impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, -}; -impl_binops_additive!(Fp, Fp); -impl_binops_multiplicative!(Fp, Fp); -field_common!( - Fp, - MODULUS, - INV, - MODULUS_STR, - TWO_INV, - ROOT_OF_UNITY_INV, - DELTA, - ZETA, - R, - R2, - R3 -); -field_arithmetic!(Fp, MODULUS, INV, dense); - -impl Fp { - pub const fn size() -> usize { - 32 - } -} - -impl ff::Field for Fp { - fn random(mut rng: impl RngCore) -> Self { - Self::from_u512([ - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - ]) - } - - fn zero() -> Self { - Self::zero() - } - - fn one() -> Self { - Self::one() - } - - fn double(&self) -> Self { - self.double() - } - - #[inline(always)] - fn square(&self) -> Self { - self.square() - } - - /// Computes the square root of this element, if it exists. - fn sqrt(&self) -> CtOption { - let tmp = self.pow(&[ - 0xffffffffbfffff0c, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0x3fffffffffffffff, - ]); - - CtOption::new(tmp, tmp.square().ct_eq(self)) - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - fn invert(&self) -> CtOption { - let tmp = self.pow_vartime([ - 0xfffffffefffffc2d, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - ]); - - CtOption::new(tmp, !self.ct_eq(&Self::zero())) - } - - fn pow_vartime>(&self, exp: S) -> Self { - let mut res = Self::one(); - let mut found_one = false; - for e in exp.as_ref().iter().rev() { - for i in (0..64).rev() { - if found_one { - res = res.square(); - } - - if ((*e >> i) & 1) == 1 { - found_one = true; - res *= self; - } - } - } - res - } -} - -impl ff::PrimeField for Fp { - type Repr = [u8; 32]; - - const NUM_BITS: u32 = 256; - const CAPACITY: u32 = 255; - const S: u32 = 1; - - fn from_repr(repr: Self::Repr) -> CtOption { - let mut tmp = Fp([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); - tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); - tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); - tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); - - // Try to subtract the modulus - let (_, borrow) = tmp.0[0].overflowing_sub(MODULUS.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)) - } - - fn to_repr(&self) -> Self::Repr { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Fp::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - - 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 - } - - fn is_odd(&self) -> Choice { - Choice::from(self.to_repr()[0] & 1) - } - - fn multiplicative_generator() -> Self { - unimplemented!(); - } - - fn root_of_unity() -> Self { - unimplemented!(); - } -} - -impl SqrtRatio for Fp { - const T_MINUS1_OVER2: [u64; 4] = [0, 0, 0, 0]; - - fn get_lower_32(&self) -> u32 { - let tmp = Fp::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - tmp.0[0] as u32 - } -} - -#[cfg(test)] -mod test { - use super::*; - use ff::Field; - use rand_core::OsRng; - - #[test] - fn test_sqrt() { - // NB: TWO_INV is standing in as a "random" field element - let v = (Fp::TWO_INV).square().sqrt().unwrap(); - assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV); - - for _ in 0..10000 { - let a = Fp::random(OsRng); - let mut b = a; - b = b.square(); - - let b = b.sqrt().unwrap(); - let mut negb = b; - negb = negb.neg(); - - assert!(a == b || a == negb); - } - } - - #[test] - fn test_field() { - crate::tests::field::random_field_tests::("secp256k1 base".to_string()); - } - - #[test] - fn test_serialization() { - crate::tests::field::random_serialization_test::("secp256k1 base".to_string()); - } -} diff --git a/arithmetic/curves/src/secp256k1/fq.rs b/arithmetic/curves/src/secp256k1/fq.rs deleted file mode 100644 index 6c8a3201c9..0000000000 --- a/arithmetic/curves/src/secp256k1/fq.rs +++ /dev/null @@ -1,326 +0,0 @@ -use core::convert::TryInto; -use core::fmt; -use core::ops::{Add, Mul, Neg, Sub}; - -use ff::PrimeField; -use rand::RngCore; -use serde::{Deserialize, Serialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::arithmetic::{adc, mac, macx, sbb}; - -use pasta_curves::arithmetic::{FieldExt, Group, SqrtRatio}; - -/// This represents an element of $\mathbb{F}_q$ where -/// -/// `q = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141` -/// -/// is the scalar field of the secp256k1 curve. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. `Fq` values are always in -// Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct Fq(pub(crate) [u64; 4]); - -/// Constant representing the modulus -/// q = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 -const MODULUS: Fq = Fq([ - 0xbfd25e8cd0364141, - 0xbaaedce6af48a03b, - 0xfffffffffffffffe, - 0xffffffffffffffff, -]); - -/// The modulus as u32 limbs. -#[cfg(not(target_pointer_width = "64"))] -const MODULUS_LIMBS_32: [u32; 8] = [ - 0xd036_4141, - 0xbfd2_5e8c, - 0xaf48_a03b, - 0xbaae_dce6, - 0xffff_fffe, - 0xffff_ffff, - 0xffff_ffff, - 0xffff_ffff, -]; - -///Constant representing the modulus as static str -const MODULUS_STR: &str = "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"; - -/// INV = -(q^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0x4b0dff665588b13f; - -/// R = 2^256 mod q -/// 0x14551231950b75fc4402da1732fc9bebf -const R: Fq = Fq([0x402da1732fc9bebf, 0x4551231950b75fc4, 0x1, 0]); - -/// R^2 = 2^512 mod q -/// 0x9d671cd581c69bc5e697f5e45bcd07c6741496c20e7cf878896cf21467d7d140 -const R2: Fq = Fq([ - 0x896cf21467d7d140, - 0x741496c20e7cf878, - 0xe697f5e45bcd07c6, - 0x9d671cd581c69bc5, -]); - -/// R^3 = 2^768 mod q -/// 0x555d800c18ef116db1b31347f1d0b2da0017648444d4322c7bc0cfe0e9ff41ed -const R3: Fq = Fq([ - 0x7bc0cfe0e9ff41ed, - 0x0017648444d4322c, - 0xb1b31347f1d0b2da, - 0x555d800c18ef116d, -]); - -/// `GENERATOR = 7 mod r` is a generator of the `q - 1` order multiplicative -/// subgroup, or in other words a primitive root of the field. -const GENERATOR: Fq = Fq::from_raw([0x07, 0x00, 0x00, 0x00]); - -/// GENERATOR^t where t * 2^s + 1 = r -/// with t odd. In other words, this -/// is a 2^s root of unity. -/// `0xc1dc060e7a91986df9879a3fbc483a898bdeab680756045992f4b5402b052f2` -const ROOT_OF_UNITY: Fq = Fq::from_raw([ - 0x992f4b5402b052f2, - 0x98bdeab680756045, - 0xdf9879a3fbc483a8, - 0xc1dc060e7a91986, -]); - -/// 1 / ROOT_OF_UNITY mod q -const ROOT_OF_UNITY_INV: Fq = Fq::from_raw([ - 0xb6fb30a0884f0d1c, - 0x77a275910aa413c3, - 0xefc7b0c75b8cbb72, - 0xfd3ae181f12d7096, -]); - -/// 1 / 2 mod q -const TWO_INV: Fq = Fq::from_raw([ - 0xdfe92f46681b20a1, - 0x5d576e7357a4501d, - 0xffffffffffffffff, - 0x7fffffffffffffff, -]); - -const ZETA: Fq = Fq::zero(); -const DELTA: Fq = Fq::zero(); - -use crate::{ - field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, - impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, - impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, -}; -impl_binops_additive!(Fq, Fq); -impl_binops_multiplicative!(Fq, Fq); -field_common!( - Fq, - MODULUS, - INV, - MODULUS_STR, - TWO_INV, - ROOT_OF_UNITY_INV, - DELTA, - ZETA, - R, - R2, - R3 -); -field_arithmetic!(Fq, MODULUS, INV, dense); - -impl Fq { - pub const fn size() -> usize { - 32 - } -} - -impl ff::Field for Fq { - fn random(mut rng: impl RngCore) -> Self { - Self::from_u512([ - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - rng.next_u64(), - ]) - } - - fn zero() -> Self { - Self::zero() - } - - fn one() -> Self { - Self::one() - } - - fn double(&self) -> Self { - self.double() - } - - #[inline(always)] - fn square(&self) -> Self { - self.square() - } - - /// Computes the square root of this element, if it exists. - fn sqrt(&self) -> CtOption { - crate::arithmetic::sqrt_tonelli_shanks(self, ::T_MINUS1_OVER2) - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - fn invert(&self) -> CtOption { - let tmp = self.pow_vartime([ - 0xbfd25e8cd036413f, - 0xbaaedce6af48a03b, - 0xfffffffffffffffe, - 0xffffffffffffffff, - ]); - - CtOption::new(tmp, !self.ct_eq(&Self::zero())) - } - - fn pow_vartime>(&self, exp: S) -> Self { - let mut res = Self::one(); - let mut found_one = false; - for e in exp.as_ref().iter().rev() { - for i in (0..64).rev() { - if found_one { - res = res.square(); - } - - if ((*e >> i) & 1) == 1 { - found_one = true; - res *= self; - } - } - } - res - } -} - -impl ff::PrimeField for Fq { - type Repr = [u8; 32]; - - const NUM_BITS: u32 = 256; - const CAPACITY: u32 = 255; - const S: u32 = 6; - - fn from_repr(repr: Self::Repr) -> CtOption { - let mut tmp = Fq([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); - tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); - tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); - tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); - - // Try to subtract the modulus - let (_, borrow) = tmp.0[0].overflowing_sub(MODULUS.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)) - } - - fn to_repr(&self) -> Self::Repr { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Fq::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - - 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 - } - - fn is_odd(&self) -> Choice { - Choice::from(self.to_repr()[0] & 1) - } - - fn multiplicative_generator() -> Self { - GENERATOR - } - - fn root_of_unity() -> Self { - ROOT_OF_UNITY - } -} - -impl SqrtRatio for Fq { - const T_MINUS1_OVER2: [u64; 4] = [ - 0x777fa4bd19a06c82, - 0xfd755db9cd5e9140, - 0xffffffffffffffff, - 0x01ffffffffffffff, - ]; - - fn get_lower_32(&self) -> u32 { - let tmp = Fq::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - tmp.0[0] as u32 - } -} - -#[cfg(test)] -mod test { - use super::*; - use ff::Field; - use rand_core::OsRng; - - #[test] - fn test_sqrt() { - // NB: TWO_INV is standing in as a "random" field element - let v = (Fq::TWO_INV).square().sqrt().unwrap(); - assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV); - - for _ in 0..10000 { - let a = Fq::random(OsRng); - let mut b = a; - b = b.square(); - - let b = b.sqrt().unwrap(); - let mut negb = b; - negb = negb.neg(); - - assert!(a == b || a == negb); - } - } - - #[test] - fn test_root_of_unity() { - assert_eq!( - Fq::root_of_unity().pow_vartime([1 << Fq::S, 0, 0, 0]), - Fq::one() - ); - } - - #[test] - fn test_inv_root_of_unity() { - assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::root_of_unity().invert().unwrap()); - } - - #[test] - fn test_field() { - crate::tests::field::random_field_tests::("secp256k1 scalar".to_string()); - } - - #[test] - fn test_serialization() { - crate::tests::field::random_serialization_test::("secp256k1 scalar".to_string()); - } -} diff --git a/arithmetic/curves/src/secp256k1/mod.rs b/arithmetic/curves/src/secp256k1/mod.rs deleted file mode 100644 index edc3c0974f..0000000000 --- a/arithmetic/curves/src/secp256k1/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod curve; -mod fp; -mod fq; - -pub use curve::*; -pub use fp::*; -pub use fq::*; diff --git a/arithmetic/curves/src/serde.rs b/arithmetic/curves/src/serde.rs deleted file mode 100644 index 5d0f24fc1a..0000000000 --- a/arithmetic/curves/src/serde.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::io::{self, Read, Write}; - -/// Trait for converting raw bytes to/from the internal representation of a type. -/// For example, field elements are represented in Montgomery form and serialized/deserialized without Montgomery reduction. -pub trait SerdeObject: Sized { - /// The purpose of unchecked functions is to read the internal memory representation - /// of a type from bytes as quickly as possible. No sanitization checks are performed - /// to ensure the bytes represent a valid object. As such this function should only be - /// used internally as an extension of machine memory. It should not be used to deserialize - /// externally provided data. - fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self; - fn from_raw_bytes(bytes: &[u8]) -> Option; - - fn to_raw_bytes(&self) -> Vec; - - /// The purpose of unchecked functions is to read the internal memory representation - /// of a type from disk as quickly as possible. No sanitization checks are performed - /// to ensure the bytes represent a valid object. This function should only be used - /// internally when some machine state cannot be kept in memory (e.g., between runs) - /// and needs to be reloaded as quickly as possible. - fn read_raw_unchecked(reader: &mut R) -> Self; - fn read_raw(reader: &mut R) -> io::Result; - - fn write_raw(&self, writer: &mut W) -> io::Result<()>; -} diff --git a/arithmetic/curves/src/tests/curve.rs b/arithmetic/curves/src/tests/curve.rs deleted file mode 100644 index e6e6797f31..0000000000 --- a/arithmetic/curves/src/tests/curve.rs +++ /dev/null @@ -1,287 +0,0 @@ -#![allow(clippy::eq_op)] -use crate::{group::GroupEncoding, serde::SerdeObject}; -use ff::Field; -use group::prime::PrimeCurveAffine; -use pasta_curves::arithmetic::{CurveAffine, CurveExt}; -use rand_core::OsRng; - -pub fn curve_tests() { - is_on_curve::(); - equality::(); - projective_to_affine_affine_to_projective::(); - projective_addition::(); - mixed_addition::(); - multiplication::(); - batch_normalize::(); - serdes::(); -} - -fn serdes() { - for _ in 0..100 { - let projective_point = G::random(OsRng); - let affine_point: G::AffineExt = projective_point.into(); - let projective_repr = projective_point.to_bytes(); - let affine_repr = affine_point.to_bytes(); - - println!( - "{:?} \n{:?}", - projective_repr.as_ref(), - affine_repr.as_ref() - ); - - let projective_point_rec = G::from_bytes(&projective_repr).unwrap(); - let projective_point_rec_unchecked = G::from_bytes(&projective_repr).unwrap(); - let affine_point_rec = G::AffineExt::from_bytes(&affine_repr).unwrap(); - let affine_point_rec_unchecked = G::AffineExt::from_bytes(&affine_repr).unwrap(); - - assert_eq!(projective_point, projective_point_rec); - assert_eq!(projective_point, projective_point_rec_unchecked); - assert_eq!(affine_point, affine_point_rec); - assert_eq!(affine_point, affine_point_rec_unchecked); - } -} - -pub fn random_serialization_test() -where - G: SerdeObject, - G::AffineExt: SerdeObject, -{ - for _ in 0..100 { - let projective_point = G::random(OsRng); - let affine_point: G::AffineExt = projective_point.into(); - - let projective_bytes = projective_point.to_raw_bytes(); - let projective_point_rec = G::from_raw_bytes(&projective_bytes).unwrap(); - assert_eq!(projective_point, projective_point_rec); - let mut buf = Vec::new(); - projective_point.write_raw(&mut buf).unwrap(); - let projective_point_rec = G::read_raw(&mut &buf[..]).unwrap(); - assert_eq!(projective_point, projective_point_rec); - - let affine_bytes = affine_point.to_raw_bytes(); - let affine_point_rec = G::AffineExt::from_raw_bytes(&affine_bytes).unwrap(); - assert_eq!(affine_point, affine_point_rec); - let mut buf = Vec::new(); - affine_point.write_raw(&mut buf).unwrap(); - let affine_point_rec = G::AffineExt::read_raw(&mut &buf[..]).unwrap(); - assert_eq!(affine_point, affine_point_rec); - } -} - -fn is_on_curve() { - assert!(bool::from(G::identity().is_on_curve())); - assert!(bool::from(G::generator().is_on_curve())); - assert!(bool::from(G::identity().is_on_curve())); - assert!(bool::from(G::generator().is_on_curve())); - - for _ in 0..100 { - let point = G::random(OsRng); - assert!(bool::from(point.is_on_curve())); - let affine_point: G::AffineExt = point.into(); - assert!(bool::from(affine_point.is_on_curve())); - } -} - -fn equality() { - let a = G::generator(); - let b = G::identity(); - - assert!(a.eq(&a)); - assert!(b.eq(&b)); - assert!(a != b); - assert!(b != a); - - for _ in 0..100 { - let a = G::random(OsRng); - let b = G::random(OsRng); - - assert!(a.eq(&a)); - assert!(b.eq(&b)); - assert!(a != b); - assert!(b != a); - - let a: G::AffineExt = a.into(); - let b: G::AffineExt = b.into(); - - assert!(a.eq(&a)); - assert!(b.eq(&b)); - assert!(a != b); - assert!(b != a); - } -} - -fn projective_to_affine_affine_to_projective() { - let a = G::generator(); - let b = G::identity(); - - assert!(bool::from(G::AffineExt::from(a).is_on_curve())); - assert!(!bool::from(G::AffineExt::from(a).is_identity())); - assert!(bool::from(G::AffineExt::from(b).is_on_curve())); - assert!(bool::from(G::AffineExt::from(b).is_identity())); - - let a = G::AffineExt::generator(); - let b = G::AffineExt::identity(); - - assert!(bool::from(G::from(a).is_on_curve())); - assert!(!bool::from(G::from(a).is_identity())); - assert!(bool::from(G::from(b).is_on_curve())); - assert!(bool::from(G::from(b).is_identity())); -} - -fn projective_addition() { - let a = G::identity(); - let b = G::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - let c = a - b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - - let a = G::identity(); - let a = -a; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(a.is_identity())); - - let a = G::random(OsRng); - assert!(a == a + G::identity()); - assert!(a == G::identity() + a); - assert!(-a == G::identity() - a); - - let a = G::identity(); - let a = a.double(); - assert!(bool::from(c.is_on_curve())); - assert!(bool::from(a.is_identity())); - - let a = G::generator(); - let a = a.double(); - assert!(bool::from(c.is_on_curve())); - assert_eq!(a, G::generator() + G::generator()); - - let a = G::random(OsRng); - assert!(a.double() - a == a); - - let a = G::random(OsRng); - let b = G::random(OsRng); - let c = G::random(OsRng); - assert!((a + b).eq(&(b + a))); - assert!(a - b == -(b - a)); - assert!(c + (a + b) == a + (c + b)); - assert!((a - b) - c == (a - c) - b); - - let a = G::generator().double().double(); // 4P - let b = G::generator().double(); // 2P - let c = a + b; - - let mut d = G::generator(); - for _ in 0..5 { - d += G::generator(); - } - - assert!(c == d); - 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())); -} - -fn mixed_addition() { - let a = G::identity(); - let b = G::AffineRepr::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - let c = a - b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - - let a = G::identity(); - let a = -a; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(a.is_identity())); - let a = G::AffineExt::identity(); - let a = -a; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(a.is_identity())); - - let a: G::AffineExt = G::random(OsRng).into(); - assert!(a.to_curve() == a + G::AffineExt::identity()); - - let a = G::random(OsRng); - assert!(a.double() - a == a); - - let a = G::random(OsRng); - let b: G::AffineExt = G::random(OsRng).into(); - let c0 = a + b; - let c1 = a + G::from(b); - assert_eq!(c0, c1); -} - -fn batch_normalize() { - let a = G::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] = G::identity() - } - if b_identity { - v[1] = G::identity() - } - if c_identity { - v[2] = G::identity() - } - - let mut t = [ - G::AffineExt::identity(), - G::AffineExt::identity(), - G::AffineExt::identity(), - ]; - let expected = [ - G::AffineExt::from(v[0]), - G::AffineExt::from(v[1]), - G::AffineExt::from(v[2]), - ]; - - G::batch_normalize(&v[..], &mut t[..]); - - assert_eq!(&t[..], &expected[..]); - } - } - } -} - -fn multiplication() { - for _ in 1..1000 { - let s1 = G::ScalarExt::random(OsRng); - let s2 = G::ScalarExt::random(OsRng); - - let t0 = G::identity() * s1; - assert!(bool::from(t0.is_identity())); - - let a = G::random(OsRng); - let t0 = a * G::ScalarExt::one(); - assert_eq!(a, t0); - - let t0 = a * G::ScalarExt::zero(); - assert!(bool::from(t0.is_identity())); - - let t0 = a * s1 + a * s2; - - let s3 = s1 + s2; - let t1 = a * s3; - - assert_eq!(t0, t1); - - let mut t0 = a * s1; - let mut t1 = a * s2; - t0 += t1; - let s3 = s1 + s2; - t1 = a * s3; - assert_eq!(t0, t1); - } -} diff --git a/arithmetic/curves/src/tests/field.rs b/arithmetic/curves/src/tests/field.rs deleted file mode 100644 index faa609291f..0000000000 --- a/arithmetic/curves/src/tests/field.rs +++ /dev/null @@ -1,231 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff::Field; -use rand::{RngCore, SeedableRng}; -use rand_xorshift::XorShiftRng; - -use crate::serde::SerdeObject; - -pub fn random_field_tests(type_name: String) { - 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, type_name.clone()); - random_addition_tests::(&mut rng, type_name.clone()); - random_subtraction_tests::(&mut rng, type_name.clone()); - random_negation_tests::(&mut rng, type_name.clone()); - random_doubling_tests::(&mut rng, type_name.clone()); - random_squaring_tests::(&mut rng, type_name.clone()); - random_inversion_tests::(&mut rng, type_name.clone()); - random_expansion_tests::(&mut rng, type_name); - - assert_eq!(F::zero().is_zero().unwrap_u8(), 1); - { - let mut z = F::zero(); - z = z.neg(); - assert_eq!(z.is_zero().unwrap_u8(), 1); - } - - assert!(bool::from(F::zero().invert().is_none())); - - // Multiplication by zero - { - let mut a = F::random(&mut rng); - a.mul_assign(&F::zero()); - assert_eq!(a.is_zero().unwrap_u8(), 1); - } - - // Addition by zero - { - let mut a = F::random(&mut rng); - let copy = a; - a.add_assign(&F::zero()); - assert_eq!(a, copy); - } -} - -fn random_multiplication_tests(mut rng: R, type_name: String) { - let message = format!("multiplication {type_name}"); - let start = start_timer!(|| message); - for _ in 0..1000000 { - let a = F::random(&mut rng); - let b = F::random(&mut rng); - let c = F::random(&mut 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); - } - end_timer!(start); -} - -fn random_addition_tests(mut rng: R, type_name: String) { - let message = format!("addition {type_name}"); - let start = start_timer!(|| message); - for _ in 0..1000000 { - let a = F::random(&mut rng); - let b = F::random(&mut rng); - let c = F::random(&mut 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); - } - end_timer!(start); -} - -fn random_subtraction_tests(mut rng: R, type_name: String) { - let message = format!("subtraction {type_name}"); - let start = start_timer!(|| message); - for _ in 0..1000000 { - let a = F::random(&mut rng); - let b = F::random(&mut 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_eq!(t2.is_zero().unwrap_u8(), 1); - } - end_timer!(start); -} - -fn random_negation_tests(mut rng: R, type_name: String) { - let message = format!("negation {type_name}"); - let start = start_timer!(|| message); - for _ in 0..1000000 { - let a = F::random(&mut rng); - let mut b = a; - b = b.neg(); - b.add_assign(&a); - - assert_eq!(b.is_zero().unwrap_u8(), 1); - } - end_timer!(start); -} - -fn random_doubling_tests(mut rng: R, type_name: String) { - let message = format!("doubling {type_name}"); - let start = start_timer!(|| message); - for _ in 0..1000000 { - let mut a = F::random(&mut rng); - let mut b = a; - a.add_assign(&b); - b = b.double(); - - assert_eq!(a, b); - } - end_timer!(start); -} - -fn random_squaring_tests(mut rng: R, type_name: String) { - let message = format!("squaring {type_name}"); - let start = start_timer!(|| message); - for _ in 0..1000000 { - let mut a = F::random(&mut rng); - let mut b = a; - a.mul_assign(&b); - b = b.square(); - - assert_eq!(a, b); - } - end_timer!(start); -} - -fn random_inversion_tests(mut rng: R, type_name: String) { - assert!(bool::from(F::zero().invert().is_none())); - - let message = format!("inversion {type_name}"); - let start = start_timer!(|| message); - for _ in 0..1000000 { - let mut a = F::random(&mut rng); - let b = a.invert().unwrap(); // probablistically nonzero - a.mul_assign(&b); - - assert_eq!(a, F::one()); - } - end_timer!(start); -} - -fn random_expansion_tests(mut rng: R, type_name: String) { - let message = format!("expansion {type_name}"); - let start = start_timer!(|| message); - for _ in 0..1000000 { - // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) - - let a = F::random(&mut rng); - let b = F::random(&mut rng); - let c = F::random(&mut rng); - let d = F::random(&mut 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); - } - end_timer!(start); -} - -pub fn random_serialization_test(type_name: String) { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - let message = format!("serialization {type_name}"); - let start = start_timer!(|| message); - for _ in 0..1000000 { - let a = F::random(&mut rng); - let bytes = a.to_raw_bytes(); - let b = F::from_raw_bytes(&bytes).unwrap(); - assert_eq!(a, b); - let mut buf = Vec::new(); - a.write_raw(&mut buf).unwrap(); - let b = F::read_raw(&mut &buf[..]).unwrap(); - assert_eq!(a, b); - } - end_timer!(start); -} diff --git a/arithmetic/curves/src/tests/mod.rs b/arithmetic/curves/src/tests/mod.rs deleted file mode 100644 index f773c8d786..0000000000 --- a/arithmetic/curves/src/tests/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod curve; -pub mod field; diff --git a/halo2/Cargo.toml b/halo2/Cargo.toml index 7a6bbaa94f..09c367b781 100644 --- a/halo2/Cargo.toml +++ b/halo2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "halo2" -version = "0.1.0-beta.2" +version = "0.2.0" authors = [ "Jack Grigg ", ] diff --git a/halo2_proofs/Cargo.toml b/halo2_proofs/Cargo.toml index 6d209bad6f..85c2e92584 100644 --- a/halo2_proofs/Cargo.toml +++ b/halo2_proofs/Cargo.toml @@ -18,6 +18,7 @@ documentation = "https://docs.rs/halo2_proofs" readme = "README.md" categories = ["cryptography"] keywords = ["halo", "proofs", "zkp", "zkSNARKs"] +autoexamples = false [package.metadata.docs.rs] all-features = true @@ -49,17 +50,18 @@ harness = false [dependencies] backtrace = { version = "0.3", optional = true } -rayon = "1.5.1" +rayon = "1.7" crossbeam = "0.8" -ff = "0.12" -group = "0.12" -halo2curves = { path = "../arithmetic/curves" } +ff = "0.13" +group = "0.13" +pairing = "0.23" +halo2curves = { git = "https://github.com/axiom-crypto/halo2curves.git", branch = "main", default-features = false, features = ["reexport", "bits", "bn256-table", "derive_serde"] } rand = "0.8" rand_core = { version = "0.6", default-features = false} tracing = "0.1" blake2b_simd = "1" -rustc-hash = "1.1.0" -sha3 = "0.9.1" +rustc-hash = "1.1" +sha3 = "0.10" ark-std = { version = "0.3.0", features = ["print-trace"], optional = true } # Developer tooling dependencies @@ -78,16 +80,24 @@ rand_chacha = "0.3.1" getrandom = { version = "0.2", features = ["js"] } [features] -default = ["batch"] +default = ["batch", "circuit-params"] dev-graph = ["plotters", "tabbycat"] gadget-traces = ["backtrace"] +# thread-safe-region = [] sanity-checks = [] batch = ["rand/getrandom"] profile = ["dep:ark-std"] +asm = ["halo2curves/asm"] +circuit-params = [] [lib] bench = false [[example]] -name = "circuit-layout" -required-features = ["dev-graph"] +name = "serialization" + +[[example]] +name = "shuffle" + +[[example]] +name = "shuffle_api" \ No newline at end of file diff --git a/halo2_proofs/benches/commit_zk.rs b/halo2_proofs/benches/commit_zk.rs index f1d2f70abf..6cd57d176f 100644 --- a/halo2_proofs/benches/commit_zk.rs +++ b/halo2_proofs/benches/commit_zk.rs @@ -27,7 +27,6 @@ fn rand_poly_par(mut rng: ChaCha20Rng, domain: usize) -> Vec { let mut rand_vec = vec![Scalar::zero(); n]; let mut thread_seeds: Vec = (0..n_chunks) - .into_iter() .map(|_| { let mut seed = [0u8; 32]; rng.fill_bytes(&mut seed); diff --git a/halo2_proofs/benches/dev_lookup.rs b/halo2_proofs/benches/dev_lookup.rs index 6665993bc1..c100cb74d3 100644 --- a/halo2_proofs/benches/dev_lookup.rs +++ b/halo2_proofs/benches/dev_lookup.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate criterion; -use halo2_proofs::arithmetic::FieldExt; +use ff::{Field, PrimeField}; use halo2_proofs::circuit::{Layouter, SimpleFloorPlanner, Value}; use halo2_proofs::dev::MockProver; use halo2_proofs::plonk::*; @@ -14,7 +14,7 @@ use criterion::{BenchmarkId, Criterion}; fn criterion_benchmark(c: &mut Criterion) { #[derive(Clone, Default)] - struct MyCircuit { + struct MyCircuit { _marker: PhantomData, } @@ -25,9 +25,11 @@ fn criterion_benchmark(c: &mut Criterion) { advice: Column, } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = MyConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -42,7 +44,7 @@ fn criterion_benchmark(c: &mut Criterion) { meta.lookup("lookup", |meta| { let selector = meta.query_selector(config.selector); - let not_selector = Expression::Constant(F::one()) - selector.clone(); + let not_selector = Expression::Constant(F::ONE) - selector.clone(); let advice = meta.query_advice(config.advice, Rotation::cur()); vec![(selector * advice + not_selector, config.table)] }); diff --git a/halo2_proofs/benches/plonk.rs b/halo2_proofs/benches/plonk.rs index 1a3091d251..1c2b962dd0 100644 --- a/halo2_proofs/benches/plonk.rs +++ b/halo2_proofs/benches/plonk.rs @@ -2,12 +2,11 @@ extern crate criterion; use group::ff::Field; -use halo2_proofs::arithmetic::FieldExt; use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner, Value}; use halo2_proofs::plonk::*; use halo2_proofs::poly::{commitment::ParamsProver, Rotation}; use halo2_proofs::transcript::{Blake2bRead, Blake2bWrite, Challenge255}; -use halo2curves::pasta::{EqAffine, Fp}; +use halo2curves::grumpkin::{Fr, G1Affine}; use rand_core::OsRng; use halo2_proofs::{ @@ -43,7 +42,7 @@ fn criterion_benchmark(c: &mut Criterion) { sm: Column, } - trait StandardCs { + trait StandardCs { fn raw_multiply( &self, layouter: &mut impl Layouter, @@ -62,17 +61,17 @@ fn criterion_benchmark(c: &mut Criterion) { } #[derive(Clone)] - struct MyCircuit { + struct MyCircuit { a: Value, k: u32, } - struct StandardPlonk { + struct StandardPlonk { config: PlonkConfig, _marker: PhantomData, } - impl StandardPlonk { + impl StandardPlonk { fn new(config: PlonkConfig) -> Self { StandardPlonk { config, @@ -81,7 +80,7 @@ fn criterion_benchmark(c: &mut Criterion) { } } - impl StandardCs for StandardPlonk { + impl StandardCs for StandardPlonk { fn raw_multiply( &self, layouter: &mut impl Layouter, @@ -93,7 +92,7 @@ fn criterion_benchmark(c: &mut Criterion) { layouter.assign_region( || "raw_multiply", |mut region| { - let mut value = None; + let value; let lhs = region.assign_advice(self.config.a, 0, { value = Some(f()); value.unwrap().map(|v| v.0) @@ -101,10 +100,10 @@ fn criterion_benchmark(c: &mut Criterion) { let rhs = region.assign_advice(self.config.b, 0, value.unwrap().map(|v| v.1)); let out = region.assign_advice(self.config.c, 0, value.unwrap().map(|v| v.2)); - region.assign_fixed(self.config.sa, 0, FF::zero()); - region.assign_fixed(self.config.sb, 0, FF::zero()); - region.assign_fixed(self.config.sc, 0, FF::one()); - region.assign_fixed(self.config.sm, 0, FF::one()); + region.assign_fixed(self.config.sa, 0, FF::ZERO); + region.assign_fixed(self.config.sb, 0, FF::ZERO); + region.assign_fixed(self.config.sc, 0, FF::ONE); + region.assign_fixed(self.config.sm, 0, FF::ONE); Ok((*lhs.cell(), *rhs.cell(), *out.cell())) }, ) @@ -120,7 +119,7 @@ fn criterion_benchmark(c: &mut Criterion) { layouter.assign_region( || "raw_add", |mut region| { - let mut value = None; + let value; let lhs = region.assign_advice(self.config.a, 0, { value = Some(f()); value.unwrap().map(|v| v.0) @@ -128,10 +127,10 @@ fn criterion_benchmark(c: &mut Criterion) { let rhs = region.assign_advice(self.config.b, 0, value.unwrap().map(|v| v.1)); let out = region.assign_advice(self.config.c, 0, value.unwrap().map(|v| v.2)); - region.assign_fixed(self.config.sa, 0, FF::one()); - region.assign_fixed(self.config.sb, 0, FF::one()); - region.assign_fixed(self.config.sc, 0, FF::one()); - region.assign_fixed(self.config.sm, 0, FF::zero()); + region.assign_fixed(self.config.sa, 0, FF::ONE); + region.assign_fixed(self.config.sb, 0, FF::ONE); + region.assign_fixed(self.config.sc, 0, FF::ONE); + region.assign_fixed(self.config.sm, 0, FF::ZERO); Ok((*lhs.cell(), *rhs.cell(), *out.cell())) }, ) @@ -152,9 +151,11 @@ fn criterion_benchmark(c: &mut Criterion) { } } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = PlonkConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self { @@ -231,9 +232,9 @@ fn criterion_benchmark(c: &mut Criterion) { } } - fn keygen(k: u32) -> (ParamsIPA, ProvingKey) { - let params: ParamsIPA = ParamsIPA::new(k); - let empty_circuit: MyCircuit = MyCircuit { + fn keygen(k: u32) -> (ParamsIPA, ProvingKey) { + let params: ParamsIPA = ParamsIPA::new(k); + let empty_circuit: MyCircuit = MyCircuit { a: Value::unknown(), k, }; @@ -242,16 +243,16 @@ fn criterion_benchmark(c: &mut Criterion) { (params, pk) } - fn prover(k: u32, params: &ParamsIPA, pk: &ProvingKey) -> Vec { + fn prover(k: u32, params: &ParamsIPA, pk: &ProvingKey) -> Vec { let rng = OsRng; - let circuit: MyCircuit = MyCircuit { - a: Value::known(Fp::random(rng)), + let circuit: MyCircuit = MyCircuit { + a: Value::known(Fr::random(rng)), k, }; - let mut transcript = Blake2bWrite::<_, _, Challenge255>::init(vec![]); - create_proof::, ProverIPA, _, _, _, _>( + let mut transcript = Blake2bWrite::<_, _, Challenge255>::init(vec![]); + create_proof::, ProverIPA, _, _, _, _>( params, pk, &[circuit], @@ -263,7 +264,7 @@ fn criterion_benchmark(c: &mut Criterion) { transcript.finalize() } - fn verifier(params: &ParamsIPA, vk: &VerifyingKey, proof: &[u8]) { + fn verifier(params: &ParamsIPA, vk: &VerifyingKey, proof: &[u8]) { let strategy = SingleStrategy::new(params); let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(proof); assert!(verify_proof(params, vk, strategy, &[&[]], &mut transcript).is_ok()); diff --git a/halo2_proofs/examples/circuit-layout.rs b/halo2_proofs/examples/circuit-layout.rs index beb99502bd..18de27a783 100644 --- a/halo2_proofs/examples/circuit-layout.rs +++ b/halo2_proofs/examples/circuit-layout.rs @@ -1,6 +1,5 @@ use ff::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Cell, Layouter, Region, SimpleFloorPlanner, Value}, plonk::{Advice, Assigned, Circuit, Column, ConstraintSystem, Error, Fixed, TableColumn}, poly::Rotation, @@ -28,7 +27,7 @@ struct PlonkConfig { sl: TableColumn, } -trait StandardCs { +trait StandardCs { fn raw_multiply(&self, region: &mut Region, f: F) -> Result<(Cell, Cell, Cell), Error> where F: FnMut() -> Value<(Assigned, Assigned, Assigned)>; @@ -39,17 +38,17 @@ trait StandardCs { fn lookup_table(&self, layouter: &mut impl Layouter, values: &[FF]) -> Result<(), Error>; } -struct MyCircuit { +struct MyCircuit { a: Value, lookup_table: Vec, } -struct StandardPlonk { +struct StandardPlonk { config: PlonkConfig, _marker: PhantomData, } -impl StandardPlonk { +impl StandardPlonk { fn new(config: PlonkConfig) -> Self { StandardPlonk { config, @@ -58,7 +57,7 @@ impl StandardPlonk { } } -impl StandardCs for StandardPlonk { +impl StandardCs for StandardPlonk { fn raw_multiply( &self, region: &mut Region, @@ -94,10 +93,10 @@ impl StandardCs for StandardPlonk { let out = region.assign_advice(|| "out", self.config.c, 0, || value.unwrap().map(|v| v.2))?; - region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::zero()))?; - region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::zero()))?; - region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?; - region.assign_fixed(|| "a * b", self.config.sm, 0, || Value::known(FF::one()))?; + region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::ZERO))?; + region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::ZERO))?; + region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::ONE))?; + region.assign_fixed(|| "a * b", self.config.sm, 0, || Value::known(FF::ONE))?; Ok((lhs.cell(), rhs.cell(), out.cell())) } fn raw_add(&self, region: &mut Region, mut f: F) -> Result<(Cell, Cell, Cell), Error> @@ -131,10 +130,10 @@ impl StandardCs for StandardPlonk { let out = region.assign_advice(|| "out", self.config.c, 0, || value.unwrap().map(|v| v.2))?; - region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::one()))?; - region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::one()))?; - region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?; - region.assign_fixed(|| "a * b", self.config.sm, 0, || Value::known(FF::zero()))?; + region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::ONE))?; + region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::ONE))?; + region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::ONE))?; + region.assign_fixed(|| "a * b", self.config.sm, 0, || Value::known(FF::ZERO))?; Ok((lhs.cell(), rhs.cell(), out.cell())) } fn copy(&self, region: &mut Region, left: Cell, right: Cell) -> Result<(), Error> { @@ -159,9 +158,11 @@ impl StandardCs for StandardPlonk { } } -impl Circuit for MyCircuit { +impl Circuit for MyCircuit { type Config = PlonkConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self { diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index 8b925b7adf..7f4a7c965a 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -86,6 +86,8 @@ struct StandardPlonk(Fr); impl Circuit for StandardPlonk { type Config = StandardPlonkConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -140,8 +142,11 @@ fn main() { let f = File::open("serialization-test.pk").unwrap(); let mut reader = BufReader::new(f); - let pk = ProvingKey::::read::<_, StandardPlonk>(&mut reader, SerdeFormat::RawBytes) - .unwrap(); + let pk = { + circuit.params(); + ProvingKey::::read::<_, StandardPlonk>(&mut reader, SerdeFormat::RawBytes, ()) + } + .unwrap(); std::fs::remove_file("serialization-test.pk").unwrap(); diff --git a/halo2_proofs/examples/shuffle.rs b/halo2_proofs/examples/shuffle.rs index 3075caea3a..0522526468 100644 --- a/halo2_proofs/examples/shuffle.rs +++ b/halo2_proofs/examples/shuffle.rs @@ -1,9 +1,8 @@ -use ff::BatchInvert; +use ff::{BatchInvert, FromUniformBytes}; use halo2_proofs::{ - arithmetic::{CurveAffine, FieldExt}, + arithmetic::Field, circuit::{Layouter, SimpleFloorPlanner, Value}, - dev::{metadata, FailureLocation, MockProver, VerifyFailure}, - halo2curves::pasta::EqAffine, + dev::{metadata, FailureLocation, MockProver}, plonk::*, poly::{ commitment::ParamsProver, @@ -12,23 +11,20 @@ use halo2_proofs::{ multiopen::{ProverSHPLONK, VerifierSHPLONK}, strategy::SingleStrategy, }, - Rotation, VerificationStrategy, }, transcript::{ Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, }, }; -use halo2curves::bn256::{Bn256, Fr, G1Affine}; +use halo2curves::bn256::{Bn256, Fr}; use rand_core::{OsRng, RngCore}; use std::iter; -fn rand_2d_array( - rng: &mut R, -) -> [[F; H]; W] { +fn rand_2d_array(rng: &mut R) -> [[F; H]; W] { [(); W].map(|_| [(); H].map(|_| F::random(&mut *rng))) } -fn shuffled( +fn shuffled( original: [[F; H]; W], rng: &mut R, ) -> [[F; H]; W] { @@ -57,7 +53,7 @@ struct MyConfig { } impl MyConfig { - fn configure(meta: &mut ConstraintSystem) -> Self { + fn configure(meta: &mut ConstraintSystem) -> Self { let [q_shuffle, q_first, q_last] = [(); 3].map(|_| meta.selector()); // First phase let original = [(); W].map(|_| meta.advice_column_in(FirstPhase)); @@ -66,29 +62,23 @@ impl MyConfig { // Second phase let z = meta.advice_column_in(SecondPhase); - meta.create_gate("z should start with 1", |meta| { - let q_first = meta.query_selector(q_first); - let z = meta.query_advice(z, Rotation::cur()); - let one = Expression::Constant(F::one()); + meta.create_gate("z should start with 1", |_| { + let one = Expression::Constant(F::ONE); - vec![q_first * (one - z)] + vec![q_first.expr() * (one - z.cur())] }); - meta.create_gate("z should end with 1", |meta| { - let q_last = meta.query_selector(q_last); - let z = meta.query_advice(z, Rotation::cur()); - let one = Expression::Constant(F::one()); + meta.create_gate("z should end with 1", |_| { + let one = Expression::Constant(F::ONE); - vec![q_last * (one - z)] + vec![q_last.expr() * (one - z.cur())] }); - meta.create_gate("z should have valid transition", |meta| { - let q_shuffle = meta.query_selector(q_shuffle); - let original = original.map(|advice| meta.query_advice(advice, Rotation::cur())); - let shuffled = shuffled.map(|advice| meta.query_advice(advice, Rotation::cur())); - let [theta, gamma] = [theta, gamma].map(|challenge| meta.query_challenge(challenge)); - let [z, z_w] = - [Rotation::cur(), Rotation::next()].map(|rotation| meta.query_advice(z, rotation)); + meta.create_gate("z should have valid transition", |_| { + let q_shuffle = q_shuffle.expr(); + let original = original.map(|advice| advice.cur()); + let shuffled = shuffled.map(|advice| advice.cur()); + let [theta, gamma] = [theta, gamma].map(|challenge| challenge.expr()); // Compress let original = original @@ -102,7 +92,7 @@ impl MyConfig { .reduce(|acc, a| acc * theta.clone() + a) .unwrap(); - vec![q_shuffle * (z * (original + gamma.clone()) - z_w * (shuffled + gamma))] + vec![q_shuffle * (z.cur() * (original + gamma.clone()) - z.next() * (shuffled + gamma))] }); Self { @@ -119,12 +109,12 @@ impl MyConfig { } #[derive(Clone, Default)] -struct MyCircuit { +struct MyCircuit { original: Value<[[F; H]; W]>, shuffled: Value<[[F; H]; W]>, } -impl MyCircuit { +impl MyCircuit { fn rand(rng: &mut R) -> Self { let original = rand_2d_array::(rng); let shuffled = shuffled(original, rng); @@ -136,7 +126,7 @@ impl MyCircuit { } } -impl Circuit for MyCircuit { +impl Circuit for MyCircuit { type Config = MyConfig; type FloorPlanner = SimpleFloorPlanner; @@ -165,7 +155,7 @@ impl Circuit for MyCircuit Circuit for MyCircuit Circuit for MyCircuit Circuit for MyCircuit Circuit for MyCircuit>(); #[cfg(feature = "sanity-checks")] - assert_eq!(F::one(), *z.last().unwrap()); + assert_eq!(F::ONE, *z.last().unwrap()); z }, @@ -241,15 +231,15 @@ impl Circuit for MyCircuit( +fn test_mock_prover, const W: usize, const H: usize>( k: u32, circuit: MyCircuit, expected: Result<(), Vec<(metadata::Constraint, FailureLocation)>>, ) { - let prover = MockProver::run::<_>(k, &circuit, vec![]).unwrap(); + let prover = MockProver::run(k, &circuit, vec![]).unwrap(); match (prover.verify(), expected) { (Ok(_), Ok(_)) => {} - (Err(err), Err(expected)) => { + (Err(_err), Err(_expected)) => { /*assert_eq!( err.into_iter() .map(|failure| match failure { diff --git a/halo2_proofs/examples/shuffle_api.rs b/halo2_proofs/examples/shuffle_api.rs new file mode 100644 index 0000000000..d3de4c2cdb --- /dev/null +++ b/halo2_proofs/examples/shuffle_api.rs @@ -0,0 +1,207 @@ +use std::{hash::Hash, marker::PhantomData, vec}; + +use ff::{FromUniformBytes, WithSmallOrderMulGroup}; +use halo2_proofs::{ + arithmetic::Field, + circuit::{Layouter, SimpleFloorPlanner, Value}, + plonk::{ + create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Circuit, Column, + ConstraintSystem, Error, Fixed, Selector, + }, + poly::{ + commitment::ParamsProver, + kzg::{ + commitment::{KZGCommitmentScheme, ParamsKZG}, + multiopen::{ProverSHPLONK, VerifierSHPLONK}, + strategy::SingleStrategy, + }, + Rotation, + }, + transcript::{ + Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, + }, +}; +use halo2curves::{bn256::Bn256, serde::SerdeObject, CurveAffine}; +use pairing::MultiMillerLoop; +use rand_core::OsRng; + +struct ShuffleChip { + config: ShuffleConfig, + _marker: PhantomData, +} + +#[derive(Clone, Debug)] +struct ShuffleConfig { + input_0: Column, + input_1: Column, + shuffle_0: Column, + shuffle_1: Column, + s_input: Selector, + s_shuffle: Selector, +} + +impl ShuffleChip { + fn construct(config: ShuffleConfig) -> Self { + Self { + config, + _marker: PhantomData, + } + } + + fn configure( + meta: &mut ConstraintSystem, + input_0: Column, + input_1: Column, + shuffle_0: Column, + shuffle_1: Column, + ) -> ShuffleConfig { + let s_shuffle = meta.complex_selector(); + let s_input = meta.complex_selector(); + meta.shuffle("shuffle", |meta| { + let s_input = meta.query_selector(s_input); + let s_shuffle = meta.query_selector(s_shuffle); + let input_0 = meta.query_advice(input_0, Rotation::cur()); + let input_1 = meta.query_fixed(input_1, Rotation::cur()); + let shuffle_0 = meta.query_advice(shuffle_0, Rotation::cur()); + let shuffle_1 = meta.query_advice(shuffle_1, Rotation::cur()); + vec![ + (s_input.clone() * input_0, s_shuffle.clone() * shuffle_0), + (s_input * input_1, s_shuffle * shuffle_1), + ] + }); + ShuffleConfig { + input_0, + input_1, + shuffle_0, + shuffle_1, + s_input, + s_shuffle, + } + } +} + +#[derive(Default)] +struct MyCircuit { + input_0: Vec>, + input_1: Vec, + shuffle_0: Vec>, + shuffle_1: Vec>, +} + +impl Circuit for MyCircuit { + // Since we are using a single chip for everything, we can just reuse its config. + type Config = ShuffleConfig; + type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + fn params(&self) -> Self::Params {} + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let input_0 = meta.advice_column(); + let input_1 = meta.fixed_column(); + let shuffle_0 = meta.advice_column(); + let shuffle_1 = meta.advice_column(); + ShuffleChip::configure(meta, input_0, input_1, shuffle_0, shuffle_1) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let ch = ShuffleChip::::construct(config); + layouter.assign_region( + || "load inputs & shuffles", + |mut region| { + for (i, (input_0, input_1)) in + self.input_0.iter().zip(self.input_1.iter()).enumerate() + { + region.assign_advice(ch.config.input_0, i, *input_0); + region.assign_fixed(ch.config.input_1, i, *input_1); + ch.config.s_input.enable(&mut region, i)?; + } + + for (i, (shuffle_0, shuffle_1)) in + self.shuffle_0.iter().zip(self.shuffle_1.iter()).enumerate() + { + region.assign_advice(ch.config.shuffle_0, i, *shuffle_0); + region.assign_advice(ch.config.shuffle_1, i, *shuffle_1); + ch.config.s_shuffle.enable(&mut region, i)?; + } + Ok(()) + }, + )?; + Ok(()) + } +} + +fn test_prover(k: u32, circuit: MyCircuit, expected: bool) +where + E::Fr: Hash + FromUniformBytes<64> + WithSmallOrderMulGroup<3>, + E::G1Affine: CurveAffine + SerdeObject, + E::G2Affine: CurveAffine + SerdeObject, +{ + let params = ParamsKZG::::new(k); + let vk = keygen_vk(¶ms, &circuit).unwrap(); + let pk = keygen_pk(¶ms, vk, &circuit).unwrap(); + + let proof = { + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + + create_proof::, ProverSHPLONK, _, _, _, _>( + ¶ms, + &pk, + &[circuit], + &[&[]], + OsRng, + &mut transcript, + ) + .expect("proof generation should not fail"); + + transcript.finalize() + }; + + let accepted = { + let strategy = SingleStrategy::new(¶ms); + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); + + verify_proof::, VerifierSHPLONK, _, _, _>( + ¶ms, + pk.get_vk(), + strategy, + &[&[]], + &mut transcript, + ) + .is_ok() + }; + + assert_eq!(accepted, expected); +} + +fn main() { + use halo2_proofs::dev::MockProver; + use halo2curves::bn256::Fr; + const K: u32 = 4; + let input_0 = [1, 2, 4, 1] + .map(|e: u64| Value::known(Fr::from(e))) + .to_vec(); + let input_1 = [10, 20, 40, 10].map(Fr::from).to_vec(); + let shuffle_0 = [4, 1, 1, 2] + .map(|e: u64| Value::known(Fr::from(e))) + .to_vec(); + let shuffle_1 = [40, 10, 10, 20] + .map(|e: u64| Value::known(Fr::from(e))) + .to_vec(); + let circuit = MyCircuit { + input_0, + input_1, + shuffle_0, + shuffle_1, + }; + let prover = MockProver::run(K, &circuit, vec![]).unwrap(); + prover.assert_satisfied(); + test_prover::(K, circuit, true); +} diff --git a/halo2_proofs/src/arithmetic.rs b/halo2_proofs/src/arithmetic.rs index 5cd83de66d..2c016fa359 100644 --- a/halo2_proofs/src/arithmetic.rs +++ b/halo2_proofs/src/arithmetic.rs @@ -5,13 +5,26 @@ use super::multicore; pub use ff::Field; use group::{ ff::{BatchInvert, PrimeField}, - Curve, Group as _, + prime::PrimeCurveAffine, + Curve, Group, GroupOpsOwned, ScalarMulOwned, }; -pub use halo2curves::{CurveAffine, CurveExt, FieldExt, Group}; +pub use halo2curves::{CurveAffine, CurveExt}; -/// TEMP -pub static mut MULTIEXP_TOTAL_TIME: usize = 0; +/// This represents an element of a group with basic operations that can be +/// performed. This allows an FFT implementation (for example) to operate +/// generically over either a field or elliptic curve group. +pub trait FftGroup: + Copy + Send + Sync + 'static + GroupOpsOwned + ScalarMulOwned +{ +} + +impl FftGroup for T +where + Scalar: Field, + T: Copy + Send + Sync + 'static + GroupOpsOwned + ScalarMulOwned, +{ +} fn multiexp_serial(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Curve) { let coeffs: Vec<_> = coeffs.iter().map(|a| a.to_repr()).collect(); @@ -168,7 +181,6 @@ pub fn best_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Cu // unsafe { // MULTIEXP_TOTAL_TIME += duration; // } - res } @@ -182,7 +194,7 @@ pub fn best_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Cu /// by $n$. /// /// This will use multithreading if beneficial. -pub fn best_fft(a: &mut [G], omega: G::Scalar, log_n: u32) { +pub fn best_fft>(a: &mut [G], omega: Scalar, log_n: u32) { fn bitreverse(mut n: usize, l: usize) -> usize { let mut r = 0; for _ in 0..l { @@ -207,13 +219,12 @@ pub fn best_fft(a: &mut [G], omega: G::Scalar, log_n: u32) { //let start = start_measure(format!("twiddles {} ({})", a.len(), threads), false); // precompute twiddle factors let twiddles: Vec<_> = (0..(n / 2)) - .scan(G::Scalar::one(), |w, _| { + .scan(Scalar::ONE, |w, _| { let tw = *w; - w.group_scale(&omega); + *w *= ω Some(tw) }) .collect(); - //stop_measure(start); if log_n <= log_threads { let mut chunk = 2_usize; @@ -227,18 +238,18 @@ pub fn best_fft(a: &mut [G], omega: G::Scalar, log_n: u32) { let (b, right) = right.split_at_mut(1); let t = b[0]; b[0] = a[0]; - a[0].group_add(&t); - b[0].group_sub(&t); + a[0] += &t; + b[0] -= &t; left.iter_mut() .zip(right.iter_mut()) .enumerate() .for_each(|(i, (a, b))| { let mut t = *b; - t.group_scale(&twiddles[(i + 1) * twiddle_chunk]); + t *= &twiddles[(i + 1) * twiddle_chunk]; *b = *a; - a.group_add(&t); - b.group_sub(&t); + *a += &t; + *b -= &t; }); }); chunk *= 2; @@ -250,17 +261,17 @@ pub fn best_fft(a: &mut [G], omega: G::Scalar, log_n: u32) { } /// This perform recursive butterfly arithmetic -pub fn recursive_butterfly_arithmetic( +pub fn recursive_butterfly_arithmetic>( a: &mut [G], n: usize, twiddle_chunk: usize, - twiddles: &[G::Scalar], + twiddles: &[Scalar], ) { if n == 2 { let t = a[1]; a[1] = a[0]; - a[0].group_add(&t); - a[1].group_sub(&t); + a[0] += &t; + a[1] -= &t; } else { let (left, right) = a.split_at_mut(n / 2); rayon::join( @@ -273,24 +284,24 @@ pub fn recursive_butterfly_arithmetic( let (b, right) = right.split_at_mut(1); let t = b[0]; b[0] = a[0]; - a[0].group_add(&t); - b[0].group_sub(&t); + a[0] += &t; + b[0] -= &t; left.iter_mut() .zip(right.iter_mut()) .enumerate() .for_each(|(i, (a, b))| { let mut t = *b; - t.group_scale(&twiddles[(i + 1) * twiddle_chunk]); + t *= &twiddles[(i + 1) * twiddle_chunk]; *b = *a; - a.group_add(&t); - b.group_sub(&t); + *a += &t; + *b -= &t; }); } } /// Convert coefficient bases group elements to lagrange basis by inverse FFT. -pub fn g_to_lagrange(g_projective: Vec, k: u32) -> Vec { +pub fn g_to_lagrange(g_projective: Vec, k: u32) -> Vec { let n_inv = C::Scalar::TWO_INV.pow_vartime([k as u64, 0, 0, 0]); let mut omega_inv = C::Scalar::ROOT_OF_UNITY_INV; for _ in k..C::Scalar::S { @@ -321,7 +332,7 @@ pub fn eval_polynomial(poly: &[F], point: F) -> F { fn evaluate(poly: &[F], point: F) -> F { poly.iter() .rev() - .fold(F::zero(), |acc, coeff| acc * point + coeff) + .fold(F::ZERO, |acc, coeff| acc * point + coeff) } let n = poly.len(); let num_threads = multicore::current_num_threads(); @@ -329,7 +340,7 @@ pub fn eval_polynomial(poly: &[F], point: F) -> F { evaluate(poly, point) } else { let chunk_size = (n + num_threads - 1) / num_threads; - let mut parts = vec![F::zero(); num_threads]; + let mut parts = vec![F::ZERO; num_threads]; multicore::scope(|scope| { for (chunk_idx, (out, poly)) in parts.chunks_mut(1).zip(poly.chunks(chunk_size)).enumerate() @@ -340,7 +351,7 @@ pub fn eval_polynomial(poly: &[F], point: F) -> F { }); } }); - parts.iter().fold(F::zero(), |acc, coeff| acc + coeff) + parts.iter().fold(F::ZERO, |acc, coeff| acc + coeff) } } @@ -351,7 +362,7 @@ pub fn compute_inner_product(a: &[F], b: &[F]) -> F { // TODO: parallelize? assert_eq!(a.len(), b.len()); - let mut acc = F::zero(); + let mut acc = F::ZERO; for (a, b) in a.iter().zip(b.iter()) { acc += (*a) * (*b); } @@ -368,9 +379,9 @@ where b = -b; let a = a.into_iter(); - let mut q = vec![F::zero(); a.len() - 1]; + let mut q = vec![F::ZERO; a.len() - 1]; - let mut tmp = F::zero(); + let mut tmp = F::ZERO; for (q, r) in q.iter_mut().rev().zip(a.rev()) { let mut lead_coeff = *r; lead_coeff.sub_assign(&tmp); @@ -382,23 +393,54 @@ where q } -/// This simple utility function will parallelize an operation that is to be +/// This utility function will parallelize an operation that is to be /// performed over a mutable slice. pub fn parallelize(v: &mut [T], f: F) { - let n = v.len(); + // Algorithm rationale: + // + // Using the stdlib `chunks_mut` will lead to severe load imbalance. + // From https://github.com/rust-lang/rust/blob/e94bda3/library/core/src/slice/iter.rs#L1607-L1637 + // if the division is not exact, the last chunk will be the remainder. + // + // Dividing 40 items on 12 threads will lead to a chunk size of 40/12 = 3, + // There will be a 13 chunks of size 3 and 1 of size 1 distributed on 12 threads. + // This leads to 1 thread working on 6 iterations, 1 on 4 iterations and 10 on 3 iterations, + // a load imbalance of 2x. + // + // Instead we can divide work into chunks of size + // 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3 = 4*4 + 3*8 = 40 + // + // This would lead to a 6/4 = 1.5x speedup compared to naive chunks_mut + // + // See also OpenMP spec (page 60) + // http://www.openmp.org/mp-documents/openmp-4.5.pdf + // "When no chunk_size is specified, the iteration space is divided into chunks + // that are approximately equal in size, and at most one chunk is distributed to + // each thread. The size of the chunks is unspecified in this case." + // This implies chunks are the same size ±1 + + let f = &f; + let total_iters = v.len(); let num_threads = multicore::current_num_threads(); - let mut chunk = n / num_threads; - if chunk < num_threads { - chunk = 1; - } + let base_chunk_size = total_iters / num_threads; + let cutoff_chunk_id = total_iters % num_threads; + let split_pos = cutoff_chunk_id * (base_chunk_size + 1); + let (v_hi, v_lo) = v.split_at_mut(split_pos); multicore::scope(|scope| { - for (chunk_num, v) in v.chunks_mut(chunk).enumerate() { - let f = f.clone(); - scope.spawn(move |_| { - let start = chunk_num * chunk; - f(v, start); - }); + // Skip special-case: number of iterations is cleanly divided by number of threads. + if cutoff_chunk_id != 0 { + for (chunk_id, chunk) in v_hi.chunks_exact_mut(base_chunk_size + 1).enumerate() { + let offset = chunk_id * (base_chunk_size + 1); + scope.spawn(move |_| f(chunk, offset)); + } + } + // Skip special-case: less iterations than number of threads. + if base_chunk_size != 0 { + for (chunk_id, chunk) in v_lo.chunks_exact_mut(base_chunk_size).enumerate() { + let offset = split_pos + (chunk_id * base_chunk_size); + scope.spawn(move |_| f(chunk, offset)); + } } }); } @@ -441,7 +483,7 @@ fn log2_floor(num: usize) -> u32 { /// Returns coefficients of an n - 1 degree polynomial given a set of n points /// and their evaluations. This function will panic if two values in `points` /// are the same. -pub fn lagrange_interpolate(points: &[F], evals: &[F]) -> Vec { +pub fn lagrange_interpolate(points: &[F], evals: &[F]) -> Vec { assert_eq!(points.len(), evals.len()); if points.len() == 1 { // Constant polynomial @@ -463,23 +505,23 @@ pub fn lagrange_interpolate(points: &[F], evals: &[F]) -> Vec { // Compute (x_j - x_k)^(-1) for each j != i denoms.iter_mut().flat_map(|v| v.iter_mut()).batch_invert(); - let mut final_poly = vec![F::zero(); points.len()]; + let mut final_poly = vec![F::ZERO; points.len()]; for (j, (denoms, eval)) in denoms.into_iter().zip(evals.iter()).enumerate() { let mut tmp: Vec = Vec::with_capacity(points.len()); let mut product = Vec::with_capacity(points.len() - 1); - tmp.push(F::one()); + tmp.push(F::ONE); for (x_k, denom) in points .iter() .enumerate() .filter(|&(k, _)| k != j) .map(|a| a.1) - .zip(denoms.into_iter()) + .zip(denoms) { - product.resize(tmp.len() + 1, F::zero()); + product.resize(tmp.len() + 1, F::ZERO); for ((a, b), product) in tmp .iter() - .chain(std::iter::once(&F::zero())) - .zip(std::iter::once(&F::zero()).chain(tmp.iter())) + .chain(std::iter::once(&F::ZERO)) + .zip(std::iter::once(&F::ZERO).chain(tmp.iter())) .zip(product.iter_mut()) { *product = *a * (-denom * x_k) + *b * denom; @@ -488,7 +530,7 @@ pub fn lagrange_interpolate(points: &[F], evals: &[F]) -> Vec { } assert_eq!(tmp.len(), points.len()); assert_eq!(product.len(), points.len() - 1); - for (final_coeff, interpolation_coeff) in final_poly.iter_mut().zip(tmp.into_iter()) { + for (final_coeff, interpolation_coeff) in final_poly.iter_mut().zip(tmp) { *final_coeff += interpolation_coeff * eval; } } @@ -496,9 +538,9 @@ pub fn lagrange_interpolate(points: &[F], evals: &[F]) -> Vec { } } -pub(crate) fn evaluate_vanishing_polynomial(roots: &[F], z: F) -> F { - fn evaluate(roots: &[F], z: F) -> F { - roots.iter().fold(F::one(), |acc, point| (z - point) * acc) +pub(crate) fn evaluate_vanishing_polynomial(roots: &[F], z: F) -> F { + fn evaluate(roots: &[F], z: F) -> F { + roots.iter().fold(F::ONE, |acc, point| (z - point) * acc) } let n = roots.len(); let num_threads = multicore::current_num_threads(); @@ -506,18 +548,18 @@ pub(crate) fn evaluate_vanishing_polynomial(roots: &[F], z: F) -> F evaluate(roots, z) } else { let chunk_size = (n + num_threads - 1) / num_threads; - let mut parts = vec![F::one(); num_threads]; + let mut parts = vec![F::ONE; num_threads]; multicore::scope(|scope| { for (out, roots) in parts.chunks_mut(1).zip(roots.chunks(chunk_size)) { scope.spawn(move |_| out[0] = evaluate(roots, z)); } }); - parts.iter().fold(F::one(), |acc, part| acc * part) + parts.iter().fold(F::ONE, |acc, part| acc * part) } } -pub(crate) fn powers(base: F) -> impl Iterator { - std::iter::successors(Some(F::one()), move |power| Some(base * power)) +pub(crate) fn powers(base: F) -> impl Iterator { + std::iter::successors(Some(F::ONE), move |power| Some(base * power)) } #[cfg(test)] diff --git a/halo2_proofs/src/circuit.rs b/halo2_proofs/src/circuit.rs index 36ee37ee5f..2dd94d5028 100644 --- a/halo2_proofs/src/circuit.rs +++ b/halo2_proofs/src/circuit.rs @@ -4,11 +4,8 @@ use std::{convert::TryInto, fmt, marker::PhantomData}; use ff::Field; -use crate::{ - arithmetic::FieldExt, - plonk::{ - Advice, Any, Assigned, Challenge, Column, Error, Fixed, Instance, Selector, TableColumn, - }, +use crate::plonk::{ + Advice, Any, Assigned, Challenge, Column, Error, Fixed, Instance, Selector, TableColumn, }; mod value; @@ -27,7 +24,7 @@ pub mod layouter; /// The chip also loads any fixed configuration needed at synthesis time /// using its own implementation of `load`, and stores it in [`Chip::Loaded`]. /// This can be accessed via [`Chip::loaded`]. -pub trait Chip: Sized { +pub trait Chip: Sized { /// A type that holds the configuration for this chip, and any other state it may need /// during circuit synthesis, that can be derived during [`Circuit::configure`]. /// diff --git a/halo2_proofs/src/circuit/floor_planner.rs b/halo2_proofs/src/circuit/floor_planner.rs index 01c5415a30..a75bb1fafd 100644 --- a/halo2_proofs/src/circuit/floor_planner.rs +++ b/halo2_proofs/src/circuit/floor_planner.rs @@ -2,5 +2,5 @@ pub(super) mod single_pass; -// mod v1; +//mod v1; //pub use v1::{V1Pass, V1}; diff --git a/halo2_proofs/src/circuit/floor_planner/single_pass.rs b/halo2_proofs/src/circuit/floor_planner/single_pass.rs index a29e86f150..8d3ee380fb 100644 --- a/halo2_proofs/src/circuit/floor_planner/single_pass.rs +++ b/halo2_proofs/src/circuit/floor_planner/single_pass.rs @@ -5,10 +5,11 @@ use std::marker::PhantomData; use ff::Field; use rustc_hash::FxHashMap; +use crate::circuit::AssignedCell; use crate::{ circuit::{ - layouter::{RegionColumn, RegionLayouter, RegionShape, TableLayouter}, - AssignedCell, Cell, Layouter, Region, RegionIndex, RegionStart, Table, Value, + layouter::{RegionColumn, RegionLayouter, RegionShape, SyncDeps, TableLayouter}, + Cell, Layouter, Region, RegionIndex, RegionStart, Table, Value, }, plonk::{ Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, Error, Fixed, FloorPlanner, @@ -25,7 +26,7 @@ use crate::{ pub struct SimpleFloorPlanner; impl FloorPlanner for SimpleFloorPlanner { - fn synthesize, C: Circuit>( + fn synthesize + SyncDeps, C: Circuit>( cs: &mut CS, circuit: &C, config: C::Config, @@ -74,7 +75,9 @@ impl<'a, F: Field, CS: Assignment> SingleChipLayouter<'a, F, CS> { } } -impl<'a, F: Field, CS: Assignment + 'a> Layouter for SingleChipLayouter<'a, F, CS> { +impl<'a, F: Field, CS: Assignment + 'a + SyncDeps> Layouter + for SingleChipLayouter<'a, F, CS> +{ type Root = Self; fn assign_region(&mut self, name: N, assignment: A) -> Result @@ -260,7 +263,12 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> SingleChipLayouterRegion<'r, 'a, } } -impl<'r, 'a, F: Field, CS: Assignment + 'a> RegionLayouter +impl<'r, 'a, F: Field, CS: Assignment + 'a + SyncDeps> SyncDeps + for SingleChipLayouterRegion<'r, 'a, F, CS> +{ +} + +impl<'r, 'a, F: Field, CS: Assignment + 'a + SyncDeps> RegionLayouter for SingleChipLayouterRegion<'r, 'a, F, CS> { fn enable_selector<'v>( @@ -283,8 +291,8 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> RegionLayouter self.layouter.cs.annotate_column(annotation, column); } - fn assign_advice<'b, 'v>( - &'b mut self, + fn assign_advice<'v>( + &mut self, // annotation: &'v (dyn Fn() -> String + 'v), column: Column, offset: usize, @@ -485,6 +493,7 @@ mod tests { type Config = Column; type FloorPlanner = SimpleFloorPlanner; + fn params(&self) -> Self::Params {} fn without_witnesses(&self) -> Self { MyCircuit {} } diff --git a/halo2_proofs/src/circuit/floor_planner/v1.rs b/halo2_proofs/src/circuit/floor_planner/v1.rs index 04af946a34..75fa07fa29 100644 --- a/halo2_proofs/src/circuit/floor_planner/v1.rs +++ b/halo2_proofs/src/circuit/floor_planner/v1.rs @@ -5,7 +5,7 @@ use ff::Field; use crate::{ circuit::{ floor_planner::single_pass::SimpleTableLayouter, - layouter::{RegionColumn, RegionLayouter, RegionShape, TableLayouter}, + layouter::{RegionColumn, RegionLayouter, RegionShape, SyncDeps, TableLayouter}, Cell, Layouter, Region, RegionIndex, RegionStart, Table, Value, }, plonk::{ @@ -43,7 +43,7 @@ impl<'a, F: Field, CS: Assignment + 'a> fmt::Debug for V1Plan<'a, F, CS> { } } -impl<'a, F: Field, CS: Assignment> V1Plan<'a, F, CS> { +impl<'a, F: Field, CS: Assignment + SyncDeps> V1Plan<'a, F, CS> { /// Creates a new v1 layouter. pub fn new(cs: &'a mut CS) -> Result { let ret = V1Plan { @@ -57,7 +57,7 @@ impl<'a, F: Field, CS: Assignment> V1Plan<'a, F, CS> { } impl FloorPlanner for V1 { - fn synthesize, C: Circuit>( + fn synthesize + SyncDeps, C: Circuit>( cs: &mut CS, circuit: &C, config: C::Config, @@ -162,7 +162,7 @@ impl<'p, 'a, F: Field, CS: Assignment + 'a> V1Pass<'p, 'a, F, CS> { } } -impl<'p, 'a, F: Field, CS: Assignment + 'a> Layouter for V1Pass<'p, 'a, F, CS> { +impl<'p, 'a, F: Field, CS: Assignment + SyncDeps> Layouter for V1Pass<'p, 'a, F, CS> { type Root = Self; fn assign_region(&mut self, name: N, assignment: A) -> Result @@ -266,7 +266,7 @@ pub struct AssignmentPass<'p, 'a, F: Field, CS: Assignment + 'a> { region_index: usize, } -impl<'p, 'a, F: Field, CS: Assignment + 'a> AssignmentPass<'p, 'a, F, CS> { +impl<'p, 'a, F: Field, CS: Assignment + SyncDeps> AssignmentPass<'p, 'a, F, CS> { fn new(plan: &'p mut V1Plan<'a, F, CS>) -> Self { AssignmentPass { plan, @@ -386,7 +386,9 @@ impl<'r, 'a, F: Field, CS: Assignment + 'a> V1Region<'r, 'a, F, CS> { } } -impl<'r, 'a, F: Field, CS: Assignment + 'a> RegionLayouter for V1Region<'r, 'a, F, CS> { +impl<'r, 'a, F: Field, CS: Assignment + SyncDeps> SyncDeps for V1Region<'r, 'a, F, CS> {} + +impl<'r, 'a, F: Field, CS: Assignment + SyncDeps> RegionLayouter for V1Region<'r, 'a, F, CS> { fn enable_selector<'v>( &'v mut self, annotation: &'v (dyn Fn() -> String + 'v), @@ -527,6 +529,8 @@ mod tests { impl Circuit for MyCircuit { type Config = Column; type FloorPlanner = super::V1; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { MyCircuit {} diff --git a/halo2_proofs/src/circuit/layouter.rs b/halo2_proofs/src/circuit/layouter.rs index 07e9249236..c2d3ad62dc 100644 --- a/halo2_proofs/src/circuit/layouter.rs +++ b/halo2_proofs/src/circuit/layouter.rs @@ -11,12 +11,20 @@ use crate::plonk::{ Advice, Any, Assigned, Challenge, Column, Error, Fixed, Instance, Selector, TableColumn, }; +/// Intermediate trait requirements for [`RegionLayouter`] when thread-safe regions are enabled. +#[cfg(feature = "thread-safe-region")] +pub trait SyncDeps: Send + Sync {} + +/// Intermediate trait requirements for [`RegionLayouter`]. +#[cfg(not(feature = "thread-safe-region"))] +pub trait SyncDeps {} + /// Helper trait for implementing a custom [`Layouter`]. /// /// This trait is used for implementing region assignments: /// /// ```ignore -/// impl<'a, F: FieldExt, C: Chip, CS: Assignment + 'a> Layouter for MyLayouter<'a, C, CS> { +/// impl<'a, F: Field, C: Chip, CS: Assignment + 'a> Layouter for MyLayouter<'a, C, CS> { /// fn assign_region( /// &mut self, /// assignment: impl FnOnce(Region<'_, F, C>) -> Result<(), Error>, @@ -41,7 +49,7 @@ use crate::plonk::{ /// `Chip::Config`). /// /// [`Layouter`]: super::Layouter -pub trait RegionLayouter: fmt::Debug { +pub trait RegionLayouter: fmt::Debug + SyncDeps { /// Enables a selector at the given offset. fn enable_selector<'v>( &'v mut self, @@ -61,8 +69,8 @@ pub trait RegionLayouter: fmt::Debug { ); /// Assign an advice column value (witness) - fn assign_advice<'b, 'v>( - &'b mut self, + fn assign_advice<'v>( + &mut self, // annotation: &'v (dyn Fn() -> String + 'v), column: Column, offset: usize, @@ -153,6 +161,8 @@ pub struct RegionShape { pub(super) row_count: usize, } +impl SyncDeps for RegionShape {} + /// The virtual column involved in a region. This includes concrete columns, /// as well as selectors that are not concrete columns at this stage. #[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)] diff --git a/halo2_proofs/src/dev.rs b/halo2_proofs/src/dev.rs index ec2e27ae2e..d07c078edb 100644 --- a/halo2_proofs/src/dev.rs +++ b/halo2_proofs/src/dev.rs @@ -1,5 +1,6 @@ //! Tools for developing circuits. +use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::HashSet; use std::fmt; @@ -10,10 +11,12 @@ use std::sync::Arc; use blake2b_simd::blake2b; use ff::Field; +use ff::FromUniformBytes; +use group::Group; +use crate::circuit::layouter::SyncDeps; use crate::plonk::permutation::keygen::Assembly; use crate::{ - arithmetic::{FieldExt, Group}, circuit, plonk::{ permutation, @@ -89,7 +92,7 @@ impl Region { /// The value of a particular cell within the circuit. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CellValue { +pub enum CellValue { /// An unassigned cell. Unassigned, /// A cell that has been assigned a value. @@ -100,7 +103,7 @@ pub enum CellValue { /// The value of a particular cell within the circuit. #[derive(Clone, Debug, PartialEq, Eq)] -pub enum AdviceCellValue { +pub enum AdviceCellValue { // A cell that has been assigned a value. Assigned(Arc>), // A unique poisoned cell. @@ -109,23 +112,32 @@ pub enum AdviceCellValue { /// A value within an expression. #[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] -enum Value { +enum Value { Real(F), Poison, } -impl From> for Value { +impl From> for Value { fn from(value: CellValue) -> Self { match value { // Cells that haven't been explicitly assigned to, default to zero. - CellValue::Unassigned => Value::Real(F::zero()), + CellValue::Unassigned => Value::Real(F::ZERO), CellValue::Assigned(v) => Value::Real(v), CellValue::Poison(_) => Value::Poison, } } } -impl Neg for Value { +impl From> for Value { + fn from(value: AdviceCellValue) -> Self { + match value { + AdviceCellValue::Assigned(v) => Value::Real(v.evaluate()), + AdviceCellValue::Poison(_) => Value::Poison, + } + } +} + +impl Neg for Value { type Output = Self; fn neg(self) -> Self::Output { @@ -136,7 +148,7 @@ impl Neg for Value { } } -impl Add for Value { +impl Add for Value { type Output = Self; fn add(self, rhs: Self) -> Self::Output { @@ -147,7 +159,7 @@ impl Add for Value { } } -impl Mul for Value { +impl Mul for Value { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { @@ -158,14 +170,14 @@ impl Mul for Value { (Value::Real(x), Value::Poison) | (Value::Poison, Value::Real(x)) if x.is_zero_vartime() => { - Value::Real(F::zero()) + Value::Real(F::ZERO) } _ => Value::Poison, } } } -impl Mul for Value { +impl Mul for Value { type Output = Self; fn mul(self, rhs: F) -> Self::Output { @@ -173,7 +185,7 @@ impl Mul for Value { Value::Real(lhs) => Value::Real(lhs * rhs), // If poison is multiplied by zero, then we treat the poison as unconstrained // and we don't propagate it. - Value::Poison if rhs.is_zero_vartime() => Value::Real(F::zero()), + Value::Poison if rhs.is_zero_vartime() => Value::Real(F::ZERO), _ => Value::Poison, } } @@ -191,12 +203,12 @@ impl Mul for Value { /// /// ``` /// use halo2_proofs::{ -/// arithmetic::FieldExt, /// circuit::{Layouter, SimpleFloorPlanner, Value}, /// dev::{FailureLocation, MockProver, VerifyFailure}, /// plonk::{Advice, Any, Circuit, Column, ConstraintSystem, Error, Selector}, /// poly::Rotation, /// }; +/// use ff::PrimeField; /// use halo2curves::pasta::Fp; /// const K: u32 = 5; /// @@ -214,9 +226,11 @@ impl Mul for Value { /// b: Value, /// } /// -/// impl Circuit for MyCircuit { +/// impl Circuit for MyCircuit { /// type Config = MyConfig; /// type FloorPlanner = SimpleFloorPlanner; +/// #[cfg(feature = "circuit-params")] +/// type Params = (); /// /// fn without_witnesses(&self) -> Self { /// Self::default() @@ -244,15 +258,9 @@ impl Mul for Value { /// fn synthesize(&self, config: MyConfig, mut layouter: impl Layouter) -> Result<(), Error> { /// layouter.assign_region(|| "Example region", |mut region| { /// config.s.enable(&mut region, 0)?; -/// region.assign_advice(|| "a", config.a, 0, || { -/// self.a.map(F::from) -/// })?; -/// region.assign_advice(|| "b", config.b, 0, || { -/// self.b.map(F::from) -/// })?; -/// region.assign_advice(|| "c", config.c, 0, || { -/// (self.a * self.b).map(F::from) -/// })?; +/// region.assign_advice(config.a, 0, self.a.map(F::from)); +/// region.assign_advice(config.b, 0, self.b.map(F::from)); +/// region.assign_advice(config.c, 0, (self.a * self.b).map(F::from)); /// Ok(()) /// }) /// } @@ -272,10 +280,7 @@ impl Mul for Value { /// prover.verify(), /// Err(vec![VerifyFailure::ConstraintNotSatisfied { /// constraint: ((0, "R1CS constraint").into(), 0, "buggy R1CS").into(), -/// location: FailureLocation::InRegion { -/// region: (0, "Example region").into(), -/// offset: 0, -/// }, +/// location: FailureLocation::OutsideRegion { row: 0 }, /// cell_values: vec![ /// (((Any::advice(), 0).into(), 0).into(), "0x2".to_string()), /// (((Any::advice(), 1).into(), 0).into(), "0x4".to_string()), @@ -295,7 +300,7 @@ impl Mul for Value { /// ); /// ``` #[derive(Debug)] -pub struct MockProver { +pub struct MockProver { k: u32, n: u32, cs: ConstraintSystem, @@ -325,13 +330,13 @@ pub struct MockProver { current_phase: sealed::Phase, } -impl MockProver { +impl MockProver { fn in_phase(&self, phase: P) -> bool { self.current_phase == phase.to_sealed() } } -impl Assignment for MockProver { +impl Assignment for MockProver { fn enter_region(&mut self, name: N) where NR: Into, @@ -429,9 +434,9 @@ impl Assignment for MockProver { .expect("bound failure")) } - fn assign_advice<'r, 'v>( + fn assign_advice<'v>( //( - &'r mut self, + &mut self, //_: A, column: Column, row: usize, @@ -579,7 +584,7 @@ impl Assignment for MockProver { } } -impl MockProver { +impl + Ord> MockProver { /// Runs a synthetic keygen-and-prove operation on the given circuit, collecting data /// about the constraints and their assignments. pub fn run>( @@ -590,6 +595,9 @@ impl MockProver { let n = 1 << k; let mut cs = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); let cs = cs; @@ -614,7 +622,7 @@ impl MockProver { cs.blinding_factors() ); - instance.resize(n, F::zero()); + instance.resize(n, F::ZERO); instance }) .collect::>(); @@ -630,7 +638,7 @@ impl MockProver { // let mut column = vec![AdviceCellValue::Unassigned; n]; // Assign advice to 0 by default so we can have gates that query unassigned rotations to minimize number of distinct rotation sets, for SHPLONK optimization let mut column = - vec![AdviceCellValue::Assigned(Arc::new(Assigned::Trivial(F::zero()))); n]; + vec![AdviceCellValue::Assigned(Arc::new(Assigned::Trivial(F::ZERO))); n]; // Poison unusable rows. for (i, cell) in column.iter_mut().enumerate().skip(usable_rows) { *cell = AdviceCellValue::Poison(i); @@ -647,7 +655,7 @@ impl MockProver { let mut hash: [u8; 64] = blake2b(b"Halo2-MockProver").as_bytes().try_into().unwrap(); iter::repeat_with(|| { hash = blake2b(&hash).as_bytes().try_into().unwrap(); - F::from_bytes_wide(&hash) + F::from_uniform_bytes(&hash) }) .take(cs.num_challenges) .collect() @@ -689,6 +697,9 @@ impl MockProver { v })); + #[cfg(feature = "thread-safe-region")] + prover.permutation.build_ordered_mapping(); + Ok(prover) } @@ -789,7 +800,7 @@ impl MockProver { AdviceCellValue::Assigned(ref a) => CellValue::Assigned(match a.as_ref() { Assigned::Trivial(a) => *a, Assigned::Rational(a, b) => *a * b.invert().unwrap(), - _ => F::zero(), + _ => F::ZERO, }), AdviceCellValue::Poison(i) => CellValue::Poison(i), }) @@ -806,11 +817,7 @@ impl MockProver { .flat_map(|(gate_index, gate)| { let blinding_rows = (self.n as usize - (self.cs.blinding_factors() + 1))..(self.n as usize); - (gate_row_ids - .clone() - .into_iter() - .chain(blinding_rows.into_iter())) - .flat_map(move |row| { + (gate_row_ids.clone().chain(blinding_rows)).flat_map(move |row| { let row = row as i32 + n; gate.polynomials().iter().enumerate().filter_map( move |(poly_index, poly)| match poly.evaluate_lazy( @@ -829,7 +836,7 @@ impl MockProver { &|a, b| a + b, &|a, b| a * b, &|a, scalar| a * scalar, - &Value::Real(F::zero()), + &Value::Real(F::ZERO), ) { Value::Real(x) if x.is_zero_vartime() => None, Value::Real(_) => Some(VerifyFailure::ConstraintNotSatisfied { @@ -871,6 +878,43 @@ impl MockProver { }) }); + let load = |expression: &Expression, row| { + expression.evaluate_lazy( + &|scalar| Value::Real(scalar), + &|_| panic!("virtual selectors are removed during optimization"), + &|query| { + let query = self.cs.fixed_queries[query.index.unwrap()]; + let column_index = query.0.index(); + let rotation = query.1 .0; + self.fixed[column_index][(row as i32 + n + rotation) as usize % n as usize] + .into() + }, + &|query| { + let query = self.cs.advice_queries[query.index.unwrap()]; + let column_index = query.0.index(); + let rotation = query.1 .0; + self.advice[column_index][(row as i32 + n + rotation) as usize % n as usize] + .clone() + .into() + }, + &|query| { + let query = self.cs.instance_queries[query.index.unwrap()]; + let column_index = query.0.index(); + let rotation = query.1 .0; + Value::Real( + self.instance[column_index] + [(row as i32 + n + rotation) as usize % n as usize], + ) + }, + &|challenge| Value::Real(self.challenges[challenge.index()]), + &|a| -a, + &|a, b| a + b, + &|a, b| a * b, + &|a, scalar| a * scalar, + &Value::Real(F::ZERO), + ) + }; + let mut cached_table = Vec::new(); let mut cached_table_identifier = Vec::new(); // Check that all lookups exist in their respective tables. @@ -880,44 +924,6 @@ impl MockProver { .iter() .enumerate() .flat_map(|(lookup_index, lookup)| { - let load = |expression: &Expression, row| { - expression.evaluate_lazy( - &|scalar| Value::Real(scalar), - &|_| panic!("virtual selectors are removed during optimization"), - &|query| { - let query = self.cs.fixed_queries[query.index]; - let column_index = query.0.index(); - let rotation = query.1 .0; - self.fixed[column_index] - [(row as i32 + n + rotation) as usize % n as usize] - .into() - }, - &|query| { - let query = self.cs.advice_queries[query.index]; - let column_index = query.0.index(); - let rotation = query.1 .0; - advice[column_index] - [(row as i32 + n + rotation) as usize % n as usize] - .into() - }, - &|query| { - let query = self.cs.instance_queries[query.index]; - let column_index = query.0.index(); - let rotation = query.1 .0; - Value::Real( - self.instance[column_index] - [(row as i32 + n + rotation) as usize % n as usize], - ) - }, - &|challenge| Value::Real(self.challenges[challenge.index()]), - &|a| -a, - &|a, b| a + b, - &|a, b| a * b, - &|a, scalar| a * scalar, - &Value::Real(F::zero()), - ) - }; - assert!(lookup.table_expressions.len() == lookup.input_expressions.len()); assert!(self.usable_rows.end > 0); @@ -966,7 +972,6 @@ impl MockProver { let mut inputs: Vec<(Vec<_>, usize)> = lookup_input_row_ids .clone() - .into_iter() .filter_map(|input_row| { let t = lookup .input_expressions @@ -995,7 +1000,7 @@ impl MockProver { assert!(table.binary_search(input).is_err()); Some(VerifyFailure::Lookup { - name: lookup.name, + name: lookup.name.clone(), lookup_index, location: FailureLocation::find_expressions( &self.cs, @@ -1011,6 +1016,67 @@ impl MockProver { .collect::>() }); + let shuffle_errors = + self.cs + .shuffles + .iter() + .enumerate() + .flat_map(|(shuffle_index, shuffle)| { + assert!(shuffle.shuffle_expressions.len() == shuffle.input_expressions.len()); + assert!(self.usable_rows.end > 0); + + let mut shuffle_rows: Vec>> = self + .usable_rows + .clone() + .map(|row| { + let t = shuffle + .shuffle_expressions + .iter() + .map(move |c| load(c, row)) + .collect(); + t + }) + .collect(); + shuffle_rows.sort(); + + let mut input_rows: Vec<(Vec>, usize)> = self + .usable_rows + .clone() + .map(|input_row| { + let t = shuffle + .input_expressions + .iter() + .map(move |c| load(c, input_row)) + .collect(); + + (t, input_row) + }) + .collect(); + input_rows.sort(); + + input_rows + .iter() + .zip(shuffle_rows.iter()) + .filter_map(|((input_value, row), shuffle_value)| { + if shuffle_value != input_value { + Some(VerifyFailure::Shuffle { + name: shuffle.name.clone(), + shuffle_index, + location: FailureLocation::find_expressions( + &self.cs, + &self.regions, + *row, + shuffle.input_expressions.iter(), + ), + }) + } else { + None + } + }) + .collect::>() + }); + + let mapping = self.permutation.mapping(); // Check that permutations preserve the original values of the cells. let perm_errors = { // Original values of columns involved in the permutation. @@ -1028,14 +1094,12 @@ impl MockProver { }; // Iterate over each column of the permutation - self.permutation - .mapping() - .iter() - .enumerate() - .flat_map(move |(column, values)| { - // Iterate over each row of the column to check that the cell's - // value is preserved by the mapping. - values.iter().enumerate().filter_map(move |(row, cell)| { + mapping.enumerate().flat_map(move |(column, values)| { + // Iterate over each row of the column to check that the cell's + // value is preserved by the mapping. + values + .enumerate() + .filter_map(move |(row, cell)| { let original_cell = original(column, row); let permuted_cell = original(cell.0, cell.1); if original_cell == permuted_cell { @@ -1053,7 +1117,8 @@ impl MockProver { }) } }) - }) + .collect::>() + }) }; let mut errors: Vec<_> = iter::empty() @@ -1061,6 +1126,7 @@ impl MockProver { .chain(gate_errors) .chain(lookup_errors) .chain(perm_errors) + .chain(shuffle_errors) .collect(); if errors.is_empty() { Ok(()) @@ -1177,7 +1243,7 @@ impl MockProver { AdviceCellValue::Assigned(ref a) => CellValue::Assigned(match a.as_ref() { Assigned::Trivial(a) => *a, Assigned::Rational(a, b) => *a * b.invert().unwrap(), - _ => F::zero(), + _ => F::ZERO, }), AdviceCellValue::Poison(i) => CellValue::Poison(i), }) @@ -1220,7 +1286,7 @@ impl MockProver { &|a, b| a + b, &|a, b| a * b, &|a, scalar| a * scalar, - &Value::Real(F::zero()), + &Value::Real(F::ZERO), ) { Value::Real(x) if x.is_zero_vartime() => None, Value::Real(_) => Some(VerifyFailure::ConstraintNotSatisfied { @@ -1264,6 +1330,36 @@ impl MockProver { .collect::>() }); + let load = |expression: &Expression, row| { + expression.evaluate_lazy( + &|scalar| Value::Real(scalar), + &|_| panic!("virtual selectors are removed during optimization"), + &|query| { + self.fixed[query.column_index] + [(row as i32 + n + query.rotation.0) as usize % n as usize] + .into() + }, + &|query| { + self.advice[query.column_index] + [(row as i32 + n + query.rotation.0) as usize % n as usize] + .clone() + .into() + }, + &|query| { + Value::Real( + self.instance[query.column_index] + [(row as i32 + n + query.rotation.0) as usize % n as usize], + ) + }, + &|challenge| Value::Real(self.challenges[challenge.index()]), + &|a| -a, + &|a, b| a + b, + &|a, b| a * b, + &|a, scalar| a * scalar, + &Value::Real(F::ZERO), + ) + }; + let mut cached_table = Vec::new(); let mut cached_table_identifier = Vec::new(); // Check that all lookups exist in their respective tables. @@ -1273,35 +1369,6 @@ impl MockProver { .iter() .enumerate() .flat_map(|(lookup_index, lookup)| { - let load = |expression: &Expression, row| { - expression.evaluate_lazy( - &|scalar| Value::Real(scalar), - &|_| panic!("virtual selectors are removed during optimization"), - &|query| { - self.fixed[query.column_index] - [(row as i32 + n + query.rotation.0) as usize % n as usize] - .into() - }, - &|query| { - advice[query.column_index] - [(row as i32 + n + query.rotation.0) as usize % n as usize] - .into() - }, - &|query| { - Value::Real( - self.instance[query.column_index] - [(row as i32 + n + query.rotation.0) as usize % n as usize], - ) - }, - &|challenge| Value::Real(self.challenges[challenge.index()]), - &|a| -a, - &|a, b| a + b, - &|a, b| a * b, - &|a, scalar| a * scalar, - &Value::Real(F::zero()), - ) - }; - assert!(lookup.table_expressions.len() == lookup.input_expressions.len()); assert!(self.usable_rows.end > 0); @@ -1374,7 +1441,7 @@ impl MockProver { .filter_map(move |(input, input_row)| { if table.binary_search(input).is_err() { Some(VerifyFailure::Lookup { - name: lookup.name, + name: lookup.name.clone(), lookup_index, location: FailureLocation::find_expressions( &self.cs, @@ -1390,6 +1457,67 @@ impl MockProver { .collect::>() }); + let shuffle_errors = + self.cs + .shuffles + .iter() + .enumerate() + .flat_map(|(shuffle_index, shuffle)| { + assert!(shuffle.shuffle_expressions.len() == shuffle.input_expressions.len()); + assert!(self.usable_rows.end > 0); + + let mut shuffle_rows: Vec>> = self + .usable_rows + .clone() + .map(|row| { + let t = shuffle + .shuffle_expressions + .iter() + .map(move |c| load(c, row)) + .collect(); + t + }) + .collect(); + shuffle_rows.sort(); + + let mut input_rows: Vec<(Vec>, usize)> = self + .usable_rows + .clone() + .map(|input_row| { + let t = shuffle + .input_expressions + .iter() + .map(move |c| load(c, input_row)) + .collect(); + + (t, input_row) + }) + .collect(); + input_rows.sort(); + + input_rows + .iter() + .zip(shuffle_rows.iter()) + .filter_map(|((input_value, row), shuffle_value)| { + if shuffle_value != input_value { + Some(VerifyFailure::Shuffle { + name: shuffle.name.clone(), + shuffle_index, + location: FailureLocation::find_expressions( + &self.cs, + &self.regions, + *row, + shuffle.input_expressions.iter(), + ), + }) + } else { + None + } + }) + .collect::>() + }); + + let mapping = self.permutation.mapping(); // Check that permutations preserve the original values of the cells. let perm_errors = { // Original values of columns involved in the permutation. @@ -1407,36 +1535,31 @@ impl MockProver { }; // Iterate over each column of the permutation - self.permutation - .mapping() - .iter() - .enumerate() - .flat_map(move |(column, values)| { - // Iterate over each row of the column to check that the cell's - // value is preserved by the mapping. - values - .par_iter() - .enumerate() - .filter_map(move |(row, cell)| { - let original_cell = original(column, row); - let permuted_cell = original(cell.0, cell.1); - if original_cell == permuted_cell { - None - } else { - let columns = self.cs.permutation.get_columns(); - let column = columns.get(column).unwrap(); - Some(VerifyFailure::Permutation { - column: (*column).into(), - location: FailureLocation::find( - &self.regions, - row, - Some(column).into_iter().cloned().collect(), - ), - }) - } - }) - .collect::>() - }) + mapping.enumerate().flat_map(move |(column, values)| { + // Iterate over each row of the column to check that the cell's + // value is preserved by the mapping. + values + .enumerate() + .filter_map(move |(row, cell)| { + let original_cell = original(column, row); + let permuted_cell = original(cell.0, cell.1); + if original_cell == permuted_cell { + None + } else { + let columns = self.cs.permutation.get_columns(); + let column = columns.get(column).unwrap(); + Some(VerifyFailure::Permutation { + column: (*column).into(), + location: FailureLocation::find( + &self.regions, + row, + Some(column).into_iter().cloned().collect(), + ), + }) + } + }) + .collect::>() + }) }; let mut errors: Vec<_> = iter::empty() @@ -1444,6 +1567,7 @@ impl MockProver { .chain(gate_errors) .chain(lookup_errors) .chain(perm_errors) + .chain(shuffle_errors) .collect(); if errors.is_empty() { Ok(()) @@ -1568,6 +1692,8 @@ mod tests { impl Circuit for FaultyCircuit { type Config = FaultyCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + fn params(&self) {} fn configure(meta: &mut ConstraintSystem) -> Self::Config { let a = meta.advice_column(); @@ -1614,6 +1740,12 @@ mod tests { // Name Column b region.name_column(|| "This is also annotated!", config.b); + // Name Column a + region.name_column(|| "This is annotated!", config.a); + + // Name Column b + region.name_column(|| "This is also annotated!", config.b); + // BUG: Forget to assign b = 0! This could go unnoticed during // development, because cell values default to zero, which in this // case is fine, but for other assignments would be broken. @@ -1626,18 +1758,19 @@ mod tests { let prover = MockProver::run(K, &FaultyCircuit {}, vec![]).unwrap(); assert_eq!( prover.verify(), - Err(vec![VerifyFailure::CellNotAssigned { - gate: (0, "Equality check").into(), - region: (0, "Faulty synthesis".to_owned()).into(), - gate_offset: 1, - column: Column::new( - 1, - Any::Advice(Advice { - phase: FirstPhase.to_sealed() - }) - ), - offset: 1, - }]) + Ok(()) // Axiom mock prover default assigns 0, so there is no bug + /*Err(vec![VerifyFailure::CellNotAssigned { + gate: (0, "Equality check").into(), + region: (0, "Faulty synthesis".to_owned()).into(), + gate_offset: 1, + column: Column::new( + 1, + Any::Advice(Advice { + phase: FirstPhase.to_sealed() + }) + ), + offset: 1, + }])*/ ); } @@ -1709,8 +1842,9 @@ mod tests { // No assignment needed for the table as is an Instance Column. layouter.assign_region( - || "Good synthesis", + || "Bad synthesis", |mut region| { + // == Good synthesis == // Enable the lookup on rows 0 and 1. config.q.enable(&mut region, 0)?; config.q.enable(&mut region, 1)?; @@ -1730,33 +1864,16 @@ mod tests { region.assign_advice(config.a, 0, Value::known(Fp::from(2))); region.assign_advice(config.a, 1, Value::known(Fp::from(6))); - Ok(()) - }, - )?; - - layouter.assign_region( - || "Faulty synthesis", - |mut region| { + // == Faulty synthesis == // Enable the lookup on rows 0 and 1. - config.q.enable(&mut region, 0)?; - config.q.enable(&mut region, 1)?; - - for i in 0..4 { - // Load Advice lookup table with Instance lookup table values. - region.assign_advice_from_instance( - || "Advice from instance tables", - config.table, - i, - config.advice_table, - i, - )?; - } + config.q.enable(&mut region, 2)?; + config.q.enable(&mut region, 3)?; // Assign a = 4. - region.assign_advice(config.a, 0, Value::known(Fp::from(4))); + region.assign_advice(config.a, 2, Value::known(Fp::from(4))); // BUG: Assign a = 5, which doesn't exist in the table! - region.assign_advice(config.a, 1, Value::known(Fp::from(5))); + region.assign_advice(config.a, 3, Value::known(Fp::from(5))); region.name_column(|| "Witness example", config.a); @@ -1781,12 +1898,9 @@ mod tests { assert_eq!( prover.verify(), Err(vec![VerifyFailure::Lookup { - name: "lookup", + name: "lookup".to_string(), lookup_index: 0, - location: FailureLocation::InRegion { - region: (1, "Faulty synthesis").into(), - offset: 1, - } + location: FailureLocation::OutsideRegion { row: 3 } }]) ); } @@ -1807,6 +1921,8 @@ mod tests { impl Circuit for FaultyCircuit { type Config = FaultyCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn configure(meta: &mut ConstraintSystem) -> Self::Config { let a = meta.advice_column(); @@ -1849,13 +1965,14 @@ mod tests { || Value::known(Fp::from(2 * i as u64)), ) }) - .fold(Ok(()), |acc, res| acc.and(res)) + .try_fold((), |_, res| res) }, )?; layouter.assign_region( - || "Good synthesis", + || "Fauly synthesis", |mut region| { + // == Good synhesis == // Enable the lookup on rows 0 and 1. config.q.enable(&mut region, 0)?; config.q.enable(&mut region, 1)?; @@ -1874,35 +1991,29 @@ mod tests { Value::known(Assigned::Trivial(Fp::from(6))), ); - Ok(()) - }, - )?; - - layouter.assign_region( - || "Faulty synthesis", - |mut region| { - // Enable the lookup on rows 0 and 1. - config.q.enable(&mut region, 0)?; - config.q.enable(&mut region, 1)?; + // == Faulty synthesis == + // Enable the lookup on rows 2 and 3. + config.q.enable(&mut region, 2)?; + config.q.enable(&mut region, 3)?; // Assign a = 4. region.assign_advice( - // || "a = 4", config.a, - 0, + 2, Value::known(Assigned::Trivial(Fp::from(4))), ); // BUG: Assign a = 5, which doesn't exist in the table! region.assign_advice( - // || "a = 5", config.a, - 1, + 3, Value::known(Assigned::Trivial(Fp::from(5))), ); region.name_column(|| "Witness example", config.a); + region.name_column(|| "Witness example", config.a); + Ok(()) }, ) @@ -1913,12 +2024,9 @@ mod tests { assert_eq!( prover.verify(), Err(vec![VerifyFailure::Lookup { - name: "lookup", + name: "lookup".to_string(), lookup_index: 0, - location: FailureLocation::InRegion { - region: (2, "Faulty synthesis").into(), - offset: 1, - } + location: FailureLocation::OutsideRegion { row: 3 } }]) ); } @@ -1973,8 +2081,9 @@ mod tests { mut layouter: impl Layouter, ) -> Result<(), Error> { layouter.assign_region( - || "Correct synthesis", + || "Wrong synthesis", |mut region| { + // == Correct synthesis == // Enable the equality gate. config.q.enable(&mut region, 0)?; @@ -1988,20 +2097,16 @@ mod tests { region.assign_advice(config.c, 0, Value::known(Fp::from(5u64))); // Assign d = 7. region.assign_fixed(config.d, 0, Fp::from(7u64)); - Ok(()) - }, - )?; - layouter.assign_region( - || "Wrong synthesis", - |mut region| { + + // == Wrong synthesis == // Enable the equality gate. - config.q.enable(&mut region, 0)?; + config.q.enable(&mut region, 1)?; // Assign a = 1. - region.assign_advice(config.a, 0, Value::known(Fp::one())); + region.assign_advice(config.a, 1, Value::known(Fp::one())); // Assign b = 0. - region.assign_advice(config.b, 0, Value::known(Fp::zero())); + region.assign_advice(config.b, 1, Value::known(Fp::zero())); // Name Column a region.name_column(|| "This is Advice!", config.a); @@ -2009,9 +2114,9 @@ mod tests { region.name_column(|| "This is Advice too!", config.b); // Assign c = 5. - region.assign_advice(config.c, 0, Value::known(Fp::from(5u64))); + region.assign_advice(config.c, 1, Value::known(Fp::from(5u64))); // Assign d = 7. - region.assign_fixed(config.d, 0, Fp::from(7u64)); + region.assign_fixed(config.d, 1, Fp::from(7u64)); // Name Column c region.name_column(|| "Another one!", config.c); @@ -2032,8 +2137,8 @@ mod tests { Err(vec![VerifyFailure::ConstraintNotSatisfied { constraint: ((0, "Equality check").into(), 0, "").into(), location: FailureLocation::InRegion { - region: (1, "Wrong synthesis").into(), - offset: 0, + region: (0, "Wrong synthesis").into(), + offset: 1, }, cell_values: vec![ ( @@ -2084,3 +2189,5 @@ mod tests { ) } } + +impl SyncDeps for MockProver {} diff --git a/halo2_proofs/src/dev/cost.rs b/halo2_proofs/src/dev/cost.rs index 8f293756b7..28874fc84e 100644 --- a/halo2_proofs/src/dev/cost.rs +++ b/halo2_proofs/src/dev/cost.rs @@ -11,7 +11,7 @@ use ff::{Field, PrimeField}; use group::prime::PrimeGroup; use crate::{ - circuit::Value, + circuit::{layouter::SyncDeps, Value}, plonk::{ Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, ConstraintSystem, Error, Fixed, FloorPlanner, Instance, Selector, @@ -46,6 +46,8 @@ struct Assembly { selectors: Vec>, } +impl SyncDeps for Assembly {} + impl Assignment for Assembly { fn enter_region(&mut self, _: N) where @@ -150,6 +152,9 @@ impl> CircuitCost Self { // Collect the layout details. let mut cs = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); let mut assembly = Assembly { selectors: vec![vec![false; 1 << k]; cs.num_selectors], diff --git a/halo2_proofs/src/dev/failure.rs b/halo2_proofs/src/dev/failure.rs index 3d08a5812d..0aae3b50be 100644 --- a/halo2_proofs/src/dev/failure.rs +++ b/halo2_proofs/src/dev/failure.rs @@ -2,7 +2,6 @@ use std::collections::{BTreeMap, HashSet}; use std::fmt::{self, Debug}; use group::ff::Field; -use halo2curves::FieldExt; use super::metadata::{DebugColumn, DebugVirtualCell}; use super::MockProver; @@ -12,11 +11,9 @@ use super::{ Region, }; use crate::dev::metadata::Constraint; -use crate::dev::{AdviceCellValue, CellValue}; -use crate::plonk::Assigned; use crate::{ - dev::{Instance, Value}, - plonk::{Any, Column, ConstraintSystem, Expression, Gate}, + dev::{AdviceCellValue, CellValue, Instance, Value}, + plonk::{Any, Assigned, Column, ConstraintSystem, Expression, Gate}, poly::Rotation, }; @@ -73,9 +70,9 @@ impl FailureLocation { expression.evaluate( &|_| vec![], &|_| panic!("virtual selectors are removed during optimization"), - &|query| vec![cs.fixed_queries[query.index].0.into()], - &|query| vec![cs.advice_queries[query.index].0.into()], - &|query| vec![cs.instance_queries[query.index].0.into()], + &|query| vec![cs.fixed_queries[query.index.unwrap()].0.into()], + &|query| vec![cs.advice_queries[query.index.unwrap()].0.into()], + &|query| vec![cs.instance_queries[query.index.unwrap()].0.into()], &|_| vec![], &|a| a, &|mut a, mut b| { @@ -162,7 +159,7 @@ pub enum VerifyFailure { /// A lookup input did not exist in its corresponding table. Lookup { /// The name of the lookup that is not satisfied. - name: &'static str, + name: String, /// The index of the lookup that is not satisfied. These indices are assigned in /// the order in which `ConstraintSystem::lookup` is called during /// `Circuit::configure`. @@ -181,6 +178,28 @@ pub enum VerifyFailure { /// lookup is active on a row adjacent to an unrelated region. location: FailureLocation, }, + /// A shuffle input did not exist in its corresponding map. + Shuffle { + /// The name of the lookup that is not satisfied. + name: String, + /// The index of the lookup that is not satisfied. These indices are assigned in + /// the order in which `ConstraintSystem::lookup` is called during + /// `Circuit::configure`. + shuffle_index: usize, + /// The location at which the lookup is not satisfied. + /// + /// `FailureLocation::InRegion` is most common, and may be due to the intentional + /// use of a lookup (if its inputs are conditional on a complex selector), or an + /// unintentional lookup constraint that overlaps the region (indicating that the + /// lookup's inputs should be made conditional). + /// + /// `FailureLocation::OutsideRegion` is uncommon, and could mean that: + /// - The input expressions do not correctly constrain a default value that exists + /// in the table when the lookup is not being used. + /// - The input expressions use a column queried at a non-zero `Rotation`, and the + /// lookup is active on a row adjacent to an unrelated region. + location: FailureLocation, + }, /// A permutation did not preserve the original value of a cell. Permutation { /// The column in which this permutation is not satisfied. @@ -244,6 +263,17 @@ impl fmt::Display for VerifyFailure { name, lookup_index, location ) } + Self::Shuffle { + name, + shuffle_index, + location, + } => { + write!( + f, + "Shuffle {}(index: {}) is not satisfied {}", + name, shuffle_index, location + ) + } Self::Permutation { column, location } => { write!( f, @@ -280,7 +310,7 @@ impl Debug for VerifyFailure { }; let debug = ConstraintCaseDebug { - constraint: *constraint, + constraint: constraint.clone(), location: location.clone(), cell_values: cell_values .iter() @@ -446,7 +476,7 @@ fn render_constraint_not_satisfied( /// | x0 = 0x5 /// | x1 = 1 /// ``` -fn render_lookup( +fn render_lookup( prover: &MockProver, name: &str, lookup_index: usize, @@ -512,7 +542,7 @@ fn render_lookup( ) }); - fn cell_value<'a, F: FieldExt, Q: Into + Copy>( + fn cell_value<'a, F: Field, Q: Into + Copy>( load: impl Fn(Q) -> Value + 'a, ) -> impl Fn(Q) -> BTreeMap + 'a { move |query| { @@ -555,10 +585,7 @@ fn render_lookup( advice .iter() .map(|rc| match rc { - AdviceCellValue::Assigned(a) => CellValue::Assigned(match a.as_ref() { - Assigned::Trivial(a) => *a, - _ => unreachable!(), - }), + AdviceCellValue::Assigned(a) => CellValue::Assigned(a.evaluate()), AdviceCellValue::Poison(i) => CellValue::Poison(*i), }) .collect::>() @@ -630,9 +657,187 @@ fn render_lookup( } } +fn render_shuffle( + prover: &MockProver, + name: &str, + shuffle_index: usize, + location: &FailureLocation, +) { + let n = prover.n as i32; + let cs = &prover.cs; + let shuffle = &cs.shuffles[shuffle_index]; + + // Get the absolute row on which the shuffle's inputs are being queried, so we can + // fetch the input values. + let row = match location { + FailureLocation::InRegion { region, offset } => { + prover.regions[region.index].rows.unwrap().0 + offset + } + FailureLocation::OutsideRegion { row } => *row, + } as i32; + + let shuffle_columns = shuffle.shuffle_expressions.iter().map(|expr| { + expr.evaluate( + &|f| format! {"Const: {:#?}", f}, + &|s| format! {"S{}", s.0}, + &|query| { + format!( + "{:?}", + prover + .cs + .general_column_annotations + .get(&metadata::Column::from((Any::Fixed, query.column_index))) + .cloned() + .unwrap_or_else(|| format!("F{}", query.column_index())) + ) + }, + &|query| { + format!( + "{:?}", + prover + .cs + .general_column_annotations + .get(&metadata::Column::from((Any::advice(), query.column_index))) + .cloned() + .unwrap_or_else(|| format!("A{}", query.column_index())) + ) + }, + &|query| { + format!( + "{:?}", + prover + .cs + .general_column_annotations + .get(&metadata::Column::from((Any::Instance, query.column_index))) + .cloned() + .unwrap_or_else(|| format!("I{}", query.column_index())) + ) + }, + &|challenge| format! {"C{}", challenge.index()}, + &|query| format! {"-{}", query}, + &|a, b| format! {"{} + {}", a,b}, + &|a, b| format! {"{} * {}", a,b}, + &|a, b| format! {"{} * {:?}", a, b}, + ) + }); + + fn cell_value<'a, F: Field, Q: Into + Copy>( + load: impl Fn(Q) -> Value + 'a, + ) -> impl Fn(Q) -> BTreeMap + 'a { + move |query| { + let AnyQuery { + column_type, + column_index, + rotation, + .. + } = query.into(); + Some(( + ((column_type, column_index).into(), rotation.0).into(), + match load(query) { + Value::Real(v) => util::format_value(v), + Value::Poison => unreachable!(), + }, + )) + .into_iter() + .collect() + } + } + + eprintln!("error: input does not exist in shuffle"); + eprint!(" ("); + for i in 0..shuffle.input_expressions.len() { + eprint!("{}L{}", if i == 0 { "" } else { ", " }, i); + } + eprint!(") <-> ("); + for (i, column) in shuffle_columns.enumerate() { + eprint!("{}{}", if i == 0 { "" } else { ", " }, column); + } + eprintln!(")"); + + eprintln!(); + eprintln!(" Shuffle '{}' inputs:", name); + let advice = prover + .advice + .iter() + .map(|advice| { + advice + .iter() + .map(|rc| match rc { + AdviceCellValue::Assigned(a) => CellValue::Assigned(a.evaluate()), + AdviceCellValue::Poison(i) => CellValue::Poison(*i), + }) + .collect::>() + }) + .collect::>(); + for (i, input) in shuffle.input_expressions.iter().enumerate() { + // Fetch the cell values (since we don't store them in VerifyFailure::Shuffle). + let cell_values = input.evaluate( + &|_| BTreeMap::default(), + &|_| panic!("virtual selectors are removed during optimization"), + &cell_value(&util::load(n, row, &cs.fixed_queries, &prover.fixed)), + &cell_value(&util::load(n, row, &cs.advice_queries, &advice)), + &cell_value(&util::load_instance( + n, + row, + &cs.instance_queries, + &prover.instance, + )), + &|_| BTreeMap::default(), + &|a| a, + &|mut a, mut b| { + a.append(&mut b); + a + }, + &|mut a, mut b| { + a.append(&mut b); + a + }, + &|a, _| a, + ); + + // Collect the necessary rendering information: + // - The columns involved in this constraint. + // - How many cells are in each column. + // - The grid of cell values, indexed by rotation. + let mut columns = BTreeMap::::default(); + let mut layout = BTreeMap::>::default(); + for (i, (cell, _)) in cell_values.iter().enumerate() { + *columns.entry(cell.column).or_default() += 1; + layout + .entry(cell.rotation) + .or_default() + .entry(cell.column) + .or_insert(format!("x{}", i)); + } + + if i != 0 { + eprintln!(); + } + eprintln!( + " Sh{} = {}", + i, + emitter::expression_to_string(input, &layout) + ); + eprintln!(" ^"); + + emitter::render_cell_layout(" | ", location, &columns, &layout, |_, rotation| { + if rotation == 0 { + eprint!(" <--{{ Shuffle '{}' inputs queried here", name); + } + }); + + // Print the map from local variables to assigned values. + eprintln!(" |"); + eprintln!(" | Assigned cell values:"); + for (i, (_, value)) in cell_values.iter().enumerate() { + eprintln!(" | x{} = {}", i, value); + } + } +} + impl VerifyFailure { /// Emits this failure in pretty-printed format to stderr. - pub(super) fn emit(&self, prover: &MockProver) { + pub(super) fn emit(&self, prover: &MockProver) { match self { Self::CellNotAssigned { gate, @@ -660,6 +865,11 @@ impl VerifyFailure { lookup_index, location, } => render_lookup(prover, name, *lookup_index, location), + Self::Shuffle { + name, + shuffle_index, + location, + } => render_shuffle(prover, name, *shuffle_index, location), _ => eprintln!("{}", self), } } diff --git a/halo2_proofs/src/dev/failure/emitter.rs b/halo2_proofs/src/dev/failure/emitter.rs index e84ba8013e..edd61f3060 100644 --- a/halo2_proofs/src/dev/failure/emitter.rs +++ b/halo2_proofs/src/dev/failure/emitter.rs @@ -153,7 +153,7 @@ pub(super) fn expression_to_string( label.clone() } else if query.rotation.0 == 0 { // This is most likely a merged selector - format!("S{}", query.index) + format!("S{}", query.index.unwrap()) } else { // No idea how we'd get here... format!("F{}@{}", query.column_index, query.rotation.0) diff --git a/halo2_proofs/src/dev/gates.rs b/halo2_proofs/src/dev/gates.rs index cfc71c021e..fd4c039623 100644 --- a/halo2_proofs/src/dev/gates.rs +++ b/halo2_proofs/src/dev/gates.rs @@ -15,14 +15,14 @@ use crate::{ #[derive(Debug)] struct Constraint { - name: &'static str, + name: String, expression: String, queries: BTreeSet, } #[derive(Debug)] struct Gate { - name: &'static str, + name: String, constraints: Vec, } @@ -49,6 +49,8 @@ struct Gate { /// impl Circuit for MyCircuit { /// type Config = MyConfig; /// type FloorPlanner = SimpleFloorPlanner; +/// #[cfg(feature = "circuit-params")] +/// type Params = (); /// /// fn without_witnesses(&self) -> Self { /// Self::default() @@ -79,6 +81,9 @@ struct Gate { /// } /// } /// +/// #[cfg(feature = "circuit-params")] +/// let gates = CircuitGates::collect::(()); +/// #[cfg(not(feature = "circuit-params"))] /// let gates = CircuitGates::collect::(); /// assert_eq!( /// format!("{}", gates), @@ -103,22 +108,27 @@ pub struct CircuitGates { impl CircuitGates { /// Collects the gates from within the circuit. - pub fn collect>() -> Self { + pub fn collect>( + #[cfg(feature = "circuit-params")] params: C::Params, + ) -> Self { // Collect the graph details. let mut cs = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let _ = C::configure_with_params(&mut cs, params); + #[cfg(not(feature = "circuit-params"))] let _ = C::configure(&mut cs); let gates = cs .gates .iter() .map(|gate| Gate { - name: gate.name(), + name: gate.name().to_string(), constraints: gate .polynomials() .iter() .enumerate() .map(|(i, constraint)| Constraint { - name: gate.constraint_name(i), + name: gate.constraint_name(i).to_string(), expression: constraint.evaluate( &util::format_value, &|selector| format!("S{}", selector.0), diff --git a/halo2_proofs/src/dev/graph.rs b/halo2_proofs/src/dev/graph.rs index 5a43313dea..3a51c8fa2b 100644 --- a/halo2_proofs/src/dev/graph.rs +++ b/halo2_proofs/src/dev/graph.rs @@ -22,6 +22,9 @@ pub fn circuit_dot_graph>( ) -> String { // Collect the graph details. let mut cs = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); let mut graph = Graph::default(); ConcreteCircuit::FloorPlanner::synthesize(&mut graph, circuit, config, cs.constants).unwrap(); @@ -77,6 +80,8 @@ struct Graph { current_namespace: Vec, } +impl crate::dev::SyncDeps for Graph {} + impl Assignment for Graph { fn enter_region(&mut self, _: N) where @@ -111,49 +116,24 @@ impl Assignment for Graph { Ok(Value::unknown()) } - fn assign_advice( + fn assign_advice<'v>( + //V, VR, A, AR>( &mut self, - _: A, + //_: A, _: Column, _: usize, - _: V, - ) -> Result<(), Error> - where - V: FnOnce() -> Value, - VR: Into>, - A: FnOnce() -> AR, - AR: Into, - { + _: Value>, + ) -> Value<&'v Assigned> { // Do nothing; we don't care about cells in this context. - Ok(()) + Value::unknown() } - fn assign_fixed( - &mut self, - _: A, - _: Column, - _: usize, - _: V, - ) -> Result<(), Error> - where - V: FnOnce() -> Value, - VR: Into>, - A: FnOnce() -> AR, - AR: Into, - { + fn assign_fixed(&mut self, _: Column, _: usize, _: Assigned) { // Do nothing; we don't care about cells in this context. - Ok(()) } - fn copy( - &mut self, - _: Column, - _: usize, - _: Column, - _: usize, - ) -> Result<(), crate::plonk::Error> { + fn copy(&mut self, _: Column, _: usize, _: Column, _: usize) { // Do nothing; we don't care about permutations in this context. - Ok(()) } fn fill_from_row( diff --git a/halo2_proofs/src/dev/graph/layout.rs b/halo2_proofs/src/dev/graph/layout.rs index 0f2e67a81d..4031bfce40 100644 --- a/halo2_proofs/src/dev/graph/layout.rs +++ b/halo2_proofs/src/dev/graph/layout.rs @@ -29,7 +29,7 @@ use crate::{ /// /// let drawing_area = BitMapBackend::new("example-circuit-layout.png", (1024, 768)) /// .into_drawing_area(); -/// drawing_area.fill(&WHITE).unwrap(); +/// drawing_area.fill(WHITE).unwrap(); /// let drawing_area = drawing_area /// .titled("Example Circuit Layout", ("sans-serif", 60)) /// .unwrap(); @@ -97,6 +97,9 @@ impl CircuitLayout { let n = 1 << k; // Collect the layout details. let mut cs = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); let mut layout = Layout::new(k, n, cs.num_selectors); ConcreteCircuit::FloorPlanner::synthesize( @@ -181,7 +184,7 @@ impl CircuitLayout { root.draw(&Rectangle::new( [(0, 0), (total_columns, view_bottom)], - &BLACK, + BLACK, ))?; let draw_region = |root: &DrawingArea<_, _>, top_left, bottom_right| { @@ -197,7 +200,7 @@ impl CircuitLayout { [top_left, bottom_right], ShapeStyle::from(&GREEN.mix(0.2)).filled(), ))?; - root.draw(&Rectangle::new([top_left, bottom_right], &BLACK))?; + root.draw(&Rectangle::new([top_left, bottom_right], BLACK))?; Ok(()) }; @@ -352,6 +355,8 @@ struct Layout { selectors: Vec>, } +impl crate::dev::SyncDeps for Layout {} + impl Layout { fn new(k: u32, n: usize, num_selectors: usize) -> Self { Layout { @@ -436,49 +441,22 @@ impl Assignment for Layout { Ok(Value::unknown()) } - fn assign_advice( + fn assign_advice<'v>( &mut self, - _: A, column: Column, row: usize, - _: V, - ) -> Result<(), Error> - where - V: FnOnce() -> Value, - VR: Into>, - A: FnOnce() -> AR, - AR: Into, - { + _: Value>, + ) -> Value<&'v Assigned> { self.update(Column::::from(column).into(), row); - Ok(()) + Value::unknown() } - fn assign_fixed( - &mut self, - _: A, - column: Column, - row: usize, - _: V, - ) -> Result<(), Error> - where - V: FnOnce() -> Value, - VR: Into>, - A: FnOnce() -> AR, - AR: Into, - { + fn assign_fixed(&mut self, column: Column, row: usize, _: Assigned) { self.update(Column::::from(column).into(), row); - Ok(()) } - fn copy( - &mut self, - l_col: Column, - l_row: usize, - r_col: Column, - r_row: usize, - ) -> Result<(), crate::plonk::Error> { + fn copy(&mut self, l_col: Column, l_row: usize, r_col: Column, r_row: usize) { self.equality.push((l_col, l_row, r_col, r_row)); - Ok(()) } fn fill_from_row( diff --git a/halo2_proofs/src/dev/metadata.rs b/halo2_proofs/src/dev/metadata.rs index 5fd0835bad..f81bfa67a7 100644 --- a/halo2_proofs/src/dev/metadata.rs +++ b/halo2_proofs/src/dev/metadata.rs @@ -15,6 +15,17 @@ pub struct Column { pub(super) index: usize, } +impl Column { + /// Return the column type. + pub fn column_type(&self) -> Any { + self.column_type + } + /// Return the column index. + pub fn index(&self) -> usize { + self.index + } +} + impl fmt::Display for Column { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Column('{:?}', {})", self.column_type, self.index) @@ -75,7 +86,7 @@ impl fmt::Display for DebugColumn { /// within a custom gate. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct VirtualCell { - name: &'static str, + name: String, pub(super) column: Column, pub(super) rotation: i32, } @@ -83,17 +94,17 @@ pub struct VirtualCell { impl From<(Column, i32)> for VirtualCell { fn from((column, rotation): (Column, i32)) -> Self { VirtualCell { - name: "", + name: "".to_string(), column, rotation, } } } -impl From<(&'static str, Column, i32)> for VirtualCell { - fn from((name, column, rotation): (&'static str, Column, i32)) -> Self { +impl> From<(S, Column, i32)> for VirtualCell { + fn from((name, column, rotation): (S, Column, i32)) -> Self { VirtualCell { - name, + name: name.as_ref().to_string(), column, rotation, } @@ -103,7 +114,7 @@ impl From<(&'static str, Column, i32)> for VirtualCell { impl From for VirtualCell { fn from(c: plonk::VirtualCell) -> Self { VirtualCell { - name: "", + name: "".to_string(), column: c.column.into(), rotation: c.rotation.0, } @@ -114,7 +125,7 @@ impl fmt::Display for VirtualCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}@{}", self.column, self.rotation)?; if !self.name.is_empty() { - write!(f, "({})", self.name)?; + write!(f, "({})", self.name.as_str())?; } Ok(()) } @@ -123,7 +134,7 @@ impl fmt::Display for VirtualCell { /// Helper structure used to be able to inject Column annotations inside a `Display` or `Debug` call. #[derive(Clone, Debug)] pub(super) struct DebugVirtualCell { - name: &'static str, + name: String, column: DebugColumn, rotation: i32, } @@ -131,7 +142,7 @@ pub(super) struct DebugVirtualCell { impl From<(&VirtualCell, Option<&HashMap>)> for DebugVirtualCell { fn from(info: (&VirtualCell, Option<&HashMap>)) -> Self { DebugVirtualCell { - name: info.0.name, + name: info.0.name.clone(), column: DebugColumn::from((info.0.column, info.1)), rotation: info.0.rotation, } @@ -149,30 +160,33 @@ impl fmt::Display for DebugVirtualCell { } /// Metadata about a configured gate within a circuit. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct Gate { /// The index of the active gate. These indices are assigned in the order in which /// `ConstraintSystem::create_gate` is called during `Circuit::configure`. pub(super) index: usize, /// The name of the active gate. These are specified by the gate creator (such as /// a chip implementation), and is not enforced to be unique. - pub(super) name: &'static str, + pub(super) name: String, } impl fmt::Display for Gate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Gate {} ('{}')", self.index, self.name) + write!(f, "Gate {} ('{}')", self.index, self.name.as_str()) } } -impl From<(usize, &'static str)> for Gate { - fn from((index, name): (usize, &'static str)) -> Self { - Gate { index, name } +impl> From<(usize, S)> for Gate { + fn from((index, name): (usize, S)) -> Self { + Gate { + index, + name: name.as_ref().to_string(), + } } } /// Metadata about a configured constraint within a circuit. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct Constraint { /// The gate containing the constraint. pub(super) gate: Gate, @@ -182,7 +196,7 @@ pub struct Constraint { pub(super) index: usize, /// The name of the constraint. This is specified by the gate creator (such as a chip /// implementation), and is not enforced to be unique. - pub(super) name: &'static str, + pub(super) name: String, } impl fmt::Display for Constraint { @@ -194,7 +208,7 @@ impl fmt::Display for Constraint { if self.name.is_empty() { String::new() } else { - format!(" ('{}')", self.name) + format!(" ('{}')", self.name.as_str()) }, self.gate.index, self.gate.name, @@ -202,9 +216,13 @@ impl fmt::Display for Constraint { } } -impl From<(Gate, usize, &'static str)> for Constraint { - fn from((gate, index, name): (Gate, usize, &'static str)) -> Self { - Constraint { gate, index, name } +impl> From<(Gate, usize, S)> for Constraint { + fn from((gate, index, name): (Gate, usize, S)) -> Self { + Constraint { + gate, + index, + name: name.as_ref().to_string(), + } } } @@ -250,7 +268,7 @@ impl Debug for Region { impl fmt::Display for Region { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Region {} ('{}')", self.index, self.name) + write!(f, "Region {} ('{}')", self.index, self.name.as_str()) } } diff --git a/halo2_proofs/src/dev/util.rs b/halo2_proofs/src/dev/util.rs index a4dbfe5a40..f09695a4a9 100644 --- a/halo2_proofs/src/dev/util.rs +++ b/halo2_proofs/src/dev/util.rs @@ -1,7 +1,5 @@ -use std::collections::BTreeMap; - use group::ff::Field; -use halo2curves::FieldExt; +use std::collections::BTreeMap; use super::{metadata, CellValue, Value}; use crate::{ @@ -14,7 +12,7 @@ use crate::{ pub(crate) struct AnyQuery { /// Query index - pub index: usize, + pub index: Option, /// Column type pub column_type: Any, /// Column index @@ -59,9 +57,9 @@ impl From for AnyQuery { pub(super) fn format_value(v: F) -> String { if v.is_zero_vartime() { "0".into() - } else if v == F::one() { + } else if v == F::ONE { "1".into() - } else if v == -F::one() { + } else if v == -F::ONE { "-1".into() } else { // Format value as hex. @@ -73,33 +71,33 @@ pub(super) fn format_value(v: F) -> String { } } -pub(super) fn load<'a, F: FieldExt, T: ColumnType, Q: Into + Copy>( +pub(super) fn load<'a, F: Field, T: ColumnType, Q: Into + Copy>( n: i32, row: i32, queries: &'a [(Column, Rotation)], cells: &'a [Vec>], ) -> impl Fn(Q) -> Value + 'a { move |query| { - let (column, at) = &queries[query.into().index]; + let (column, at) = &queries[query.into().index.unwrap()]; let resolved_row = (row + at.0) % n; cells[column.index()][resolved_row as usize].into() } } -pub(super) fn load_instance<'a, F: FieldExt, T: ColumnType, Q: Into + Copy>( +pub(super) fn load_instance<'a, F: Field, T: ColumnType, Q: Into + Copy>( n: i32, row: i32, queries: &'a [(Column, Rotation)], cells: &'a [Vec], ) -> impl Fn(Q) -> Value + 'a { move |query| { - let (column, at) = &queries[query.into().index]; + let (column, at) = &queries[query.into().index.unwrap()]; let resolved_row = (row + at.0) % n; Value::Real(cells[column.index()][resolved_row as usize]) } } -fn cell_value<'a, F: FieldExt, Q: Into + Copy>( +fn cell_value<'a, F: Field, Q: Into + Copy>( virtual_cells: &'a [VirtualCell], load: impl Fn(Q) -> Value + 'a, ) -> impl Fn(Q) -> BTreeMap + 'a { @@ -132,7 +130,7 @@ fn cell_value<'a, F: FieldExt, Q: Into + Copy>( } } -pub(super) fn cell_values<'a, F: FieldExt>( +pub(super) fn cell_values<'a, F: Field>( gate: &Gate, poly: &Expression, load_fixed: impl Fn(FixedQuery) -> Value + 'a, diff --git a/halo2_proofs/src/helpers.rs b/halo2_proofs/src/helpers.rs index 8eba57359b..6b2019be9c 100644 --- a/halo2_proofs/src/helpers.rs +++ b/halo2_proofs/src/helpers.rs @@ -1,6 +1,7 @@ use crate::poly::Polynomial; use ff::PrimeField; -use halo2curves::{pairing::Engine, serde::SerdeObject, CurveAffine}; +use halo2curves::{serde::SerdeObject, CurveAffine}; +use pairing::Engine; use std::io; /// This enum specifies how various types are serialized and deserialized. diff --git a/halo2_proofs/src/lib.rs b/halo2_proofs/src/lib.rs index c4ff31da1a..03848c59d0 100644 --- a/halo2_proofs/src/lib.rs +++ b/halo2_proofs/src/lib.rs @@ -24,6 +24,7 @@ // Remove this once we update pasta_curves #![allow(unused_imports)] #![allow(clippy::derive_partial_eq_without_eq)] +#![feature(associated_type_defaults)] pub mod arithmetic; pub mod circuit; diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 9097b7ef78..f3a03f1cd4 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -6,10 +6,9 @@ //! [plonk]: https://eprint.iacr.org/2019/953 use blake2b_simd::Params as Blake2bParams; -use ff::PrimeField; -use group::ff::Field; +use group::ff::{Field, FromUniformBytes, PrimeField}; -use crate::arithmetic::{CurveAffine, FieldExt}; +use crate::arithmetic::CurveAffine; use crate::helpers::{ polynomial_slice_byte_length, read_polynomial_vec, write_polynomial_slice, SerdeCurveAffine, SerdePrimeField, @@ -20,8 +19,6 @@ use crate::poly::{ }; use crate::transcript::{ChallengeScalar, EncodedChallenge, Transcript}; use crate::SerdeFormat; -#[cfg(feature = "profile")] -use ark_std::perf_trace::{AtomicUsize, Ordering}; mod assigned; mod circuit; @@ -30,6 +27,7 @@ mod evaluation; mod keygen; mod lookup; pub mod permutation; +mod shuffle; mod vanishing; mod prover; @@ -45,108 +43,6 @@ pub use verifier::*; use evaluation::Evaluator; use std::env::var; use std::io; -// use std::time::Instant; - -// /// Temp -// #[allow(missing_debug_implementations)] -// pub struct MeasurementInfo { -// /// Temp -// pub measure: bool, -// /// Temp -// pub time: Instant, -// /// Message -// pub message: String, -// /// Indent -// pub indent: usize, -// } - -// /// TEMP -// #[cfg(feature = "profile")] -// pub static NUM_INDENT: AtomicUsize = AtomicUsize::new(0); - -// /// Temp -// pub fn get_time() -> Instant { -// Instant::now() -// } - -// /// Temp -// pub fn get_duration(start: Instant) -> usize { -// let final_time = Instant::now() - start; -// let secs = final_time.as_secs() as usize; -// let millis = final_time.subsec_millis() as usize; -// let micros = (final_time.subsec_micros() % 1000) as usize; -// secs * 1000000 + millis * 1000 + micros -// } - -/// Temp -pub fn log_measurement(indent: Option, msg: String, duration: usize) { - let indent = indent.unwrap_or(0); - println!( - "{}{} ........ {}s", - "*".repeat(indent), - msg, - (duration as f32) / 1000000.0 - ); -} - -/// Temp -#[cfg(feature = "profile")] -pub fn start_measure>(msg: S, always: bool) -> MeasurementInfo { - let measure: u32 = var("MEASURE") - .unwrap_or_else(|_| "0".to_string()) - .parse() - .expect("Cannot parse MEASURE env var as u32"); - - let indent = NUM_INDENT.fetch_add(1, Ordering::Relaxed); - - if always || measure == 1 - /* || msg.starts_with("compressed_cosets")*/ - { - MeasurementInfo { - measure: true, - time: get_time(), - message: msg.as_ref().to_string(), - indent, - } - } else { - MeasurementInfo { - measure: false, - time: get_time(), - message: "".to_string(), - indent, - } - } -} -#[cfg(not(feature = "profile"))] -pub fn start_measure>(_: S, _: bool) {} - -/// Temp -#[cfg(feature = "profile")] -pub fn stop_measure(info: MeasurementInfo) -> usize { - NUM_INDENT.fetch_sub(1, Ordering::Relaxed); - let duration = get_duration(info.time); - if info.measure { - log_measurement(Some(info.indent), info.message, duration); - } - duration -} -#[cfg(not(feature = "profile"))] -pub fn stop_measure(_: ()) {} - -/// Get env variable -pub fn env_value(key: &str, default: usize) -> usize { - match var(key) { - Ok(val) => val.parse().unwrap(), - Err(_) => default, - } -} - -/// Temp -pub fn log_info(msg: String) { - if env_value("INFO", 0) != 0 { - println!("{}", msg); - } -} /// This is a verifying key which allows for the verification of proofs for a /// particular circuit. @@ -165,7 +61,7 @@ pub struct VerifyingKey { impl VerifyingKey where - C::Scalar: SerdePrimeField, + C::Scalar: SerdePrimeField + FromUniformBytes<64>, // the FromUniformBytes<64> should not be necessary: currently serialization always stores a Blake2b hash of verifying key; this should be removed { /// Writes a verifying key to a buffer. /// @@ -209,11 +105,16 @@ where pub fn read>( reader: &mut R, format: SerdeFormat, + #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result { let mut k = [0u8; 4]; reader.read_exact(&mut k)?; let k = u32::from_be_bytes(k); - let (domain, cs, _) = keygen::create_domain::(k); + let (domain, cs, _) = keygen::create_domain::( + k, + #[cfg(feature = "circuit-params")] + params, + ); let mut num_fixed_columns = [0u8; 4]; reader.read_exact(&mut num_fixed_columns).unwrap(); let num_fixed_columns = u32::from_be_bytes(num_fixed_columns); @@ -230,7 +131,7 @@ where .map(|mut selector| { let mut selector_bytes = vec![0u8; (selector.len() + 7) / 8]; reader.read_exact(&mut selector_bytes).unwrap(); - for (bits, byte) in selector.chunks_mut(8).into_iter().zip(selector_bytes) { + for (bits, byte) in selector.chunks_mut(8).zip(selector_bytes) { crate::helpers::unpack(byte, bits); } selector @@ -258,8 +159,14 @@ where pub fn from_bytes>( mut bytes: &[u8], format: SerdeFormat, + #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result { - Self::read::<_, ConcreteCircuit>(&mut bytes, format) + Self::read::<_, ConcreteCircuit>( + &mut bytes, + format, + #[cfg(feature = "circuit-params")] + params, + ) } } @@ -271,7 +178,7 @@ impl VerifyingKey { * (self .selectors .get(0) - .map(|selector| selector.len() / 8 + 1) + .map(|selector| (selector.len() + 7) / 8) .unwrap_or(0)) } @@ -281,7 +188,10 @@ impl VerifyingKey { permutation: permutation::VerifyingKey, cs: ConstraintSystem, selectors: Vec>, - ) -> Self { + ) -> Self + where + C::Scalar: FromUniformBytes<64>, + { // Compute cached values. let cs_degree = cs.degree(); @@ -292,7 +202,7 @@ impl VerifyingKey { cs, cs_degree, // Temporary, this is not pinned. - transcript_repr: C::Scalar::zero(), + transcript_repr: C::Scalar::ZERO, selectors, }; @@ -307,7 +217,7 @@ impl VerifyingKey { hasher.update(s.as_bytes()); // Hash in final Blake2bState - vk.transcript_repr = C::Scalar::from_bytes_wide(hasher.finalize().as_array()); + vk.transcript_repr = C::Scalar::from_uniform_bytes(hasher.finalize().as_array()); vk } @@ -378,7 +288,10 @@ pub struct ProvingKey { ev: Evaluator, } -impl ProvingKey { +impl ProvingKey +where + C::Scalar: FromUniformBytes<64>, +{ /// Get the underlying [`VerifyingKey`]. pub fn get_vk(&self) -> &VerifyingKey { &self.vk @@ -399,7 +312,7 @@ impl ProvingKey { impl ProvingKey where - C::Scalar: SerdePrimeField, + C::Scalar: SerdePrimeField + FromUniformBytes<64>, { /// Writes a proving key to a buffer. /// @@ -437,8 +350,15 @@ where pub fn read>( reader: &mut R, format: SerdeFormat, + #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result { - let vk = VerifyingKey::::read::(reader, format).unwrap(); + let vk = VerifyingKey::::read::( + reader, + format, + #[cfg(feature = "circuit-params")] + params, + ) + .unwrap(); let l0 = Polynomial::read(reader, format); let l_last = Polynomial::read(reader, format); let l_active_row = Polynomial::read(reader, format); @@ -471,8 +391,14 @@ where pub fn from_bytes>( mut bytes: &[u8], format: SerdeFormat, + #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result { - Self::read::<_, ConcreteCircuit>(&mut bytes, format) + Self::read::<_, ConcreteCircuit>( + &mut bytes, + format, + #[cfg(feature = "circuit-params")] + params, + ) } } diff --git a/halo2_proofs/src/plonk/assigned.rs b/halo2_proofs/src/plonk/assigned.rs index 64bf144b94..919ddcdfb5 100644 --- a/halo2_proofs/src/plonk/assigned.rs +++ b/halo2_proofs/src/plonk/assigned.rs @@ -280,7 +280,7 @@ impl Assigned { /// Returns the numerator. pub fn numerator(&self) -> F { match self { - Self::Zero => F::zero(), + Self::Zero => F::ZERO, Self::Trivial(x) => *x, Self::Rational(numerator, _) => *numerator, } @@ -341,7 +341,7 @@ impl Assigned { pub fn invert(&self) -> Self { match self { Self::Zero => Self::Zero, - Self::Trivial(x) => Self::Rational(F::one(), *x), + Self::Trivial(x) => Self::Rational(F::ONE, *x), Self::Rational(numerator, denominator) => Self::Rational(*denominator, *numerator), } } @@ -352,13 +352,13 @@ impl Assigned { /// If the denominator is zero, this returns zero. pub fn evaluate(self) -> F { match self { - Self::Zero => F::zero(), + Self::Zero => F::ZERO, Self::Trivial(x) => x, Self::Rational(numerator, denominator) => { - if denominator == F::one() { + if denominator == F::ONE { numerator } else { - numerator * denominator.invert().unwrap_or(F::zero()) + numerator * denominator.invert().unwrap_or(F::ZERO) } } } @@ -451,7 +451,7 @@ mod proptests { }; use group::ff::Field; - use halo2curves::{pasta::Fp, FieldExt}; + use halo2curves::pasta::Fp; use proptest::{collection::vec, prelude::*, sample::select}; use super::Assigned; @@ -477,7 +477,7 @@ mod proptests { } fn inv0(&self) -> Self { - self.invert().unwrap_or(F::zero()) + self.invert().unwrap_or(F::ZERO) } } diff --git a/halo2_proofs/src/plonk/circuit.rs b/halo2_proofs/src/plonk/circuit.rs index b9c0213802..a972cccb28 100644 --- a/halo2_proofs/src/plonk/circuit.rs +++ b/halo2_proofs/src/plonk/circuit.rs @@ -1,27 +1,31 @@ +use super::{lookup, permutation, shuffle, Assigned, Error}; +use crate::circuit::layouter::SyncDeps; +use crate::dev::metadata; +use crate::{ + circuit::{Layouter, Region, Value}, + poly::Rotation, +}; use core::cmp::max; use core::ops::{Add, Mul}; use ff::Field; +use sealed::SealedPhase; +use std::cmp::Ordering; use std::collections::HashMap; use std::env::var; +use std::fmt::{Debug, Formatter}; use std::{ convert::TryFrom, ops::{Neg, Sub}, }; -use super::{lookup, permutation, Assigned, Error}; -use crate::dev::metadata; -use crate::{ - circuit::{Layouter, Region, Value}, - poly::Rotation, -}; -use sealed::SealedPhase; - mod compress_selectors; /// A column type pub trait ColumnType: 'static + Sized + Copy + std::fmt::Debug + PartialEq + Eq + Into { + /// Return expression from cell + fn query_cell(&self, index: usize, at: Rotation) -> Expression; } /// A column with an index and type @@ -33,7 +37,7 @@ pub struct Column { impl Column { #[cfg(test)] - pub(crate) fn new(index: usize, column_type: C) -> Self { + pub fn new(index: usize, column_type: C) -> Self { Column { index, column_type } } @@ -46,6 +50,31 @@ impl Column { pub fn column_type(&self) -> &C { &self.column_type } + + /// Return expression from column at a relative position + pub fn query_cell(&self, at: Rotation) -> Expression { + self.column_type.query_cell(self.index, at) + } + + /// Return expression from column at the current row + pub fn cur(&self) -> Expression { + self.query_cell(Rotation::cur()) + } + + /// Return expression from column at the next row + pub fn next(&self) -> Expression { + self.query_cell(Rotation::next()) + } + + /// Return expression from column at the previous row + pub fn prev(&self) -> Expression { + self.query_cell(Rotation::prev()) + } + + /// Return expression from column at the specified rotation + pub fn rot(&self, rotation: i32) -> Expression { + self.query_cell(Rotation(rotation)) + } } impl Ord for Column { @@ -245,10 +274,56 @@ impl PartialOrd for Any { } } -impl ColumnType for Advice {} -impl ColumnType for Fixed {} -impl ColumnType for Instance {} -impl ColumnType for Any {} +impl ColumnType for Advice { + fn query_cell(&self, index: usize, at: Rotation) -> Expression { + Expression::Advice(AdviceQuery { + index: None, + column_index: index, + rotation: at, + phase: self.phase, + }) + } +} +impl ColumnType for Fixed { + fn query_cell(&self, index: usize, at: Rotation) -> Expression { + Expression::Fixed(FixedQuery { + index: None, + column_index: index, + rotation: at, + }) + } +} +impl ColumnType for Instance { + fn query_cell(&self, index: usize, at: Rotation) -> Expression { + Expression::Instance(InstanceQuery { + index: None, + column_index: index, + rotation: at, + }) + } +} +impl ColumnType for Any { + fn query_cell(&self, index: usize, at: Rotation) -> Expression { + match self { + Any::Advice(Advice { phase }) => Expression::Advice(AdviceQuery { + index: None, + column_index: index, + rotation: at, + phase: *phase, + }), + Any::Fixed => Expression::Fixed(FixedQuery { + index: None, + column_index: index, + rotation: at, + }), + Any::Instance => Expression::Instance(InstanceQuery { + index: None, + column_index: index, + rotation: at, + }), + } + } +} impl From for Any { fn from(advice: Advice) -> Any { @@ -365,11 +440,10 @@ impl TryFrom> for Column { /// row when required: /// ``` /// use halo2_proofs::{ -/// arithmetic::FieldExt, /// circuit::{Chip, Layouter, Value}, /// plonk::{Advice, Column, Error, Selector}, /// }; -/// # use ff::Field; +/// use ff::Field; /// # use halo2_proofs::plonk::Fixed; /// /// struct Config { @@ -378,12 +452,12 @@ impl TryFrom> for Column { /// s: Selector, /// } /// -/// fn circuit_logic>(chip: C, mut layouter: impl Layouter) -> Result<(), Error> { +/// fn circuit_logic>(chip: C, mut layouter: impl Layouter) -> Result<(), Error> { /// let config = chip.config(); /// # let config: Config = todo!(); /// layouter.assign_region(|| "bar", |mut region| { -/// region.assign_advice(|| "a", config.a, 0, || Value::known(F::one()))?; -/// region.assign_advice(|| "a", config.b, 1, || Value::known(F::one()))?; +/// region.assign_advice(config.a, 0, Value::known(F::ONE)); +/// region.assign_advice(config.b, 1, Value::known(F::ONE)); /// config.s.enable(&mut region, 1) /// })?; /// Ok(()) @@ -403,13 +477,23 @@ impl Selector { pub fn is_simple(&self) -> bool { self.1 } + + /// Returns index of this selector + pub fn index(&self) -> usize { + self.0 + } + + /// Return expression from selector + pub fn expr(&self) -> Expression { + Expression::Selector(*self) + } } /// Query of fixed column at a certain relative location #[derive(Copy, Clone, Debug)] pub struct FixedQuery { /// Query index - pub(crate) index: usize, + pub(crate) index: Option, /// Column index pub(crate) column_index: usize, /// Rotation of this query @@ -432,7 +516,7 @@ impl FixedQuery { #[derive(Copy, Clone, Debug)] pub struct AdviceQuery { /// Query index - pub(crate) index: usize, + pub(crate) index: Option, /// Column index pub(crate) column_index: usize, /// Rotation of this query @@ -462,7 +546,7 @@ impl AdviceQuery { #[derive(Copy, Clone, Debug)] pub struct InstanceQuery { /// Query index - pub(crate) index: usize, + pub(crate) index: Option, /// Column index pub(crate) column_index: usize, /// Rotation of this query @@ -526,6 +610,11 @@ impl Challenge { pub fn phase(&self) -> u8 { self.phase.0 } + + /// Return Expression + pub fn expr(&self) -> Expression { + Expression::Challenge(*self) + } } /// This trait allows a [`Circuit`] to direct some backend to assign a witness @@ -577,8 +666,8 @@ pub trait Assignment { fn query_instance(&self, column: Column, row: usize) -> Result, Error>; /// Assign an advice column value (witness) - fn assign_advice<'r, 'v>( - &'r mut self, + fn assign_advice<'v>( + &mut self, column: Column, row: usize, to: Value>, @@ -646,7 +735,7 @@ pub trait FloorPlanner { /// - Perform any necessary setup or measurement tasks, which may involve one or more /// calls to `Circuit::default().synthesize(config, &mut layouter)`. /// - Call `circuit.synthesize(config, &mut layouter)` exactly once. - fn synthesize, C: Circuit>( + fn synthesize + SyncDeps, C: Circuit>( cs: &mut CS, circuit: &C, config: C::Config, @@ -663,11 +752,33 @@ pub trait Circuit { /// The floor planner used for this circuit. This is an associated type of the /// `Circuit` trait because its behaviour is circuit-critical. type FloorPlanner: FloorPlanner; + /// Optional circuit configuration parameters. Requires the `circuit-params` feature. + #[cfg(feature = "circuit-params")] + type Params: Default = (); /// Returns a copy of this circuit with no witness values (i.e. all witnesses set to /// `None`). For most circuits, this will be equal to `Self::default()`. fn without_witnesses(&self) -> Self; + /// Returns a reference to the parameters that should be used to configure the circuit. + /// Requires the `circuit-params` feature. + #[cfg(feature = "circuit-params")] + fn params(&self) -> Self::Params { + Self::Params::default() + } + + /// The circuit is given an opportunity to describe the exact gate + /// arrangement, column arrangement, etc. Takes a runtime parameter. The default + /// implementation calls `configure` ignoring the `_params` argument in order to easily support + /// circuits that don't use configuration parameters. + #[cfg(feature = "circuit-params")] + fn configure_with_params( + meta: &mut ConstraintSystem, + _params: Self::Params, + ) -> Self::Config { + Self::configure(meta) + } + /// The circuit is given an opportunity to describe the exact gate /// arrangement, column arrangement, etc. fn configure(meta: &mut ConstraintSystem) -> Self::Config; @@ -704,6 +815,59 @@ pub enum Expression { } impl Expression { + /// Make side effects + pub fn query_cells(&mut self, cells: &mut VirtualCells<'_, F>) { + match self { + Expression::Constant(_) => (), + Expression::Selector(selector) => { + if !cells.queried_selectors.contains(selector) { + cells.queried_selectors.push(*selector); + } + } + Expression::Fixed(query) => { + if query.index.is_none() { + let col = Column { + index: query.column_index, + column_type: Fixed, + }; + cells.queried_cells.push((col, query.rotation).into()); + query.index = Some(cells.meta.query_fixed_index(col, query.rotation)); + } + } + Expression::Advice(query) => { + if query.index.is_none() { + let col = Column { + index: query.column_index, + column_type: Advice { phase: query.phase }, + }; + cells.queried_cells.push((col, query.rotation).into()); + query.index = Some(cells.meta.query_advice_index(col, query.rotation)); + } + } + Expression::Instance(query) => { + if query.index.is_none() { + let col = Column { + index: query.column_index, + column_type: Instance, + }; + cells.queried_cells.push((col, query.rotation).into()); + query.index = Some(cells.meta.query_instance_index(col, query.rotation)); + } + } + Expression::Challenge(_) => (), + Expression::Negated(a) => a.query_cells(cells), + Expression::Sum(a, b) => { + a.query_cells(cells); + b.query_cells(cells); + } + Expression::Product(a, b) => { + a.query_cells(cells); + b.query_cells(cells); + } + Expression::Scaled(a, _) => a.query_cells(cells), + }; + } + /// Evaluate the polynomial using the provided closures to perform the /// operations. pub fn evaluate( @@ -1090,43 +1254,43 @@ impl std::fmt::Debug for Expression { Expression::Constant(scalar) => f.debug_tuple("Constant").field(scalar).finish(), Expression::Selector(selector) => f.debug_tuple("Selector").field(selector).finish(), // Skip enum variant and print query struct directly to maintain backwards compatibility. - Expression::Fixed(FixedQuery { - index, - column_index, - rotation, - }) => f - .debug_struct("Fixed") - .field("query_index", index) - .field("column_index", column_index) - .field("rotation", rotation) - .finish(), - Expression::Advice(AdviceQuery { - index, - column_index, - rotation, - phase, - }) => { + Expression::Fixed(query) => { + let mut debug_struct = f.debug_struct("Fixed"); + match query.index { + None => debug_struct.field("query_index", &query.index), + Some(idx) => debug_struct.field("query_index", &idx), + }; + debug_struct + .field("column_index", &query.column_index) + .field("rotation", &query.rotation) + .finish() + } + Expression::Advice(query) => { let mut debug_struct = f.debug_struct("Advice"); + match query.index { + None => debug_struct.field("query_index", &query.index), + Some(idx) => debug_struct.field("query_index", &idx), + }; debug_struct - .field("query_index", index) - .field("column_index", column_index) - .field("rotation", rotation); + .field("column_index", &query.column_index) + .field("rotation", &query.rotation); // Only show advice's phase if it's not in first phase. - if *phase != FirstPhase.to_sealed() { - debug_struct.field("phase", phase); + if query.phase != FirstPhase.to_sealed() { + debug_struct.field("phase", &query.phase); } debug_struct.finish() } - Expression::Instance(InstanceQuery { - index, - column_index, - rotation, - }) => f - .debug_struct("Instance") - .field("query_index", index) - .field("column_index", column_index) - .field("rotation", rotation) - .finish(), + Expression::Instance(query) => { + let mut debug_struct = f.debug_struct("Instance"); + match query.index { + None => debug_struct.field("query_index", &query.index), + Some(idx) => debug_struct.field("query_index", &idx), + }; + debug_struct + .field("column_index", &query.column_index) + .field("rotation", &query.rotation) + .finish() + } Expression::Challenge(challenge) => { f.debug_tuple("Challenge").field(challenge).finish() } @@ -1211,25 +1375,34 @@ impl>> From<(Col, Rotation)> for VirtualCell { /// These are returned by the closures passed to `ConstraintSystem::create_gate`. #[derive(Debug)] pub struct Constraint { - name: &'static str, + name: String, poly: Expression, } impl From> for Constraint { fn from(poly: Expression) -> Self { - Constraint { name: "", poly } + Constraint { + name: "".to_string(), + poly, + } } } -impl From<(&'static str, Expression)> for Constraint { - fn from((name, poly): (&'static str, Expression)) -> Self { - Constraint { name, poly } +impl> From<(S, Expression)> for Constraint { + fn from((name, poly): (S, Expression)) -> Self { + Constraint { + name: name.as_ref().to_string(), + poly, + } } } impl From> for Vec> { fn from(poly: Expression) -> Self { - vec![Constraint { name: "", poly }] + vec![Constraint { + name: "".to_string(), + poly, + }] } } @@ -1311,7 +1484,7 @@ impl>, Iter: IntoIterator> IntoIterato fn into_iter(self) -> Self::IntoIter { std::iter::repeat(self.selector) - .zip(self.constraints.into_iter()) + .zip(self.constraints) .map(apply_selector_to_constraint) } } @@ -1319,8 +1492,8 @@ impl>, Iter: IntoIterator> IntoIterato /// Gate #[derive(Clone, Debug)] pub struct Gate { - name: &'static str, - constraint_names: Vec<&'static str>, + name: String, + constraint_names: Vec, polys: Vec>, /// We track queried selectors separately from other cells, so that we can use them to /// trigger debug checks on gates. @@ -1329,12 +1502,14 @@ pub struct Gate { } impl Gate { - pub(crate) fn name(&self) -> &'static str { - self.name + /// Returns the gate name. + pub fn name(&self) -> &str { + self.name.as_str() } - pub(crate) fn constraint_name(&self, constraint_index: usize) -> &'static str { - self.constraint_names[constraint_index] + /// Returns the name of the constraint at index `constraint_index`. + pub fn constraint_name(&self, constraint_index: usize) -> &str { + self.constraint_names[constraint_index].as_str() } /// Returns constraints of this gate @@ -1387,6 +1562,10 @@ pub struct ConstraintSystem { // input expressions and a sequence of table expressions involved in the lookup. pub(crate) lookups: Vec>, + // Vector of shuffle arguments, where each corresponds to a sequence of + // input expressions and a sequence of shuffle expressions involved in the shuffle. + pub(crate) shuffles: Vec>, + // List of indexes of Fixed columns which are associated to a circuit-general Column tied to their annotation. pub(crate) general_column_annotations: HashMap, @@ -1413,6 +1592,7 @@ pub struct PinnedConstraintSystem<'a, F: Field> { fixed_queries: &'a Vec<(Column, Rotation)>, permutation: &'a permutation::Argument, lookups: &'a Vec>, + shuffles: &'a Vec>, constants: &'a Vec>, minimum_degree: &'a Option, } @@ -1473,6 +1653,7 @@ impl Default for ConstraintSystem { instance_queries: Vec::new(), permutation: permutation::Argument::new(), lookups: Vec::new(), + shuffles: Vec::new(), general_column_annotations: HashMap::new(), constants: vec![], minimum_degree: None, @@ -1499,6 +1680,7 @@ impl ConstraintSystem { instance_queries: &self.instance_queries, permutation: &self.permutation, lookups: &self.lookups, + shuffles: &self.shuffles, constants: &self.constants, minimum_degree: &self.minimum_degree, } @@ -1527,28 +1709,28 @@ impl ConstraintSystem { /// /// `table_map` returns a map between input expressions and the table columns /// they need to match. - pub fn lookup( + pub fn lookup>( &mut self, - name: &'static str, + name: S, table_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression, TableColumn)>, ) -> usize { let mut cells = VirtualCells::new(self); let table_map = table_map(&mut cells) .into_iter() - .map(|(input, table)| { + .map(|(mut input, table)| { if input.contains_simple_selector() { panic!("expression containing simple selector supplied to lookup argument"); } - - let table = cells.query_fixed(table.inner(), Rotation::cur()); - + let mut table = cells.query_fixed(table.inner(), Rotation::cur()); + input.query_cells(&mut cells); + table.query_cells(&mut cells); (input, table) }) .collect(); - let index = self.lookups.len(); - self.lookups.push(lookup::Argument::new(name, table_map)); + self.lookups + .push(lookup::Argument::new(name.as_ref(), table_map)); index } @@ -1557,17 +1739,47 @@ impl ConstraintSystem { /// /// `table_map` returns a map between input expressions and the table expressions /// they need to match. - pub fn lookup_any( + pub fn lookup_any>( &mut self, - name: &'static str, + name: S, table_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression, Expression)>, ) -> usize { let mut cells = VirtualCells::new(self); - let table_map = table_map(&mut cells); - + let table_map = table_map(&mut cells) + .into_iter() + .map(|(mut input, mut table)| { + input.query_cells(&mut cells); + table.query_cells(&mut cells); + (input, table) + }) + .collect(); let index = self.lookups.len(); - self.lookups.push(lookup::Argument::new(name, table_map)); + self.lookups + .push(lookup::Argument::new(name.as_ref(), table_map)); + + index + } + + /// Add a shuffle argument for some input expressions and table expressions. + pub fn shuffle>( + &mut self, + name: S, + shuffle_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression, Expression)>, + ) -> usize { + let mut cells = VirtualCells::new(self); + let shuffle_map = shuffle_map(&mut cells) + .into_iter() + .map(|(mut input, mut table)| { + input.query_cells(&mut cells); + table.query_cells(&mut cells); + (input, table) + }) + .collect(); + let index = self.shuffles.len(); + + self.shuffles + .push(shuffle::Argument::new(name.as_ref(), shuffle_map)); index } @@ -1687,29 +1899,32 @@ impl ConstraintSystem { /// /// A gate is required to contain polynomial constraints. This method will panic if /// `constraints` returns an empty iterator. - pub fn create_gate>, Iter: IntoIterator>( + pub fn create_gate>, Iter: IntoIterator, S: AsRef>( &mut self, - name: &'static str, + name: S, constraints: impl FnOnce(&mut VirtualCells<'_, F>) -> Iter, ) { let mut cells = VirtualCells::new(self); let constraints = constraints(&mut cells); - let queried_selectors = cells.queried_selectors; - let queried_cells = cells.queried_cells; - let (constraint_names, polys): (_, Vec<_>) = constraints .into_iter() .map(|c| c.into()) - .map(|c| (c.name, c.poly)) + .map(|mut c: Constraint| { + c.poly.query_cells(&mut cells); + (c.name, c.poly) + }) .unzip(); + let queried_selectors = cells.queried_selectors; + let queried_cells = cells.queried_cells; + assert!( !polys.is_empty(), "Gates must contain at least one constraint." ); self.gates.push(Gate { - name, + name: name.as_ref().to_string(), constraint_names, polys, queried_selectors, @@ -1724,7 +1939,7 @@ impl ConstraintSystem { /// find which fixed column corresponds with a given `Selector`. /// /// Do not call this twice. Yes, this should be a builder pattern instead. - pub(crate) fn compress_selectors(mut self, selectors: Vec>) -> (Self, Vec>) { + pub fn compress_selectors(mut self, selectors: Vec>) -> (Self, Vec>) { // The number of provided selector assignments must be the number we // counted for this constraint system. assert_eq!(selectors.len(), self.num_selectors); @@ -1748,7 +1963,7 @@ impl ConstraintSystem { let (polys, selector_assignment) = compress_selectors::process( selectors .into_iter() - .zip(degrees.into_iter()) + .zip(degrees) .enumerate() .map( |(i, (activations, max_degree))| compress_selectors::SelectorDescription { @@ -1763,7 +1978,7 @@ impl ConstraintSystem { let column = self.fixed_column(); new_columns.push(column); Expression::Fixed(FixedQuery { - index: self.query_fixed_index(column, Rotation::cur()), + index: Some(self.query_fixed_index(column, Rotation::cur())), column_index: column.index, rotation: Rotation::cur(), }) @@ -1830,6 +2045,15 @@ impl ConstraintSystem { replace_selectors(expr, &selector_replacements, true); } + for expr in self.shuffles.iter_mut().flat_map(|shuffle| { + shuffle + .input_expressions + .iter_mut() + .chain(shuffle.shuffle_expressions.iter_mut()) + }) { + replace_selectors(expr, &selector_replacements, true); + } + (self, polys) } @@ -1991,6 +2215,17 @@ impl ConstraintSystem { .unwrap_or(1), ); + // The lookup argument also serves alongside the gates and must be accounted + // for. + degree = std::cmp::max( + degree, + self.shuffles + .iter() + .map(|l| l.required_degree()) + .max() + .unwrap_or(1), + ); + // Account for each gate to ensure our quotient polynomial is the // correct degree and that our extended domain is the right size. degree = std::cmp::max( @@ -2069,6 +2304,11 @@ impl ConstraintSystem { self.num_instance_columns } + /// Returns number of selectors + pub fn num_selectors(&self) -> usize { + self.num_selectors + } + /// Returns number of challenges pub fn num_challenges(&self) -> usize { self.num_challenges @@ -2092,6 +2332,11 @@ impl ConstraintSystem { &self.gates } + /// Returns general column annotations + pub fn general_column_annotations(&self) -> &HashMap { + &self.general_column_annotations + } + /// Returns advice queries pub fn advice_queries(&self) -> &Vec<(Column, Rotation)> { &self.advice_queries @@ -2117,6 +2362,11 @@ impl ConstraintSystem { &self.lookups } + /// Returns shuffle arguments + pub fn shuffles(&self) -> &Vec> { + &self.shuffles + } + /// Returns constants pub fn constants(&self) -> &Vec> { &self.constants @@ -2151,7 +2401,7 @@ impl<'a, F: Field> VirtualCells<'a, F> { pub fn query_fixed(&mut self, column: Column, at: Rotation) -> Expression { self.queried_cells.push((column, at).into()); Expression::Fixed(FixedQuery { - index: self.meta.query_fixed_index(column, at), + index: Some(self.meta.query_fixed_index(column, at)), column_index: column.index, rotation: at, }) @@ -2161,7 +2411,7 @@ impl<'a, F: Field> VirtualCells<'a, F> { pub fn query_advice(&mut self, column: Column, at: Rotation) -> Expression { self.queried_cells.push((column, at).into()); Expression::Advice(AdviceQuery { - index: self.meta.query_advice_index(column, at), + index: Some(self.meta.query_advice_index(column, at)), column_index: column.index, rotation: at, phase: column.column_type().phase, @@ -2172,7 +2422,7 @@ impl<'a, F: Field> VirtualCells<'a, F> { pub fn query_instance(&mut self, column: Column, at: Rotation) -> Expression { self.queried_cells.push((column, at).into()); Expression::Instance(InstanceQuery { - index: self.meta.query_instance_index(column, at), + index: Some(self.meta.query_instance_index(column, at)), column_index: column.index, rotation: at, }) diff --git a/halo2_proofs/src/plonk/circuit/compress_selectors.rs b/halo2_proofs/src/plonk/circuit/compress_selectors.rs index b6807e1163..053ebe3178 100644 --- a/halo2_proofs/src/plonk/circuit/compress_selectors.rs +++ b/halo2_proofs/src/plonk/circuit/compress_selectors.rs @@ -79,7 +79,7 @@ where let combination_assignment = selector .activations .iter() - .map(|b| if *b { F::one() } else { F::zero() }) + .map(|b| if *b { F::ONE } else { F::ZERO }) .collect::>(); let combination_index = combination_assignments.len(); combination_assignments.push(combination_assignment); @@ -177,12 +177,12 @@ where } // Now, compute the selector and combination assignments. - let mut combination_assignment = vec![F::zero(); n]; + let mut combination_assignment = vec![F::ZERO; n]; let combination_len = combination.len(); let combination_index = combination_assignments.len(); let query = allocate_fixed_column(); - let mut assigned_root = F::one(); + let mut assigned_root = F::ONE; selector_assignments.extend(combination.into_iter().map(|selector| { // Compute the expression for substitution. This produces an expression of the // form @@ -192,12 +192,12 @@ where // `assigned_root`. In particular, rows set to 0 correspond to all selectors // being disabled. let mut expression = query.clone(); - let mut root = F::one(); + let mut root = F::ONE; for _ in 0..combination_len { if root != assigned_root { expression = expression * (Expression::Constant(root) - query.clone()); } - root += F::one(); + root += F::ONE; } // Update the combination assignment @@ -212,7 +212,7 @@ where } } - assigned_root += F::one(); + assigned_root += F::ONE; SelectorAssignment { selector: selector.selector, @@ -281,7 +281,7 @@ mod tests { let (combination_assignments, selector_assignments) = process::(selectors.clone(), max_degree, || { let tmp = Expression::Fixed(FixedQuery { - index: query, + index: Some(query), column_index: query, rotation: Rotation::cur(), }); @@ -320,7 +320,7 @@ mod tests { &|_| panic!("should not occur in returned expressions"), &|query| { // Should be the correct combination in the expression - assert_eq!(selector.combination_index, query.index); + assert_eq!(selector.combination_index, query.index.unwrap()); assignment }, &|_| panic!("should not occur in returned expressions"), diff --git a/halo2_proofs/src/plonk/evaluation.rs b/halo2_proofs/src/plonk/evaluation.rs index 25662387ab..f1bee28b3a 100644 --- a/halo2_proofs/src/plonk/evaluation.rs +++ b/halo2_proofs/src/plonk/evaluation.rs @@ -4,7 +4,7 @@ use crate::plonk::permutation::Argument; use crate::plonk::{lookup, permutation, AdviceQuery, Any, FixedQuery, InstanceQuery, ProvingKey}; use crate::poly::Basis; use crate::{ - arithmetic::{eval_polynomial, parallelize, CurveAffine, FieldExt}, + arithmetic::{eval_polynomial, parallelize, CurveAffine}, poly::{ commitment::Params, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, ProverQuery, Rotation, @@ -13,7 +13,7 @@ use crate::{ }; use group::prime::PrimeCurve; use group::{ - ff::{BatchInvert, Field}, + ff::{BatchInvert, Field, PrimeField, WithSmallOrderMulGroup}, Curve, }; use std::any::TypeId; @@ -26,7 +26,7 @@ use std::{ ops::{Index, Mul, MulAssign}, }; -use super::{start_measure, stop_measure, ConstraintSystem, Expression}; +use super::{shuffle, ConstraintSystem, Expression}; /// Return the index in the polynomial of size `isize` after rotation `rot`. fn get_rotation_idx(idx: usize, rot: i32, rot_scale: i32, isize: i32) -> usize { @@ -186,6 +186,8 @@ pub struct Evaluator { pub custom_gates: GraphEvaluator, /// Lookups evalution pub lookups: Vec>, + /// Shuffle evalution + pub shuffles: Vec>, } /// GraphEvaluator @@ -273,6 +275,39 @@ impl Evaluator { ev.lookups.push(graph); } + // Shuffles + for shuffle in cs.shuffles.iter() { + let evaluate_lc = |expressions: &Vec>, graph: &mut GraphEvaluator| { + let parts = expressions + .iter() + .map(|expr| graph.add_expression(expr)) + .collect(); + graph.add_calculation(Calculation::Horner( + ValueSource::Constant(0), + parts, + ValueSource::Theta(), + )) + }; + + let mut graph_input = GraphEvaluator::default(); + let compressed_input_coset = evaluate_lc(&shuffle.input_expressions, &mut graph_input); + let _ = graph_input.add_calculation(Calculation::Add( + compressed_input_coset, + ValueSource::Gamma(), + )); + + let mut graph_shuffle = GraphEvaluator::default(); + let compressed_shuffle_coset = + evaluate_lc(&shuffle.shuffle_expressions, &mut graph_shuffle); + let _ = graph_shuffle.add_calculation(Calculation::Add( + compressed_shuffle_coset, + ValueSource::Gamma(), + )); + + ev.shuffles.push(graph_input); + ev.shuffles.push(graph_shuffle); + } + ev } @@ -288,6 +323,7 @@ impl Evaluator { gamma: C::ScalarExt, theta: C::ScalarExt, lookups: &[Vec>], + shuffles: &[Vec>], permutations: &[permutation::prover::Committed], ) -> Polynomial { let domain = &pk.vk.domain; @@ -296,15 +332,13 @@ impl Evaluator { let fixed = &pk.fixed_cosets[..]; let extended_omega = domain.get_extended_omega(); let isize = size as i32; - let one = C::ScalarExt::one(); + let one = C::ScalarExt::ONE; let l0 = &pk.l0; let l_last = &pk.l_last; let l_active_row = &pk.l_active_row; let p = &pk.vk.cs.permutation; // Calculate the advice and instance cosets - #[cfg(feature = "profile")] - let start = start_measure("cosets", false); let advice: Vec>> = advice_polys .iter() .map(|advice_polys| { @@ -323,20 +357,19 @@ impl Evaluator { .collect() }) .collect(); - // stop_measure(start); let mut values = domain.empty_extended(); // Core expression evaluations let num_threads = multicore::current_num_threads(); - for (((advice, instance), lookups), permutation) in advice + for ((((advice, instance), lookups), shuffles), permutation) in advice .iter() .zip(instance.iter()) .zip(lookups.iter()) + .zip(shuffles.iter()) .zip(permutations.iter()) { // Custom gates - // let start = start_measure("custom gates", false); multicore::scope(|scope| { let chunk_size = (size + num_threads - 1) / num_threads; for (thread_idx, values) in values.chunks_mut(chunk_size).enumerate() { @@ -364,12 +397,8 @@ impl Evaluator { }); } }); - #[cfg(feature = "profile")] - stop_measure(start); // Permutations - #[cfg(feature = "profile")] - let start = start_measure("permutations", false); let sets = &permutation.sets; if !sets.is_empty() { let blinding_factors = pk.vk.cs.blinding_factors(); @@ -450,12 +479,8 @@ impl Evaluator { } }); } - #[cfg(feature = "profile")] - stop_measure(start); // Lookups - #[cfg(feature = "profile")] - let start = start_measure("lookups", false); for (n, lookup) in lookups.iter().enumerate() { // Polynomials required for this lookup. // Calculated here so these only have to be kept in memory for the short time @@ -483,7 +508,7 @@ impl Evaluator { &gamma, &theta, &y, - &C::ScalarExt::zero(), + &C::ScalarExt::ZERO, idx, rot_scale, isize, @@ -525,8 +550,68 @@ impl Evaluator { } }); } - #[cfg(feature = "profile")] - stop_measure(start); + + // Shuffle constraints + for (n, shuffle) in shuffles.iter().enumerate() { + let product_coset = pk.vk.domain.coeff_to_extended(&shuffle.product_poly); + + // Shuffle constraints + parallelize(&mut values, |values, start| { + let input_evaluator = &self.shuffles[2 * n]; + let shuffle_evaluator = &self.shuffles[2 * n + 1]; + let mut eval_data_input = shuffle_evaluator.instance(); + let mut eval_data_shuffle = shuffle_evaluator.instance(); + for (i, value) in values.iter_mut().enumerate() { + let idx = start + i; + + let input_value = input_evaluator.evaluate( + &mut eval_data_input, + fixed, + advice, + instance, + challenges, + &beta, + &gamma, + &theta, + &y, + &C::ScalarExt::ZERO, + idx, + rot_scale, + isize, + ); + + let shuffle_value = shuffle_evaluator.evaluate( + &mut eval_data_shuffle, + fixed, + advice, + instance, + challenges, + &beta, + &gamma, + &theta, + &y, + &C::ScalarExt::ZERO, + idx, + rot_scale, + isize, + ); + + let r_next = get_rotation_idx(idx, 1, rot_scale, isize); + + // l_0(X) * (1 - z(X)) = 0 + *value = *value * y + ((one - product_coset[idx]) * l0[idx]); + // l_last(X) * (z(X)^2 - z(X)) = 0 + *value = *value * y + + ((product_coset[idx] * product_coset[idx] - product_coset[idx]) + * l_last[idx]); + // (1 - (l_last(X) + l_blind(X))) * (z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma)) = 0 + *value = *value * y + + l_active_row[idx] + * (product_coset[r_next] * shuffle_value + - product_coset[idx] * input_value) + } + }); + } } values } @@ -537,8 +622,8 @@ impl Default for GraphEvaluator { Self { // Fixed positions to allow easy access constants: vec![ - C::ScalarExt::zero(), - C::ScalarExt::one(), + C::ScalarExt::ZERO, + C::ScalarExt::ONE, C::ScalarExt::from(2u64), ], rotations: Vec::new(), @@ -686,9 +771,9 @@ impl GraphEvaluator { } } Expression::Scaled(a, f) => { - if *f == C::ScalarExt::zero() { + if *f == C::ScalarExt::ZERO { ValueSource::Constant(0) - } else if *f == C::ScalarExt::one() { + } else if *f == C::ScalarExt::ONE { self.add_expression(a) } else { let cst = self.add_constant(f); @@ -702,7 +787,7 @@ impl GraphEvaluator { /// Creates a new evaluation structure pub fn instance(&self) -> EvaluationData { EvaluationData { - intermediates: vec![C::ScalarExt::zero(); self.num_intermediates], + intermediates: vec![C::ScalarExt::ZERO; self.num_intermediates], rotations: vec![0usize; self.rotations.len()], } } @@ -750,13 +835,13 @@ impl GraphEvaluator { if let Some(calc) = self.calculations.last() { data.intermediates[calc.target] } else { - C::ScalarExt::zero() + C::ScalarExt::ZERO } } } /// Simple evaluation of an expression -pub fn evaluate( +pub fn evaluate( expression: &Expression, size: usize, rot_scale: i32, @@ -765,7 +850,7 @@ pub fn evaluate( instance: &[Polynomial], challenges: &[F], ) -> Vec { - let mut values = vec![F::zero(); size]; + let mut values = vec![F::ZERO; size]; let isize = size as i32; parallelize(&mut values, |values, start| { for (i, value) in values.iter_mut().enumerate() { diff --git a/halo2_proofs/src/plonk/keygen.rs b/halo2_proofs/src/plonk/keygen.rs index c770366d6f..5ae0007469 100644 --- a/halo2_proofs/src/plonk/keygen.rs +++ b/halo2_proofs/src/plonk/keygen.rs @@ -2,7 +2,7 @@ use std::ops::Range; -use ff::Field; +use ff::{Field, FromUniformBytes}; use group::Curve; use super::{ @@ -16,7 +16,7 @@ use super::{ }; use crate::{ arithmetic::{parallelize, CurveAffine}, - circuit::Value, + circuit::{layouter::SyncDeps, Value}, poly::{ batch_invert_assigned, commitment::{Blind, Params, MSM}, @@ -26,6 +26,7 @@ use crate::{ pub(crate) fn create_domain( k: u32, + #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> ( EvaluationDomain, ConstraintSystem, @@ -36,6 +37,9 @@ where ConcreteCircuit: Circuit, { let mut cs = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, params); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); let degree = cs.degree(); @@ -57,6 +61,8 @@ struct Assembly { _marker: std::marker::PhantomData, } +impl SyncDeps for Assembly {} + impl Assignment for Assembly { fn enter_region(&mut self, _: N) where @@ -93,9 +99,9 @@ impl Assignment for Assembly { Ok(Value::unknown()) } - fn assign_advice<'r, 'v>( + fn assign_advice<'v>( //( - &'r mut self, + &mut self, //_: A, _: Column, _: usize, @@ -192,8 +198,13 @@ where C: CurveAffine, P: Params<'params, C>, ConcreteCircuit: Circuit, + C::Scalar: FromUniformBytes<64>, { - let (domain, cs, config) = create_domain::(params.k()); + let (domain, cs, config) = create_domain::( + params.k(), + #[cfg(feature = "circuit-params")] + circuit.params(), + ); if (params.n() as usize) < cs.minimum_rows() { return Err(Error::not_enough_rows_available(params.k())); @@ -254,6 +265,9 @@ where ConcreteCircuit: Circuit, { let mut cs = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); let cs = cs; @@ -304,7 +318,7 @@ where // Compute l_0(X) // TODO: this can be done more efficiently let mut l0 = vk.domain.empty_lagrange(); - l0[0] = C::Scalar::one(); + l0[0] = C::Scalar::ONE; let l0 = vk.domain.lagrange_to_coeff(l0); let l0 = vk.domain.coeff_to_extended(&l0); @@ -312,7 +326,7 @@ where // and 0 otherwise over the domain. let mut l_blind = vk.domain.empty_lagrange(); for evaluation in l_blind[..].iter_mut().rev().take(cs.blinding_factors()) { - *evaluation = C::Scalar::one(); + *evaluation = C::Scalar::ONE; } let l_blind = vk.domain.lagrange_to_coeff(l_blind); let l_blind = vk.domain.coeff_to_extended(&l_blind); @@ -320,12 +334,12 @@ where // Compute l_last(X) which evaluates to 1 on the first inactive row (just // before the blinding factors) and 0 otherwise over the domain let mut l_last = vk.domain.empty_lagrange(); - l_last[params.n() as usize - cs.blinding_factors() - 1] = C::Scalar::one(); + l_last[params.n() as usize - cs.blinding_factors() - 1] = C::Scalar::ONE; let l_last = vk.domain.lagrange_to_coeff(l_last); let l_last = vk.domain.coeff_to_extended(&l_last); // Compute l_active_row(X) - let one = C::Scalar::one(); + let one = C::Scalar::ONE; let mut l_active_row = vk.domain.empty_extended(); parallelize(&mut l_active_row, |values, start| { for (i, value) in values.iter_mut().enumerate() { diff --git a/halo2_proofs/src/plonk/lookup.rs b/halo2_proofs/src/plonk/lookup.rs index 68cda75d37..a7c4f68af2 100644 --- a/halo2_proofs/src/plonk/lookup.rs +++ b/halo2_proofs/src/plonk/lookup.rs @@ -7,7 +7,7 @@ pub(crate) mod verifier; #[derive(Clone)] pub struct Argument { - pub(crate) name: &'static str, + pub(crate) name: String, pub(crate) input_expressions: Vec>, pub(crate) table_expressions: Vec>, } @@ -25,10 +25,10 @@ impl Argument { /// Constructs a new lookup argument. /// /// `table_map` is a sequence of `(input, table)` tuples. - pub fn new(name: &'static str, table_map: Vec<(Expression, Expression)>) -> Self { + pub fn new>(name: S, table_map: Vec<(Expression, Expression)>) -> Self { let (input_expressions, table_expressions) = table_map.into_iter().unzip(); Argument { - name, + name: name.as_ref().to_string(), input_expressions, table_expressions, } @@ -91,4 +91,9 @@ impl Argument { pub fn table_expressions(&self) -> &Vec> { &self.table_expressions } + + /// Returns name of this argument + pub fn name(&self) -> &str { + &self.name + } } diff --git a/halo2_proofs/src/plonk/lookup/prover.rs b/halo2_proofs/src/plonk/lookup/prover.rs index 43e98e37cc..ce1fa735bc 100644 --- a/halo2_proofs/src/plonk/lookup/prover.rs +++ b/halo2_proofs/src/plonk/lookup/prover.rs @@ -5,7 +5,7 @@ use super::super::{ use super::Argument; use crate::plonk::evaluation::evaluate; use crate::{ - arithmetic::{eval_polynomial, parallelize, CurveAffine, FieldExt}, + arithmetic::{eval_polynomial, parallelize, CurveAffine}, poly::{ commitment::{Blind, Params}, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, ProverQuery, @@ -15,6 +15,7 @@ use crate::{ }; #[cfg(feature = "profile")] use ark_std::{end_timer, start_timer}; +use ff::WithSmallOrderMulGroup; use group::{ ff::{BatchInvert, Field}, Curve, @@ -57,7 +58,7 @@ pub(in crate::plonk) struct Evaluated { constructed: Committed, } -impl Argument { +impl> Argument { /// Given a Lookup with input expressions [A_0, A_1, ..., A_{m-1}] and table expressions /// [S_0, S_1, ..., S_{m-1}], this method /// - constructs A_compressed = \theta^{m-1} A_0 + theta^{m-2} A_1 + ... + \theta A_{m-2} + A_{m-1} @@ -198,7 +199,7 @@ impl Permuted { // s_j(X) is the jth table expression in this lookup, // s'(X) is the compression of the permuted table expressions, // and i is the ith row of the expression. - let mut lookup_product = vec![C::Scalar::zero(); params.n() as usize]; + let mut lookup_product = vec![C::Scalar::ZERO; params.n() as usize]; // Denominator uses the permuted input expression and permuted table expression parallelize(&mut lookup_product, |lookup_product, start| { for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product @@ -241,9 +242,9 @@ impl Permuted { // Compute the evaluations of the lookup product polynomial // over our domain, starting with z[0] = 1 - let z = iter::once(C::Scalar::one()) + let z = iter::once(C::Scalar::ONE) .chain(lookup_product) - .scan(C::Scalar::one(), |state, cur| { + .scan(C::Scalar::ONE, |state, cur| { *state *= &cur; Some(*state) }) @@ -264,7 +265,7 @@ impl Permuted { let u = (params.n() as usize) - (blinding_factors + 1); // l_0(X) * (1 - z(X)) = 0 - assert_eq!(z[0], C::Scalar::one()); + assert_eq!(z[0], C::Scalar::ONE); // z(\omega X) (a'(X) + \beta) (s'(X) + \gamma) // - z(X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma) @@ -291,7 +292,7 @@ impl Permuted { // l_last(X) * (z(X)^2 - z(X)) = 0 // Assertion will fail only when soundness is broken, in which // case this z[u] value will be zero. (bad!) - assert_eq!(z[u], C::Scalar::one()); + assert_eq!(z[u], C::Scalar::ONE); } let product_blind = Blind(C::Scalar::random(rng)); @@ -530,13 +531,7 @@ where /// that has the corresponding value in S'. /// This method returns (A', S') if no errors are encountered. #[allow(dead_code)] -fn permute_expression_pair_seq< - 'params, - C: CurveAffine, - P: Params<'params, C>, - R: RngCore, - const ZK: bool, ->( +fn permute_expression_pair_seq<'params, C: CurveAffine, P: Params<'params, C>, R: RngCore>( pk: &ProvingKey, params: &P, domain: &EvaluationDomain, @@ -561,7 +556,7 @@ fn permute_expression_pair_seq< *acc.entry(*coeff).or_insert(0) += 1; acc }); - let mut permuted_table_coeffs = vec![C::Scalar::zero(); usable_rows]; + let mut permuted_table_coeffs = vec![C::Scalar::ZERO; usable_rows]; let mut repeated_input_rows = permuted_input_expression .iter() diff --git a/halo2_proofs/src/plonk/lookup/verifier.rs b/halo2_proofs/src/plonk/lookup/verifier.rs index 88041c29b3..1dc111f2cc 100644 --- a/halo2_proofs/src/plonk/lookup/verifier.rs +++ b/halo2_proofs/src/plonk/lookup/verifier.rs @@ -5,7 +5,7 @@ use super::super::{ }; use super::Argument; use crate::{ - arithmetic::{CurveAffine, FieldExt}, + arithmetic::CurveAffine, plonk::{Error, VerifyingKey}, poly::{commitment::MSM, Rotation, VerifierQuery}, transcript::{EncodedChallenge, TranscriptRead}, @@ -31,7 +31,7 @@ pub struct Evaluated { permuted_table_eval: C::Scalar, } -impl Argument { +impl Argument { pub(in crate::plonk) fn read_permuted_commitments< C: CurveAffine, E: EncodedChallenge, @@ -104,7 +104,7 @@ impl Evaluated { instance_evals: &[C::Scalar], challenges: &[C::Scalar], ) -> impl Iterator + 'a { - let active_rows = C::Scalar::one() - (l_last + l_blind); + let active_rows = C::Scalar::ONE - (l_last + l_blind); let product_expression = || { // z(\omega X) (a'(X) + \beta) (s'(X) + \gamma) @@ -120,9 +120,9 @@ impl Evaluated { expression.evaluate( &|scalar| scalar, &|_| panic!("virtual selectors are removed during optimization"), - &|query| fixed_evals[query.index], - &|query| advice_evals[query.index], - &|query| instance_evals[query.index], + &|query| fixed_evals[query.index.unwrap()], + &|query| advice_evals[query.index.unwrap()], + &|query| instance_evals[query.index.unwrap()], &|challenge| challenges[challenge.index()], &|a| -a, &|a, b| a + &b, @@ -130,7 +130,7 @@ impl Evaluated { &|a, scalar| a * &scalar, ) }) - .fold(C::Scalar::zero(), |acc, eval| acc * &*theta + &eval) + .fold(C::Scalar::ZERO, |acc, eval| acc * &*theta + &eval) }; let right = self.product_eval * &(compress_expressions(&argument.input_expressions) + &*beta) @@ -142,7 +142,7 @@ impl Evaluated { std::iter::empty() .chain( // l_0(X) * (1 - z'(X)) = 0 - Some(l_0 * &(C::Scalar::one() - &self.product_eval)), + Some(l_0 * &(C::Scalar::ONE - &self.product_eval)), ) .chain( // l_last(X) * (z(X)^2 - z(X)) = 0 diff --git a/halo2_proofs/src/plonk/permutation/keygen.rs b/halo2_proofs/src/plonk/permutation/keygen.rs index a47b178854..e694fc81b7 100644 --- a/halo2_proofs/src/plonk/permutation/keygen.rs +++ b/halo2_proofs/src/plonk/permutation/keygen.rs @@ -1,9 +1,15 @@ -use ff::Field; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; + +use ff::{Field, PrimeField}; use group::Curve; +use rayon::prelude::{ + IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, + IntoParallelRefMutIterator, ParallelIterator, ParallelSliceMut, +}; use super::{Argument, ProvingKey, VerifyingKey}; use crate::{ - arithmetic::{parallelize, CurveAffine, FieldExt}, + arithmetic::{parallelize, CurveAffine}, plonk::{Any, Column, Error}, poly::{ commitment::{Blind, CommitmentScheme, Params}, @@ -11,6 +17,7 @@ use crate::{ }, }; +#[cfg(not(feature = "thread-safe-region"))] /// Struct that accumulates all the necessary data in order to construct the permutation argument. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Assembly { @@ -24,6 +31,7 @@ pub struct Assembly { sizes: Vec>, } +#[cfg(not(feature = "thread-safe-region"))] impl Assembly { pub(crate) fn new(n: usize, p: &Argument) -> Self { // Initialize the copy vector to keep track of copy constraints in all @@ -108,145 +116,340 @@ impl Assembly { domain: &EvaluationDomain, p: &Argument, ) -> VerifyingKey { - // Compute [omega^0, omega^1, ..., omega^{params.n - 1}] - let mut omega_powers = vec![C::Scalar::zero(); params.n() as usize]; - { - let omega = domain.get_omega(); - parallelize(&mut omega_powers, |o, start| { - let mut cur = omega.pow_vartime([start as u64]); - for v in o.iter_mut() { - *v = cur; - cur *= ω - } - }) + build_vk(params, domain, p, |i, j| self.mapping[i][j]) + } + + pub(crate) fn build_pk<'params, C: CurveAffine, P: Params<'params, C>>( + self, + params: &P, + domain: &EvaluationDomain, + p: &Argument, + ) -> ProvingKey { + build_pk(params, domain, p, |i, j| self.mapping[i][j]) + } + + /// Returns columns that participate in the permutation argument. + pub fn columns(&self) -> &[Column] { + &self.columns + } + + /// Returns mappings of the copies. + pub fn mapping( + &self, + ) -> impl Iterator + '_> { + self.mapping.iter().map(|c| c.par_iter().copied()) + } +} + +#[cfg(feature = "thread-safe-region")] +/// Struct that accumulates all the necessary data in order to construct the permutation argument. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Assembly { + /// Columns that participate on the copy permutation argument. + columns: Vec>, + /// Mapping of the actual copies done. + cycles: Vec>, + /// Mapping of the actual copies done. + ordered_cycles: Vec>, + /// Mapping of the actual copies done. + aux: HashMap<(usize, usize), usize>, + /// total length of a column + col_len: usize, + /// number of columns + num_cols: usize, +} + +#[cfg(feature = "thread-safe-region")] +impl Assembly { + pub(crate) fn new(n: usize, p: &Argument) -> Self { + Assembly { + columns: p.columns.clone(), + cycles: Vec::with_capacity(n), + ordered_cycles: Vec::with_capacity(n), + aux: HashMap::new(), + col_len: n, + num_cols: p.columns.len(), } + } - // Compute [omega_powers * \delta^0, omega_powers * \delta^1, ..., omega_powers * \delta^m] - let mut deltaomega = vec![omega_powers; p.columns.len()]; - { - parallelize(&mut deltaomega, |o, start| { - let mut cur = C::Scalar::DELTA.pow_vartime([start as u64]); - for omega_powers in o.iter_mut() { - for v in omega_powers { - *v *= &cur; - } - cur *= &C::Scalar::DELTA; - } - }); + pub(crate) fn copy( + &mut self, + left_column: Column, + left_row: usize, + right_column: Column, + right_row: usize, + ) -> Result<(), Error> { + let left_column = self + .columns + .iter() + .position(|c| c == &left_column) + .ok_or(Error::ColumnNotInPermutation(left_column))?; + let right_column = self + .columns + .iter() + .position(|c| c == &right_column) + .ok_or(Error::ColumnNotInPermutation(right_column))?; + + // Check bounds + if left_row >= self.col_len || right_row >= self.col_len { + return Err(Error::BoundsFailure); } - // Computes the permutation polynomial based on the permutation - // description in the assembly. - let mut permutations = vec![domain.empty_lagrange(); p.columns.len()]; - { - parallelize(&mut permutations, |o, start| { - for (x, permutation_poly) in o.iter_mut().enumerate() { - let i = start + x; - for (j, p) in permutation_poly.iter_mut().enumerate() { - let (permuted_i, permuted_j) = self.mapping[i][j]; - *p = deltaomega[permuted_i][permuted_j]; - } - } - }); + let left_cycle = self.aux.get(&(left_column, left_row)); + let right_cycle = self.aux.get(&(right_column, right_row)); + + // extract cycle elements + let right_cycle_elems = match right_cycle { + Some(i) => { + let entry = self.cycles[*i].clone(); + self.cycles[*i] = vec![]; + entry + } + None => [(right_column, right_row)].into(), + }; + + assert!(right_cycle_elems.contains(&(right_column, right_row))); + + // merge cycles + let cycle_idx = match left_cycle { + Some(i) => { + let entry = &mut self.cycles[*i]; + entry.extend(right_cycle_elems.clone()); + *i + } + // if they were singletons -- create a new cycle entry + None => { + let mut set: Vec<(usize, usize)> = right_cycle_elems.clone(); + set.push((left_column, left_row)); + self.cycles.push(set); + let cycle_idx = self.cycles.len() - 1; + self.aux.insert((left_column, left_row), cycle_idx); + cycle_idx + } + }; + + let index_updates = vec![cycle_idx; right_cycle_elems.len()].into_iter(); + let updates = right_cycle_elems.into_iter().zip(index_updates); + + self.aux.extend(updates); + + Ok(()) + } + + /// Builds the ordered mapping of the cycles. + /// This will only get executed once. + pub fn build_ordered_mapping(&mut self) { + // will only get called once + if self.ordered_cycles.is_empty() && !self.cycles.is_empty() { + self.ordered_cycles = self + .cycles + .par_iter_mut() + .map(|col| { + let mut set = BTreeSet::new(); + set.extend(col.clone()); + // free up memory + *col = vec![]; + set + }) + .collect(); } + } - // Pre-compute commitments for the URS. - let mut commitments = Vec::with_capacity(p.columns.len()); - for permutation in &permutations { - // Compute commitment to permutation polynomial - commitments.push( - params - .commit_lagrange(permutation, Blind::default()) - .to_affine(), - ); + fn mapping_at_idx(&self, col: usize, row: usize) -> (usize, usize) { + assert!( + !self.ordered_cycles.is_empty() || self.cycles.is_empty(), + "cycles have not been ordered" + ); + + if let Some(cycle_idx) = self.aux.get(&(col, row)) { + let cycle = &self.ordered_cycles[*cycle_idx]; + let mut cycle_iter = cycle.range(( + std::ops::Bound::Excluded((col, row)), + std::ops::Bound::Unbounded, + )); + // point to the next node in the cycle + match cycle_iter.next() { + Some((i, j)) => (*i, *j), + // wrap back around to the first element which SHOULD exist + None => *(cycle.iter().next().unwrap()), + } + // is a singleton + } else { + (col, row) } + } - VerifyingKey { commitments } + pub(crate) fn build_vk<'params, C: CurveAffine, P: Params<'params, C>>( + &mut self, + params: &P, + domain: &EvaluationDomain, + p: &Argument, + ) -> VerifyingKey { + self.build_ordered_mapping(); + build_vk(params, domain, p, |i, j| self.mapping_at_idx(i, j)) } pub(crate) fn build_pk<'params, C: CurveAffine, P: Params<'params, C>>( - self, + &mut self, params: &P, domain: &EvaluationDomain, p: &Argument, ) -> ProvingKey { - // Compute [omega^0, omega^1, ..., omega^{params.n - 1}] - let mut omega_powers = vec![C::Scalar::zero(); params.n() as usize]; - { - let omega = domain.get_omega(); - parallelize(&mut omega_powers, |o, start| { - let mut cur = omega.pow_vartime([start as u64]); - for v in o.iter_mut() { - *v = cur; - cur *= ω - } - }) - } + self.build_ordered_mapping(); + build_pk(params, domain, p, |i, j| self.mapping_at_idx(i, j)) + } - // Compute [omega_powers * \delta^0, omega_powers * \delta^1, ..., omega_powers * \delta^m] - let mut deltaomega = vec![omega_powers; p.columns.len()]; - { - parallelize(&mut deltaomega, |o, start| { - let mut cur = C::Scalar::DELTA.pow_vartime([start as u64]); - for omega_powers in o.iter_mut() { - for v in omega_powers { - *v *= &cur; - } - cur *= &C::Scalar::DELTA; - } - }); - } + /// Returns columns that participate in the permutation argument. + pub fn columns(&self) -> &[Column] { + &self.columns + } - // Compute permutation polynomials, convert to coset form. - let mut permutations = vec![domain.empty_lagrange(); p.columns.len()]; - { - parallelize(&mut permutations, |o, start| { - for (x, permutation_poly) in o.iter_mut().enumerate() { - let i = start + x; - for (j, p) in permutation_poly.iter_mut().enumerate() { - let (permuted_i, permuted_j) = self.mapping[i][j]; - *p = deltaomega[permuted_i][permuted_j]; - } - } - }); - } + /// Returns mappings of the copies. + pub fn mapping( + &self, + ) -> impl Iterator + '_> { + (0..self.num_cols).map(move |i| { + (0..self.col_len) + .into_par_iter() + .map(move |j| self.mapping_at_idx(i, j)) + }) + } +} - let mut polys = vec![domain.empty_coeff(); p.columns.len()]; - { - parallelize(&mut polys, |o, start| { - for (x, poly) in o.iter_mut().enumerate() { - let i = start + x; - let permutation_poly = permutations[i].clone(); - *poly = domain.lagrange_to_coeff(permutation_poly); +pub(crate) fn build_pk<'params, C: CurveAffine, P: Params<'params, C>>( + params: &P, + domain: &EvaluationDomain, + p: &Argument, + mapping: impl Fn(usize, usize) -> (usize, usize) + Sync, +) -> ProvingKey { + // Compute [omega^0, omega^1, ..., omega^{params.n - 1}] + let mut omega_powers = vec![C::Scalar::ZERO; params.n() as usize]; + { + let omega = domain.get_omega(); + parallelize(&mut omega_powers, |o, start| { + let mut cur = omega.pow_vartime([start as u64]); + for v in o.iter_mut() { + *v = cur; + cur *= ω + } + }) + } + + // Compute [omega_powers * \delta^0, omega_powers * \delta^1, ..., omega_powers * \delta^m] + let mut deltaomega = vec![omega_powers; p.columns.len()]; + { + parallelize(&mut deltaomega, |o, start| { + let mut cur = C::Scalar::DELTA.pow_vartime([start as u64]); + for omega_powers in o.iter_mut() { + for v in omega_powers { + *v *= &cur; } - }); - } + cur *= &C::Scalar::DELTA; + } + }); + } - let mut cosets = vec![domain.empty_extended(); p.columns.len()]; - { - parallelize(&mut cosets, |o, start| { - for (x, coset) in o.iter_mut().enumerate() { - let i = start + x; - let poly = polys[i].clone(); - *coset = domain.coeff_to_extended(&poly); + // Compute permutation polynomials, convert to coset form. + let mut permutations = vec![domain.empty_lagrange(); p.columns.len()]; + { + parallelize(&mut permutations, |o, start| { + for (x, permutation_poly) in o.iter_mut().enumerate() { + let i = start + x; + for (j, p) in permutation_poly.iter_mut().enumerate() { + let (permuted_i, permuted_j) = mapping(i, j); + *p = deltaomega[permuted_i][permuted_j]; } - }); - } + } + }); + } - ProvingKey { - permutations, - polys, - cosets, - } + let mut polys = vec![domain.empty_coeff(); p.columns.len()]; + { + parallelize(&mut polys, |o, start| { + for (x, poly) in o.iter_mut().enumerate() { + let i = start + x; + let permutation_poly = permutations[i].clone(); + *poly = domain.lagrange_to_coeff(permutation_poly); + } + }); } - /// Returns columns that participate on the permutation argument. - pub fn columns(&self) -> &[Column] { - &self.columns + let mut cosets = vec![domain.empty_extended(); p.columns.len()]; + { + parallelize(&mut cosets, |o, start| { + for (x, coset) in o.iter_mut().enumerate() { + let i = start + x; + let poly = &polys[i]; + *coset = domain.coeff_to_extended(poly); + } + }); } - /// Returns mappings of the copies. - pub fn mapping(&self) -> &[Vec<(usize, usize)>] { - &self.mapping + ProvingKey { + permutations, + polys, + cosets, + } +} + +pub(crate) fn build_vk<'params, C: CurveAffine, P: Params<'params, C>>( + params: &P, + domain: &EvaluationDomain, + p: &Argument, + mapping: impl Fn(usize, usize) -> (usize, usize) + Sync, +) -> VerifyingKey { + // Compute [omega^0, omega^1, ..., omega^{params.n - 1}] + let mut omega_powers = vec![C::Scalar::ZERO; params.n() as usize]; + { + let omega = domain.get_omega(); + parallelize(&mut omega_powers, |o, start| { + let mut cur = omega.pow_vartime([start as u64]); + for v in o.iter_mut() { + *v = cur; + cur *= ω + } + }) + } + + // Compute [omega_powers * \delta^0, omega_powers * \delta^1, ..., omega_powers * \delta^m] + let mut deltaomega = vec![omega_powers; p.columns.len()]; + { + parallelize(&mut deltaomega, |o, start| { + let mut cur = C::Scalar::DELTA.pow_vartime([start as u64]); + for omega_powers in o.iter_mut() { + for v in omega_powers { + *v *= &cur; + } + cur *= &::DELTA; + } + }); + } + + // Computes the permutation polynomial based on the permutation + // description in the assembly. + let mut permutations = vec![domain.empty_lagrange(); p.columns.len()]; + { + parallelize(&mut permutations, |o, start| { + for (x, permutation_poly) in o.iter_mut().enumerate() { + let i = start + x; + for (j, p) in permutation_poly.iter_mut().enumerate() { + let (permuted_i, permuted_j) = mapping(i, j); + *p = deltaomega[permuted_i][permuted_j]; + } + } + }); + } + + // Pre-compute commitments for the URS. + let mut commitments = Vec::with_capacity(p.columns.len()); + for permutation in &permutations { + // Compute commitment to permutation polynomial + commitments.push( + params + .commit_lagrange(permutation, Blind::default()) + .to_affine(), + ); } + + VerifyingKey { commitments } } diff --git a/halo2_proofs/src/plonk/permutation/prover.rs b/halo2_proofs/src/plonk/permutation/prover.rs index ed23a6b4a7..e0ea709575 100644 --- a/halo2_proofs/src/plonk/permutation/prover.rs +++ b/halo2_proofs/src/plonk/permutation/prover.rs @@ -1,3 +1,4 @@ +use ff::PrimeField; use group::{ ff::{BatchInvert, Field}, Curve, @@ -11,7 +12,7 @@ use std::iter::{self, ExactSizeIterator}; use super::super::{circuit::Any, ChallengeBeta, ChallengeGamma, ChallengeX}; use super::{Argument, ProvingKey}; use crate::{ - arithmetic::{eval_polynomial, parallelize, CurveAffine, FieldExt}, + arithmetic::{eval_polynomial, parallelize, CurveAffine}, plonk::{self, Error}, poly::{ self, @@ -76,10 +77,10 @@ impl Argument { let blinding_factors = pk.vk.cs.blinding_factors(); // Each column gets its own delta power. - let mut deltaomega = C::Scalar::one(); + let mut deltaomega = C::Scalar::ONE; // Track the "last" value from the previous column set - let mut last_z = C::Scalar::one(); + let mut last_z = C::Scalar::ONE; let mut sets = vec![]; @@ -96,7 +97,7 @@ impl Argument { // where p_j(X) is the jth column in this permutation, // and i is the ith row of the column. - let mut modified_values = vec![C::Scalar::one(); params.n() as usize]; + let mut modified_values = vec![C::Scalar::ONE; params.n() as usize]; // Iterate over each column of the permutation for (&column, permuted_column_values) in columns.iter().zip(permutations.iter()) { @@ -139,7 +140,7 @@ impl Argument { deltaomega *= ω } }); - deltaomega *= &C::Scalar::DELTA; + deltaomega *= &::DELTA; } // The modified_values vector is a vector of products of fractions diff --git a/halo2_proofs/src/plonk/permutation/verifier.rs b/halo2_proofs/src/plonk/permutation/verifier.rs index 4e6a8c0066..88fed41a18 100644 --- a/halo2_proofs/src/plonk/permutation/verifier.rs +++ b/halo2_proofs/src/plonk/permutation/verifier.rs @@ -1,10 +1,10 @@ -use ff::Field; +use ff::{Field, PrimeField}; use std::iter; use super::super::{circuit::Any, ChallengeBeta, ChallengeGamma, ChallengeX}; use super::{Argument, VerifyingKey}; use crate::{ - arithmetic::{CurveAffine, FieldExt}, + arithmetic::CurveAffine, plonk::{self, Error}, poly::{commitment::MSM, Rotation, VerifierQuery}, transcript::{EncodedChallenge, TranscriptRead}, @@ -119,9 +119,9 @@ impl Evaluated { // Enforce only for the first set. // l_0(X) * (1 - z_0(X)) = 0 .chain( - self.sets.first().map(|first_set| { - l_0 * &(C::Scalar::one() - &first_set.permutation_product_eval) - }), + self.sets + .first() + .map(|first_set| l_0 * &(C::Scalar::ONE - &first_set.permutation_product_eval)), ) // Enforce only for the last set. // l_last(X) * (z_l(X)^2 - z_l(X)) = 0 @@ -178,7 +178,8 @@ impl Evaluated { let mut right = set.permutation_product_eval; let mut current_delta = (*beta * &*x) - * &(C::Scalar::DELTA.pow_vartime([(chunk_index * chunk_len) as u64])); + * &(::DELTA + .pow_vartime([(chunk_index * chunk_len) as u64])); for eval in columns.iter().map(|&column| match column.column_type() { Any::Advice(_) => { advice_evals[vk.cs.get_any_query_index(column, Rotation::cur())] @@ -194,7 +195,7 @@ impl Evaluated { current_delta *= &C::Scalar::DELTA; } - (left - &right) * (C::Scalar::one() - &(l_last + &l_blind)) + (left - &right) * (C::Scalar::ONE - &(l_last + &l_blind)) }), ) } diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index b12743e4d4..b7c9652bde 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -1,6 +1,6 @@ #[cfg(feature = "profile")] use ark_std::{end_timer, start_timer}; -use ff::Field; +use ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use group::Curve; use halo2curves::CurveExt; use rand_core::RngCore; @@ -19,17 +19,12 @@ use super::{ Advice, Any, Assignment, Challenge, Circuit, Column, ConstraintSystem, FirstPhase, Fixed, FloorPlanner, Instance, Selector, }, - lookup, permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, - ChallengeY, Error, Expression, ProvingKey, + lookup, permutation, shuffle, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, + ChallengeX, ChallengeY, Error, Expression, ProvingKey, }; -use crate::arithmetic::MULTIEXP_TOTAL_TIME; -use crate::plonk::{start_measure, stop_measure}; -use crate::poly::batch_invert_assigned_ref; -use crate::poly::commitment::ParamsProver; -use crate::poly::FFT_TOTAL_TIME; -use crate::transcript::Transcript; +use crate::circuit::layouter::SyncDeps; use crate::{ - arithmetic::{eval_polynomial, CurveAffine, FieldExt}, + arithmetic::{eval_polynomial, CurveAffine}, circuit::Value, plonk::Assigned, poly::{ @@ -66,14 +61,8 @@ pub fn create_proof< mut transcript: &'a mut T, ) -> Result<(), Error> where - Scheme::Scalar: Hash, + Scheme::Scalar: Hash + WithSmallOrderMulGroup<3>, { - #[allow(unsafe_code)] - unsafe { - FFT_TOTAL_TIME = 0; - MULTIEXP_TOTAL_TIME = 0; - } - for instance in instances.iter() { if instance.len() != pk.vk.cs.num_instance_columns { return Err(Error::InvalidInstances); @@ -85,6 +74,9 @@ where let domain = &pk.vk.domain; let mut meta = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut meta, circuits[0].params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut meta); // Selector optimizations cannot be applied here; use the ConstraintSystem @@ -160,10 +152,23 @@ where _marker: PhantomData<(P, E)>, } + impl<'params, 'a, 'b, F, Scheme, P, C, E, R, T> SyncDeps + for WitnessCollection<'params, 'a, 'b, Scheme, P, C, E, R, T> + where + F: Field, + Scheme: CommitmentScheme, + P: Prover<'params, Scheme>, + C: CurveAffine, + E: EncodedChallenge, + R: RngCore, + T: TranscriptWrite, + { + } + impl<'params, 'a, 'b, F, Scheme, P, C, E, R, T> Assignment for WitnessCollection<'params, 'a, 'b, Scheme, P, C, E, R, T> where - F: FieldExt, + F: Field, Scheme: CommitmentScheme, P: Prover<'params, Scheme>, C: CurveAffine, @@ -213,9 +218,9 @@ where .ok_or(Error::BoundsFailure) } - fn assign_advice<'r, 'v>( + fn assign_advice<'v>( //( - &'r mut self, + &mut self, //_: A, column: Column, row: usize, @@ -325,12 +330,12 @@ where } } // Commit the advice columns in the current phase - let mut advice_values = batch_invert_assigned_ref::( + let mut advice_values = batch_invert_assigned( self.column_indices .get(phase) .expect("The API only supports 3 phases right now") .iter() - .map(|column_index| &self.advice[*column_index]) + .map(|column_index| &self.advice[*column_index][..]) .collect(), ); // Add blinding factors to advice columns @@ -551,6 +556,40 @@ where #[cfg(feature = "profile")] end_timer!(phase3b_time); + #[cfg(feature = "profile")] + let shuffle_time = start_timer!(|| "Shuffles"); + let shuffles: Vec>> = instance + .iter() + .zip(advice.iter()) + .map(|(instance, advice)| -> Vec<_> { + // Compress expressions for each shuffle + pk.vk + .cs + .shuffles + .iter() + .map(|shuffle| { + shuffle + .commit_product( + pk, + params, + domain, + theta, + gamma, + &advice.advice_polys, + &pk.fixed_values, + &instance.instance_values, + &challenges, + &mut rng, + transcript, + ) + .unwrap() + }) + .collect() + }) + .collect(); + #[cfg(feature = "profile")] + end_timer!(shuffle_time); + #[cfg(feature = "profile")] let vanishing_time = start_timer!(|| "Commit to vanishing argument's random poly"); // Commit to the vanishing argument's random polynomial for blinding h(x_3) @@ -604,6 +643,7 @@ where *gamma, *theta, &lookups, + &shuffles, &permutations, ); #[cfg(feature = "profile")] @@ -619,7 +659,7 @@ where let eval_time = start_timer!(|| "Commit to vanishing argument's h(X) commitments"); let x: ChallengeX<_> = transcript.squeeze_challenge_scalar(); - let xn = x.pow(&[params.n(), 0, 0, 0]); + let xn = x.pow([params.n(), 0, 0, 0]); if P::QUERY_INSTANCE { // Compute and hash instance evals for each circuit instance @@ -701,12 +741,24 @@ where #[cfg(feature = "profile")] end_timer!(eval_time); + // Evaluate the shuffles, if any, at omega^i x. + let shuffles: Vec>> = shuffles + .into_iter() + .map(|shuffles| -> Result, _> { + shuffles + .into_iter() + .map(|p| p.evaluate(pk, x, transcript)) + .collect::, _>>() + }) + .collect::, _>>()?; + let instances = instance .iter() .zip(advice.iter()) .zip(permutations.iter()) .zip(lookups.iter()) - .flat_map(|(((instance, advice), permutation), lookups)| { + .zip(shuffles.iter()) + .flat_map(|((((instance, advice), permutation), lookups), shuffles)| { iter::empty() .chain( P::QUERY_INSTANCE @@ -732,7 +784,8 @@ where }), ) .chain(permutation.open(pk, x)) - .chain(lookups.iter().flat_map(move |p| p.open(pk, x)).into_iter()) + .chain(lookups.iter().flat_map(move |p| p.open(pk, x))) + .chain(shuffles.iter().flat_map(move |p| p.open(pk, x))) }) .chain( pk.vk diff --git a/halo2_proofs/src/plonk/shuffle.rs b/halo2_proofs/src/plonk/shuffle.rs new file mode 100644 index 0000000000..e32353c710 --- /dev/null +++ b/halo2_proofs/src/plonk/shuffle.rs @@ -0,0 +1,67 @@ +use super::circuit::Expression; +use ff::Field; +use std::fmt::{self, Debug}; + +pub(crate) mod prover; +pub(crate) mod verifier; + +#[derive(Clone)] +pub struct Argument { + pub(crate) name: String, + pub(crate) input_expressions: Vec>, + pub(crate) shuffle_expressions: Vec>, +} + +impl Debug for Argument { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Argument") + .field("input_expressions", &self.input_expressions) + .field("shuffle_expressions", &self.shuffle_expressions) + .finish() + } +} + +impl Argument { + /// Constructs a new shuffle argument. + /// + /// `shuffle` is a sequence of `(input, shuffle)` tuples. + pub fn new>(name: S, shuffle: Vec<(Expression, Expression)>) -> Self { + let (input_expressions, shuffle_expressions) = shuffle.into_iter().unzip(); + Argument { + name: name.as_ref().to_string(), + input_expressions, + shuffle_expressions, + } + } + + pub(crate) fn required_degree(&self) -> usize { + assert_eq!(self.input_expressions.len(), self.shuffle_expressions.len()); + + let mut input_degree = 1; + for expr in self.input_expressions.iter() { + input_degree = std::cmp::max(input_degree, expr.degree()); + } + let mut shuffle_degree = 1; + for expr in self.shuffle_expressions.iter() { + shuffle_degree = std::cmp::max(shuffle_degree, expr.degree()); + } + + // (1 - (l_last + l_blind)) (z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma)) + std::cmp::max(2 + shuffle_degree, 2 + input_degree) + } + + /// Returns input of this argument + pub fn input_expressions(&self) -> &Vec> { + &self.input_expressions + } + + /// Returns table of this argument + pub fn shuffle_expressions(&self) -> &Vec> { + &self.shuffle_expressions + } + + /// Returns name of this argument + pub fn name(&self) -> &str { + &self.name + } +} diff --git a/halo2_proofs/src/plonk/shuffle/prover.rs b/halo2_proofs/src/plonk/shuffle/prover.rs new file mode 100644 index 0000000000..ef3a459f22 --- /dev/null +++ b/halo2_proofs/src/plonk/shuffle/prover.rs @@ -0,0 +1,255 @@ +use super::super::{ + circuit::Expression, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error, + ProvingKey, +}; +use super::Argument; +use crate::plonk::evaluation::evaluate; +use crate::{ + arithmetic::{eval_polynomial, parallelize, CurveAffine}, + poly::{ + commitment::{Blind, Params}, + Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, ProverQuery, + Rotation, + }, + transcript::{EncodedChallenge, TranscriptWrite}, +}; +use ff::WithSmallOrderMulGroup; +use group::{ + ff::{BatchInvert, Field}, + Curve, +}; +use rand_core::RngCore; +use std::{any::TypeId, convert::TryInto, num::ParseIntError, ops::Index}; +use std::{ + collections::BTreeMap, + iter, + ops::{Mul, MulAssign}, +}; + +#[derive(Debug)] +struct Compressed { + input_expression: Polynomial, + shuffle_expression: Polynomial, +} + +#[derive(Debug)] +pub(in crate::plonk) struct Committed { + pub(in crate::plonk) product_poly: Polynomial, + product_blind: Blind, +} + +pub(in crate::plonk) struct Evaluated { + constructed: Committed, +} + +impl> Argument { + /// Given a Shuffle with input expressions [A_0, A_1, ..., A_{m-1}] and table expressions + /// [S_0, S_1, ..., S_{m-1}], this method + /// - constructs A_compressed = \theta^{m-1} A_0 + theta^{m-2} A_1 + ... + \theta A_{m-2} + A_{m-1} + /// and S_compressed = \theta^{m-1} S_0 + theta^{m-2} S_1 + ... + \theta S_{m-2} + S_{m-1}, + fn compress<'a, 'params: 'a, C, P: Params<'params, C>>( + &self, + pk: &ProvingKey, + params: &P, + domain: &EvaluationDomain, + theta: ChallengeTheta, + advice_values: &'a [Polynomial], + fixed_values: &'a [Polynomial], + instance_values: &'a [Polynomial], + challenges: &'a [C::Scalar], + ) -> Compressed + where + C: CurveAffine, + C::Curve: Mul + MulAssign, + { + // Closure to get values of expressions and compress them + let compress_expressions = |expressions: &[Expression]| { + let compressed_expression = expressions + .iter() + .map(|expression| { + pk.vk.domain.lagrange_from_vec(evaluate( + expression, + params.n() as usize, + 1, + fixed_values, + advice_values, + instance_values, + challenges, + )) + }) + .fold(domain.empty_lagrange(), |acc, expression| { + acc * *theta + &expression + }); + compressed_expression + }; + + // Get values of input expressions involved in the shuffle and compress them + let input_expression = compress_expressions(&self.input_expressions); + + // Get values of table expressions involved in the shuffle and compress them + let shuffle_expression = compress_expressions(&self.shuffle_expressions); + + Compressed { + input_expression, + shuffle_expression, + } + } + + /// Given a Shuffle with input expressions and table expressions this method + /// constructs the grand product polynomial over the shuffle. + /// The grand product polynomial is used to populate the Product struct. + /// The Product struct is added to the Shuffle and finally returned by the method. + pub(in crate::plonk) fn commit_product< + 'a, + 'params: 'a, + C, + P: Params<'params, C>, + E: EncodedChallenge, + R: RngCore, + T: TranscriptWrite, + >( + &self, + pk: &ProvingKey, + params: &P, + domain: &EvaluationDomain, + theta: ChallengeTheta, + gamma: ChallengeGamma, + advice_values: &'a [Polynomial], + fixed_values: &'a [Polynomial], + instance_values: &'a [Polynomial], + challenges: &'a [C::Scalar], + mut rng: R, + transcript: &mut T, + ) -> Result, Error> + where + C: CurveAffine, + C::Curve: Mul + MulAssign, + { + let compressed = self.compress( + pk, + params, + domain, + theta, + advice_values, + fixed_values, + instance_values, + challenges, + ); + + let blinding_factors = pk.vk.cs.blinding_factors(); + + let mut shuffle_product = vec![C::Scalar::ZERO; params.n() as usize]; + parallelize(&mut shuffle_product, |shuffle_product, start| { + for (shuffle_product, shuffle_value) in shuffle_product + .iter_mut() + .zip(compressed.shuffle_expression[start..].iter()) + { + *shuffle_product = *gamma + shuffle_value; + } + }); + + shuffle_product.iter_mut().batch_invert(); + + parallelize(&mut shuffle_product, |product, start| { + for (i, product) in product.iter_mut().enumerate() { + let i = i + start; + *product *= &(*gamma + compressed.input_expression[i]); + } + }); + + // Compute the evaluations of the shuffle product polynomial + // over our domain, starting with z[0] = 1 + let z = iter::once(C::Scalar::ONE) + .chain(shuffle_product) + .scan(C::Scalar::ONE, |state, cur| { + *state *= &cur; + Some(*state) + }) + // Take all rows including the "last" row which should + // be a boolean (and ideally 1, else soundness is broken) + .take(params.n() as usize - blinding_factors) + // Chain random blinding factors. + .chain((0..blinding_factors).map(|_| C::Scalar::random(&mut rng))) + .collect::>(); + assert_eq!(z.len(), params.n() as usize); + let z = pk.vk.domain.lagrange_from_vec(z); + + #[cfg(feature = "sanity-checks")] + { + // While in Lagrange basis, check that product is correctly constructed + let u = (params.n() as usize) - (blinding_factors + 1); + assert_eq!(z[0], C::Scalar::ONE); + for i in 0..u { + let mut left = z[i + 1]; + let input_value = &compressed.input_expression[i]; + let shuffle_value = &compressed.shuffle_expression[i]; + left *= &(*gamma + shuffle_value); + let mut right = z[i]; + right *= &(*gamma + input_value); + assert_eq!(left, right); + } + assert_eq!(z[u], C::Scalar::ONE); + } + + let product_blind = Blind(C::Scalar::random(rng)); + let product_commitment = params.commit_lagrange(&z, product_blind).to_affine(); + let z = pk.vk.domain.lagrange_to_coeff(z); + + // Hash product commitment + transcript.write_point(product_commitment)?; + + Ok(Committed:: { + product_poly: z, + product_blind, + }) + } +} + +impl Committed { + pub(in crate::plonk) fn evaluate, T: TranscriptWrite>( + self, + pk: &ProvingKey, + x: ChallengeX, + transcript: &mut T, + ) -> Result, Error> { + let domain = &pk.vk.domain; + let x_next = domain.rotate_omega(*x, Rotation::next()); + + let product_eval = eval_polynomial(&self.product_poly, *x); + let product_next_eval = eval_polynomial(&self.product_poly, x_next); + + // Hash each advice evaluation + for eval in iter::empty() + .chain(Some(product_eval)) + .chain(Some(product_next_eval)) + { + transcript.write_scalar(eval)?; + } + + Ok(Evaluated { constructed: self }) + } +} + +impl Evaluated { + pub(in crate::plonk) fn open<'a>( + &'a self, + pk: &'a ProvingKey, + x: ChallengeX, + ) -> impl Iterator> + Clone { + let x_next = pk.vk.domain.rotate_omega(*x, Rotation::next()); + + iter::empty() + // Open shuffle product commitments at x + .chain(Some(ProverQuery { + point: *x, + poly: &self.constructed.product_poly, + blind: self.constructed.product_blind, + })) + // Open shuffle product commitments at x_next + .chain(Some(ProverQuery { + point: x_next, + poly: &self.constructed.product_poly, + blind: self.constructed.product_blind, + })) + } +} diff --git a/halo2_proofs/src/plonk/shuffle/verifier.rs b/halo2_proofs/src/plonk/shuffle/verifier.rs new file mode 100644 index 0000000000..b02febb694 --- /dev/null +++ b/halo2_proofs/src/plonk/shuffle/verifier.rs @@ -0,0 +1,137 @@ +use std::iter; + +use super::super::{circuit::Expression, ChallengeGamma, ChallengeTheta, ChallengeX}; +use super::Argument; +use crate::{ + arithmetic::CurveAffine, + plonk::{Error, VerifyingKey}, + poly::{commitment::MSM, Rotation, VerifierQuery}, + transcript::{EncodedChallenge, TranscriptRead}, +}; +use ff::Field; + +pub struct Committed { + product_commitment: C, +} + +pub struct Evaluated { + committed: Committed, + product_eval: C::Scalar, + product_next_eval: C::Scalar, +} + +impl Argument { + pub(in crate::plonk) fn read_product_commitment< + C: CurveAffine, + E: EncodedChallenge, + T: TranscriptRead, + >( + &self, + transcript: &mut T, + ) -> Result, Error> { + let product_commitment = transcript.read_point()?; + + Ok(Committed { product_commitment }) + } +} + +impl Committed { + pub(crate) fn evaluate, T: TranscriptRead>( + self, + transcript: &mut T, + ) -> Result, Error> { + let product_eval = transcript.read_scalar()?; + let product_next_eval = transcript.read_scalar()?; + + Ok(Evaluated { + committed: self, + product_eval, + product_next_eval, + }) + } +} + +impl Evaluated { + pub(in crate::plonk) fn expressions<'a>( + &'a self, + l_0: C::Scalar, + l_last: C::Scalar, + l_blind: C::Scalar, + argument: &'a Argument, + theta: ChallengeTheta, + gamma: ChallengeGamma, + advice_evals: &[C::Scalar], + fixed_evals: &[C::Scalar], + instance_evals: &[C::Scalar], + challenges: &[C::Scalar], + ) -> impl Iterator + 'a { + let active_rows = C::Scalar::ONE - (l_last + l_blind); + + let product_expression = || { + // z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma) + let compress_expressions = |expressions: &[Expression]| { + expressions + .iter() + .map(|expression| { + expression.evaluate( + &|scalar| scalar, + &|_| panic!("virtual selectors are removed during optimization"), + &|query| fixed_evals[query.index.unwrap()], + &|query| advice_evals[query.index.unwrap()], + &|query| instance_evals[query.index.unwrap()], + &|challenge| challenges[challenge.index()], + &|a| -a, + &|a, b| a + &b, + &|a, b| a * &b, + &|a, scalar| a * &scalar, + ) + }) + .fold(C::Scalar::ZERO, |acc, eval| acc * &*theta + &eval) + }; + // z(\omega X) (s(X) + \gamma) + let left = self.product_next_eval + * &(compress_expressions(&argument.shuffle_expressions) + &*gamma); + // z(X) (a(X) + \gamma) + let right = + self.product_eval * &(compress_expressions(&argument.input_expressions) + &*gamma); + + (left - &right) * &active_rows + }; + + std::iter::empty() + .chain( + // l_0(X) * (1 - z'(X)) = 0 + Some(l_0 * &(C::Scalar::ONE - &self.product_eval)), + ) + .chain( + // l_last(X) * (z(X)^2 - z(X)) = 0 + Some(l_last * &(self.product_eval.square() - &self.product_eval)), + ) + .chain( + // (1 - (l_last(X) + l_blind(X))) * ( z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma)) + Some(product_expression()), + ) + } + + pub(in crate::plonk) fn queries<'r, M: MSM + 'r>( + &'r self, + vk: &'r VerifyingKey, + x: ChallengeX, + ) -> impl Iterator> + Clone { + let x_next = vk.domain.rotate_omega(*x, Rotation::next()); + + iter::empty() + // Open shuffle product commitment at x + .chain(Some(VerifierQuery::new_commitment( + &self.committed.product_commitment, + *x, + self.product_eval, + ))) + // Open shuffle product commitment at \omega x + .chain(Some(VerifierQuery::new_commitment( + &self.committed.product_commitment, + x_next, + self.product_next_eval, + ))) + } +} diff --git a/halo2_proofs/src/plonk/vanishing/prover.rs b/halo2_proofs/src/plonk/vanishing/prover.rs index 05c30d78e4..f003d7cb4d 100644 --- a/halo2_proofs/src/plonk/vanishing/prover.rs +++ b/halo2_proofs/src/plonk/vanishing/prover.rs @@ -7,7 +7,7 @@ use rayon::{current_num_threads, prelude::*}; use super::Argument; use crate::{ - arithmetic::{eval_polynomial, parallelize, CurveAffine, FieldExt}, + arithmetic::{eval_polynomial, parallelize, CurveAffine}, plonk::{ChallengeX, ChallengeY, Error}, poly::{ self, @@ -142,9 +142,7 @@ impl Constructed { .h_blinds .iter() .rev() - .fold(Blind(C::Scalar::zero()), |acc, eval| { - acc * Blind(xn) + *eval - }); + .fold(Blind(C::Scalar::ZERO), |acc, eval| acc * Blind(xn) + *eval); let random_eval = eval_polynomial(&self.committed.random_poly, *x); transcript.write_scalar(random_eval)?; diff --git a/halo2_proofs/src/plonk/vanishing/verifier.rs b/halo2_proofs/src/plonk/vanishing/verifier.rs index 3570dee6c1..0881dfb2c0 100644 --- a/halo2_proofs/src/plonk/vanishing/verifier.rs +++ b/halo2_proofs/src/plonk/vanishing/verifier.rs @@ -94,8 +94,8 @@ impl PartiallyEvaluated { y: ChallengeY, xn: C::Scalar, ) -> Evaluated { - let expected_h_eval = expressions.fold(C::Scalar::zero(), |h_eval, v| h_eval * &*y + &v); - let expected_h_eval = expected_h_eval * ((xn - C::Scalar::one()).invert().unwrap()); + let expected_h_eval = expressions.fold(C::Scalar::ZERO, |h_eval, v| h_eval * &*y + &v); + let expected_h_eval = expected_h_eval * ((xn - C::Scalar::ONE).invert().unwrap()); let h_commitment = self.h_commitments @@ -104,7 +104,7 @@ impl PartiallyEvaluated { .fold(params.empty_msm(), |mut acc, commitment| { acc.scale(xn); let commitment: C::CurveExt = (*commitment).into(); - acc.append_term(C::Scalar::one(), commitment); + acc.append_term(C::Scalar::ONE, commitment); acc }); diff --git a/halo2_proofs/src/plonk/verifier.rs b/halo2_proofs/src/plonk/verifier.rs index cded3f4997..1e431be41b 100644 --- a/halo2_proofs/src/plonk/verifier.rs +++ b/halo2_proofs/src/plonk/verifier.rs @@ -1,4 +1,4 @@ -use ff::Field; +use ff::{Field, FromUniformBytes, WithSmallOrderMulGroup}; use group::Curve; use rand_core::RngCore; use std::iter; @@ -7,7 +7,7 @@ use super::{ vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, VerifyingKey, }; -use crate::arithmetic::{compute_inner_product, CurveAffine, FieldExt}; +use crate::arithmetic::{compute_inner_product, CurveAffine}; use crate::poly::commitment::{CommitmentScheme, Verifier}; use crate::poly::VerificationStrategy; use crate::poly::{ @@ -37,7 +37,10 @@ pub fn verify_proof< strategy: Strategy, instances: &[&[&[Scheme::Scalar]]], transcript: &mut T, -) -> Result { +) -> Result +where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, +{ // Check that instances matches the expected number of instance columns for instances in instances.iter() { if instances.len() != vk.cs.num_instance_columns { @@ -56,7 +59,7 @@ pub fn verify_proof< return Err(Error::InstanceTooLarge); } let mut poly = instance.to_vec(); - poly.resize(params.n() as usize, Scheme::Scalar::zero()); + poly.resize(params.n() as usize, Scheme::Scalar::ZERO); let poly = vk.domain.lagrange_from_vec(poly); Ok(params.commit_lagrange(&poly, Blind::default()).to_affine()) @@ -94,7 +97,7 @@ pub fn verify_proof< let (advice_commitments, challenges) = { let mut advice_commitments = vec![vec![Scheme::Curve::default(); vk.cs.num_advice_columns]; num_proofs]; - let mut challenges = vec![Scheme::Scalar::zero(); vk.cs.num_challenges]; + let mut challenges = vec![Scheme::Scalar::ZERO; vk.cs.num_challenges]; for current_phase in vk.cs.phases() { for advice_commitments in advice_commitments.iter_mut() { @@ -157,6 +160,17 @@ pub fn verify_proof< }) .collect::, _>>()?; + let shuffles_committed = (0..num_proofs) + .map(|_| -> Result, _> { + // Hash each shuffle product commitment + vk.cs + .shuffles + .iter() + .map(|argument| argument.read_product_commitment(transcript)) + .collect::, _>>() + }) + .collect::, _>>()?; + let vanishing = vanishing::Argument::read_commitments_before_y(transcript)?; // Sample y challenge, which keeps the gates linearly independent. @@ -174,7 +188,7 @@ pub fn verify_proof< }) .collect::, _>>()? } else { - let xn = x.pow(&[params.n(), 0, 0, 0]); + let xn = x.pow([params.n(), 0, 0, 0]); let (min_rotation, max_rotation) = vk.cs .instance_queries @@ -239,11 +253,21 @@ pub fn verify_proof< }) .collect::, _>>()?; + let shuffles_evaluated = shuffles_committed + .into_iter() + .map(|shuffles| -> Result, _> { + shuffles + .into_iter() + .map(|shuffle| shuffle.evaluate(transcript)) + .collect::, _>>() + }) + .collect::, _>>()?; + // This check ensures the circuit is satisfied so long as the polynomial // commitments open to the correct values. let vanishing = { // x^n - let xn = x.pow(&[params.n(), 0, 0, 0]); + let xn = x.pow([params.n(), 0, 0, 0]); let blinding_factors = vk.cs.blinding_factors(); let l_evals = vk @@ -253,7 +277,7 @@ pub fn verify_proof< let l_last = l_evals[0]; let l_blind: Scheme::Scalar = l_evals[1..(1 + blinding_factors)] .iter() - .fold(Scheme::Scalar::zero(), |acc, eval| acc + eval); + .fold(Scheme::Scalar::ZERO, |acc, eval| acc + eval); let l_0 = l_evals[1 + blinding_factors]; // Compute the expected value of h(x) @@ -262,46 +286,47 @@ pub fn verify_proof< .zip(instance_evals.iter()) .zip(permutations_evaluated.iter()) .zip(lookups_evaluated.iter()) - .flat_map(|(((advice_evals, instance_evals), permutation), lookups)| { - let challenges = &challenges; - let fixed_evals = &fixed_evals; - std::iter::empty() - // Evaluate the circuit using the custom gates provided - .chain(vk.cs.gates.iter().flat_map(move |gate| { - gate.polynomials().iter().map(move |poly| { - poly.evaluate( - &|scalar| scalar, - &|_| panic!("virtual selectors are removed during optimization"), - &|query| fixed_evals[query.index], - &|query| advice_evals[query.index], - &|query| instance_evals[query.index], - &|challenge| challenges[challenge.index()], - &|a| -a, - &|a, b| a + &b, - &|a, b| a * &b, - &|a, scalar| a * &scalar, - ) - }) - })) - .chain(permutation.expressions( - vk, - &vk.cs.permutation, - &permutations_common, - advice_evals, - fixed_evals, - instance_evals, - l_0, - l_last, - l_blind, - beta, - gamma, - x, - )) - .chain( - lookups - .iter() - .zip(vk.cs.lookups.iter()) - .flat_map(move |(p, argument)| { + .zip(shuffles_evaluated.iter()) + .flat_map( + |((((advice_evals, instance_evals), permutation), lookups), shuffles)| { + let challenges = &challenges; + let fixed_evals = &fixed_evals; + std::iter::empty() + // Evaluate the circuit using the custom gates provided + .chain(vk.cs.gates.iter().flat_map(move |gate| { + gate.polynomials().iter().map(move |poly| { + poly.evaluate( + &|scalar| scalar, + &|_| { + panic!("virtual selectors are removed during optimization") + }, + &|query| fixed_evals[query.index.unwrap()], + &|query| advice_evals[query.index.unwrap()], + &|query| instance_evals[query.index.unwrap()], + &|challenge| challenges[challenge.index()], + &|a| -a, + &|a, b| a + &b, + &|a, b| a * &b, + &|a, scalar| a * &scalar, + ) + }) + })) + .chain(permutation.expressions( + vk, + &vk.cs.permutation, + &permutations_common, + advice_evals, + fixed_evals, + instance_evals, + l_0, + l_last, + l_blind, + beta, + gamma, + x, + )) + .chain(lookups.iter().zip(vk.cs.lookups.iter()).flat_map( + move |(p, argument)| { p.expressions( l_0, l_last, @@ -315,10 +340,26 @@ pub fn verify_proof< instance_evals, challenges, ) - }) - .into_iter(), - ) - }); + }, + )) + .chain(shuffles.iter().zip(vk.cs.shuffles.iter()).flat_map( + move |(p, argument)| { + p.expressions( + l_0, + l_last, + l_blind, + argument, + theta, + gamma, + advice_evals, + fixed_evals, + instance_evals, + challenges, + ) + }, + )) + }, + ); vanishing.verify(params, expressions, y, xn) }; @@ -330,13 +371,20 @@ pub fn verify_proof< .zip(advice_evals.iter()) .zip(permutations_evaluated.iter()) .zip(lookups_evaluated.iter()) + .zip(shuffles_evaluated.iter()) .flat_map( |( ( - (((instance_commitments, instance_evals), advice_commitments), advice_evals), - permutation, + ( + ( + ((instance_commitments, instance_evals), advice_commitments), + advice_evals, + ), + permutation, + ), + lookups, ), - lookups, + shuffles, )| { iter::empty() .chain( @@ -363,12 +411,8 @@ pub fn verify_proof< }, )) .chain(permutation.queries(vk, x)) - .chain( - lookups - .iter() - .flat_map(move |p| p.queries(vk, x)) - .into_iter(), - ) + .chain(lookups.iter().flat_map(move |p| p.queries(vk, x))) + .chain(shuffles.iter().flat_map(move |p| p.queries(vk, x))) }, ) .chain( diff --git a/halo2_proofs/src/plonk/verifier/batch.rs b/halo2_proofs/src/plonk/verifier/batch.rs index f07ba4141f..04e08be4af 100644 --- a/halo2_proofs/src/plonk/verifier/batch.rs +++ b/halo2_proofs/src/plonk/verifier/batch.rs @@ -1,5 +1,6 @@ use std::{io, marker::PhantomData}; +use ff::FromUniformBytes; use group::ff::Field; use halo2curves::CurveAffine; use rand_core::{OsRng, RngCore}; @@ -67,7 +68,10 @@ pub struct BatchVerifier { items: Vec>, } -impl BatchVerifier { +impl BatchVerifier +where + C::Scalar: FromUniformBytes<64>, +{ /// Constructs a new batch verifier. pub fn new() -> Self { Self { items: vec![] } diff --git a/halo2_proofs/src/poly.rs b/halo2_proofs/src/poly.rs index d7fd0fb3e8..8124a43ede 100644 --- a/halo2_proofs/src/poly.rs +++ b/halo2_proofs/src/poly.rs @@ -2,6 +2,11 @@ //! various forms, including computing commitments to them and provably opening //! the committed polynomials at arbitrary points. +use std::fmt::Debug; +use std::io; +use std::marker::PhantomData; +use std::ops::{Add, Deref, DerefMut, Index, IndexMut, Mul, Range, RangeFrom, RangeFull, Sub}; + use crate::arithmetic::parallelize; use crate::helpers::SerdePrimeField; use crate::plonk::Assigned; @@ -9,11 +14,7 @@ use crate::SerdeFormat; use ff::PrimeField; use group::ff::{BatchInvert, Field}; -use halo2curves::FieldExt; -use std::fmt::Debug; -use std::io; -use std::marker::PhantomData; -use std::ops::{Add, Deref, DerefMut, Index, IndexMut, Mul, Range, RangeFrom, RangeFull, Sub}; +use rayon::prelude::*; /// Generic commitment scheme structures pub mod commitment; @@ -185,17 +186,20 @@ impl Polynomial { } /// Invert each polynomial in place for memory efficiency -pub(crate) fn batch_invert_assigned_ref( - assigned: Vec<&Polynomial, LagrangeCoeff>>, -) -> Vec> { +pub(crate) fn batch_invert_assigned( + assigned: Vec, +) -> Vec> +where + PA: Deref]> + Sync, +{ if assigned.is_empty() { return vec![]; } - let n = assigned[0].num_coeffs(); + let n = assigned[0].as_ref().len(); // 1d vector better for memory allocation let mut assigned_denominators: Vec<_> = assigned .iter() - .flat_map(|f| f.iter().map(|value| value.denominator())) + .flat_map(|f| f.as_ref().iter().map(|value| value.denominator())) .collect(); assigned_denominators @@ -206,56 +210,25 @@ pub(crate) fn batch_invert_assigned_ref( .batch_invert(); assigned - .iter() - .zip(assigned_denominators.chunks(n)) + .par_iter() + .zip(assigned_denominators.par_chunks(n)) .map(|(poly, inv_denoms)| { - debug_assert_eq!(inv_denoms.len(), poly.values.len()); + debug_assert_eq!(inv_denoms.len(), poly.as_ref().len()); Polynomial { values: poly - .values + .as_ref() .iter() .zip(inv_denoms.iter()) - .map(|(a, inv_den)| a.numerator() * inv_den.unwrap_or(F::one())) + .map(|(a, inv_den)| a.numerator() * inv_den.unwrap_or(F::ONE)) .collect(), - _marker: poly._marker, + _marker: PhantomData, } }) .collect() } -pub(crate) fn batch_invert_assigned( - assigned: Vec, LagrangeCoeff>>, -) -> Vec> { - let mut assigned_denominators: Vec<_> = assigned - .iter() - .map(|f| { - f.iter() - .map(|value| value.denominator()) - .collect::>() - }) - .collect(); - - assigned_denominators - .iter_mut() - .flat_map(|f| { - f.iter_mut() - // If the denominator is trivial, we can skip it, reducing the - // size of the batch inversion. - .filter_map(|d| d.as_mut()) - }) - .batch_invert(); - - assigned - .iter() - .zip(assigned_denominators.into_iter()) - .map(|(poly, inv_denoms)| { - poly.invert(inv_denoms.into_iter().map(|d| d.unwrap_or_else(F::one))) - }) - .collect() -} - impl Polynomial, LagrangeCoeff> { - pub(crate) fn invert( + pub fn invert( &self, inv_denoms: impl Iterator + ExactSizeIterator, ) -> Polynomial { @@ -264,7 +237,7 @@ impl Polynomial, LagrangeCoeff> { values: self .values .iter() - .zip(inv_denoms.into_iter()) + .zip(inv_denoms) .map(|(a, inv_den)| a.numerator() * inv_den) .collect(), _marker: self._marker, @@ -320,13 +293,13 @@ impl Mul for Polynomial { type Output = Polynomial; fn mul(mut self, rhs: F) -> Polynomial { - if rhs == F::zero() { + if rhs == F::ZERO { return Polynomial { - values: vec![F::zero(); self.len()], + values: vec![F::ZERO; self.len()], _marker: PhantomData, }; } - if rhs == F::one() { + if rhs == F::ONE { return self; } diff --git a/halo2_proofs/src/poly/commitment.rs b/halo2_proofs/src/poly/commitment.rs index e82515cfff..cacf764512 100644 --- a/halo2_proofs/src/poly/commitment.rs +++ b/halo2_proofs/src/poly/commitment.rs @@ -6,8 +6,8 @@ use super::{ use crate::poly::Error; use crate::transcript::{EncodedChallenge, TranscriptRead, TranscriptWrite}; use ff::Field; -use group::Curve; -use halo2curves::{CurveAffine, CurveExt, FieldExt}; +use group::{prime::PrimeCurveAffine, Curve, Group}; +use halo2curves::{CurveAffine, CurveExt}; use rand_core::RngCore; use std::{ fmt::Debug, @@ -18,7 +18,7 @@ use std::{ /// Defines components of a commitment scheme. pub trait CommitmentScheme { /// Application field of this commitment scheme - type Scalar: FieldExt + halo2curves::Group; + type Scalar: Field; /// Elliptic curve used to commit the application and witnesses type Curve: CurveAffine; @@ -101,7 +101,7 @@ pub trait ParamsVerifier<'params, C: CurveAffine>: Params<'params, C> {} /// Multi scalar multiplication engine pub trait MSM: Clone + Debug + Send + Sync { /// Add arbitrary term (the scalar and the point) - fn append_term(&mut self, scalar: C::Scalar, point: C::CurveExt); + fn append_term(&mut self, scalar: C::Scalar, point: C::Curve); /// Add another multiexp into this one fn add_msm(&mut self, other: &Self) @@ -115,10 +115,10 @@ pub trait MSM: Clone + Debug + Send + Sync { fn check(&self) -> bool; /// Perform multiexp and return the result - fn eval(&self) -> C::CurveExt; + fn eval(&self) -> C::Curve; /// Return base points - fn bases(&self) -> Vec; + fn bases(&self) -> Vec; /// Scalars fn scalars(&self) -> Vec; @@ -192,20 +192,20 @@ pub trait Verifier<'params, Scheme: CommitmentScheme> { #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub struct Blind(pub F); -impl Default for Blind { +impl Default for Blind { fn default() -> Self { - Blind(F::one()) + Blind(F::ONE) } } -impl Blind { +impl Blind { /// Given `rng` creates new blinding scalar pub fn new(rng: &mut R) -> Self { Blind(F::random(rng)) } } -impl Add for Blind { +impl Add for Blind { type Output = Self; fn add(self, rhs: Blind) -> Self { @@ -213,7 +213,7 @@ impl Add for Blind { } } -impl Mul for Blind { +impl Mul for Blind { type Output = Self; fn mul(self, rhs: Blind) -> Self { @@ -221,25 +221,25 @@ impl Mul for Blind { } } -impl AddAssign for Blind { +impl AddAssign for Blind { fn add_assign(&mut self, rhs: Blind) { self.0 += rhs.0; } } -impl MulAssign for Blind { +impl MulAssign for Blind { fn mul_assign(&mut self, rhs: Blind) { self.0 *= rhs.0; } } -impl AddAssign for Blind { +impl AddAssign for Blind { fn add_assign(&mut self, rhs: F) { self.0 += rhs; } } -impl MulAssign for Blind { +impl MulAssign for Blind { fn mul_assign(&mut self, rhs: F) { self.0 *= rhs; } diff --git a/halo2_proofs/src/poly/domain.rs b/halo2_proofs/src/poly/domain.rs index 9fba645791..d16e87867c 100644 --- a/halo2_proofs/src/poly/domain.rs +++ b/halo2_proofs/src/poly/domain.rs @@ -2,20 +2,20 @@ //! domain that is of a suitable size for the application. use crate::{ - arithmetic::{best_fft, parallelize, parallelize_count, FieldExt, Group}, + arithmetic::{best_fft, parallelize, parallelize_count}, multicore, - plonk::{Assigned}, + plonk::Assigned, }; use super::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation}; -use group::ff::{BatchInvert, Field, PrimeField}; +use group::{ + ff::{BatchInvert, Field, PrimeField, WithSmallOrderMulGroup}, + Group, +}; use std::{env::var, marker::PhantomData}; -/// TEMP -pub static mut FFT_TOTAL_TIME: usize = 0; - fn get_fft_mode() -> usize { var("FFT_MODE") .unwrap_or_else(|_| "1".to_string()) @@ -66,7 +66,7 @@ pub fn get_stages(size: usize, radixes: Vec) -> Vec { /// FFTData #[derive(Clone, Debug)] -struct FFTData { +struct FFTData { n: usize, stages: Vec, @@ -76,13 +76,13 @@ struct FFTData { //scratch: Vec, } -impl FFTData { +impl FFTData { /// Create FFT data pub fn new(n: usize, omega: F, omega_inv: F) -> Self { let stages = get_stages(n, vec![]); let mut f_twiddles = vec![]; let mut inv_twiddles = vec![]; - let mut scratch = vec![F::zero(); n]; + let mut scratch = vec![F::ONE; n]; // Generate stage twiddles for inv in 0..2 { @@ -114,7 +114,7 @@ impl FFTData { let stage_length = stages[l].length; let num_twiddles = stage_length * (radix - 1); - stage_twiddles[l].resize(num_twiddles + 1, F::zero()); + stage_twiddles[l].resize(num_twiddles + 1, F::ZERO); // Set j stage_twiddles[l][num_twiddles] = twiddles[(twiddles.len() * 3) / 4]; @@ -141,7 +141,7 @@ impl FFTData { } /// Radix 2 butterfly -pub fn butterfly_2(out: &mut [F], twiddles: &[F], stage_length: usize) { +pub fn butterfly_2(out: &mut [F], twiddles: &[F], stage_length: usize) { let mut out_offset = 0; let mut out_offset2 = stage_length; @@ -161,7 +161,7 @@ pub fn butterfly_2(out: &mut [F], twiddles: &[F], stage_length: usi } /// Radix 2 butterfly -fn butterfly_2_parallel( +fn butterfly_2_parallel( out: &mut [F], twiddles: &[F], _stage_length: usize, @@ -193,7 +193,7 @@ fn butterfly_2_parallel( } /// Radix 4 butterfly -pub fn butterfly_4(out: &mut [F], twiddles: &[F], stage_length: usize) { +pub fn butterfly_4(out: &mut [F], twiddles: &[F], stage_length: usize) { let j = twiddles[twiddles.len() - 1]; let mut tw = 0; @@ -248,7 +248,7 @@ pub fn butterfly_4(out: &mut [F], twiddles: &[F], stage_length: usi } /// Radix 4 butterfly -pub fn butterfly_4_parallel( +pub fn butterfly_4_parallel( out: &mut [F], twiddles: &[F], _stage_length: usize, @@ -301,7 +301,7 @@ pub fn butterfly_4_parallel( } /// Inner recursion -fn recursive_fft_inner( +fn recursive_fft_inner( data_in: &[F], data_out: &mut [F], twiddles: &Vec>, @@ -375,13 +375,13 @@ fn recursive_fft_inner( } } -fn recursive_fft(data: &FFTData, data_in: &mut Vec, inverse: bool) { +fn recursive_fft(data: &FFTData, data_in: &mut Vec, inverse: bool) { let num_threads = multicore::current_num_threads(); //let start = start_measure(format!("recursive fft {} ({})", data_in.len(), num_threads), false); // TODO: reuse scratch buffer between FFTs //let start_mem = start_measure(format!("alloc"), false); - let mut scratch = vec![F::zero(); data_in.len()]; + let mut scratch = vec![F::ZERO; data_in.len()]; //stop_measure(start_mem); recursive_fft_inner( @@ -398,40 +398,37 @@ fn recursive_fft(data: &FFTData, data_in: &mut Vec, inverse: 0, num_threads, ); - //let duration = stop_measure(start); - //let start = start_measure(format!("copy"), false); // Will simply swap the vector's buffer, no data is actually copied std::mem::swap(data_in, &mut /*data.*/scratch); - //stop_measure(start); } /// This structure contains precomputed constants and other details needed for /// performing operations on an evaluation domain of size $2^k$ and an extended /// domain of size $2^{k} * j$ with $j \neq 0$. #[derive(Clone, Debug)] -pub struct EvaluationDomain { +pub struct EvaluationDomain { n: u64, k: u32, extended_k: u32, - omega: G::Scalar, - omega_inv: G::Scalar, - extended_omega: G::Scalar, - extended_omega_inv: G::Scalar, - g_coset: G::Scalar, - g_coset_inv: G::Scalar, + omega: F, + omega_inv: F, + extended_omega: F, + extended_omega_inv: F, + g_coset: F, + g_coset_inv: F, quotient_poly_degree: u64, - ifft_divisor: G::Scalar, - extended_ifft_divisor: G::Scalar, - t_evaluations: Vec, - barycentric_weight: G::Scalar, + ifft_divisor: F, + extended_ifft_divisor: F, + t_evaluations: Vec, + barycentric_weight: F, // Recursive stuff - fft_data: FFTData, - extended_fft_data: FFTData, + fft_data: FFTData, + extended_fft_data: FFTData, } -impl EvaluationDomain { +impl> EvaluationDomain { /// This constructs a new evaluation domain object based on the provided /// values $j, k$. pub fn new(j: u32, k: u32) -> Self { @@ -448,14 +445,15 @@ impl EvaluationDomain { while (1 << extended_k) < (n * quotient_poly_degree) { extended_k += 1; } + #[cfg(feature = "profile")] println!("k: {}, extended_k: {}", k, extended_k); - let mut extended_omega = G::Scalar::root_of_unity(); + let mut extended_omega = F::ROOT_OF_UNITY; // Get extended_omega, the 2^{extended_k}'th root of unity // The loop computes extended_omega = omega^{2 ^ (S - extended_k)} // Notice that extended_omega ^ {2 ^ extended_k} = omega ^ {2^S} = 1. - for _ in extended_k..G::Scalar::S { + for _ in extended_k..F::S { extended_omega = extended_omega.square(); } let extended_omega = extended_omega; @@ -477,14 +475,14 @@ impl EvaluationDomain { // already. // The coset evaluation domain is: // zeta {1, extended_omega, extended_omega^2, ..., extended_omega^{(2^extended_k) - 1}} - let g_coset = G::Scalar::ZETA; + let g_coset = F::ZETA; let g_coset_inv = g_coset.square(); let mut t_evaluations = Vec::with_capacity(1 << (extended_k - k)); { // Compute the evaluations of t(X) = X^n - 1 in the coset evaluation domain. // We don't have to compute all of them, because it will repeat. - let orig = G::Scalar::ZETA.pow_vartime([n]); + let orig = F::ZETA.pow_vartime([n]); let step = extended_omega.pow_vartime([n]); let mut cur = orig; loop { @@ -498,19 +496,19 @@ impl EvaluationDomain { // Subtract 1 from each to give us t_evaluations[i] = t(zeta * extended_omega^i) for coeff in &mut t_evaluations { - *coeff -= &G::Scalar::one(); + *coeff -= &F::ONE; } // Invert, because we're dividing by this polynomial. // We invert in a batch, below. } - let mut ifft_divisor = G::Scalar::from(1 << k); // Inversion computed later - let mut extended_ifft_divisor = G::Scalar::from(1 << extended_k); // Inversion computed later + let mut ifft_divisor = F::from(1 << k); // Inversion computed later + let mut extended_ifft_divisor = F::from(1 << extended_k); // Inversion computed later // The barycentric weight of 1 over the evaluation domain // 1 / \prod_{i != 0} (1 - omega^i) - let mut barycentric_weight = G::Scalar::from(n); // Inversion computed later + let mut barycentric_weight = F::from(n); // Inversion computed later // Compute batch inversion t_evaluations @@ -537,8 +535,8 @@ impl EvaluationDomain { extended_ifft_divisor, t_evaluations, barycentric_weight, - fft_data: FFTData::::new(n as usize, omega, omega_inv), - extended_fft_data: FFTData::::new( + fft_data: FFTData::::new(n as usize, omega, omega_inv), + extended_fft_data: FFTData::::new( (1 << extended_k) as usize, extended_omega, extended_omega_inv, @@ -549,7 +547,7 @@ impl EvaluationDomain { /// Obtains a polynomial in Lagrange form when given a vector of Lagrange /// coefficients of size `n`; panics if the provided vector is the wrong /// length. - pub fn lagrange_from_vec(&self, values: Vec) -> Polynomial { + pub fn lagrange_from_vec(&self, values: Vec) -> Polynomial { assert_eq!(values.len(), self.n as usize); Polynomial { @@ -560,8 +558,8 @@ impl EvaluationDomain { pub fn lagrange_assigned_from_vec( &self, - values: Vec>, - ) -> Polynomial, LagrangeCoeff> { + values: Vec>, + ) -> Polynomial, LagrangeCoeff> { assert_eq!(values.len(), self.n as usize); Polynomial { @@ -573,7 +571,7 @@ impl EvaluationDomain { /// Obtains a polynomial in coefficient form when given a vector of /// coefficients of size `n`; panics if the provided vector is the wrong /// length. - pub fn coeff_from_vec(&self, values: Vec) -> Polynomial { + pub fn coeff_from_vec(&self, values: Vec) -> Polynomial { assert_eq!(values.len(), self.n as usize); Polynomial { @@ -583,35 +581,32 @@ impl EvaluationDomain { } /// Returns an empty (zero) polynomial in the coefficient basis - pub fn empty_coeff(&self) -> Polynomial { + pub fn empty_coeff(&self) -> Polynomial { Polynomial { - values: vec![G::group_zero(); self.n as usize], + values: vec![F::ZERO; self.n as usize], _marker: PhantomData, } } /// Returns an empty (zero) polynomial in the Lagrange coefficient basis - pub fn empty_lagrange(&self) -> Polynomial { + pub fn empty_lagrange(&self) -> Polynomial { Polynomial { - values: vec![G::group_zero(); self.n as usize], + values: vec![F::ZERO; self.n as usize], _marker: PhantomData, } } /// Returns an empty (zero) polynomial in the Lagrange coefficient basis, with /// deferred inversions. - pub(crate) fn empty_lagrange_assigned(&self) -> Polynomial, LagrangeCoeff> - where - G: Field, - { + pub(crate) fn empty_lagrange_assigned(&self) -> Polynomial, LagrangeCoeff> { Polynomial { - values: vec![G::group_zero().into(); self.n as usize], + values: vec![F::ZERO.into(); self.n as usize], _marker: PhantomData, } } /// Returns a constant polynomial in the Lagrange coefficient basis - pub fn constant_lagrange(&self, scalar: G) -> Polynomial { + pub fn constant_lagrange(&self, scalar: F) -> Polynomial { Polynomial { values: vec![scalar; self.n as usize], _marker: PhantomData, @@ -620,16 +615,16 @@ impl EvaluationDomain { /// Returns an empty (zero) polynomial in the extended Lagrange coefficient /// basis - pub fn empty_extended(&self) -> Polynomial { + pub fn empty_extended(&self) -> Polynomial { Polynomial { - values: vec![G::group_zero(); self.extended_len()], + values: vec![F::ZERO; self.extended_len()], _marker: PhantomData, } } /// Returns a constant polynomial in the extended Lagrange coefficient /// basis - pub fn constant_extended(&self, scalar: G) -> Polynomial { + pub fn constant_extended(&self, scalar: F) -> Polynomial { Polynomial { values: vec![scalar; self.extended_len()], _marker: PhantomData, @@ -640,10 +635,7 @@ impl EvaluationDomain { /// /// This function will panic if the provided vector is not the correct /// length. - pub fn lagrange_to_coeff( - &self, - mut a: Polynomial, - ) -> Polynomial { + pub fn lagrange_to_coeff(&self, mut a: Polynomial) -> Polynomial { assert_eq!(a.values.len(), 1 << self.k); // Perform inverse FFT to obtain the polynomial in coefficient form @@ -659,15 +651,15 @@ impl EvaluationDomain { /// evaluation domain, rotating by `rotation` if desired. pub fn coeff_to_extended( &self, - p: &Polynomial, - ) -> Polynomial { + p: &Polynomial, + ) -> Polynomial { assert_eq!(p.values.len(), 1 << self.k); let mut a = Vec::with_capacity(self.extended_len()); a.extend(&p.values); self.distribute_powers_zeta(&mut a, true); - a.resize(self.extended_len(), G::Scalar::zero()); + a.resize(self.extended_len(), F::ZERO); self.fft_inner(&mut a, self.extended_omega, self.extended_k, false); Polynomial { @@ -679,9 +671,9 @@ impl EvaluationDomain { /// Rotate the extended domain polynomial over the original domain. pub fn rotate_extended( &self, - poly: &Polynomial, + poly: &Polynomial, rotation: Rotation, - ) -> Polynomial { + ) -> Polynomial { let new_rotation = ((1 << (self.extended_k - self.k)) * rotation.0.abs()) as usize; let mut poly = poly.clone(); @@ -701,10 +693,7 @@ impl EvaluationDomain { /// This function will panic if the provided vector is not the correct /// length. // TODO/FIXME: caller should be responsible for truncating - pub fn extended_to_coeff( - &self, - mut a: Polynomial, - ) -> Vec { + pub fn extended_to_coeff(&self, mut a: Polynomial) -> Vec { assert_eq!(a.values.len(), self.extended_len()); // Inverse FFT @@ -732,15 +721,15 @@ impl EvaluationDomain { /// polynomial of the $2^k$ size domain. pub fn divide_by_vanishing_poly( &self, - mut a: Polynomial, - ) -> Polynomial { + mut a: Polynomial, + ) -> Polynomial { assert_eq!(a.values.len(), self.extended_len()); // Divide to obtain the quotient polynomial in the coset evaluation // domain. parallelize(&mut a.values, |h, mut index| { for h in h { - h.group_scale(&self.t_evaluations[index % self.t_evaluations.len()]); + *h *= &self.t_evaluations[index % self.t_evaluations.len()]; index += 1; } }); @@ -758,7 +747,7 @@ impl EvaluationDomain { /// /// `into_coset` should be set to `true` when moving into the coset, /// and `false` when moving out. This toggles the choice of `zeta`. - fn distribute_powers_zeta(&self, a: &mut [G::Scalar], into_coset: bool) { + fn distribute_powers_zeta(&self, a: &mut [F], into_coset: bool) { let coset_powers = if into_coset { [self.g_coset, self.g_coset_inv] } else { @@ -769,25 +758,24 @@ impl EvaluationDomain { // Distribute powers to move into/from coset let i = index % (coset_powers.len() + 1); if i != 0 { - a.group_scale(&coset_powers[i - 1]); + *a *= &coset_powers[i - 1]; } index += 1; } }); } - fn ifft(&self, a: &mut Vec, omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) { + fn ifft(&self, a: &mut Vec, omega_inv: F, log_n: u32, divisor: F) { self.fft_inner(a, omega_inv, log_n, true); parallelize(a, |a, _| { for a in a { // Finish iFFT - a.group_scale(&divisor); + *a *= &divisor; } }); } - fn fft_inner(&self, a: &mut Vec, omega: G::Scalar, log_n: u32, inverse: bool) { - // let start = get_time(); + fn fft_inner(&self, a: &mut Vec, omega: F, log_n: u32, inverse: bool) { if get_fft_mode() == 1 { let fft_data = if a.len() == self.fft_data.n { &self.fft_data @@ -798,12 +786,6 @@ impl EvaluationDomain { } else { best_fft(a, omega, log_n); } - // let duration = get_duration(start); - - // #[allow(unsafe_code)] - // unsafe { - // FFT_TOTAL_TIME += duration; - // } } /// Get the size of the domain @@ -822,24 +804,24 @@ impl EvaluationDomain { } /// Get $\omega$, the generator of the $2^k$ order multiplicative subgroup. - pub fn get_omega(&self) -> G::Scalar { + pub fn get_omega(&self) -> F { self.omega } /// Get $\omega^{-1}$, the inverse of the generator of the $2^k$ order /// multiplicative subgroup. - pub fn get_omega_inv(&self) -> G::Scalar { + pub fn get_omega_inv(&self) -> F { self.omega_inv } /// Get the generator of the extended domain's multiplicative subgroup. - pub fn get_extended_omega(&self) -> G::Scalar { + pub fn get_extended_omega(&self) -> F { self.extended_omega } /// Multiplies a value by some power of $\omega$, essentially rotating over /// the domain. - pub fn rotate_omega(&self, value: G::Scalar, rotation: Rotation) -> G::Scalar { + pub fn rotate_omega(&self, value: F, rotation: Rotation) -> F { let mut point = value; if rotation.0 >= 0 { point *= &self.get_omega().pow_vartime([rotation.0 as u64]); @@ -880,23 +862,23 @@ impl EvaluationDomain { /// which is the barycentric weight of $\omega^i$. pub fn l_i_range + Clone>( &self, - x: G::Scalar, - xn: G::Scalar, + x: F, + xn: F, rotations: I, - ) -> Vec { + ) -> Vec { let mut results; { let rotations = rotations.clone().into_iter(); results = Vec::with_capacity(rotations.size_hint().1.unwrap_or(0)); for rotation in rotations { let rotation = Rotation(rotation); - let result = x - self.rotate_omega(G::Scalar::one(), rotation); + let result = x - self.rotate_omega(F::ONE, rotation); results.push(result); } results.iter_mut().batch_invert(); } - let common = (xn - G::Scalar::one()) * self.barycentric_weight; + let common = (xn - F::ONE) * self.barycentric_weight; for (rotation, result) in rotations.into_iter().zip(results.iter_mut()) { let rotation = Rotation(rotation); *result = self.rotate_omega(*result * common, rotation); @@ -913,7 +895,7 @@ impl EvaluationDomain { /// Obtain a pinned version of this evaluation domain; a structure with the /// minimal parameters needed to determine the rest of the evaluation /// domain. - pub fn pinned(&self) -> PinnedEvaluationDomain<'_, G> { + pub fn pinned(&self) -> PinnedEvaluationDomain<'_, F> { PinnedEvaluationDomain { k: &self.k, extended_k: &self.extended_k, @@ -925,10 +907,10 @@ impl EvaluationDomain { /// Represents the minimal parameters that determine an `EvaluationDomain`. #[allow(dead_code)] #[derive(Debug)] -pub struct PinnedEvaluationDomain<'a, G: Group> { +pub struct PinnedEvaluationDomain<'a, F: Field> { k: &'a u32, extended_k: &'a u32, - omega: &'a G::Scalar, + omega: &'a F, } #[test] @@ -983,17 +965,17 @@ fn test_l_i() { let mut l = vec![]; let mut points = vec![]; for i in 0..8 { - points.push(domain.omega.pow(&[i, 0, 0, 0])); + points.push(domain.omega.pow([i, 0, 0, 0])); } for i in 0..8 { let mut l_i = vec![Scalar::zero(); 8]; - l_i[i] = Scalar::one(); + l_i[i] = Scalar::ONE; let l_i = lagrange_interpolate(&points[..], &l_i[..]); l.push(l_i); } let x = Scalar::random(OsRng); - let xn = x.pow(&[8, 0, 0, 0]); + let xn = x.pow([8, 0, 0, 0]); let evaluations = domain.l_i_range(x, xn, -7..=7); for i in 0..8 { @@ -1025,21 +1007,11 @@ fn test_fft() { input[i] = Scalar::random(OsRng); }*/ - #[cfg(feature = "profile")] - let num_threads = multicore::current_num_threads(); - let mut a = input.clone(); - // let start = start_measure(format!("best fft {} ({})", a.len(), num_threads), false); best_fft(&mut a, domain.omega, k); - // stop_measure(start); let mut b = input.clone(); - // let start = start_measure( - // format!("recursive fft {} ({})", a.len(), num_threads), - // false, - // ); recursive_fft(&domain.fft_data, &mut b, false); - // stop_measure(start); for i in 0..n { //println!("{}: {} {}", i, a[i], b[i]); diff --git a/halo2_proofs/src/poly/evaluator.rs b/halo2_proofs/src/poly/evaluator.rs index d1ba853c47..5d20221255 100644 --- a/halo2_proofs/src/poly/evaluator.rs +++ b/halo2_proofs/src/poly/evaluator.rs @@ -9,7 +9,7 @@ use std::{ }; use group::ff::Field; -use halo2curves::FieldExt; +use halo2curves::Field; use super::{ Basis, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation, @@ -135,7 +135,7 @@ impl Evaluator { ) -> Polynomial where E: Copy + Send + Sync, - F: FieldExt, + F: Field, B: BasisOps, { // Traverse `ast` to collect the used leaves. @@ -192,7 +192,7 @@ impl Evaluator { }) .collect(); - struct AstContext<'a, E, F: FieldExt, B: Basis> { + struct AstContext<'a, E, F: Field, B: Basis> { domain: &'a EvaluationDomain, poly_len: usize, chunk_size: usize, @@ -200,7 +200,7 @@ impl Evaluator { leaves: &'a HashMap, &'a [F]>, } - fn recurse( + fn recurse( ast: &Ast, ctx: &AstContext<'_, E, F, B>, ) -> Vec { @@ -230,7 +230,7 @@ impl Evaluator { lhs } Ast::DistributePowers(terms, base) => terms.iter().fold( - B::constant_term(ctx.poly_len, ctx.chunk_size, ctx.chunk_index, F::zero()), + B::constant_term(ctx.poly_len, ctx.chunk_size, ctx.chunk_index, F::ZERO), |mut acc, term| { let term = recurse(term, ctx); for (acc, term) in acc.iter_mut().zip(term) { @@ -347,7 +347,7 @@ impl From> for Ast { impl Ast { pub(crate) fn one() -> Self { - Self::ConstantTerm(F::one()) + Self::ConstantTerm(F::ONE) } } @@ -355,7 +355,7 @@ impl Neg for Ast { type Output = Ast; fn neg(self) -> Self::Output { - Ast::Scale(Arc::new(self), -F::one()) + Ast::Scale(Arc::new(self), -F::ONE) } } @@ -489,21 +489,21 @@ impl MulAssign for Ast { /// Operations which can be performed over a given basis. pub(crate) trait BasisOps: Basis { - fn empty_poly(domain: &EvaluationDomain) -> Polynomial; - fn constant_term( + fn empty_poly(domain: &EvaluationDomain) -> Polynomial; + fn constant_term( poly_len: usize, chunk_size: usize, chunk_index: usize, scalar: F, ) -> Vec; - fn linear_term( + fn linear_term( domain: &EvaluationDomain, poly_len: usize, chunk_size: usize, chunk_index: usize, scalar: F, ) -> Vec; - fn rotate( + fn rotate( domain: &EvaluationDomain, poly: &Polynomial, rotation: Rotation, @@ -511,31 +511,31 @@ pub(crate) trait BasisOps: Basis { } impl BasisOps for Coeff { - fn empty_poly(domain: &EvaluationDomain) -> Polynomial { + fn empty_poly(domain: &EvaluationDomain) -> Polynomial { domain.empty_coeff() } - fn constant_term( + fn constant_term( poly_len: usize, chunk_size: usize, chunk_index: usize, scalar: F, ) -> Vec { - let mut chunk = vec![F::zero(); cmp::min(chunk_size, poly_len - chunk_size * chunk_index)]; + let mut chunk = vec![F::ZERO; cmp::min(chunk_size, poly_len - chunk_size * chunk_index)]; if chunk_index == 0 { chunk[0] = scalar; } chunk } - fn linear_term( + fn linear_term( _: &EvaluationDomain, poly_len: usize, chunk_size: usize, chunk_index: usize, scalar: F, ) -> Vec { - let mut chunk = vec![F::zero(); cmp::min(chunk_size, poly_len - chunk_size * chunk_index)]; + let mut chunk = vec![F::ZERO; cmp::min(chunk_size, poly_len - chunk_size * chunk_index)]; // If the chunk size is 1 (e.g. if we have a small k and many threads), then the // linear coefficient is the second chunk. Otherwise, the chunk size is greater // than one, and the linear coefficient is the second element of the first chunk. @@ -550,7 +550,7 @@ impl BasisOps for Coeff { chunk } - fn rotate( + fn rotate( _: &EvaluationDomain, _: &Polynomial, _: Rotation, @@ -560,11 +560,11 @@ impl BasisOps for Coeff { } impl BasisOps for LagrangeCoeff { - fn empty_poly(domain: &EvaluationDomain) -> Polynomial { + fn empty_poly(domain: &EvaluationDomain) -> Polynomial { domain.empty_lagrange() } - fn constant_term( + fn constant_term( poly_len: usize, chunk_size: usize, chunk_index: usize, @@ -573,7 +573,7 @@ impl BasisOps for LagrangeCoeff { vec![scalar; cmp::min(chunk_size, poly_len - chunk_size * chunk_index)] } - fn linear_term( + fn linear_term( domain: &EvaluationDomain, poly_len: usize, chunk_size: usize, @@ -592,7 +592,7 @@ impl BasisOps for LagrangeCoeff { .collect() } - fn rotate( + fn rotate( _: &EvaluationDomain, poly: &Polynomial, rotation: Rotation, @@ -602,11 +602,11 @@ impl BasisOps for LagrangeCoeff { } impl BasisOps for ExtendedLagrangeCoeff { - fn empty_poly(domain: &EvaluationDomain) -> Polynomial { + fn empty_poly(domain: &EvaluationDomain) -> Polynomial { domain.empty_extended() } - fn constant_term( + fn constant_term( poly_len: usize, chunk_size: usize, chunk_index: usize, @@ -615,7 +615,7 @@ impl BasisOps for ExtendedLagrangeCoeff { vec![scalar; cmp::min(chunk_size, poly_len - chunk_size * chunk_index)] } - fn linear_term( + fn linear_term( domain: &EvaluationDomain, poly_len: usize, chunk_size: usize, @@ -637,7 +637,7 @@ impl BasisOps for ExtendedLagrangeCoeff { .collect() } - fn rotate( + fn rotate( domain: &EvaluationDomain, poly: &Polynomial, rotation: Rotation, diff --git a/halo2_proofs/src/poly/ipa/commitment.rs b/halo2_proofs/src/poly/ipa/commitment.rs index 9060e8315c..f9b4ad059b 100644 --- a/halo2_proofs/src/poly/ipa/commitment.rs +++ b/halo2_proofs/src/poly/ipa/commitment.rs @@ -4,7 +4,7 @@ //! [halo]: https://eprint.iacr.org/2019/1021 use crate::arithmetic::{ - best_fft, best_multiexp, g_to_lagrange, parallelize, CurveAffine, CurveExt, FieldExt, Group, + best_fft, best_multiexp, g_to_lagrange, parallelize, CurveAffine, CurveExt, }; use crate::helpers::CurveRead; use crate::poly::commitment::{Blind, CommitmentScheme, Params, ParamsProver, ParamsVerifier, MSM}; @@ -12,7 +12,7 @@ use crate::poly::ipa::msm::MSMIPA; use crate::poly::{Coeff, LagrangeCoeff, Polynomial}; use ff::{Field, PrimeField}; -use group::{prime::PrimeCurveAffine, Curve, Group as _}; +use group::{prime::PrimeCurveAffine, Curve, Group}; use std::marker::PhantomData; use std::ops::{Add, AddAssign, Mul, MulAssign}; @@ -234,9 +234,7 @@ impl<'params, C: CurveAffine> ParamsProver<'params, C> for ParamsIPA { #[cfg(test)] mod test { - use crate::arithmetic::{ - best_fft, best_multiexp, parallelize, CurveAffine, CurveExt, FieldExt, Group, - }; + use crate::arithmetic::{best_fft, best_multiexp, parallelize, CurveAffine, CurveExt}; use crate::helpers::CurveRead; use crate::poly::commitment::ParamsProver; use crate::poly::commitment::{Blind, CommitmentScheme, Params, MSM}; @@ -245,7 +243,7 @@ mod test { use crate::poly::{Coeff, LagrangeCoeff, Polynomial}; use ff::{Field, PrimeField}; - use group::{prime::PrimeCurveAffine, Curve, Group as _}; + use group::{prime::PrimeCurveAffine, Curve, Group}; use std::marker::PhantomData; use std::ops::{Add, AddAssign, Mul, MulAssign}; @@ -309,7 +307,7 @@ mod test { use rand_core::OsRng; use super::super::commitment::{Blind, Params}; - use crate::arithmetic::{eval_polynomial, FieldExt}; + use crate::arithmetic::eval_polynomial; use crate::halo2curves::pasta::{EpAffine, Fq}; use crate::poly::EvaluationDomain; use crate::transcript::{ @@ -363,7 +361,7 @@ mod test { assert_eq!(v, v_prime); let mut commitment_msm = MSMIPA::new(¶ms); - commitment_msm.append_term(Field::one(), p.into()); + commitment_msm.append_term(Fq::one(), p.into()); let guard = verify_proof(¶ms, commitment_msm, &mut transcript, *x, v).unwrap(); let ch_verifier = transcript.squeeze_challenge(); diff --git a/halo2_proofs/src/poly/ipa/commitment/prover.rs b/halo2_proofs/src/poly/ipa/commitment/prover.rs index 3b22b31b36..d176987c96 100644 --- a/halo2_proofs/src/poly/ipa/commitment/prover.rs +++ b/halo2_proofs/src/poly/ipa/commitment/prover.rs @@ -3,7 +3,7 @@ use rand_core::RngCore; use super::{Params, ParamsIPA}; use crate::arithmetic::{ - best_multiexp, compute_inner_product, eval_polynomial, parallelize, CurveAffine, FieldExt, + best_multiexp, compute_inner_product, eval_polynomial, parallelize, CurveAffine, }; use crate::poly::commitment::ParamsProver; @@ -87,7 +87,7 @@ pub fn create_proof< // `p_prime` and `b` is the evaluation of the polynomial at `x_3`. let mut b = Vec::with_capacity(1 << params.k); { - let mut cur = C::Scalar::one(); + let mut cur = C::Scalar::ONE; for _ in 0..(1 << params.k) { b.push(cur); cur *= &x_3; diff --git a/halo2_proofs/src/poly/ipa/commitment/verifier.rs b/halo2_proofs/src/poly/ipa/commitment/verifier.rs index b3b30e0b4b..0b60842899 100644 --- a/halo2_proofs/src/poly/ipa/commitment/verifier.rs +++ b/halo2_proofs/src/poly/ipa/commitment/verifier.rs @@ -96,10 +96,10 @@ pub fn verify_proof<'params, C: CurveAffine, E: EncodedChallenge, T: Transcri /// Computes $\prod\limits_{i=0}^{k-1} (1 + u_{k - 1 - i} x^{2^i})$. fn compute_b(x: F, u: &[F]) -> F { - let mut tmp = F::one(); + let mut tmp = F::ONE; let mut cur = x; for u_j in u.iter().rev() { - tmp *= F::one() + &(*u_j * &cur); + tmp *= F::ONE + &(*u_j * &cur); cur *= cur; } tmp diff --git a/halo2_proofs/src/poly/ipa/msm.rs b/halo2_proofs/src/poly/ipa/msm.rs index 63f994b46e..3316e25337 100644 --- a/halo2_proofs/src/poly/ipa/msm.rs +++ b/halo2_proofs/src/poly/ipa/msm.rs @@ -191,7 +191,7 @@ impl<'a, C: CurveAffine> MSMIPA<'a, C> { if let Some(g_scalars) = self.g_scalars.as_mut() { g_scalars[0] += &constant; } else { - let mut g_scalars = vec![C::Scalar::zero(); self.params.n as usize]; + let mut g_scalars = vec![C::Scalar::ZERO; self.params.n as usize]; g_scalars[0] += &constant; self.g_scalars = Some(g_scalars); } diff --git a/halo2_proofs/src/poly/ipa/multiopen.rs b/halo2_proofs/src/poly/ipa/multiopen.rs index b724139a8f..fd6aa78544 100644 --- a/halo2_proofs/src/poly/ipa/multiopen.rs +++ b/halo2_proofs/src/poly/ipa/multiopen.rs @@ -3,14 +3,10 @@ //! //! [halo]: https://eprint.iacr.org/2019/1021 -use std::collections::{BTreeMap, BTreeSet}; - use super::*; -use crate::{ - arithmetic::{CurveAffine, FieldExt}, - poly::query::Query, - transcript::ChallengeScalar, -}; +use crate::{arithmetic::CurveAffine, poly::query::Query, transcript::ChallengeScalar}; +use ff::Field; +use std::collections::{BTreeMap, BTreeSet}; mod prover; mod verifier; @@ -63,7 +59,7 @@ type IntermediateSets = ( Vec>, ); -fn construct_intermediate_sets>(queries: I) -> IntermediateSets +fn construct_intermediate_sets>(queries: I) -> IntermediateSets where I: IntoIterator + Clone, { diff --git a/halo2_proofs/src/poly/ipa/multiopen/prover.rs b/halo2_proofs/src/poly/ipa/multiopen/prover.rs index bba038c93a..f09bc4425f 100644 --- a/halo2_proofs/src/poly/ipa/multiopen/prover.rs +++ b/halo2_proofs/src/poly/ipa/multiopen/prover.rs @@ -1,7 +1,7 @@ use super::{ construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4, Query, }; -use crate::arithmetic::{eval_polynomial, kate_division, CurveAffine, FieldExt}; +use crate::arithmetic::{eval_polynomial, kate_division, CurveAffine}; use crate::poly::commitment::ParamsProver; use crate::poly::commitment::{Blind, Params, Prover}; use crate::poly::ipa::commitment::{self, IPACommitmentScheme, ParamsIPA}; @@ -47,7 +47,7 @@ impl<'params, C: CurveAffine> Prover<'params, IPACommitmentScheme> for Prover // Collapse openings at same point sets together into single openings using // x_1 challenge. let mut q_polys: Vec>> = vec![None; point_sets.len()]; - let mut q_blinds = vec![Blind(C::Scalar::zero()); point_sets.len()]; + let mut q_blinds = vec![Blind(C::Scalar::ZERO); point_sets.len()]; { let mut accumulate = |set_idx: usize, @@ -80,7 +80,7 @@ impl<'params, C: CurveAffine> Prover<'params, IPACommitmentScheme> for Prover .fold(poly.clone().unwrap().values, |poly, point| { kate_division(&poly, *point) }); - poly.resize(self.params.n as usize, C::Scalar::zero()); + poly.resize(self.params.n as usize, C::Scalar::ZERO); let poly = Polynomial { values: poly, _marker: PhantomData, @@ -109,7 +109,7 @@ impl<'params, C: CurveAffine> Prover<'params, IPACommitmentScheme> for Prover let x_4: ChallengeX4<_> = transcript.squeeze_challenge_scalar(); - let (p_poly, p_poly_blind) = q_polys.into_iter().zip(q_blinds.into_iter()).fold( + let (p_poly, p_poly_blind) = q_polys.into_iter().zip(q_blinds).fold( (q_prime_poly, q_prime_blind), |(q_prime_poly, q_prime_blind), (poly, blind)| { ( diff --git a/halo2_proofs/src/poly/ipa/multiopen/verifier.rs b/halo2_proofs/src/poly/ipa/multiopen/verifier.rs index ff4c7626f6..391f89e15b 100644 --- a/halo2_proofs/src/poly/ipa/multiopen/verifier.rs +++ b/halo2_proofs/src/poly/ipa/multiopen/verifier.rs @@ -8,7 +8,7 @@ use rand_core::RngCore; use super::{ construct_intermediate_sets, ChallengeX1, ChallengeX2, ChallengeX3, ChallengeX4, Query, }; -use crate::arithmetic::{eval_polynomial, lagrange_interpolate, CurveAffine, FieldExt}; +use crate::arithmetic::{eval_polynomial, lagrange_interpolate, CurveAffine}; use crate::poly::commitment::{Params, Verifier, MSM}; use crate::poly::ipa::commitment::{IPACommitmentScheme, ParamsIPA, ParamsVerifierIPA}; use crate::poly::ipa::msm::MSMIPA; @@ -63,7 +63,7 @@ impl<'params, C: CurveAffine> Verifier<'params, IPACommitmentScheme> // while the inner vec corresponds to the points in a particular set. let mut q_eval_sets = Vec::with_capacity(point_sets.len()); for point_set in point_sets.iter() { - q_eval_sets.push(vec![C::Scalar::zero(); point_set.len()]); + q_eval_sets.push(vec![C::Scalar::ZERO; point_set.len()]); } { let mut accumulate = |set_idx: usize, @@ -72,7 +72,7 @@ impl<'params, C: CurveAffine> Verifier<'params, IPACommitmentScheme> q_commitments[set_idx].scale(*x_1); match new_commitment { CommitmentReference::Commitment(c) => { - q_commitments[set_idx].append_term(C::Scalar::one(), (*c).into()); + q_commitments[set_idx].append_term(C::Scalar::ONE, (*c).into()); } CommitmentReference::MSM(msm) => { q_commitments[set_idx].add_msm(msm); @@ -116,7 +116,7 @@ impl<'params, C: CurveAffine> Verifier<'params, IPACommitmentScheme> .zip(q_eval_sets.iter()) .zip(u.iter()) .fold( - C::Scalar::zero(), + C::Scalar::ZERO, |msm_eval, ((points, evals), proof_eval)| { let r_poly = lagrange_interpolate(points, evals); let r_eval = eval_polynomial(&r_poly, *x_3); @@ -132,7 +132,7 @@ impl<'params, C: CurveAffine> Verifier<'params, IPACommitmentScheme> let x_4: ChallengeX4<_> = transcript.squeeze_challenge_scalar(); // Compute the final commitment that has to be opened - msm.append_term(C::Scalar::one(), q_prime_commitment.into()); + msm.append_term(C::Scalar::ONE, q_prime_commitment.into()); let (msm, v) = q_commitments.into_iter().zip(u.iter()).fold( (msm, msm_eval), |(mut msm, msm_eval), (q_commitment, q_eval)| { diff --git a/halo2_proofs/src/poly/ipa/strategy.rs b/halo2_proofs/src/poly/ipa/strategy.rs index 6f3b4b7228..c8d385f90c 100644 --- a/halo2_proofs/src/poly/ipa/strategy.rs +++ b/halo2_proofs/src/poly/ipa/strategy.rs @@ -70,7 +70,7 @@ impl<'params, C: CurveAffine> GuardIPA<'params, C> { /// Computes G = ⟨s, params.g⟩ pub fn compute_g(&self) -> C { - let s = compute_s(&self.u, C::Scalar::one()); + let s = compute_s(&self.u, C::Scalar::ONE); best_multiexp(&s, &self.msm.params.g).to_affine() } @@ -160,7 +160,7 @@ impl<'params, C: CurveAffine> /// Computes the coefficients of $g(X) = \prod\limits_{i=0}^{k-1} (1 + u_{k - 1 - i} X^{2^i})$. fn compute_s(u: &[F], init: F) -> Vec { assert!(!u.is_empty()); - let mut v = vec![F::zero(); 1 << u.len()]; + let mut v = vec![F::ZERO; 1 << u.len()]; v[0] = init; for (len, u_j) in u.iter().rev().enumerate().map(|(i, u_j)| (1 << i, u_j)) { diff --git a/halo2_proofs/src/poly/kzg/commitment.rs b/halo2_proofs/src/poly/kzg/commitment.rs index 5994327c77..253fd1b86a 100644 --- a/halo2_proofs/src/poly/kzg/commitment.rs +++ b/halo2_proofs/src/poly/kzg/commitment.rs @@ -1,5 +1,5 @@ use crate::arithmetic::{ - best_fft, best_multiexp, g_to_lagrange, parallelize, CurveAffine, CurveExt, FieldExt, Group, + best_fft, best_multiexp, g_to_lagrange, parallelize, CurveAffine, CurveExt, }; use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::{Blind, CommitmentScheme, Params, ParamsProver, ParamsVerifier, MSM}; @@ -7,8 +7,8 @@ use crate::poly::{Coeff, LagrangeCoeff, Polynomial}; use crate::SerdeFormat; use ff::{Field, PrimeField}; -use group::{prime::PrimeCurveAffine, Curve, Group as _}; -use halo2curves::pairing::Engine; +use group::{prime::PrimeCurveAffine, Curve, Group}; +use pairing::Engine; use rand_core::{OsRng, RngCore}; use std::fmt::Debug; use std::marker::PhantomData; @@ -37,10 +37,10 @@ pub struct KZGCommitmentScheme { impl CommitmentScheme for KZGCommitmentScheme where - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, { - type Scalar = E::Scalar; + type Scalar = E::Fr; type Curve = E::G1Affine; type ParamsProver = ParamsKZG; @@ -61,14 +61,14 @@ impl ParamsKZG { pub fn setup(k: u32, rng: R) -> Self { // Largest root of unity exponent of the Engine is `2^E::Scalar::S`, so we can // only support FFTs of polynomials below degree `2^E::Scalar::S`. - assert!(k <= E::Scalar::S); + assert!(k <= E::Fr::S); let n: u64 = 1 << k; // Calculate g = [G1, [s] G1, [s^2] G1, ..., [s^(n-1)] G1] in parallel. let g1 = E::G1Affine::generator(); - let s = ::random(rng); + let s = ::random(rng); - let mut g_projective = vec![E::G1::group_zero(); n as usize]; + let mut g_projective = vec![E::G1::identity(); n as usize]; parallelize(&mut g_projective, |g, start| { let mut current_g: E::G1 = g1.into(); current_g *= s.pow_vartime([start as u64]); @@ -86,14 +86,14 @@ impl ParamsKZG { g }; - let mut g_lagrange_projective = vec![E::G1::group_zero(); n as usize]; - let mut root = E::Scalar::ROOT_OF_UNITY_INV.invert().unwrap(); - for _ in k..E::Scalar::S { + let mut g_lagrange_projective = vec![E::G1::identity(); n as usize]; + let mut root = E::Fr::ROOT_OF_UNITY_INV.invert().unwrap(); + for _ in k..E::Fr::S { root = root.square(); } - let n_inv = Option::::from(E::Scalar::from(n).invert()) + let n_inv = Option::::from(E::Fr::from(n).invert()) .expect("inversion should be ok for n = 1< ParamsKZG { } } + /// Initializes parameters for the curve through existing parameters + /// k, g, g_lagrange (optional), g2, s_g2 + pub fn from_parts( + &self, + k: u32, + g: Vec, + g_lagrange: Option>, + g2: E::G2Affine, + s_g2: E::G2Affine, + ) -> Self { + Self { + k, + n: 1 << k, + g_lagrange: if let Some(g_l) = g_lagrange { + g_l + } else { + g_to_lagrange(g.iter().map(PrimeCurveAffine::to_curve).collect(), k) + }, + g, + g2, + s_g2, + } + } + /// Returns gernerator on G2 pub fn g2(&self) -> E::G2Affine { self.g2 @@ -242,7 +266,7 @@ pub type ParamsVerifierKZG = ParamsKZG; impl<'params, E: Engine + Debug> Params<'params, E::G1Affine> for ParamsKZG where - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, { type MSM = MSMKZG; @@ -269,11 +293,7 @@ where MSMKZG::new() } - fn commit_lagrange( - &self, - poly: &Polynomial, - _: Blind, - ) -> E::G1 { + fn commit_lagrange(&self, poly: &Polynomial, _: Blind) -> E::G1 { let size = poly.len(); assert!(self.n() >= size as u64); best_multiexp(poly, &self.g_lagrange[0..size]) @@ -293,14 +313,14 @@ where impl<'params, E: Engine + Debug> ParamsVerifier<'params, E::G1Affine> for ParamsKZG where - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, { } impl<'params, E: Engine + Debug> ParamsProver<'params, E::G1Affine> for ParamsKZG where - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, { type ParamsVerifier = ParamsVerifierKZG; @@ -313,7 +333,7 @@ where Self::setup(k, OsRng) } - fn commit(&self, poly: &Polynomial, _: Blind) -> E::G1 { + fn commit(&self, poly: &Polynomial, _: Blind) -> E::G1 { let size = poly.len(); assert!(self.n() >= size as u64); best_multiexp(poly, &self.g[0..size]) @@ -326,9 +346,7 @@ where #[cfg(test)] mod test { - use crate::arithmetic::{ - best_fft, best_multiexp, parallelize, CurveAffine, CurveExt, FieldExt, Group, - }; + use crate::arithmetic::{best_fft, best_multiexp, parallelize, CurveAffine, CurveExt}; use crate::poly::commitment::ParamsProver; use crate::poly::commitment::{Blind, CommitmentScheme, Params, MSM}; use crate::poly::kzg::commitment::{ParamsKZG, ParamsVerifierKZG}; @@ -337,7 +355,7 @@ mod test { use crate::poly::{Coeff, LagrangeCoeff, Polynomial}; use ff::{Field, PrimeField}; - use group::{prime::PrimeCurveAffine, Curve, Group as _}; + use group::{prime::PrimeCurveAffine, Curve, Group}; use halo2curves::bn256::G1Affine; use std::marker::PhantomData; use std::ops::{Add, AddAssign, Mul, MulAssign}; @@ -377,7 +395,7 @@ mod test { use rand_core::OsRng; use super::super::commitment::{Blind, Params}; - use crate::arithmetic::{eval_polynomial, FieldExt}; + use crate::arithmetic::eval_polynomial; use crate::halo2curves::bn256::{Bn256, Fr}; use crate::poly::EvaluationDomain; diff --git a/halo2_proofs/src/poly/kzg/msm.rs b/halo2_proofs/src/poly/kzg/msm.rs index 19754146a0..4116c8bd53 100644 --- a/halo2_proofs/src/poly/kzg/msm.rs +++ b/halo2_proofs/src/poly/kzg/msm.rs @@ -6,12 +6,12 @@ use crate::{ poly::commitment::MSM, }; use group::{Curve, Group}; -use halo2curves::pairing::{Engine, MillerLoopResult, MultiMillerLoop}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop}; /// A multiscalar multiplication in the polynomial commitment scheme #[derive(Clone, Default, Debug)] pub struct MSMKZG { - pub(crate) scalars: Vec, + pub(crate) scalars: Vec, pub(crate) bases: Vec, } @@ -25,9 +25,9 @@ impl MSMKZG { } /// Prepares all scalars in the MSM to linear combination - pub fn combine_with_base(&mut self, base: E::Scalar) { + pub fn combine_with_base(&mut self, base: E::Fr) { use ff::Field; - let mut acc = E::Scalar::one(); + let mut acc = E::Fr::ONE; if !self.scalars.is_empty() { for scalar in self.scalars.iter_mut().rev() { *scalar *= &acc; @@ -37,8 +37,12 @@ impl MSMKZG { } } -impl MSM for MSMKZG { - fn append_term(&mut self, scalar: E::Scalar, point: E::G1) { +impl MSM for MSMKZG +where + E: Engine + Debug, + E::G1Affine: CurveAffine, +{ + fn append_term(&mut self, scalar: E::Fr, point: E::G1) { self.scalars.push(scalar); self.bases.push(point); } @@ -48,7 +52,7 @@ impl MSM for MSMKZG { self.bases.extend(other.bases().iter()); } - fn scale(&mut self, factor: E::Scalar) { + fn scale(&mut self, factor: E::Fr) { if !self.scalars.is_empty() { parallelize(&mut self.scalars, |scalars, _| { for other_scalar in scalars { @@ -73,7 +77,7 @@ impl MSM for MSMKZG { self.bases.clone() } - fn scalars(&self) -> Vec { + fn scalars(&self) -> Vec { self.scalars.clone() } } @@ -134,9 +138,14 @@ impl<'a, E: MultiMillerLoop + Debug> DualMSM<'a, E> { right: MSMKZG::new(), } } +} +impl<'a, E: MultiMillerLoop + Debug> DualMSM<'a, E> +where + E::G1Affine: CurveAffine, +{ /// Scale all scalars in the MSM by some scaling factor - pub fn scale(&mut self, e: E::Scalar) { + pub fn scale(&mut self, e: E::Fr) { self.left.scale(e); self.right.scale(e); } diff --git a/halo2_proofs/src/poly/kzg/multiopen/gwc.rs b/halo2_proofs/src/poly/kzg/multiopen/gwc.rs index 4869238ae7..8e7d742fc0 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/gwc.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/gwc.rs @@ -5,7 +5,7 @@ pub use prover::ProverGWC; pub use verifier::VerifierGWC; use crate::{ - arithmetic::{eval_polynomial, CurveAffine, FieldExt}, + arithmetic::{eval_polynomial, CurveAffine}, poly::{ commitment::{Params, ParamsVerifier}, query::Query, @@ -13,7 +13,7 @@ use crate::{ }, transcript::ChallengeScalar, }; - +use ff::Field; use std::{ collections::{BTreeMap, BTreeSet}, marker::PhantomData, @@ -27,13 +27,13 @@ type ChallengeU = ChallengeScalar; struct V {} type ChallengeV = ChallengeScalar; -struct CommitmentData> { +struct CommitmentData> { queries: Vec, point: F, _marker: PhantomData, } -fn construct_intermediate_sets>(queries: I) -> Vec> +fn construct_intermediate_sets>(queries: I) -> Vec> where I: IntoIterator + Clone, { diff --git a/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs b/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs index e7bff84ade..4d772bfa75 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs @@ -1,5 +1,5 @@ use super::{construct_intermediate_sets, ChallengeV, Query}; -use crate::arithmetic::{eval_polynomial, kate_division, powers, CurveAffine, FieldExt}; +use crate::arithmetic::{eval_polynomial, kate_division, powers, CurveAffine}; use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::ParamsProver; use crate::poly::commitment::Prover; @@ -12,9 +12,9 @@ use crate::poly::{ }; use crate::transcript::{EncodedChallenge, TranscriptWrite}; -use ff::Field; +use ff::{Field, PrimeField}; use group::Curve; -use halo2curves::pairing::Engine; +use pairing::Engine; use rand_core::RngCore; use std::fmt::Debug; use std::io::{self, Write}; @@ -29,7 +29,7 @@ pub struct ProverGWC<'params, E: Engine> { /// Create a multi-opening proof impl<'params, E: Engine + Debug> Prover<'params, KZGCommitmentScheme> for ProverGWC<'params, E> where - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, { const QUERY_INSTANCE: bool = false; diff --git a/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs b/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs index 1ec003d638..62f16417d7 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/gwc/verifier.rs @@ -3,7 +3,7 @@ use std::io::Read; use std::marker::PhantomData; use super::{construct_intermediate_sets, ChallengeU, ChallengeV}; -use crate::arithmetic::{eval_polynomial, lagrange_interpolate, powers, CurveAffine, FieldExt}; +use crate::arithmetic::{eval_polynomial, lagrange_interpolate, powers, CurveAffine}; use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::Verifier; use crate::poly::commitment::MSM; @@ -19,9 +19,9 @@ use crate::poly::{ }; use crate::transcript::{EncodedChallenge, TranscriptRead}; -use ff::Field; +use ff::{Field, PrimeField}; use group::Group; -use halo2curves::pairing::{Engine, MillerLoopResult, MultiMillerLoop}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop}; use rand_core::OsRng; #[derive(Debug)] @@ -33,7 +33,7 @@ pub struct VerifierGWC<'params, E: Engine> { impl<'params, E> Verifier<'params, KZGCommitmentScheme> for VerifierGWC<'params, E> where E: MultiMillerLoop + Debug, - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, { type Guard = GuardKZG<'params, E>; @@ -70,13 +70,13 @@ where let u: ChallengeU<_> = transcript.squeeze_challenge_scalar(); let mut commitment_multi = MSMKZG::::new(); - let mut eval_multi = E::Scalar::zero(); + let mut eval_multi = E::Fr::ZERO; let mut witness = MSMKZG::::new(); let mut witness_with_aux = MSMKZG::::new(); for ((commitment_at_a_point, wi), power_of_u) in - commitment_data.iter().zip(w.into_iter()).zip(powers(*u)) + commitment_data.iter().zip(w).zip(powers(*u)) { assert!(!commitment_at_a_point.queries.is_empty()); let z = commitment_at_a_point.point; diff --git a/halo2_proofs/src/poly/kzg/multiopen/shplonk.rs b/halo2_proofs/src/poly/kzg/multiopen/shplonk.rs index 43d3687da3..8a746ab402 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/shplonk.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/shplonk.rs @@ -1,17 +1,6 @@ mod prover; mod verifier; -pub use prover::ProverSHPLONK; -use rayon::prelude::*; -use rustc_hash::FxHashSet; -pub use verifier::VerifierSHPLONK; - -use crate::{ - arithmetic::{eval_polynomial, lagrange_interpolate, CurveAffine, FieldExt}, - poly::{query::Query, Coeff, Polynomial}, - transcript::ChallengeScalar, -}; -use rayon::prelude::*; use std::{ collections::{btree_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, hash::Hash, @@ -19,6 +8,18 @@ use std::{ sync::Arc, }; +use crate::{ + arithmetic::{eval_polynomial, lagrange_interpolate, CurveAffine}, + poly::{query::Query, Coeff, Polynomial}, + transcript::ChallengeScalar, +}; + +use ff::Field; +pub use prover::ProverSHPLONK; +use rayon::prelude::*; +use rustc_hash::FxHashSet; +pub use verifier::VerifierSHPLONK; + #[derive(Clone, Copy, Debug)] struct U {} type ChallengeU = ChallengeScalar; @@ -32,9 +33,9 @@ struct Y {} type ChallengeY = ChallengeScalar; #[derive(Debug, Clone, PartialEq)] -struct Commitment((T, Vec)); +struct Commitment((T, Vec)); -impl Commitment { +impl Commitment { fn get(&self) -> T { self.0 .0.clone() } @@ -45,18 +46,18 @@ impl Commitment { } #[derive(Debug, Clone, PartialEq)] -struct RotationSet { +struct RotationSet { commitments: Vec>, points: Vec, } #[derive(Debug, PartialEq)] -struct IntermediateSets> { +struct IntermediateSets> { rotation_sets: Vec>, super_point_set: FxHashSet, } -fn construct_intermediate_sets>( +fn construct_intermediate_sets>( queries: I, ) -> IntermediateSets where @@ -159,8 +160,8 @@ mod proptests { use super::{construct_intermediate_sets, Commitment, IntermediateSets}; use crate::poly::Rotation; - use halo2curves::{bn256::Fr, FieldExt}; - + use ff::{Field, FromUniformBytes}; + use halo2curves::bn256::Fr; use std::collections::BTreeMap; use std::convert::TryFrom; @@ -192,7 +193,7 @@ mod proptests { fn arb_point()( bytes in vec(any::(), 64) ) -> Fr { - Fr::from_bytes_wide(&<[u8; 64]>::try_from(bytes).unwrap()) + Fr::from_uniform_bytes(&<[u8; 64]>::try_from(bytes).unwrap()) } } @@ -214,7 +215,7 @@ mod proptests { col_indices in vec(select((0..num_cols).collect::>()), num_queries), point_indices in vec(select((0..num_points).collect::>()), num_queries) ) -> Vec<(usize, usize)> { - col_indices.into_iter().zip(point_indices.into_iter()).collect() + col_indices.into_iter().zip(point_indices).collect() } } diff --git a/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs b/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs index 155ef9debb..0a98ee0af6 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs @@ -1,9 +1,15 @@ +use std::fmt::Debug; +use std::hash::Hash; +use std::io::{self, Write}; +use std::marker::PhantomData; +use std::ops::MulAssign; + use super::{ construct_intermediate_sets, ChallengeU, ChallengeV, ChallengeY, Commitment, Query, RotationSet, }; use crate::arithmetic::{ eval_polynomial, evaluate_vanishing_polynomial, kate_division, lagrange_interpolate, - parallelize, powers, CurveAffine, FieldExt, + parallelize, powers, CurveAffine, }; use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::{Blind, ParamsProver, Prover}; @@ -13,19 +19,14 @@ use crate::poly::Rotation; use crate::poly::{commitment::Params, Coeff, Polynomial}; use crate::transcript::{EncodedChallenge, TranscriptWrite}; -use ff::Field; +use ff::{Field, PrimeField}; use group::Curve; -use halo2curves::pairing::Engine; +use pairing::Engine; use rand_core::RngCore; use rayon::prelude::*; use rustc_hash::FxHashSet; -use std::fmt::Debug; -use std::hash::Hash; -use std::io::{self, Write}; -use std::marker::PhantomData; -use std::ops::MulAssign; -fn div_by_vanishing(poly: Polynomial, roots: &[F]) -> Vec { +fn div_by_vanishing(poly: Polynomial, roots: &[F]) -> Vec { let poly = roots .iter() .fold(poly.values, |poly, point| kate_division(&poly, *point)); @@ -107,9 +108,9 @@ impl<'a, E: Engine> ProverSHPLONK<'a, E> { impl<'params, E: Engine + Debug> Prover<'params, KZGCommitmentScheme> for ProverSHPLONK<'params, E> where - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, - E::Scalar: Hash, + E::Fr: Hash, { const QUERY_INSTANCE: bool = false; @@ -139,7 +140,7 @@ where let y: ChallengeY<_> = transcript.squeeze_challenge_scalar(); let quotient_contribution = - |rotation_set: &RotationSetExtension| -> Polynomial { + |rotation_set: &RotationSetExtension| -> Polynomial { // [P_i_0(X) - R_i_0(X), P_i_1(X) - R_i_1(X), ... ] let numerators = rotation_set .commitments @@ -165,7 +166,7 @@ where // Q_i(X) = N_i(X) / Z_i(X) where // Z_i(X) = (x - r_i_0) * (x - r_i_1) * ... let mut poly = div_by_vanishing(n_x, points); - poly.resize(self.params.n as usize, E::Scalar::zero()); + poly.resize(self.params.n as usize, E::Fr::ZERO); Polynomial { values: poly, @@ -198,7 +199,7 @@ where .map(quotient_contribution) .collect::>(); - let h_x: Polynomial = quotient_polynomials + let h_x: Polynomial = quotient_polynomials .into_iter() .zip(powers(*v)) .map(|(poly, power_of_v)| poly * power_of_v) @@ -210,7 +211,7 @@ where let u: ChallengeU<_> = transcript.squeeze_challenge_scalar(); let linearisation_contribution = - |rotation_set: RotationSetExtension| -> (Polynomial, E::Scalar) { + |rotation_set: RotationSetExtension| -> (Polynomial, E::Fr) { let mut diffs = super_point_set.clone(); for point in rotation_set.points.iter() { diffs.remove(point); @@ -227,14 +228,20 @@ where let inner_contributions = rotation_set .commitments .par_iter() - .map(|commitment| commitment.linearisation_contribution(*u)).collect::>(); + .map(|commitment| commitment.linearisation_contribution(*u)) + .collect::>(); // define inner contributor polynomial as // L_i_j(X) = (P_i_j(X) - r_i_j) // and combine polynomials with same evaluation point set // L_i(X) = linear_combinination(y, L_i_j(X)) // where y is random scalar to combine inner contibutors - let l_x: Polynomial = inner_contributions.into_iter().zip(powers(*y)).map(|(poly, power_of_y)| poly * power_of_y).reduce(|acc, poly| acc + &poly).unwrap(); + let l_x: Polynomial = inner_contributions + .into_iter() + .zip(powers(*y)) + .map(|(poly, power_of_y)| poly * power_of_y) + .reduce(|acc, poly| acc + &poly) + .unwrap(); // finally scale l_x by difference vanishing polynomial evaluation z_i (l_x * z_i, z_i) @@ -242,14 +249,14 @@ where #[allow(clippy::type_complexity)] let (linearisation_contibutions, z_diffs): ( - Vec>, - Vec, + Vec>, + Vec, ) = rotation_sets .into_par_iter() .map(linearisation_contribution) .unzip(); - let l_x: Polynomial = linearisation_contibutions + let l_x: Polynomial = linearisation_contibutions .into_iter() .zip(powers(*v)) .map(|(poly, power_of_v)| poly * power_of_v) @@ -264,7 +271,7 @@ where #[cfg(debug_assertions)] { let must_be_zero = eval_polynomial(&l_x.values[..], *u); - assert_eq!(must_be_zero, E::Scalar::zero()); + assert_eq!(must_be_zero, E::Fr::ZERO); } let mut h_x = div_by_vanishing(l_x, &[*u]); diff --git a/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs b/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs index 77861867bc..0fe1bcfe2f 100644 --- a/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs +++ b/halo2_proofs/src/poly/kzg/multiopen/shplonk/verifier.rs @@ -6,7 +6,6 @@ use super::ChallengeY; use super::{construct_intermediate_sets, ChallengeU, ChallengeV}; use crate::arithmetic::{ eval_polynomial, evaluate_vanishing_polynomial, lagrange_interpolate, powers, CurveAffine, - FieldExt, }; use crate::helpers::SerdeCurveAffine; use crate::poly::commitment::Verifier; @@ -23,9 +22,9 @@ use crate::poly::{ Error, }; use crate::transcript::{EncodedChallenge, TranscriptRead}; -use ff::Field; +use ff::{Field, PrimeField}; use group::Group; -use halo2curves::pairing::{Engine, MillerLoopResult, MultiMillerLoop}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop}; use rand_core::OsRng; use std::ops::MulAssign; @@ -38,9 +37,9 @@ pub struct VerifierSHPLONK<'params, E: Engine> { impl<'params, E> Verifier<'params, KZGCommitmentScheme> for VerifierSHPLONK<'params, E> where E: MultiMillerLoop + Debug, - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, - E::Scalar: Hash, + E::Fr: Hash, { type Guard = GuardKZG<'params, E>; type MSMAccumulator = DualMSM<'params, E>; @@ -79,10 +78,10 @@ where let u: ChallengeU<_> = transcript.squeeze_challenge_scalar(); let h2 = transcript.read_point().map_err(|_| Error::SamplingError)?; - let (mut z_0_diff_inverse, mut z_0) = (E::Scalar::zero(), E::Scalar::zero()); - let (mut outer_msm, mut r_outer_acc) = (PreMSM::::new(), E::Scalar::zero()); + let (mut z_0_diff_inverse, mut z_0) = (E::Fr::ZERO, E::Fr::ZERO); + let (mut outer_msm, mut r_outer_acc) = (PreMSM::::new(), E::Fr::ZERO); for (i, (rotation_set, power_of_v)) in rotation_sets.iter().zip(powers(*v)).enumerate() { - let diffs: Vec = super_point_set + let diffs: Vec = super_point_set .iter() .filter(|point| !rotation_set.points.contains(point)) .copied() @@ -93,7 +92,7 @@ where if i == 0 { z_0 = evaluate_vanishing_polynomial(&rotation_set.points[..], *u); z_0_diff_inverse = z_diff_i.invert().unwrap(); - z_diff_i = E::Scalar::one(); + z_diff_i = E::Fr::ONE; } else { z_diff_i.mul_assign(z_0_diff_inverse); } @@ -139,9 +138,7 @@ where outer_msm.append_term(-z_0, h1.into()); outer_msm.append_term(*u, h2.into()); - msm_accumulator - .left - .append_term(E::Scalar::one(), h2.into()); + msm_accumulator.left.append_term(E::Fr::ONE, h2.into()); msm_accumulator.right.add_msm(&outer_msm); diff --git a/halo2_proofs/src/poly/kzg/strategy.rs b/halo2_proofs/src/poly/kzg/strategy.rs index ca4b4fb18a..9ee93b130f 100644 --- a/halo2_proofs/src/poly/kzg/strategy.rs +++ b/halo2_proofs/src/poly/kzg/strategy.rs @@ -15,12 +15,10 @@ use crate::{ }, transcript::{EncodedChallenge, TranscriptRead}, }; -use ff::Field; +use ff::{Field, PrimeField}; use group::Group; -use halo2curves::{ - pairing::{Engine, MillerLoopResult, MultiMillerLoop}, - CurveAffine, -}; +use halo2curves::CurveAffine; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop}; use rand_core::OsRng; /// Wrapper for linear verification accumulator @@ -33,7 +31,7 @@ pub struct GuardKZG<'params, E: MultiMillerLoop + Debug> { impl<'params, E> Guard> for GuardKZG<'params, E> where E: MultiMillerLoop + Debug, - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, { type MSMAccumulator = DualMSM<'params, E>; @@ -92,7 +90,7 @@ impl< >, > VerificationStrategy<'params, KZGCommitmentScheme, V> for AccumulatorStrategy<'params, E> where - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, { type Output = Self; @@ -105,7 +103,7 @@ where mut self, f: impl FnOnce(V::MSMAccumulator) -> Result, ) -> Result { - self.msm_accumulator.scale(E::Scalar::random(OsRng)); + self.msm_accumulator.scale(E::Fr::random(OsRng)); // Guard is updated with new msm contributions let guard = f(self.msm_accumulator)?; @@ -130,7 +128,7 @@ impl< >, > VerificationStrategy<'params, KZGCommitmentScheme, V> for SingleStrategy<'params, E> where - E::G1Affine: SerdeCurveAffine, + E::G1Affine: SerdeCurveAffine, E::G2Affine: SerdeCurveAffine, { type Output = (); diff --git a/halo2_proofs/src/poly/multiopen_test.rs b/halo2_proofs/src/poly/multiopen_test.rs index 8dd563b15a..b7c36d908e 100644 --- a/halo2_proofs/src/poly/multiopen_test.rs +++ b/halo2_proofs/src/poly/multiopen_test.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod test { - use crate::arithmetic::{eval_polynomial, FieldExt}; + use crate::arithmetic::eval_polynomial; use crate::plonk::Error; use crate::poly::commitment::ParamsProver; use crate::poly::commitment::{Blind, ParamsVerifier, MSM}; @@ -17,7 +17,7 @@ mod test { Keccak256Write, TranscriptRead, TranscriptReadBuffer, TranscriptWrite, TranscriptWriterBuffer, }; - use ff::Field; + use ff::{Field, PrimeField, WithSmallOrderMulGroup}; use group::{Curve, Group}; use halo2curves::CurveAffine; use rand_core::{OsRng, RngCore}; @@ -103,7 +103,7 @@ mod test { use crate::poly::kzg::multiopen::{ProverGWC, VerifierGWC}; use crate::poly::kzg::strategy::AccumulatorStrategy; use halo2curves::bn256::{Bn256, G1Affine}; - use halo2curves::pairing::Engine; + use pairing::Engine; const K: u32 = 4; @@ -135,7 +135,7 @@ mod test { use crate::poly::kzg::multiopen::{ProverSHPLONK, VerifierSHPLONK}; use crate::poly::kzg::strategy::AccumulatorStrategy; use halo2curves::bn256::{Bn256, G1Affine}; - use halo2curves::pairing::Engine; + use pairing::Engine; const K: u32 = 4; @@ -233,28 +233,25 @@ mod test { T: TranscriptWriterBuffer, Scheme::Curve, E>, >( params: &'params Scheme::ParamsProver, - ) -> Vec { + ) -> Vec + where + Scheme::Scalar: WithSmallOrderMulGroup<3>, + { let domain = EvaluationDomain::new(1, params.k()); let mut ax = domain.empty_coeff(); for (i, a) in ax.iter_mut().enumerate() { - *a = <::Curve as CurveAffine>::ScalarExt::from( - 10 + i as u64, - ); + *a = <::Scalar>::from(10 + i as u64); } let mut bx = domain.empty_coeff(); for (i, a) in bx.iter_mut().enumerate() { - *a = <::Curve as CurveAffine>::ScalarExt::from( - 100 + i as u64, - ); + *a = <::Scalar>::from(100 + i as u64); } let mut cx = domain.empty_coeff(); for (i, a) in cx.iter_mut().enumerate() { - *a = <::Curve as CurveAffine>::ScalarExt::from( - 100 + i as u64, - ); + *a = <::Scalar>::from(100 + i as u64); } let mut transcript = T::init(vec![]); diff --git a/halo2_proofs/src/transcript.rs b/halo2_proofs/src/transcript.rs index 14c41768f5..ea8cf4f5ea 100644 --- a/halo2_proofs/src/transcript.rs +++ b/halo2_proofs/src/transcript.rs @@ -2,11 +2,11 @@ //! transcripts. use blake2b_simd::{Params as Blake2bParams, State as Blake2bState}; -use group::ff::PrimeField; +use group::ff::{FromUniformBytes, PrimeField}; use sha3::{Digest, Keccak256}; use std::convert::TryInto; -use halo2curves::{Coordinates, CurveAffine, FieldExt}; +use halo2curves::{Coordinates, CurveAffine}; use std::io::{self, Read, Write}; use std::marker::PhantomData; @@ -116,6 +116,8 @@ pub struct Keccak256Read> { impl TranscriptReadBuffer> for Blake2bRead> +where + C::Scalar: FromUniformBytes<64>, { /// Initialize a transcript given an input buffer. fn init(reader: R) -> Self { @@ -132,6 +134,8 @@ impl TranscriptReadBuffer> impl TranscriptReadBuffer> for Keccak256Read> +where + C::Scalar: FromUniformBytes<64>, { /// Initialize a transcript given an input buffer. fn init(reader: R) -> Self { @@ -147,6 +151,8 @@ impl TranscriptReadBuffer> impl TranscriptRead> for Blake2bRead> +where + C::Scalar: FromUniformBytes<64>, { fn read_point(&mut self) -> io::Result { let mut compressed = C::Repr::default(); @@ -176,6 +182,8 @@ impl TranscriptRead> impl TranscriptRead> for Keccak256Read> +where + C::Scalar: FromUniformBytes<64>, { fn read_point(&mut self) -> io::Result { let mut compressed = C::Repr::default(); @@ -203,8 +211,9 @@ impl TranscriptRead> } } -impl Transcript> - for Blake2bRead> +impl Transcript> for Blake2bRead> +where + C::Scalar: FromUniformBytes<64>, { fn squeeze_challenge(&mut self) -> Challenge255 { self.state.update(&[BLAKE2B_PREFIX_CHALLENGE]); @@ -237,6 +246,8 @@ impl Transcript> impl Transcript> for Keccak256Read> +where + C::Scalar: FromUniformBytes<64>, { fn squeeze_challenge(&mut self) -> Challenge255 { self.state.update([KECCAK256_PREFIX_CHALLENGE]); @@ -295,6 +306,8 @@ pub struct Keccak256Write> { impl TranscriptWriterBuffer> for Blake2bWrite> +where + C::Scalar: FromUniformBytes<64>, { /// Initialize a transcript given an output buffer. fn init(writer: W) -> Self { @@ -316,6 +329,8 @@ impl TranscriptWriterBuffer> impl TranscriptWriterBuffer> for Keccak256Write> +where + C::Scalar: FromUniformBytes<64>, { /// Initialize a transcript given an output buffer. fn init(writer: W) -> Self { @@ -337,6 +352,8 @@ impl TranscriptWriterBuffer> impl TranscriptWrite> for Blake2bWrite> +where + C::Scalar: FromUniformBytes<64>, { fn write_point(&mut self, point: C) -> io::Result<()> { self.common_point(point)?; @@ -352,6 +369,8 @@ impl TranscriptWrite> impl TranscriptWrite> for Keccak256Write> +where + C::Scalar: FromUniformBytes<64>, { fn write_point(&mut self, point: C) -> io::Result<()> { self.common_point(point)?; @@ -367,6 +386,8 @@ impl TranscriptWrite> impl Transcript> for Blake2bWrite> +where + C::Scalar: FromUniformBytes<64>, { fn squeeze_challenge(&mut self) -> Challenge255 { self.state.update(&[BLAKE2B_PREFIX_CHALLENGE]); @@ -399,6 +420,8 @@ impl Transcript> impl Transcript> for Keccak256Write> +where + C::Scalar: FromUniformBytes<64>, { fn squeeze_challenge(&mut self) -> Challenge255 { self.state.update([KECCAK256_PREFIX_CHALLENGE]); @@ -493,12 +516,15 @@ impl std::ops::Deref for Challenge255 { } } -impl EncodedChallenge for Challenge255 { +impl EncodedChallenge for Challenge255 +where + C::Scalar: FromUniformBytes<64>, +{ type Input = [u8; 64]; fn new(challenge_input: &[u8; 64]) -> Self { Challenge255( - C::Scalar::from_bytes_wide(challenge_input) + C::Scalar::from_uniform_bytes(challenge_input) .to_repr() .as_ref() .try_into() diff --git a/halo2_proofs/tests/plonk_api.rs b/halo2_proofs/tests/plonk_api.rs index 5f0919f086..3e1db8231d 100644 --- a/halo2_proofs/tests/plonk_api.rs +++ b/halo2_proofs/tests/plonk_api.rs @@ -4,7 +4,8 @@ #![allow(dead_code)] // use assert_matches::assert_matches; -use halo2_proofs::arithmetic::{Field, FieldExt}; +use ff::{FromUniformBytes, WithSmallOrderMulGroup}; +use halo2_proofs::arithmetic::Field; use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner, Value}; use halo2_proofs::dev::MockProver; use halo2_proofs::plonk::{ @@ -49,7 +50,7 @@ fn plonk_api() { } #[allow(clippy::type_complexity)] - trait StandardCs { + trait StandardCs { fn raw_multiply( &self, layouter: &mut impl Layouter, @@ -76,17 +77,17 @@ fn plonk_api() { } #[derive(Clone)] - struct MyCircuit { + struct MyCircuit { a: Value, lookup_table: Vec, } - struct StandardPlonk { + struct StandardPlonk { config: PlonkConfig, _marker: PhantomData, } - impl StandardPlonk { + impl StandardPlonk { fn new(config: PlonkConfig) -> Self { StandardPlonk { config, @@ -95,7 +96,7 @@ fn plonk_api() { } } - impl StandardCs for StandardPlonk { + impl StandardCs for StandardPlonk { fn raw_multiply( &self, layouter: &mut impl Layouter, @@ -107,7 +108,7 @@ fn plonk_api() { layouter.assign_region( || "raw_multiply", |mut region| { - let mut value = None; + let value; let lhs = region.assign_advice(self.config.a, 0, { value = Some(f()); value.unwrap().map(|v| v.0) @@ -125,10 +126,10 @@ fn plonk_api() { ); let out = region.assign_advice(self.config.c, 0, value.unwrap().map(|v| v.2)); - region.assign_fixed(self.config.sa, 0, FF::zero()); - region.assign_fixed(self.config.sb, 0, FF::zero()); - region.assign_fixed(self.config.sc, 0, FF::one()); - region.assign_fixed(self.config.sm, 0, FF::one()); + region.assign_fixed(self.config.sa, 0, FF::ZERO); + region.assign_fixed(self.config.sb, 0, FF::ZERO); + region.assign_fixed(self.config.sc, 0, FF::ONE); + region.assign_fixed(self.config.sm, 0, FF::ONE); Ok((*lhs.cell(), *rhs.cell(), *out.cell())) }, ) @@ -144,7 +145,7 @@ fn plonk_api() { layouter.assign_region( || "raw_add", |mut region| { - let mut value = None; + let value; let lhs = region.assign_advice(self.config.a, 0, { value = Some(f()); value.unwrap().map(|v| v.0) @@ -162,10 +163,10 @@ fn plonk_api() { ); let out = region.assign_advice(self.config.c, 0, value.unwrap().map(|v| v.2)); - region.assign_fixed(self.config.sa, 0, FF::one()); - region.assign_fixed(self.config.sb, 0, FF::one()); - region.assign_fixed(self.config.sc, 0, FF::one()); - region.assign_fixed(self.config.sm, 0, FF::zero()); + region.assign_fixed(self.config.sa, 0, FF::ONE); + region.assign_fixed(self.config.sb, 0, FF::ONE); + region.assign_fixed(self.config.sc, 0, FF::ONE); + region.assign_fixed(self.config.sm, 0, FF::ZERO); Ok((*lhs.cell(), *rhs.cell(), *out.cell())) }, ) @@ -193,7 +194,7 @@ fn plonk_api() { || "public_input", |mut region| { let value = region.assign_advice(self.config.a, 0, f()); - region.assign_fixed(self.config.sp, 0, FF::one()); + region.assign_fixed(self.config.sp, 0, FF::ONE); Ok(*value.cell()) }, @@ -222,9 +223,11 @@ fn plonk_api() { } } - impl Circuit for MyCircuit { + impl Circuit for MyCircuit { type Config = PlonkConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self { @@ -330,7 +333,7 @@ fn plonk_api() { ) -> Result<(), Error> { let cs = StandardPlonk::new(config); - let _ = cs.public_input(&mut layouter, || Value::known(F::one() + F::one()))?; + let _ = cs.public_input(&mut layouter, || Value::known(F::ONE + F::ONE))?; for _ in 0..10 { let a: Value> = self.a.into(); @@ -359,14 +362,9 @@ fn plonk_api() { ($scheme:ident) => {{ let a = <$scheme as CommitmentScheme>::Scalar::from(2834758237) * <$scheme as CommitmentScheme>::Scalar::ZETA; - let instance = <$scheme as CommitmentScheme>::Scalar::one() - + <$scheme as CommitmentScheme>::Scalar::one(); - let lookup_table = vec![ - instance, - a, - a, - <$scheme as CommitmentScheme>::Scalar::zero(), - ]; + let instance = <$scheme as CommitmentScheme>::Scalar::ONE + + <$scheme as CommitmentScheme>::Scalar::ONE; + let lookup_table = vec![instance, a, a, <$scheme as CommitmentScheme>::Scalar::ZERO]; (a, instance, lookup_table) }}; } @@ -401,9 +399,10 @@ fn plonk_api() { }}; } - fn keygen( - params: &Scheme::ParamsProver, - ) -> ProvingKey { + fn keygen(params: &Scheme::ParamsProver) -> ProvingKey + where + Scheme::Scalar: FromUniformBytes<64> + WithSmallOrderMulGroup<3>, + { let (_, _, lookup_table) = common!(Scheme); let empty_circuit: MyCircuit = MyCircuit { a: Value::unknown(), @@ -429,7 +428,7 @@ fn plonk_api() { pk: &ProvingKey, ) -> Vec where - Scheme::Scalar: Hash, + Scheme::Scalar: Hash + Ord + WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { let (a, instance, lookup_table) = common!(Scheme); @@ -472,9 +471,11 @@ fn plonk_api() { params_verifier: &'params Scheme::ParamsVerifier, vk: &VerifyingKey, proof: &'a [u8], - ) { + ) where + Scheme::Scalar: Ord + WithSmallOrderMulGroup<3> + FromUniformBytes<64>, + { let (_, instance, _) = common!(Scheme); - let pubinputs = vec![instance]; + let pubinputs = [instance]; let mut transcript = T::init(proof); diff --git a/primitives/poseidon/.gitignore b/primitives/poseidon/.gitignore deleted file mode 100644 index 869df07dae..0000000000 --- a/primitives/poseidon/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock \ No newline at end of file diff --git a/primitives/poseidon/.rustfmt.toml b/primitives/poseidon/.rustfmt.toml deleted file mode 100644 index 606e29234d..0000000000 --- a/primitives/poseidon/.rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -wrap_comments = true \ No newline at end of file diff --git a/primitives/poseidon/Cargo.toml b/primitives/poseidon/Cargo.toml deleted file mode 100644 index afd41baec9..0000000000 --- a/primitives/poseidon/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "poseidon" -version = "0.2.0" -authors = ["kilic "] -edition = "2021" -license = "MIT OR Apache-2.0" - -[dependencies] -halo2curves = { path = "../../arithmetic/curves" } -group = "0.12" -subtle = { version = "2.3", default-features = false } - -[dev-dependencies] -rand_core = { version = "0.6", default-features = false } diff --git a/primitives/poseidon/LICENSE-APACHE b/primitives/poseidon/LICENSE-APACHE deleted file mode 100644 index 8e50fa5fe0..0000000000 --- a/primitives/poseidon/LICENSE-APACHE +++ /dev/null @@ -1,64 +0,0 @@ -APACHE LICENSE -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -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. - -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. - -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. - -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: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and -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 -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. - -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. - -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. - -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. - -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. - -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 2022 Ethereum Foundation - -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. \ No newline at end of file diff --git a/primitives/poseidon/LICENSE-MIT b/primitives/poseidon/LICENSE-MIT deleted file mode 100644 index f29698d015..0000000000 --- a/primitives/poseidon/LICENSE-MIT +++ /dev/null @@ -1,9 +0,0 @@ -MIT LICENSE - -Copyright 2022 Ethereum Foundation - -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. \ No newline at end of file diff --git a/primitives/poseidon/README.md b/primitives/poseidon/README.md deleted file mode 100644 index 09e40fe852..0000000000 --- a/primitives/poseidon/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# `poseidon` - -`poseidon` is built to be used in SNARK and non native recursion friendly transcript for [appliedzkp/halo2](https://github.com/appliedzkp/halo2/). - -[Poseidon hash function](https://eprint.iacr.org/2019/458.pdf) implmenetation is in line with the reference and the [test vectors](https://extgit.iaik.tugraz.at/krypto/hadeshash/-/tree/master/code). It also uses optimized constants and sparse MDS matrices to reduce number of multiplications. For now constants are calculated in construction time they are planned to be hardcoded once transcript design matures. Currently only supports variable length hashing with $\alpha = 5$ sbox. Some parts of Poseidon implementation are adapted or ported from: - -* [filecoin-project/neptune](https://github.com/filecoin-project/neptune/tree/master/spec) -* [matter-labs/rescue-poseidon](https://github.com/matter-labs/rescue-poseidon) -* [zcash/halo2/halo2_gadgets](https://github.com/zcash/halo2/tree/main/halo2_gadgets) -* [dusk-network/Poseidon252](https://github.com/dusk-network/Poseidon252) - -## Example usage - -```rust -// Initialize a mutable hasher with constant capacity parameters -// and number of rounds arguments. This will also generate matrices -// and constants according to the specification -let mut hasher = Poseidon::::new(number_of_full_rounds, number_of_half_rounds); - -// In sake of the example we generate some dummy scalar inputs -let inputs = (0..number_of_inputs_0) - .map(|_| Fr::random(&mut rng)) - .collect::>(); - -// Feed inputs to the Absorption line -hasher.update(&inputs[..]); - -// Yield your challange with squeeze function -let challenge_alpha = hasher.squeeze(); - -// Then again ... -let inputs = (0..number_of_inputs_1) - .map(|_| Fr::random(&mut rng)) - .collect::>(); -hasher.update(&inputs[..]); -let challenge_beta = hasher.squeeze(); - -``` diff --git a/primitives/poseidon/src/grain.rs b/primitives/poseidon/src/grain.rs deleted file mode 100644 index 569150aeba..0000000000 --- a/primitives/poseidon/src/grain.rs +++ /dev/null @@ -1,161 +0,0 @@ -use crate::spec::MDSMatrix; -use halo2curves::FieldExt; -use std::marker::PhantomData; - -/// Grain initializes round constants and MDS matrix at given sponge parameters -pub(super) struct Grain { - bit_sequence: Vec, - _field: PhantomData, -} - -impl Grain { - pub(crate) fn generate(r_f: usize, r_p: usize) -> (Vec<[F; T]>, MDSMatrix) { - debug_assert!(T > 1 && T == RATE + 1); - - // Support only prime field construction - const FIELD_TYPE: u8 = 1u8; - // Support only \alpha s-box - const SBOX_TYPE: u8 = 0; - - let field_size = F::NUM_BITS; - let n_bytes = F::Repr::default().as_ref().len(); - assert_eq!((field_size as f32 / 8.0).ceil() as usize, n_bytes); - assert_eq!(r_f % 2, 0); - - // Pseudo random number generation. See: - // Initialization of the Grain LFSR Used for Parameter Generation - // Supplementary Material Section F - // https://eprint.iacr.org/2019/458.pdf - let mut bit_sequence: Vec = Vec::new(); - append_bits(&mut bit_sequence, 2, FIELD_TYPE); - append_bits(&mut bit_sequence, 4, SBOX_TYPE); - append_bits(&mut bit_sequence, 12, field_size); - append_bits(&mut bit_sequence, 12, T as u32); - append_bits(&mut bit_sequence, 10, r_f as u16); - append_bits(&mut bit_sequence, 10, r_p as u16); - append_bits(&mut bit_sequence, 30, 0b111111111111111111111111111111u128); - debug_assert_eq!(bit_sequence.len(), 80); - - let mut grain: Grain = Grain { - bit_sequence, - _field: PhantomData, - }; - - for _ in 0..160 { - grain.new_bit(); - } - assert_eq!(grain.bit_sequence.len(), 80); - - let number_of_rounds = r_p + r_f; - let constants = (0..number_of_rounds) - .map(|_| { - let mut round_constants = [F::zero(); T]; - for c in round_constants.iter_mut() { - *c = grain.next_field_element(); - } - round_constants - }) - .collect::>(); - - let (mut xs, mut ys) = ([F::zero(); T], [F::zero(); T]); - for x in xs.iter_mut() { - *x = grain.next_field_element_without_rejection(); - } - for y in ys.iter_mut() { - *y = grain.next_field_element_without_rejection(); - } - - (constants, MDSMatrix::cauchy(&xs, &ys)) - } - - /// Credit: https://github.com/zcash/halo2/tree/main/halo2_gadgets/src/primitives/poseidon - /// Returns the next field element from this Grain instantiation. - pub(super) fn next_field_element(&mut self) -> F { - // Loop until we get an element in the field. - loop { - let mut bytes = F::Repr::default(); - - // Poseidon reference impl interprets the bits as a repr in MSB order, because - // it's easy to do that in Python. Meanwhile, our field elements all use LSB - // order. There's little motivation to diverge from the reference impl; these - // are all constants, so we aren't introducing big-endianness into the rest of - // the circuit (assuming unkeyed Poseidon, but we probably wouldn't want to - // implement Grain inside a circuit, so we'd use a different round constant - // derivation function there). - let view = bytes.as_mut(); - for (i, bit) in self.take(F::NUM_BITS as usize).enumerate() { - // If we diverged from the reference impl and interpreted the bits in LSB - // order, we would remove this line. - let i = F::NUM_BITS as usize - 1 - i; - - view[i / 8] |= if bit { 1 << (i % 8) } else { 0 }; - } - - if let Some(f) = F::from_repr_vartime(bytes) { - break f; - } - } - } - - /// Credit: https://github.com/zcash/halo2/tree/main/halo2_gadgets/src/primitives/poseidon - /// Returns the next field element from this Grain instantiation, without - /// using rejection sampling. - pub(super) fn next_field_element_without_rejection(&mut self) -> F { - let mut bytes = [0u8; 64]; - - // Poseidon reference impl interprets the bits as a repr in MSB order, because - // it's easy to do that in Python. Additionally, it does not use rejection - // sampling in cases where the constants don't specifically need to be uniformly - // random for security. We do not provide APIs that take a field-element-sized - // array and reduce it modulo the field order, because those are unsafe APIs to - // offer generally (accidentally using them can lead to divergence in consensus - // systems due to not rejecting canonical forms). - // - // Given that we don't want to diverge from the reference implementation, we - // hack around this restriction by serializing the bits into a 64-byte - // array and then calling F::from_bytes_wide. PLEASE DO NOT COPY THIS - // INTO YOUR OWN CODE! - let view = bytes.as_mut(); - for (i, bit) in self.take(F::NUM_BITS as usize).enumerate() { - // If we diverged from the reference impl and interpreted the bits in LSB - // order, we would remove this line. - let i = F::NUM_BITS as usize - 1 - i; - - view[i / 8] |= if bit { 1 << (i % 8) } else { 0 }; - } - - F::from_bytes_wide(&bytes) - } - - fn new_bit(&mut self) -> bool { - // See supplementary material Section F. Step 2. - // https://eprint.iacr.org/2019/458.pdf - let new_bit = vec![62, 51, 38, 23, 13usize] - .iter() - .fold(self.bit_sequence[0], |acc, pos| { - acc ^ self.bit_sequence[*pos] - }); - assert_eq!(self.bit_sequence.len(), 80); - self.bit_sequence.remove(0); - self.bit_sequence.push(new_bit); - new_bit - } -} - -impl Iterator for Grain { - type Item = bool; - - fn next(&mut self) -> Option { - while !self.new_bit() { - self.new_bit(); - } - Some(self.new_bit()) - } -} - -fn append_bits>(vec: &mut Vec, n: usize, from: T) { - let val = from.into(); - for i in (0..n).rev() { - vec.push((val >> i) & 1 != 0); - } -} diff --git a/primitives/poseidon/src/lib.rs b/primitives/poseidon/src/lib.rs deleted file mode 100644 index 492a77b1ad..0000000000 --- a/primitives/poseidon/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Poseidon hashing implemention with variable lenght input setting. This crate -//! also exposes constant parameters for circuit implementations - -#![deny(missing_debug_implementations)] -#![deny(missing_docs)] - -mod grain; -mod matrix; -mod permutation; -mod poseidon; -mod spec; - -pub use crate::poseidon::Poseidon; -pub use crate::spec::{MDSMatrices, MDSMatrix, SparseMDSMatrix, Spec, State}; diff --git a/primitives/poseidon/src/matrix.rs b/primitives/poseidon/src/matrix.rs deleted file mode 100644 index d29770b90f..0000000000 --- a/primitives/poseidon/src/matrix.rs +++ /dev/null @@ -1,145 +0,0 @@ -//! Most of these operations here are not suitable for general purpose matrix -//! operations. Besides vector multiplication other operations are presented -//! with the intention of construction of parameters and are not used in the -//! actual permutation process. - -use halo2curves::FieldExt; - -#[derive(PartialEq, Debug, Clone)] -pub(crate) struct Matrix(pub(crate) [[F; T]; T]); - -impl Default for Matrix { - fn default() -> Self { - Matrix([[F::zero(); T]; T]) - } -} - -impl Matrix { - #[inline] - pub(crate) fn zero_matrix() -> Self { - Self([[F::zero(); T]; T]) - } - - #[inline] - pub(crate) fn identity() -> Self { - let mut m = Self::zero_matrix(); - for i in 0..T { - m.set(i, i, F::one()) - } - m - } - - pub(crate) fn set(&mut self, row: usize, col: usize, value: F) { - self.0[row][col] = value; - } - - pub(crate) fn from_vec(vec: Vec>) -> Self { - let n = vec.len(); - // Expect square and well formed matrix - for row in vec.iter() { - assert_eq!(row.len(), n); - } - - let mut result = Self::default(); - for (row_result, row_inverted) in result.0.iter_mut().zip(vec.iter()) { - for (result_cell, cell) in row_result.iter_mut().zip(row_inverted.iter()) { - *result_cell = *cell - } - } - result - } - - pub(crate) fn transpose(&self) -> Self { - let mut result = Self::default(); - for (i, row) in self.0.iter().enumerate() { - for (j, e) in row.iter().enumerate() { - result.0[j][i] = *e - } - } - result - } - - pub(crate) fn mul(&self, other: &Self) -> Self { - let mut result = Self::default(); - for i in 0..T { - for j in 0..T { - for k in 0..T { - result.0[i][j] += self.0[i][k] * other.0[k][j]; - } - } - } - result - } - - pub(crate) fn mul_vector(&self, v: &[F; T]) -> [F; T] { - let mut result = [F::zero(); T]; - for (row, cell) in self.0.iter().zip(result.iter_mut()) { - for (a_i, v_i) in row.iter().zip(v.iter()) { - *cell += *v_i * *a_i; - } - } - result - } - - // This is very pesky implementation of matrix inversion, - // It won't even alarm when a matrix is not invertable. - pub(crate) fn invert(&self) -> Self { - let identity = Self::identity(); - - let mut m: Vec> = identity - .0 - .iter() - .zip(self.0.iter()) - .map(|(u_row, v_row)| { - let mut row = v_row.to_vec(); - row.extend(u_row.to_vec()); - row - }) - .collect(); - - for i in 0..T { - for j in 0..T { - if i != j { - let r = m[j][i] * m[i][i].invert().unwrap(); - for k in 0..2 * T { - let e = m[i][k]; - m[j][k] -= r * e; - } - } - } - } - - let mut res = Self::default(); - for (i, row) in m.iter_mut().enumerate().take(T) { - for j in T..2 * T { - let e = row[i]; - row[j] *= e.invert().unwrap() - } - } - - for (i, row) in m.iter().enumerate().take(T) { - for j in 0..T { - res.set(i, j, row[j + T]); - } - } - res - } - - #[inline] - pub(crate) fn w(&self) -> [F; RATE] { - assert_eq!(RATE + 1, T); - self.0 - .iter() - .skip(1) - .map(|row| row[0]) - .collect::>() - .try_into() - .unwrap() - } - - #[inline] - pub(crate) fn sub(&self) -> Matrix { - assert_eq!(RATE + 1, T); - Matrix::::from_vec(self.0.iter().skip(1).map(|row| row[1..].to_vec()).collect()) - } -} diff --git a/primitives/poseidon/src/permutation.rs b/primitives/poseidon/src/permutation.rs deleted file mode 100644 index 2f7baf213a..0000000000 --- a/primitives/poseidon/src/permutation.rs +++ /dev/null @@ -1,207 +0,0 @@ -use crate::spec::{Spec, State}; -use halo2curves::FieldExt; - -impl Spec { - /// Applies the Poseidon permutation to the given state - pub fn permute(&self, state: &mut State) { - let r_f = self.r_f / 2; - - // First half of the full rounds - { - state.add_constants(&self.constants.start[0]); - for round_constants in self.constants.start.iter().skip(1).take(r_f - 1) { - state.sbox_full(); - state.add_constants(round_constants); - self.mds_matrices.mds.apply(state); - } - state.sbox_full(); - state.add_constants(self.constants.start.last().unwrap()); - self.mds_matrices.pre_sparse_mds.apply(state) - } - - // Partial rounds - { - for (round_constant, sparse_mds) in self - .constants - .partial - .iter() - .zip(self.mds_matrices.sparse_matrices.iter()) - { - state.sbox_part(); - state.add_constant(round_constant); - sparse_mds.apply(state); - } - } - - // Second half of the full rounds - { - for round_constants in self.constants.end.iter() { - state.sbox_full(); - state.add_constants(round_constants); - self.mds_matrices.mds.apply(state); - } - state.sbox_full(); - self.mds_matrices.mds.apply(state); - } - } -} - -#[cfg(test)] -mod tests { - use super::State; - use crate::spec::{tests::SpecRef, Spec}; - use group::ff::PrimeField; - use halo2curves::bn256::Fr; - use halo2curves::FieldExt; - - /// We want to keep unoptimized poseidion construction and permutation to - /// cross test with optimized one - impl SpecRef { - fn permute(&self, state: &mut State) { - let (r_f, r_p) = (self.r_f / 2, self.r_p); - - for constants in self.constants.iter().take(r_f) { - state.add_constants(constants); - state.sbox_full(); - self.mds.apply(state); - } - - for constants in self.constants.iter().skip(r_f).take(r_p) { - state.add_constants(constants); - state.sbox_part(); - self.mds.apply(state); - } - - for constants in self.constants.iter().skip(r_f + r_p) { - state.add_constants(constants); - state.sbox_full(); - self.mds.apply(state); - } - } - } - - #[test] - fn cross_test() { - use halo2curves::group::ff::Field; - use rand_core::OsRng; - use std::time::Instant; - - macro_rules! run_test { - ( - $([$RF:expr, $RP:expr, $T:expr, $RATE:expr]),* - ) => { - $( - { - const R_F: usize = $RF; - const R_P: usize = $RP; - const T: usize = $T; - const RATE: usize = $RATE; - let mut state = State( - (0..T) - .map(|_| Fr::random(OsRng)) - .collect::>() - .try_into().unwrap(), - ); - let spec = SpecRef::::new(R_F, R_P); - let mut state_expected = state.clone(); - spec.permute(&mut state_expected); - - let spec = Spec::::new(R_F, R_P); - let now = Instant::now(); - { - spec.permute(&mut state); - } - let elapsed = now.elapsed(); - println!("Elapsed: {:.2?}", elapsed); - assert_eq!(state_expected, state); - } - )* - }; - } - run_test!([8, 57, 3, 2]); - run_test!([8, 57, 4, 3]); - run_test!([8, 57, 5, 4]); - run_test!([8, 57, 6, 5]); - run_test!([8, 57, 7, 6]); - run_test!([8, 57, 8, 7]); - run_test!([8, 57, 9, 8]); - run_test!([8, 57, 10, 9]); - } - - #[test] - fn test_against_test_vectors() { - // https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt - // poseidonperm_x5_254_3 - { - const R_F: usize = 8; - const R_P: usize = 57; - const T: usize = 3; - const RATE: usize = 2; - - let state = State( - vec![0u64, 1, 2] - .into_iter() - .map(Fr::from) - .collect::>() - .try_into() - .unwrap(), - ); - - let spec_ref = SpecRef::::new(R_F, R_P); - let mut state_0 = state.clone(); - - spec_ref.permute(&mut state_0); - let expected = vec![ - "7853200120776062878684798364095072458815029376092732009249414926327459813530", - "7142104613055408817911962100316808866448378443474503659992478482890339429929", - "6549537674122432311777789598043107870002137484850126429160507761192163713804", - ]; - for (word, expected) in state_0.words().into_iter().zip(expected.iter()) { - assert_eq!(word, Fr::from_str_vartime(expected).unwrap()); - } - - let spec = Spec::::new(R_F, R_P); - let mut state_1 = state; - spec.permute(&mut state_1); - assert_eq!(state_0, state_1); - } - - // https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt - // poseidonperm_x5_254_5 - { - const R_F: usize = 8; - const R_P: usize = 60; - const T: usize = 5; - const RATE: usize = 4; - - let state = State( - vec![0u64, 1, 2, 3, 4] - .into_iter() - .map(Fr::from) - .collect::>() - .try_into() - .unwrap(), - ); - - let spec_ref = SpecRef::::new(R_F, R_P); - let mut state_0 = state.clone(); - - spec_ref.permute(&mut state_0); - let expected = vec![ - "18821383157269793795438455681495246036402687001665670618754263018637548127333", - "7817711165059374331357136443537800893307845083525445872661165200086166013245", - "16733335996448830230979566039396561240864200624113062088822991822580465420551", - "6644334865470350789317807668685953492649391266180911382577082600917830417726", - "3372108894677221197912083238087960099443657816445944159266857514496320565191", - ]; - for (word, expected) in state_0.words().into_iter().zip(expected.iter()) { - assert_eq!(word, Fr::from_str_vartime(expected).unwrap()); - } - - let spec = Spec::::new(R_F, R_P); - let mut state_1 = state; - spec.permute(&mut state_1); - assert_eq!(state_0, state_1); - } - } -} diff --git a/primitives/poseidon/src/poseidon.rs b/primitives/poseidon/src/poseidon.rs deleted file mode 100644 index 90023e3da6..0000000000 --- a/primitives/poseidon/src/poseidon.rs +++ /dev/null @@ -1,187 +0,0 @@ -use crate::{Spec, State}; -use halo2curves::FieldExt; - -/// Poseidon hasher that maintains state and inputs and yields single element -/// output when desired -#[derive(Debug, Clone)] -pub struct Poseidon { - state: State, - spec: Spec, - absorbing: Vec, -} - -impl Poseidon { - /// Constructs a clear state poseidon instance - pub fn new(r_f: usize, r_p: usize) -> Self { - Self { - spec: Spec::new(r_f, r_p), - state: State::default(), - absorbing: Vec::new(), - } - } - - /// Appends elements to the absorption line updates state while `RATE` is - /// full - pub fn update(&mut self, elements: &[F]) { - let mut input_elements = self.absorbing.clone(); - input_elements.extend_from_slice(elements); - - for chunk in input_elements.chunks(RATE) { - if chunk.len() < RATE { - // Must be the last iteration of this update. Feed unpermutaed inputs to the - // absorbation line - self.absorbing = chunk.to_vec(); - } else { - // Add new chunk of inputs for the next permutation cycle. - for (input_element, state) in chunk.iter().zip(self.state.0.iter_mut().skip(1)) { - state.add_assign(input_element); - } - // Perform intermediate permutation - self.spec.permute(&mut self.state); - // Flush the absorption line - self.absorbing.clear(); - } - } - } - - /// Results a single element by absorbing already added inputs - pub fn squeeze(&mut self) -> F { - let mut last_chunk = self.absorbing.clone(); - { - // Expect padding offset to be in [0, RATE) - debug_assert!(last_chunk.len() < RATE); - } - // Add the finishing sign of the variable length hashing. Note that this mut - // also apply when absorbing line is empty - last_chunk.push(F::one()); - // Add the last chunk of inputs to the state for the final permutation cycle - - for (input_element, state) in last_chunk.iter().zip(self.state.0.iter_mut().skip(1)) { - state.add_assign(input_element); - } - - // Perform final permutation - self.spec.permute(&mut self.state); - // Flush the absorption line - self.absorbing.clear(); - // Returns the challenge while preserving internal state - self.state.result() - } -} - -#[test] -fn test_padding() { - use group::ff::Field; - use halo2curves::bn256::Fr; - - const R_F: usize = 8; - const R_P: usize = 57; - const T: usize = 5; - const RATE: usize = 4; - - use rand_core::OsRng; - - // w/o extra permutation - { - let mut poseidon = Poseidon::::new(R_F, R_P); - let number_of_permutation = 5; - let number_of_inputs = RATE * number_of_permutation - 1; - let inputs = (0..number_of_inputs) - .map(|_| Fr::random(OsRng)) - .collect::>(); - poseidon.update(&inputs[..]); - let result_0 = poseidon.squeeze(); - - let spec = poseidon.spec.clone(); - let mut inputs = inputs.clone(); - inputs.push(Fr::one()); - assert!(inputs.len() % RATE == 0); - let mut state = State::::default(); - for chunk in inputs.chunks(RATE) { - let mut inputs = vec![Fr::zero()]; - inputs.extend_from_slice(chunk); - state.add_constants(&inputs.try_into().unwrap()); - spec.permute(&mut state) - } - let result_1 = state.result(); - - assert_eq!(result_0, result_1); - } - - // w/ extra permutation - { - let mut poseidon = Poseidon::::new(R_F, R_P); - let number_of_permutation = 5; - let number_of_inputs = RATE * number_of_permutation; - let inputs = (0..number_of_inputs) - .map(|_| Fr::random(OsRng)) - .collect::>(); - poseidon.update(&inputs[..]); - let result_0 = poseidon.squeeze(); - - let spec = poseidon.spec.clone(); - let mut inputs = inputs.clone(); - let mut extra_padding = vec![Fr::zero(); RATE]; - extra_padding[0] = Fr::one(); - inputs.extend(extra_padding); - - assert!(inputs.len() % RATE == 0); - let mut state = State::::default(); - for chunk in inputs.chunks(RATE) { - let mut inputs = vec![Fr::zero()]; - inputs.extend_from_slice(chunk); - state.add_constants(&inputs.try_into().unwrap()); - spec.permute(&mut state) - } - let result_1 = state.result(); - - assert_eq!(result_0, result_1); - } - - // Much generic comparision - fn run() { - for number_of_iters in 1..25 { - let mut poseidon = Poseidon::::new(R_F, R_P); - - let mut inputs = vec![]; - for number_of_inputs in 0..=number_of_iters { - let chunk = (0..number_of_inputs) - .map(|_| Fr::random(OsRng)) - .collect::>(); - poseidon.update(&chunk[..]); - inputs.extend(chunk); - } - let result_0 = poseidon.squeeze(); - - // Accept below as reference and check consistency - inputs.push(Fr::one()); - let offset = inputs.len() % RATE; - if offset != 0 { - inputs.extend(vec![Fr::zero(); RATE - offset]); - } - - let spec = poseidon.spec.clone(); - let mut state = State::::default(); - for chunk in inputs.chunks(RATE) { - // First element is zero - let mut round_inputs = vec![Fr::zero()]; - // Round inputs must be T sized now - round_inputs.extend_from_slice(chunk); - - state.add_constants(&round_inputs.try_into().unwrap()); - spec.permute(&mut state) - } - let result_1 = state.result(); - assert_eq!(result_0, result_1); - } - } - - run::<3, 2>(); - run::<4, 3>(); - run::<5, 4>(); - run::<6, 5>(); - run::<7, 6>(); - run::<8, 7>(); - run::<9, 8>(); - run::<10, 9>(); -} diff --git a/primitives/poseidon/src/spec.rs b/primitives/poseidon/src/spec.rs deleted file mode 100644 index 6c9a9382ce..0000000000 --- a/primitives/poseidon/src/spec.rs +++ /dev/null @@ -1,424 +0,0 @@ -use std::ops::Index; - -use crate::{grain::Grain, matrix::Matrix}; -use halo2curves::FieldExt; - -/// `State` is structure `T` sized field elements that are subjected to -/// permutation -#[derive(Clone, Debug, PartialEq)] -pub struct State(pub(crate) [F; T]); - -impl Default for State { - /// The capacity value is 2**64 + (o − 1) where o the output length. - fn default() -> Self { - let mut state = [F::zero(); T]; - state[0] = F::from_u128(1 << 64); - State(state) - } -} - -impl State { - /// Applies sbox for all elements of the state. - /// Only supports `alpha = 5` sbox case. - pub(crate) fn sbox_full(&mut self) { - for e in self.0.iter_mut() { - let tmp = e.mul(*e); - e.mul_assign(tmp); - e.mul_assign(tmp); - } - } - - /// Partial round sbox applies sbox to the first element of the state. - /// Only supports `alpha = 5` sbox case - pub(crate) fn sbox_part(&mut self) { - let tmp = self.0[0].mul(self.0[0]); - self.0[0].mul_assign(tmp); - self.0[0].mul_assign(tmp); - } - - /// Adds constants to all elements of the state - pub(crate) fn add_constants(&mut self, constants: &[F; T]) { - for (e, constant) in self.0.iter_mut().zip(constants.iter()) { - e.add_assign(constant) - } - } - - /// Only adds a constant to the first element of the state.It is used with - /// optimized rounds constants where only single element is added in - /// each partial round - pub(crate) fn add_constant(&mut self, constant: &F) { - self.0[0].add_assign(constant) - } - - /// Copies elements of the state - pub fn words(&self) -> [F; T] { - self.0 - } - - /// Second element of the state is the result - pub(crate) fn result(&self) -> F { - self.0[1] - } -} - -/// `Spec` holds construction parameters as well as constants that are used in -/// permutation step. Constants are planned to be hardcoded once transcript -/// design matures. Number of partial rounds can be deriven from number of -/// constants. -#[derive(Debug, Clone)] -pub struct Spec { - pub(crate) r_f: usize, - pub(crate) mds_matrices: MDSMatrices, - pub(crate) constants: OptimizedConstants, -} - -impl Spec { - /// Number of full rounds - pub fn r_f(&self) -> usize { - self.r_f - } - /// Set of MDS Matrices used in permutation line - pub fn mds_matrices(&self) -> &MDSMatrices { - &self.mds_matrices - } - /// Optimised round constants - pub fn constants(&self) -> &OptimizedConstants { - &self.constants - } -} - -/// `OptimizedConstants` has round constants that are added each round. While -/// full rounds has T sized constants there is a single constant for each -/// partial round -#[derive(Debug, Clone)] -pub struct OptimizedConstants { - pub(crate) start: Vec<[F; T]>, - pub(crate) partial: Vec, - pub(crate) end: Vec<[F; T]>, -} - -impl OptimizedConstants { - /// Returns rounds constants for first part of full rounds - pub fn start(&self) -> &Vec<[F; T]> { - &self.start - } - - /// Returns rounds constants for partial rounds - pub fn partial(&self) -> &Vec { - &self.partial - } - - /// Returns rounds constants for second part of full rounds - pub fn end(&self) -> &Vec<[F; T]> { - &self.end - } -} - -/// `MDSMatrices` holds the MDS matrix as well as transition matrix which is -/// also called `pre_sparse_mds` and sparse matrices that enables us to reduce -/// number of multiplications in apply MDS step -#[derive(Debug, Clone)] -pub struct MDSMatrices { - pub(crate) mds: MDSMatrix, - pub(crate) pre_sparse_mds: MDSMatrix, - pub(crate) sparse_matrices: Vec>, -} - -impl MDSMatrices { - /// Returns original MDS matrix - pub fn mds(&self) -> &MDSMatrix { - &self.mds - } - - /// Returns transition matrix for sparse trick - pub fn pre_sparse_mds(&self) -> &MDSMatrix { - &self.pre_sparse_mds - } - - /// Returns sparse matrices for partial rounds - pub fn sparse_matrices(&self) -> &Vec> { - &self.sparse_matrices - } -} - -/// `MDSMatrix` is applied to `State` to achive linear layer of Poseidon -#[derive(Clone, Debug)] -pub struct MDSMatrix(pub(crate) Matrix); - -impl Index for MDSMatrix { - type Output = [F; T]; - - fn index(&self, idx: usize) -> &Self::Output { - &self.0 .0[idx] - } -} - -impl MDSMatrix { - /// Applies `MDSMatrix` to the state - pub(crate) fn apply(&self, state: &mut State) { - state.0 = self.0.mul_vector(&state.0); - } - - /// Given two `T` sized vector constructs the `t * t` Cauchy matrix - pub(super) fn cauchy(xs: &[F; T], ys: &[F; T]) -> Self { - let mut m = Matrix::default(); - for (i, x) in xs.iter().enumerate() { - for (j, y) in ys.iter().enumerate() { - let sum = *x + *y; - debug_assert!(!sum.is_zero_vartime()); - m.set(i, j, sum.invert().unwrap()); - } - } - MDSMatrix(m) - } - - /// Inverts the MDS matrix - fn invert(&self) -> Self { - Self(self.0.invert()) - } - - /// Used in calculation of optimized round constants. Calculates `v' = M * - /// v` where vectors are `T` sized - fn mul_constants(&self, v: &[F; T]) -> [F; T] { - self.0.mul_vector(v) - } - - /// Multiplies two MDS matrices. Used in sparse matrix calculations - fn mul(&self, other: &Self) -> Self { - Self(self.0.mul(&other.0)) - } - - fn transpose(&self) -> Self { - Self(self.0.transpose()) - } - - /// See Section B in Supplementary Material https://eprint.iacr.org/2019/458.pdf - /// Factorises an MDS matrix `M` into `M'` and `M''` where `M = M' * M''`. - /// Resulted `M''` matrices are the sparse ones while `M'` will contribute - /// to the accumulator of the process - fn factorise(&self) -> (Self, SparseMDSMatrix) { - // Given `(t-1 * t-1)` MDS matrix called `hat` constructs the matrix in - // form `[[1 | 0], [0 | m]]` - let prime = |hat: Matrix| -> MDSMatrix { - let mut prime = Matrix::identity(); - for (prime_row, hat_row) in prime.0.iter_mut().skip(1).zip(hat.0.iter()) { - for (el_prime, el_hat) in prime_row.iter_mut().skip(1).zip(hat_row.iter()) { - *el_prime = *el_hat; - } - } - Self(prime) - }; - - // Given `(t-1)` sized `w_hat` vector constructs the matrix in form - // `[[m_0_0 | m_0_i], [w_hat | identity]]` - let prime_prime = |w_hat: [F; RATE]| -> Self { - let mut prime_prime = Matrix::identity(); - prime_prime.0[0] = self.0 .0[0]; - for (row, w) in prime_prime.0.iter_mut().skip(1).zip(w_hat.iter()) { - row[0] = *w - } - Self(prime_prime) - }; - - let w = self.0.w(); - let m_hat = self.0.sub::(); - let m_hat_inverse = m_hat.invert(); - let w_hat = m_hat_inverse.mul_vector(&w); - (prime(m_hat), prime_prime(w_hat).transpose().into()) - } - - /// Returns rows of the MDS matrix - pub fn rows(&self) -> [[F; T]; T] { - self.0 .0 - } -} - -/// `SparseMDSMatrix` are in `[row], [hat | identity]` form and used in linear -/// layer of partial rounds instead of the original MDS -#[derive(Debug, Clone)] -pub struct SparseMDSMatrix { - pub(crate) row: [F; T], - pub(crate) col_hat: [F; RATE], -} - -impl SparseMDSMatrix { - /// Returns the first row - pub fn row(&self) -> &[F; T] { - &self.row - } - - /// Returns the first column without first element in the first row - pub fn col_hat(&self) -> &[F; RATE] { - &self.col_hat - } - - /// Applies the sparse MDS matrix to the state - pub(crate) fn apply(&self, state: &mut State) { - let words = state.words(); - state.0[0] = self - .row - .iter() - .zip(words.iter()) - .fold(F::zero(), |acc, (e, cell)| acc + (*e * *cell)); - - for ((new_word, col_el), word) in (state.0) - .iter_mut() - .skip(1) - .zip(self.col_hat.iter()) - .zip(words.iter().skip(1)) - { - *new_word = *col_el * words[0] + word; - } - } -} - -impl From> - for SparseMDSMatrix -{ - /// Assert the form and represent an MDS matrix as a sparse MDS matrix - fn from(mds: MDSMatrix) -> Self { - let mds = mds.0; - for (i, row) in mds.0.iter().enumerate().skip(1) { - for (j, _) in row.iter().enumerate().skip(1) { - assert_eq!(row[j], if i != j { F::zero() } else { F::one() }); - } - } - - let (mut row, mut col_hat) = ([F::zero(); T], [F::zero(); RATE]); - for (row_el, el) in row.iter_mut().zip(mds.0[0].iter()) { - *row_el = *el - } - for (col_el, row) in col_hat.iter_mut().zip(mds.0.iter().skip(1)) { - *col_el = row[0] - } - - SparseMDSMatrix { row, col_hat } - } -} - -impl Spec { - /// Given number of round parameters constructs new Posedion instance - /// calculating unoptimized round constants with reference `Grain` then - /// calculates optimized constants and sparse matrices - pub fn new(r_f: usize, r_p: usize) -> Self { - let (unoptimized_constants, mds) = Grain::generate(r_f, r_p); - let constants = Self::calculate_optimized_constants(r_f, r_p, unoptimized_constants, &mds); - let (sparse_matrices, pre_sparse_mds) = Self::calculate_sparse_matrices(r_p, &mds); - - Self { - r_f, - constants, - mds_matrices: MDSMatrices { - mds, - sparse_matrices, - pre_sparse_mds, - }, - } - } - - fn calculate_optimized_constants( - r_f: usize, - r_p: usize, - constants: Vec<[F; T]>, - mds: &MDSMatrix, - ) -> OptimizedConstants { - let inverse_mds = mds.invert(); - let (number_of_rounds, r_f_half) = (r_f + r_p, r_f / 2); - assert_eq!(constants.len(), number_of_rounds); - - // Calculate optimized constants for first half of the full rounds - let mut constants_start: Vec<[F; T]> = vec![[F::zero(); T]; r_f_half]; - constants_start[0] = constants[0]; - for (optimized, constants) in constants_start - .iter_mut() - .skip(1) - .zip(constants.iter().skip(1)) - { - *optimized = inverse_mds.mul_constants(constants); - } - - // Calculate constants for partial rounds - let mut acc = constants[r_f_half + r_p]; - let mut constants_partial = vec![F::zero(); r_p]; - for (optimized, constants) in constants_partial - .iter_mut() - .rev() - .zip(constants.iter().skip(r_f_half).rev().skip(r_f_half)) - { - let mut tmp = inverse_mds.mul_constants(&acc); - *optimized = tmp[0]; - - tmp[0] = F::zero(); - for ((acc, tmp), constant) in acc - .iter_mut() - .zip(tmp.into_iter()) - .zip(constants.iter()) - { - *acc = tmp + constant - } - } - constants_start.push(inverse_mds.mul_constants(&acc)); - - // Calculate optimized constants for ending half of the full rounds - let mut constants_end: Vec<[F; T]> = vec![[F::zero(); T]; r_f_half - 1]; - for (optimized, constants) in constants_end - .iter_mut() - .zip(constants.iter().skip(r_f_half + r_p + 1)) - { - *optimized = inverse_mds.mul_constants(constants); - } - - OptimizedConstants { - start: constants_start, - partial: constants_partial, - end: constants_end, - } - } - - fn calculate_sparse_matrices( - r_p: usize, - mds: &MDSMatrix, - ) -> (Vec>, MDSMatrix) { - let mds = mds.transpose(); - let mut acc = mds.clone(); - let mut sparse_matrices = (0..r_p) - .map(|_| { - let (m_prime, m_prime_prime) = acc.factorise(); - acc = mds.mul(&m_prime); - m_prime_prime - }) - .collect::>>(); - - sparse_matrices.reverse(); - (sparse_matrices, acc.transpose()) - } -} - -#[cfg(test)] -pub(super) mod tests { - use halo2curves::FieldExt; - - use super::MDSMatrix; - use crate::grain::Grain; - - /// We want to keep unoptimized parameters to cross test with optimized one - pub(crate) struct SpecRef { - pub(crate) r_f: usize, - pub(crate) r_p: usize, - pub(crate) mds: MDSMatrix, - pub(crate) constants: Vec<[F; T]>, - } - - impl SpecRef { - pub(crate) fn new(r_f: usize, r_p: usize) -> Self { - let (constants, mds) = Grain::generate(r_f, r_p); - - SpecRef { - r_f, - r_p, - mds, - constants, - } - } - } -} diff --git a/rust-toolchain b/rust-toolchain index d0217cd34a..a7aaf4d16c 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2022-10-28 +nightly-2023-08-11