Initial commit
This commit is contained in:
commit
dd1ad9f114
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
|
@ -0,0 +1,5 @@
|
|||
Copyright 2020 The Electric Coin Company
|
||||
|
||||
You may use this package under the Transitive Grace Period Public Licence,
|
||||
version 1.0, or at your option, any later version. See the file ./LICENSE-TGPPL
|
||||
for the terms of the Transitive Grace Period Public Licence, version 1.0.
|
|
@ -0,0 +1,27 @@
|
|||
[package]
|
||||
name = "halo2"
|
||||
version = "0.0.1"
|
||||
authors = [
|
||||
"Sean Bowe <sean@electriccoin.co>",
|
||||
"Ying Tong Lai <yingtong@electriccoin.co>",
|
||||
]
|
||||
edition = "2018"
|
||||
description = """
|
||||
Fast proof-carrying data implementation with no trusted setup
|
||||
"""
|
||||
license = "TGPPL"
|
||||
repository = "https://github.com/Electric-Coin-Company/halo2"
|
||||
documentation = "https://docs.rs/halo2"
|
||||
readme = "README.md"
|
||||
|
||||
# We are not publishing this yet.
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = [ "--html-in-header", "katex-header.html" ]
|
||||
|
||||
[dependencies]
|
||||
subtle = "2.2.1"
|
||||
crossbeam-utils = "0.7"
|
||||
num_cpus = "1.13"
|
||||
rand = "0.7"
|
|
@ -0,0 +1,58 @@
|
|||
ECC Contributor Agreement
|
||||
|
||||
This Contributor Agreement ("Agreement") applies to your contribution of any
|
||||
software, documentation, or other materials to any project ("project")
|
||||
established or managed by ZeroCoin Electric Coin Company LLC (dba Electric Coin
|
||||
Company) ("ECC", "we", "us" or "our") and establishes the intellectual property
|
||||
rights you grant to ECC in your contribution.
|
||||
|
||||
Please read this Agreement carefully. If you agree to this Agreement, please
|
||||
complete and sign the Agreement below. We suggest you also keep a copy of this
|
||||
Agreement for your records.
|
||||
|
||||
Please provide your information below. If you are making a contribution
|
||||
individually, the term "you" refers to the individual identified below. If you
|
||||
are making a contribution on behalf of a company or other entity, "you" will
|
||||
also mean the company or entity you identify below.
|
||||
|
||||
Your name:
|
||||
|
||||
Company/entity name (if applicable):
|
||||
|
||||
Your mailing address:
|
||||
|
||||
Your telephone:
|
||||
|
||||
Your preferred email:
|
||||
|
||||
1. The term "contribution" means any software, documentation, materials, or
|
||||
other work of authorship, including any modifications or additions to any
|
||||
existing software, documentation, materials, or other work, that you submit to
|
||||
ECC in any form, including electronic, verbal, or written.
|
||||
|
||||
2. You grant to ECC, and recipients of any contributions distributed by ECC, the
|
||||
following rights and licenses:
|
||||
|
||||
3. A perpetual, worldwide, non-exclusive, no-charge, royalty-free, fully
|
||||
sublicensable, irrevocable right and license to reproduce, prepare derivative
|
||||
works of, display, perform, sublicense, and distribute and make available any
|
||||
contributions and such derivative works.
|
||||
|
||||
4. A perpetual, worldwide, non-exclusive, no-charge, royalty-free, fully
|
||||
sublicensable, irrevocable[ (except as stated in this section)] right and
|
||||
license to make, have made, use, offer to sell, sell, import, transfer and
|
||||
otherwise practice any contributions, in whole or in part, alone or in
|
||||
combination with or included in any other contributions or any other software,
|
||||
documentation, materials, or works. [If any entity institutes patent litigation
|
||||
against you or any other entity (including a cross-claim or counterclaim in a
|
||||
lawsuit) alleging that one of your contributions, or any work to which you have
|
||||
contributed, constitutes direct or contributory patent infringement, then any
|
||||
patent licenses granted to that entity under this Agreement for that
|
||||
contribution or work shall terminate as of the date such litigation is filed.]
|
||||
|
||||
You also agree that you will not assert any moral rights in any contribution
|
||||
against us, our licensees or transferees. The rights in this Section are
|
||||
effective on the date a contribution is first submitted to us, even if the
|
||||
submission took place before the date you agree to this Agreement. Except as
|
||||
stated in this Section, as between you and us, you keep all right, title, and
|
||||
interest in your contribution.
|
|
@ -0,0 +1,182 @@
|
|||
|
||||
=======================================================
|
||||
Transitive Grace Period Public Licence ("TGPPL") v. 1.0
|
||||
=======================================================
|
||||
|
||||
This Transitive Grace Period Public Licence (the "License") applies to any
|
||||
original work of authorship (the "Original Work") whose owner (the
|
||||
"Licensor") has placed the following licensing notice adjacent to the
|
||||
copyright notice for the Original Work:
|
||||
|
||||
*Licensed under the Transitive Grace Period Public Licence version 1.0*
|
||||
|
||||
1. **Grant of Copyright License.** Licensor grants You a worldwide,
|
||||
royalty-free, non-exclusive, sublicensable license, for the duration of
|
||||
the copyright, to do the following:
|
||||
|
||||
a. to reproduce the Original Work in copies, either alone or as part of a
|
||||
collective work;
|
||||
|
||||
b. to translate, adapt, alter, transform, modify, or arrange the Original
|
||||
Work, thereby creating derivative works ("Derivative Works") based upon
|
||||
the Original Work;
|
||||
|
||||
c. to distribute or communicate copies of the Original Work and Derivative
|
||||
Works to the public, with the proviso that copies of Original Work or
|
||||
Derivative Works that You distribute or communicate shall be licensed
|
||||
under this Transitive Grace Period Public Licence no later than 12
|
||||
months after You distributed or communicated said copies;
|
||||
|
||||
d. to perform the Original Work publicly; and
|
||||
|
||||
e. to display the Original Work publicly.
|
||||
|
||||
2. **Grant of Patent License.** Licensor grants You a worldwide,
|
||||
royalty-free, non-exclusive, sublicensable license, under patent claims
|
||||
owned or controlled by the Licensor that are embodied in the Original
|
||||
Work as furnished by the Licensor, for the duration of the patents, to
|
||||
make, use, sell, offer for sale, have made, and import the Original Work
|
||||
and Derivative Works.
|
||||
|
||||
3. **Grant of Source Code License.** The term "Source Code" means the
|
||||
preferred form of the Original Work for making modifications to it and
|
||||
all available documentation describing how to modify the Original
|
||||
Work. Licensor agrees to provide a machine-readable copy of the Source
|
||||
Code of the Original Work along with each copy of the Original Work that
|
||||
Licensor distributes. Licensor reserves the right to satisfy this
|
||||
obligation by placing a machine-readable copy of the Source Code in an
|
||||
information repository reasonably calculated to permit inexpensive and
|
||||
convenient access by You for as long as Licensor continues to distribute
|
||||
the Original Work.
|
||||
|
||||
4. **Exclusions From License Grant.** Neither the names of Licensor, nor the
|
||||
names of any contributors to the Original Work, nor any of their
|
||||
trademarks or service marks, may be used to endorse or promote products
|
||||
derived from this Original Work without express prior permission of the
|
||||
Licensor. Except as expressly stated herein, nothing in this License
|
||||
grants any license to Licensor's trademarks, copyrights, patents, trade
|
||||
secrets or any other intellectual property. No patent license is granted
|
||||
to make, use, sell, offer for sale, have made, or import embodiments of
|
||||
any patent claims other than the licensed claims defined in Section 2. No
|
||||
license is granted to the trademarks of Licensor even if such marks are
|
||||
included in the Original Work. Nothing in this License shall be
|
||||
interpreted to prohibit Licensor from licensing under terms different
|
||||
from this License any Original Work that Licensor otherwise would have a
|
||||
right to license.
|
||||
|
||||
5. **External Deployment.** The term "External Deployment" means the use,
|
||||
distribution, or communication of the Original Work or Derivative Works
|
||||
in any way such that the Original Work or Derivative Works may be used by
|
||||
anyone other than You, whether those works are distributed or
|
||||
communicated to those persons or made available as an application
|
||||
intended for use over a network. As an express condition for the grants
|
||||
of license hereunder, You must treat any External Deployment by You of
|
||||
the Original Work or a Derivative Work as a distribution under section
|
||||
1(c).
|
||||
|
||||
6. **Attribution Rights.** You must retain, in the Source Code of any
|
||||
Derivative Works that You create, all copyright, patent, or trademark
|
||||
notices from the Source Code of the Original Work, as well as any notices
|
||||
of licensing and any descriptive text identified therein as an
|
||||
"Attribution Notice." You must cause the Source Code for any Derivative
|
||||
Works that You create to carry a prominent Attribution Notice reasonably
|
||||
calculated to inform recipients that You have modified the Original Work.
|
||||
|
||||
7. **Warranty of Provenance and Disclaimer of Warranty.** Licensor warrants
|
||||
that the copyright in and to the Original Work and the patent rights
|
||||
granted herein by Licensor are owned by the Licensor or are sublicensed
|
||||
to You under the terms of this License with the permission of the
|
||||
contributor(s) of those copyrights and patent rights. Except as expressly
|
||||
stated in the immediately preceding sentence, the Original Work is
|
||||
provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY,
|
||||
either express or implied, including, without limitation, the warranties
|
||||
of non-infringement, merchantability or fitness for a particular
|
||||
purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH
|
||||
YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
|
||||
License. No license to the Original Work is granted by this License
|
||||
except under this disclaimer.
|
||||
|
||||
8. **Limitation of Liability.** Under no circumstances and under no legal
|
||||
theory, whether in tort (including negligence), contract, or otherwise,
|
||||
shall the Licensor be liable to anyone for any indirect, special,
|
||||
incidental, or consequential damages of any character arising as a result
|
||||
of this License or the use of the Original Work including, without
|
||||
limitation, damages for loss of goodwill, work stoppage, computer failure
|
||||
or malfunction, or any and all other commercial damages or losses. This
|
||||
limitation of liability shall not apply to the extent applicable law
|
||||
prohibits such limitation.
|
||||
|
||||
9. **Acceptance and Termination.** If, at any time, You expressly assented
|
||||
to this License, that assent indicates your clear and irrevocable
|
||||
acceptance of this License and all of its terms and conditions. If You
|
||||
distribute or communicate copies of the Original Work or a Derivative
|
||||
Work, You must make a reasonable effort under the circumstances to obtain
|
||||
the express assent of recipients to the terms of this License. This
|
||||
License conditions your rights to undertake the activities listed in
|
||||
Section 1, including your right to create Derivative Works based upon the
|
||||
Original Work, and doing so without honoring these terms and conditions
|
||||
is prohibited by copyright law and international treaty. Nothing in this
|
||||
License is intended to affect copyright exceptions and limitations
|
||||
(including 'fair use' or 'fair dealing'). This License shall terminate
|
||||
immediately and You may no longer exercise any of the rights granted to
|
||||
You by this License upon your failure to honor the conditions in Section
|
||||
1(c).
|
||||
|
||||
10. **Termination for Patent Action.** This License shall terminate
|
||||
automatically and You may no longer exercise any of the rights granted to
|
||||
You by this License as of the date You commence an action, including a
|
||||
cross-claim or counterclaim, against Licensor or any licensee alleging
|
||||
that the Original Work infringes a patent. This termination provision
|
||||
shall not apply for an action alleging patent infringement by
|
||||
combinations of the Original Work with other software or hardware.
|
||||
|
||||
11. **Jurisdiction, Venue and Governing Law.** Any action or suit relating to
|
||||
this License may be brought only in the courts of a jurisdiction wherein
|
||||
the Licensor resides or in which Licensor conducts its primary business,
|
||||
and under the laws of that jurisdiction excluding its conflict-of-law
|
||||
provisions. The application of the United Nations Convention on Contracts
|
||||
for the International Sale of Goods is expressly excluded. Any use of the
|
||||
Original Work outside the scope of this License or after its termination
|
||||
shall be subject to the requirements and penalties of copyright or patent
|
||||
law in the appropriate jurisdiction. This section shall survive the
|
||||
termination of this License.
|
||||
|
||||
12. **Attorneys' Fees.** In any action to enforce the terms of this License
|
||||
or seeking damages relating thereto, the prevailing party shall be
|
||||
entitled to recover its costs and expenses, including, without
|
||||
limitation, reasonable attorneys' fees and costs incurred in connection
|
||||
with such action, including any appeal of such action. This section shall
|
||||
survive the termination of this License.
|
||||
|
||||
13. **Miscellaneous.** If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable.
|
||||
|
||||
14. **Definition of "You" in This License.** "You" throughout this License,
|
||||
whether in upper or lower case, means an individual or a legal entity
|
||||
exercising rights under, and complying with all of the terms of, this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with you. For 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.
|
||||
|
||||
15. **Right to Use.** You may use the Original Work in all ways not otherwise
|
||||
restricted or conditioned by this License or by law, and Licensor
|
||||
promises not to interfere with or be responsible for such uses by You.
|
||||
|
||||
16. **Modification of This License.** This License is Copyright © 2007 Zooko
|
||||
Wilcox-O'Hearn. Permission is granted to copy, distribute, or communicate
|
||||
this License without modification. Nothing in this License permits You to
|
||||
modify this License as applied to the Original Work or to Derivative
|
||||
Works. However, You may modify the text of this License and copy,
|
||||
distribute or communicate your modified version (the "Modified License")
|
||||
and apply it to other original works of authorship subject to the
|
||||
following conditions: (i) You may not indicate in any way that your
|
||||
Modified License is the "Transitive Grace Period Public Licence" or
|
||||
"TGPPL" and you may not use those names in the name of your Modified
|
||||
License; and (ii) You must replace the notice specified in the first
|
||||
paragraph above with the notice "Licensed under <insert your license name
|
||||
here>" or with a notice of your own that is not confusingly similar to
|
||||
the notice in this License.
|
|
@ -0,0 +1,19 @@
|
|||
# halo2 [![Crates.io](https://img.shields.io/crates/v/halo2.svg)](https://crates.io/crates/halo2) #
|
||||
|
||||
**IMPORTANT**: This library is being actively developed and should not be used in production software.
|
||||
|
||||
## [Documentation](https://docs.rs/halo2)
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2020 The Electric Coin Company.
|
||||
|
||||
You may use this package under the Transitive Grace Period Public Licence,
|
||||
version 1.0, or at your option, any later version. See the file
|
||||
[`LICENSE-TGPPL`](LICENSE-TGPPL) for the terms of the Transitive Grace Period
|
||||
Public Licence, version 1.0.
|
||||
|
||||
The purpose of the TGPPL is to allow commercial improvements to the package
|
||||
while ensuring that all improvements are eventually open source. See
|
||||
[here](https://tahoe-lafs.org/~zooko/tgppl.pdf) for why the TGPPL exists,
|
||||
graphically illustrated on three slides.
|
|
@ -0,0 +1,15 @@
|
|||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.css" integrity="sha384-9eLZqc9ds8eNjO3TmqPeYcDj8n+Qfa4nuSiGYa6DjLNcv9BtN69ZIulL9+8CqC9Y" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.js" integrity="sha384-K3vbOmF2BtaVai+Qk37uypf7VrgBubhQreNQe9aGsz9lB63dIFiQVlJbr92dw2Lx" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/contrib/auto-render.min.js" integrity="sha384-kmZOZB5ObwgQnS/DuDg6TScgOiWWBiVt0plIRkZCmE6rDZGrEOQeHM5PcHi+nyqe" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [
|
||||
{left: "$$", right: "$$", display: true},
|
||||
{left: "\\(", right: "\\)", display: false},
|
||||
{left: "$", right: "$", display: false},
|
||||
{left: "\\[", right: "\\]", display: true}
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1 @@
|
|||
1.45
|
|
@ -0,0 +1,348 @@
|
|||
//! This module provides common utilities, traits and structures for group,
|
||||
//! field and polynomial arithmetic.
|
||||
|
||||
use crossbeam_utils::thread;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod curves;
|
||||
mod fields;
|
||||
|
||||
pub use curves::*;
|
||||
pub use fields::*;
|
||||
|
||||
/// 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 Group: Copy + Clone + Send + Sync + 'static {
|
||||
/// The group is assumed to be of prime order $p$. `Scalar` is the
|
||||
/// associated scalar field of size $p$.
|
||||
type Scalar: Field;
|
||||
|
||||
/// Returns the additive identity of the group.
|
||||
fn group_zero() -> Self;
|
||||
|
||||
/// Adds `rhs` to this group element.
|
||||
fn group_add(&mut self, rhs: &Self);
|
||||
|
||||
/// Subtracts `rhs` from this group element.
|
||||
fn group_sub(&mut self, rhs: &Self);
|
||||
|
||||
/// Scales this group element by a scalar.
|
||||
fn group_scale(&mut self, by: &Self::Scalar);
|
||||
}
|
||||
|
||||
/// This is a 128-bit verifier challenge.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Challenge(pub(crate) u128);
|
||||
|
||||
/// This algorithm applies the mapping of Algorithm 1 from the
|
||||
/// [Halo](https://eprint.iacr.org/2019/1021) paper.
|
||||
pub fn get_challenge_scalar<F: Field>(challenge: Challenge) -> F {
|
||||
let mut acc = (F::ZETA + F::one()).double();
|
||||
|
||||
for i in (0..64).rev() {
|
||||
let should_negate = ((challenge.0 >> ((i << 1) + 1)) & 1) == 1;
|
||||
let should_endo = ((challenge.0 >> (i << 1)) & 1) == 1;
|
||||
|
||||
let q = if should_negate { -F::one() } else { F::one() };
|
||||
let q = if should_endo { q * F::ZETA } else { q };
|
||||
acc = acc + q + acc;
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
fn multiexp_serial<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Projective) {
|
||||
let coeffs: Vec<[u8; 32]> = coeffs.iter().map(|a| a.to_bytes()).collect();
|
||||
|
||||
let c = if bases.len() < 4 {
|
||||
1
|
||||
} else if bases.len() < 32 {
|
||||
3
|
||||
} else {
|
||||
(f64::from(bases.len() as u32)).ln().ceil() as usize
|
||||
};
|
||||
|
||||
fn get_at(segment: usize, c: usize, bytes: &[u8; 32]) -> usize {
|
||||
let skip_bits = segment * c;
|
||||
let skip_bytes = skip_bits / 8;
|
||||
|
||||
if skip_bytes >= 32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut v = [0; 8];
|
||||
for (v, o) in v.iter_mut().zip(bytes[skip_bytes..].iter()) {
|
||||
*v = *o;
|
||||
}
|
||||
|
||||
let mut tmp = u64::from_le_bytes(v);
|
||||
tmp >>= skip_bits - (skip_bytes * 8);
|
||||
tmp = tmp % (1 << c);
|
||||
|
||||
tmp as usize
|
||||
}
|
||||
|
||||
let segments = (256 / c) + 1;
|
||||
|
||||
for current_segment in (0..segments).rev() {
|
||||
for _ in 0..c {
|
||||
*acc = acc.double();
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Bucket<C: CurveAffine> {
|
||||
None,
|
||||
Affine(C),
|
||||
Projective(C::Projective),
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> Bucket<C> {
|
||||
fn add_assign(&mut self, other: &C) {
|
||||
*self = match *self {
|
||||
Bucket::None => Bucket::Affine(*other),
|
||||
Bucket::Affine(a) => Bucket::Projective(a + *other),
|
||||
Bucket::Projective(mut a) => {
|
||||
a += *other;
|
||||
Bucket::Projective(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add(self, mut other: C::Projective) -> C::Projective {
|
||||
match self {
|
||||
Bucket::None => other,
|
||||
Bucket::Affine(a) => {
|
||||
other += a;
|
||||
other
|
||||
}
|
||||
Bucket::Projective(a) => other + &a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut buckets: Vec<Bucket<C>> = vec![Bucket::None; (1 << c) - 1];
|
||||
|
||||
for (coeff, base) in coeffs.iter().zip(bases.iter()) {
|
||||
let coeff = get_at(current_segment, c, coeff);
|
||||
if coeff != 0 {
|
||||
buckets[coeff - 1].add_assign(base);
|
||||
}
|
||||
}
|
||||
|
||||
// Summation by parts
|
||||
// e.g. 3a + 2b + 1c = a +
|
||||
// (a) + b +
|
||||
// ((a) + b) + c
|
||||
let mut running_sum = C::Projective::zero();
|
||||
for exp in buckets.into_iter().rev() {
|
||||
running_sum = exp.add(running_sum);
|
||||
*acc = *acc + &running_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a multi-exponentiation operation.
|
||||
///
|
||||
/// This function will panic if coeffs and bases have a different length.
|
||||
///
|
||||
/// This will use multithreading if beneficial.
|
||||
pub fn best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Projective {
|
||||
assert_eq!(coeffs.len(), bases.len());
|
||||
|
||||
let num_cpus = num_cpus::get();
|
||||
if coeffs.len() > num_cpus {
|
||||
let chunk = coeffs.len() / num_cpus;
|
||||
let num_chunks = coeffs.chunks(chunk).len();
|
||||
let mut results = vec![C::Projective::zero(); num_chunks];
|
||||
thread::scope(|scope| {
|
||||
let chunk = coeffs.len() / num_cpus;
|
||||
|
||||
for ((coeffs, bases), acc) in coeffs
|
||||
.chunks(chunk)
|
||||
.zip(bases.chunks(chunk))
|
||||
.zip(results.iter_mut())
|
||||
{
|
||||
scope.spawn(move |_| {
|
||||
multiexp_serial(coeffs, bases, acc);
|
||||
});
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
results.iter().fold(C::Projective::zero(), |a, b| a + b)
|
||||
} else {
|
||||
let mut acc = C::Projective::zero();
|
||||
multiexp_serial(coeffs, bases, &mut acc);
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a radix-$2$ Fast-Fourier Transformation (FFT) on a vector of size
|
||||
/// $n = 2^k$, when provided `log_n` = $k$ and an element of multiplicative
|
||||
/// order $n$ called `omega` ($\omega$). The result is that the vector `a`, when
|
||||
/// interpreted as the coefficients of a polynomial of degree $n - 1$, is
|
||||
/// transformed into the evaluations of this polynomial at each of the $n$
|
||||
/// distinct powers of $\omega$. This transformation is invertible by providing
|
||||
/// $\omega^{-1}$ in place of $\omega$ and dividing each resulting field element
|
||||
/// by $n$.
|
||||
///
|
||||
/// This will use multithreading if beneficial.
|
||||
pub fn best_fft<G: Group>(a: &mut [G], omega: G::Scalar, log_n: u32) {
|
||||
let cpus = num_cpus::get();
|
||||
let log_cpus = log2_floor(cpus);
|
||||
|
||||
if log_n <= log_cpus {
|
||||
serial_fft(a, omega, log_n);
|
||||
} else {
|
||||
parallel_fft(a, omega, log_n, log_cpus);
|
||||
}
|
||||
}
|
||||
|
||||
fn serial_fft<G: Group>(a: &mut [G], omega: G::Scalar, log_n: u32) {
|
||||
fn bitreverse(mut n: u32, l: u32) -> u32 {
|
||||
let mut r = 0;
|
||||
for _ in 0..l {
|
||||
r = (r << 1) | (n & 1);
|
||||
n >>= 1;
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
let n = a.len() as u32;
|
||||
assert_eq!(n, 1 << log_n);
|
||||
|
||||
for k in 0..n {
|
||||
let rk = bitreverse(k, log_n);
|
||||
if k < rk {
|
||||
a.swap(rk as usize, k as usize);
|
||||
}
|
||||
}
|
||||
|
||||
let mut m = 1;
|
||||
for _ in 0..log_n {
|
||||
let w_m = omega.pow(&[u64::from(n / (2 * m)), 0, 0, 0]);
|
||||
|
||||
let mut k = 0;
|
||||
while k < n {
|
||||
let mut w = G::Scalar::one();
|
||||
for j in 0..m {
|
||||
let mut t = a[(k + j + m) as usize];
|
||||
t.group_scale(&w);
|
||||
a[(k + j + m) as usize] = a[(k + j) as usize];
|
||||
a[(k + j + m) as usize].group_sub(&t);
|
||||
a[(k + j) as usize].group_add(&t);
|
||||
w *= &w_m;
|
||||
}
|
||||
|
||||
k += 2 * m;
|
||||
}
|
||||
|
||||
m *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn parallel_fft<G: Group>(a: &mut [G], omega: G::Scalar, log_n: u32, log_cpus: u32) {
|
||||
assert!(log_n >= log_cpus);
|
||||
|
||||
let num_cpus = 1 << log_cpus;
|
||||
let log_new_n = log_n - log_cpus;
|
||||
let mut tmp = vec![vec![G::group_zero(); 1 << log_new_n]; num_cpus];
|
||||
let new_omega = omega.pow(&[num_cpus as u64, 0, 0, 0]);
|
||||
|
||||
thread::scope(|scope| {
|
||||
let a = &*a;
|
||||
|
||||
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||
scope.spawn(move |_| {
|
||||
// Shuffle into a sub-FFT
|
||||
let omega_j = omega.pow(&[j as u64, 0, 0, 0]);
|
||||
let omega_step = omega.pow(&[(j as u64) << log_new_n, 0, 0, 0]);
|
||||
|
||||
let mut elt = G::Scalar::one();
|
||||
|
||||
for (i, tmp) in tmp.iter_mut().enumerate() {
|
||||
for s in 0..num_cpus {
|
||||
let idx = (i + (s << log_new_n)) % (1 << log_n);
|
||||
let mut t = a[idx];
|
||||
t.group_scale(&elt);
|
||||
tmp.group_add(&t);
|
||||
elt *= &omega_step;
|
||||
}
|
||||
elt *= &omega_j;
|
||||
}
|
||||
|
||||
// Perform sub-FFT
|
||||
serial_fft(tmp, new_omega, log_new_n);
|
||||
});
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Unshuffle
|
||||
let mask = (1 << log_cpus) - 1;
|
||||
for (idx, a) in a.iter_mut().enumerate() {
|
||||
*a = tmp[idx & mask][idx >> log_cpus];
|
||||
}
|
||||
}
|
||||
|
||||
/// This evaluates a provided polynomial (in coefficient form) at `point`.
|
||||
pub fn eval_polynomial<F: Field>(poly: &[F], point: F) -> F {
|
||||
// TODO: parallelize?
|
||||
let mut acc = F::zero();
|
||||
let mut cur = F::one();
|
||||
for coeff in poly {
|
||||
acc += &(cur * coeff);
|
||||
cur *= &point;
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
/// This computes the inner product of two vectors `a` and `b`.
|
||||
///
|
||||
/// This function will panic if the two vectors are not the same size.
|
||||
pub fn compute_inner_product<F: Field>(a: &[F], b: &[F]) -> F {
|
||||
// TODO: parallelize?
|
||||
assert_eq!(a.len(), b.len());
|
||||
|
||||
let mut acc = F::zero();
|
||||
for (a, b) in a.iter().zip(b.iter()) {
|
||||
acc += (*a) * (*b);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
/// This simple utility function will parallelize an operation that is to be
|
||||
/// performed over a mutable slice.
|
||||
pub fn parallelize<T: Send, F: Fn(&mut [T], usize) + Send + Clone>(v: &mut [T], f: F) {
|
||||
let n = v.len();
|
||||
let num_cpus = num_cpus::get();
|
||||
let mut chunk = (n as usize) / num_cpus;
|
||||
if chunk < num_cpus {
|
||||
chunk = n as usize;
|
||||
}
|
||||
|
||||
thread::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);
|
||||
});
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn log2_floor(num: usize) -> u32 {
|
||||
assert!(num > 0);
|
||||
|
||||
let mut pow = 0;
|
||||
|
||||
while (1 << (pow + 1)) <= num {
|
||||
pow += 1;
|
||||
}
|
||||
|
||||
pow
|
||||
}
|
|
@ -0,0 +1,860 @@
|
|||
//! This module contains implementations for the Tweedledum and Tweedledee
|
||||
//! elliptic curve groups. The `Curve`/`CurveAffine` abstractions allow us to
|
||||
//! write code that generalizes over these two groups.
|
||||
|
||||
use core::cmp;
|
||||
use core::fmt::Debug;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use super::{Field, Fp, Fq, Group};
|
||||
|
||||
/// This trait is a common interface for dealing with elements of an elliptic
|
||||
/// curve group in the "projective" form, where that arithmetic is usually more
|
||||
/// efficient.
|
||||
pub trait Curve:
|
||||
Sized
|
||||
+ Default
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ Debug
|
||||
+ Add<Output = Self>
|
||||
+ Sub<Output = Self>
|
||||
+ Mul<<Self as Curve>::Scalar, Output = Self>
|
||||
+ Neg<Output = Self>
|
||||
+ for<'a> Add<&'a Self, Output = Self>
|
||||
+ for<'a> Sub<&'a Self, Output = Self>
|
||||
+ MulAssign<<Self as Curve>::Scalar>
|
||||
+ AddAssign
|
||||
+ SubAssign
|
||||
+ for<'a> AddAssign<&'a Self>
|
||||
+ for<'a> SubAssign<&'a Self>
|
||||
+ AddAssign<<Self as Curve>::Affine>
|
||||
+ SubAssign<<Self as Curve>::Affine>
|
||||
+ PartialEq
|
||||
+ cmp::Eq
|
||||
+ ConditionallySelectable
|
||||
+ ConstantTimeEq
|
||||
+ From<<Self as Curve>::Affine>
|
||||
+ Group<Scalar = <Self as Curve>::Scalar>
|
||||
{
|
||||
/// The representation of a point on this curve in the affine coordinate space.
|
||||
type Affine: CurveAffine<
|
||||
Projective = Self,
|
||||
Scalar = <Self as Curve>::Scalar,
|
||||
Base = <Self as Curve>::Base,
|
||||
> + Add<Output = Self>
|
||||
+ Sub<Output = Self>
|
||||
+ Mul<<Self as Curve>::Scalar, Output = Self>
|
||||
+ Neg<Output = <Self as Curve>::Affine>
|
||||
+ From<Self>;
|
||||
/// The scalar field of this elliptic curve.
|
||||
type Scalar: Field;
|
||||
/// The base field over which this elliptic curve is constructed.
|
||||
type Base: Field;
|
||||
|
||||
/// Obtains the additive identity.
|
||||
fn zero() -> Self;
|
||||
|
||||
/// Obtains the base point of the curve.
|
||||
fn one() -> Self;
|
||||
|
||||
/// Doubles this element.
|
||||
fn double(&self) -> Self;
|
||||
|
||||
/// Returns whether or not this element is the identity.
|
||||
fn is_zero(&self) -> Choice;
|
||||
|
||||
/// Apply the curve endomorphism by multiplying the x-coordinate
|
||||
/// by an element of multiplicative order 3.
|
||||
fn endo(&self) -> Self;
|
||||
|
||||
/// Converts this element into its affine form.
|
||||
fn to_affine(&self) -> Self::Affine;
|
||||
|
||||
/// Returns whether or not this element is on the curve; should
|
||||
/// always be true unless an "unchecked" API was used.
|
||||
fn is_on_curve(&self) -> Choice;
|
||||
|
||||
/// Converts many elements into their affine form. Panics if the
|
||||
/// sizes of the slices are different.
|
||||
fn batch_to_affine(v: &[Self], target: &mut [Self::Affine]);
|
||||
|
||||
/// Returns the curve constant b
|
||||
fn b() -> Self::Base;
|
||||
}
|
||||
|
||||
/// This trait is the affine counterpart to `Curve` and is used for
|
||||
/// serialization, storage in memory, and inspection of $x$ and $y$ coordinates.
|
||||
pub trait CurveAffine:
|
||||
Sized
|
||||
+ Default
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ Debug
|
||||
+ Add<Output = <Self as CurveAffine>::Projective>
|
||||
+ Sub<Output = <Self as CurveAffine>::Projective>
|
||||
+ Mul<<Self as CurveAffine>::Scalar, Output = <Self as CurveAffine>::Projective>
|
||||
+ Neg<Output = Self>
|
||||
+ PartialEq
|
||||
+ cmp::Eq
|
||||
+ ConditionallySelectable
|
||||
+ ConstantTimeEq
|
||||
+ From<<Self as CurveAffine>::Projective>
|
||||
{
|
||||
/// The representation of a point on this curve in the projective coordinate space.
|
||||
type Projective: Curve<
|
||||
Affine = Self,
|
||||
Scalar = <Self as CurveAffine>::Scalar,
|
||||
Base = <Self as CurveAffine>::Base,
|
||||
> + Mul<<Self as CurveAffine>::Scalar, Output = <Self as CurveAffine>::Projective>
|
||||
+ MulAssign<<Self as CurveAffine>::Scalar>
|
||||
+ AddAssign<Self>
|
||||
+ SubAssign<Self>
|
||||
+ From<Self>;
|
||||
/// The scalar field of this elliptic curve.
|
||||
type Scalar: Field;
|
||||
/// The base field over which this elliptic curve is constructed.
|
||||
type Base: Field;
|
||||
|
||||
/// Obtains the additive identity.
|
||||
fn zero() -> Self;
|
||||
|
||||
/// Obtains the base point of the curve.
|
||||
fn one() -> Self;
|
||||
|
||||
/// Returns whether or not this element is the identity.
|
||||
fn is_zero(&self) -> Choice;
|
||||
|
||||
/// Converts this element into its projective form.
|
||||
fn to_projective(&self) -> Self::Projective;
|
||||
|
||||
/// Gets the $(x, y)$ coordinates of this point.
|
||||
fn get_xy(&self) -> CtOption<(Self::Base, Self::Base)>;
|
||||
|
||||
/// Obtains a point given $(x, y)$, failing if it is not on the
|
||||
/// curve.
|
||||
fn from_xy(x: Self::Base, y: Self::Base) -> CtOption<Self>;
|
||||
|
||||
/// Returns whether or not this element is on the curve; should
|
||||
/// always be true unless an "unchecked" API was used.
|
||||
fn is_on_curve(&self) -> Choice;
|
||||
|
||||
/// Attempts to obtain a group element from its compressed 32-byte little
|
||||
/// endian representation.
|
||||
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self>;
|
||||
|
||||
/// Obtains the compressed, 32-byte little endian representation of this
|
||||
/// element.
|
||||
fn to_bytes(&self) -> [u8; 32];
|
||||
|
||||
/// Attempts to obtain a group element from its uncompressed 64-byte little
|
||||
/// endian representation.
|
||||
fn from_bytes_wide(bytes: &[u8; 64]) -> CtOption<Self>;
|
||||
|
||||
/// Obtains the uncompressed, 64-byte little endian representation of this
|
||||
/// element.
|
||||
fn to_bytes_wide(&self) -> [u8; 64];
|
||||
|
||||
/// Returns the curve constant $b$
|
||||
fn b() -> Self::Base;
|
||||
}
|
||||
|
||||
macro_rules! new_curve_impl {
|
||||
($name:ident, $name_affine:ident, $base:ident, $scalar:ident) => {
|
||||
/// Represents a point in the projective coordinate space.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct $name {
|
||||
x: $base,
|
||||
y: $base,
|
||||
z: $base,
|
||||
}
|
||||
|
||||
impl $name {
|
||||
const fn curve_constant_b() -> $base {
|
||||
// NOTE: this is specific to b = 5
|
||||
$base::from_raw([5, 0, 0, 0])
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a point in the affine coordinate space (or the point at
|
||||
/// infinity).
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct $name_affine {
|
||||
x: $base,
|
||||
y: $base,
|
||||
infinity: Choice,
|
||||
}
|
||||
|
||||
impl Curve for $name {
|
||||
type Affine = $name_affine;
|
||||
type Scalar = $scalar;
|
||||
type Base = $base;
|
||||
|
||||
fn zero() -> Self {
|
||||
Self {
|
||||
x: $base::zero(),
|
||||
y: $base::zero(),
|
||||
z: $base::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
// NOTE: This is specific to b = 5
|
||||
|
||||
const NEGATIVE_ONE: $base = $base::neg(&$base::one());
|
||||
const TWO: $base = $base::from_raw([2, 0, 0, 0]);
|
||||
|
||||
Self {
|
||||
x: NEGATIVE_ONE,
|
||||
y: TWO,
|
||||
z: $base::one(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> Choice {
|
||||
self.z.is_zero()
|
||||
}
|
||||
|
||||
fn to_affine(&self) -> Self::Affine {
|
||||
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,
|
||||
infinity: Choice::from(0u8),
|
||||
};
|
||||
|
||||
$name_affine::conditional_select(&tmp, &$name_affine::zero(), zinv.is_zero())
|
||||
}
|
||||
|
||||
fn double(&self) -> Self {
|
||||
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
||||
//
|
||||
// There are no points of order 2.
|
||||
|
||||
let a = self.x.square();
|
||||
let b = self.y.square();
|
||||
let c = b.square();
|
||||
let d = self.x + b;
|
||||
let d = d.square();
|
||||
let d = d - a - c;
|
||||
let d = d + d;
|
||||
let e = a + a + a;
|
||||
let f = e.square();
|
||||
let z3 = self.z * self.y;
|
||||
let z3 = z3 + z3;
|
||||
let x3 = f - (d + d);
|
||||
let c = c + c;
|
||||
let c = c + c;
|
||||
let c = c + c;
|
||||
let y3 = e * (d - x3) - c;
|
||||
|
||||
let tmp = $name {
|
||||
x: x3,
|
||||
y: y3,
|
||||
z: z3,
|
||||
};
|
||||
|
||||
$name::conditional_select(&tmp, &$name::zero(), self.is_zero())
|
||||
}
|
||||
|
||||
/// Apply the curve endomorphism by multiplying the x-coordinate
|
||||
/// by an element of multiplicative order 3.
|
||||
fn endo(&self) -> Self {
|
||||
$name {
|
||||
x: self.x * $base::ZETA,
|
||||
y: self.y,
|
||||
z: self.z,
|
||||
}
|
||||
}
|
||||
|
||||
fn b() -> Self::Base {
|
||||
$name::curve_constant_b()
|
||||
}
|
||||
|
||||
fn is_on_curve(&self) -> Choice {
|
||||
// Y^2 - X^3 = 5(Z^6)
|
||||
|
||||
(self.y.square() - (self.x.square() * self.x))
|
||||
.ct_eq(&((self.z.square() * self.z).square() * $name::curve_constant_b()))
|
||||
| self.z.is_zero()
|
||||
}
|
||||
|
||||
fn batch_to_affine(p: &[Self], q: &mut [Self::Affine]) {
|
||||
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_zero());
|
||||
}
|
||||
|
||||
// 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_zero();
|
||||
|
||||
// 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.infinity = Choice::from(0u8);
|
||||
|
||||
*q = $name_affine::conditional_select(&q, &$name_affine::zero(), skip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a $name_affine> for $name {
|
||||
fn from(p: &'a $name_affine) -> $name {
|
||||
p.to_projective()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$name_affine> for $name {
|
||||
fn from(p: $name_affine) -> $name {
|
||||
p.to_projective()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for $name {
|
||||
fn default() -> $name {
|
||||
$name::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl 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_zero();
|
||||
let other_is_zero = other.is_zero();
|
||||
|
||||
(self_is_zero & other_is_zero) // Both point at infinity
|
||||
| ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2))
|
||||
// Neither point at infinity, coordinates are the same
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for $name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::Eq for $name {}
|
||||
|
||||
impl 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<'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<'a, 'b> Add<&'a $name> for &'b $name {
|
||||
type Output = $name;
|
||||
|
||||
fn add(self, rhs: &'a $name) -> $name {
|
||||
if bool::from(self.is_zero()) {
|
||||
*rhs
|
||||
} else if bool::from(rhs.is_zero()) {
|
||||
*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::zero()
|
||||
}
|
||||
} 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_zero()) {
|
||||
rhs.to_projective()
|
||||
} else if bool::from(rhs.is_zero()) {
|
||||
*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::zero()
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b $scalar> for &'a $name {
|
||||
type Output = $name;
|
||||
|
||||
fn mul(self, other: &'b $scalar) -> Self::Output {
|
||||
// TODO: make this faster
|
||||
|
||||
let mut acc = $name::zero();
|
||||
|
||||
// This is a simple double-and-add implementation of point
|
||||
// multiplication, moving from most significant to least
|
||||
// significant bit of the scalar.
|
||||
//
|
||||
// NOTE: We skip the leading bit because it's always unset.
|
||||
for bit in other
|
||||
.to_bytes()
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
|
||||
.skip(1)
|
||||
{
|
||||
acc = acc.double();
|
||||
acc = $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,
|
||||
infinity: self.infinity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_zero()) {
|
||||
rhs.to_projective()
|
||||
} else if bool::from(rhs.is_zero()) {
|
||||
self.to_projective()
|
||||
} else {
|
||||
if self.x == rhs.x {
|
||||
if self.y == rhs.y {
|
||||
self.to_projective().double()
|
||||
} else {
|
||||
$name::zero()
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b $scalar> for &'a $name_affine {
|
||||
type Output = $name;
|
||||
|
||||
fn mul(self, other: &'b $scalar) -> Self::Output {
|
||||
// TODO: make this faster
|
||||
|
||||
let mut acc = $name::zero();
|
||||
|
||||
// This is a simple double-and-add implementation of point
|
||||
// multiplication, moving from most significant to least
|
||||
// significant bit of the scalar.
|
||||
//
|
||||
// NOTE: We skip the leading bit because it's always unset.
|
||||
for bit in other
|
||||
.to_bytes()
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
|
||||
.skip(1)
|
||||
{
|
||||
acc = acc.double();
|
||||
acc = $name::conditional_select(&acc, &(acc + self), bit);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
impl CurveAffine for $name_affine {
|
||||
type Projective = $name;
|
||||
type Scalar = $scalar;
|
||||
type Base = $base;
|
||||
|
||||
fn zero() -> Self {
|
||||
Self {
|
||||
x: $base::zero(),
|
||||
y: $base::zero(),
|
||||
infinity: Choice::from(1u8),
|
||||
}
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
// NOTE: This is specific to b = 5
|
||||
|
||||
const NEGATIVE_ONE: $base = $base::neg(&$base::from_raw([1, 0, 0, 0]));
|
||||
const TWO: $base = $base::from_raw([2, 0, 0, 0]);
|
||||
|
||||
Self {
|
||||
x: NEGATIVE_ONE,
|
||||
y: TWO,
|
||||
infinity: Choice::from(0u8),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> Choice {
|
||||
self.infinity
|
||||
}
|
||||
|
||||
fn is_on_curve(&self) -> Choice {
|
||||
// y^2 - x^3 ?= b
|
||||
(self.y.square() - (self.x.square() * self.x)).ct_eq(&$name::curve_constant_b())
|
||||
| self.infinity
|
||||
}
|
||||
|
||||
fn to_projective(&self) -> Self::Projective {
|
||||
$name {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
z: $base::conditional_select(&$base::one(), &$base::zero(), self.infinity),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xy(&self) -> CtOption<(Self::Base, Self::Base)> {
|
||||
CtOption::new((self.x, self.y), !self.is_zero())
|
||||
}
|
||||
|
||||
fn from_xy(x: Self::Base, y: Self::Base) -> CtOption<Self> {
|
||||
let p = $name_affine {
|
||||
x, y, infinity: 0u8.into()
|
||||
};
|
||||
CtOption::new(p, p.is_on_curve())
|
||||
}
|
||||
|
||||
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
||||
let mut tmp = *bytes;
|
||||
let ysign = Choice::from(tmp[31] >> 7);
|
||||
tmp[31] &= 0b0111_1111;
|
||||
|
||||
$base::from_bytes(&tmp).and_then(|x| {
|
||||
CtOption::new(Self::zero(), 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,
|
||||
infinity: Choice::from(0u8),
|
||||
},
|
||||
Choice::from(1u8),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> [u8; 32] {
|
||||
// TODO: not constant time
|
||||
if bool::from(self.is_zero()) {
|
||||
[0; 32]
|
||||
} else {
|
||||
let (x, y) = (self.x, self.y);
|
||||
let sign = (y.to_bytes()[0] & 1) << 7;
|
||||
let mut xbytes = x.to_bytes();
|
||||
xbytes[31] |= sign;
|
||||
xbytes
|
||||
}
|
||||
}
|
||||
|
||||
fn from_bytes_wide(bytes: &[u8; 64]) -> CtOption<Self> {
|
||||
let mut xbytes = [0u8; 32];
|
||||
let mut ybytes = [0u8; 32];
|
||||
xbytes.copy_from_slice(&bytes[0..32]);
|
||||
ybytes.copy_from_slice(&bytes[32..64]);
|
||||
|
||||
$base::from_bytes(&xbytes).and_then(|x| {
|
||||
$base::from_bytes(&ybytes).and_then(|y| {
|
||||
CtOption::new(Self::zero(), x.is_zero() & y.is_zero()).or_else(|| {
|
||||
let on_curve =
|
||||
(x * x.square() + $name::curve_constant_b()).ct_eq(&y.square());
|
||||
|
||||
CtOption::new(
|
||||
$name_affine {
|
||||
x,
|
||||
y,
|
||||
infinity: Choice::from(0u8),
|
||||
},
|
||||
Choice::from(on_curve),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn to_bytes_wide(&self) -> [u8; 64] {
|
||||
// TODO: not constant time
|
||||
if bool::from(self.is_zero()) {
|
||||
[0; 64]
|
||||
} else {
|
||||
let mut out = [0u8; 64];
|
||||
(&mut out[0..32]).copy_from_slice(&self.x.to_bytes());
|
||||
(&mut out[32..64]).copy_from_slice(&self.y.to_bytes());
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
fn b() -> Self::Base {
|
||||
$name::curve_constant_b()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for $name_affine {
|
||||
fn default() -> $name_affine {
|
||||
$name_affine::zero()
|
||||
}
|
||||
}
|
||||
|
||||
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 ConstantTimeEq for $name_affine {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
let z1 = self.infinity;
|
||||
let z2 = other.infinity;
|
||||
|
||||
(z1 & z2) | ((!z1) & (!z2) & (self.x.ct_eq(&other.x)) & (self.y.ct_eq(&other.y)))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for $name_affine {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::Eq for $name_affine {}
|
||||
|
||||
impl 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),
|
||||
infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 Group for $name {
|
||||
type Scalar = $scalar;
|
||||
|
||||
fn group_zero() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
fn group_add(&mut self, rhs: &Self) {
|
||||
*self = *self + *rhs;
|
||||
}
|
||||
fn group_sub(&mut self, rhs: &Self) {
|
||||
*self = *self - *rhs;
|
||||
}
|
||||
fn group_scale(&mut self, by: &Self::Scalar) {
|
||||
*self = *self * (*by);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
new_curve_impl!(Ep, EpAffine, Fp, Fq);
|
||||
new_curve_impl!(Eq, EqAffine, Fq, Fp);
|
|
@ -0,0 +1,296 @@
|
|||
//! This module contains implementations for the two finite fields of the
|
||||
//! Tweedledum and Tweedledee curves. The `Field` abstraction allows us to write
|
||||
//! code that generalizes over these two fields.
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use super::Group;
|
||||
|
||||
/// This trait is a common interface for dealing with elements of a finite
|
||||
/// field.
|
||||
pub trait Field:
|
||||
Sized
|
||||
+ Default
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ Debug
|
||||
+ From<bool>
|
||||
+ From<u64>
|
||||
+ Add<Output = Self>
|
||||
+ Sub<Output = Self>
|
||||
+ Mul<Output = Self>
|
||||
+ Neg<Output = Self>
|
||||
+ for<'a> Add<&'a Self, Output = Self>
|
||||
+ for<'a> Mul<&'a Self, Output = Self>
|
||||
+ for<'a> Sub<&'a Self, Output = Self>
|
||||
+ MulAssign
|
||||
+ AddAssign
|
||||
+ SubAssign
|
||||
+ for<'a> MulAssign<&'a Self>
|
||||
+ for<'a> AddAssign<&'a Self>
|
||||
+ for<'a> SubAssign<&'a Self>
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ ConditionallySelectable
|
||||
+ ConstantTimeEq
|
||||
+ Group<Scalar = Self>
|
||||
{
|
||||
/// How many bits needed to express the modulus $p$?
|
||||
const NUM_BITS: u32;
|
||||
|
||||
/// How many bits of information can be stored reliably?
|
||||
const CAPACITY: u32;
|
||||
|
||||
/// Represents $S$ where $p - 1 = 2^S \cdot t$ with $t$ odd.
|
||||
const S: u32;
|
||||
|
||||
/// Generator of the $2^S$ multiplicative subgroup
|
||||
const ROOT_OF_UNITY: Self;
|
||||
|
||||
/// Inverse of `ROOT_OF_UNITY`
|
||||
const ROOT_OF_UNITY_INV: Self;
|
||||
|
||||
/// The value $(2^S)^{-1} \mod t$.
|
||||
const UNROLL_T_EXPONENT: [u64; 4];
|
||||
|
||||
/// Represents $t$ where $2^S \cdot t = p - 1$ with $t$ odd.
|
||||
const T_EXPONENT: [u64; 4];
|
||||
|
||||
/// The value $t^{-1} \mod 2^S$.
|
||||
const UNROLL_S_EXPONENT: u64;
|
||||
|
||||
/// Inverse of $2$ in the field.
|
||||
const TWO_INV: Self;
|
||||
|
||||
/// Ideally the smallest prime $\alpha$ such that gcd($p - 1$, $\alpha$) = $1$
|
||||
const RESCUE_ALPHA: u64;
|
||||
|
||||
/// $RESCUE_INVALPHA \cdot RESCUE_ALPHA = 1 \mod p - 1$ such that
|
||||
/// `(a^RESCUE_ALPHA)^RESCUE_INVALPHA = a`.
|
||||
const RESCUE_INVALPHA: [u64; 4];
|
||||
|
||||
/// Element of multiplicative order $3$.
|
||||
const ZETA: Self;
|
||||
|
||||
/// This computes a random element of the field using system randomness.
|
||||
fn random() -> Self {
|
||||
use rand::{thread_rng, RngCore};
|
||||
|
||||
let mut random_bytes = [0; 64];
|
||||
thread_rng().fill_bytes(&mut random_bytes[..]);
|
||||
|
||||
Self::from_bytes_wide(&random_bytes)
|
||||
}
|
||||
|
||||
/// Returns whether or not this element is zero.
|
||||
fn is_zero(&self) -> Choice;
|
||||
|
||||
/// Doubles this element in the field.
|
||||
fn double(&self) -> Self;
|
||||
|
||||
/// Obtains a field element congruent to the integer `v`.
|
||||
fn from_u64(v: u64) -> Self;
|
||||
|
||||
/// Obtains a field element congruent to the integer `v`.
|
||||
fn from_u128(v: u128) -> Self;
|
||||
|
||||
/// Attempts to obtain the square root of this field element.
|
||||
fn sqrt(&self) -> CtOption<Self>;
|
||||
|
||||
/// Attempts to find the multiplicative inverse of this field element.
|
||||
fn invert(&self) -> CtOption<Self>;
|
||||
|
||||
/// Returns zero, the additive identity.
|
||||
fn zero() -> Self;
|
||||
|
||||
/// Returns one, the multiplicative identity.
|
||||
fn one() -> Self;
|
||||
|
||||
/// Squares this element in the field.
|
||||
fn square(&self) -> Self;
|
||||
|
||||
/// Converts this field element to its normalized, little endian byte
|
||||
/// representation.
|
||||
fn to_bytes(&self) -> [u8; 32];
|
||||
|
||||
/// Attempts to obtain a field element from its normalized, little endian
|
||||
/// byte representation.
|
||||
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self>;
|
||||
|
||||
/// Obtains a field element that is congruent to the provided little endian
|
||||
/// byte representation of an integer.
|
||||
fn from_bytes_wide(bytes: &[u8; 64]) -> Self;
|
||||
|
||||
/// Returns a square root of this element, if it exists and this element is
|
||||
/// nonzero. Always returns the same square root, and it is efficient to
|
||||
/// check that it has done so using `extract_radix2_vartime`.
|
||||
fn deterministic_sqrt(&self) -> Option<Self> {
|
||||
let sqrt = self.sqrt();
|
||||
if bool::from(sqrt.is_none()) {
|
||||
return None;
|
||||
}
|
||||
let sqrt = sqrt.unwrap();
|
||||
let extracted = sqrt.extract_radix2_vartime()?;
|
||||
|
||||
if extracted.1 >> (Self::S - 1) == 1 {
|
||||
Some(-sqrt)
|
||||
} else {
|
||||
Some(sqrt)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an element $a$ of multiplicative order $t$ together with an
|
||||
/// integer `s` such that `self` is the square of $a \cdot \omega^{s}$ if
|
||||
/// indeed `self` is a square.
|
||||
fn extract_radix2_vartime(&self) -> Option<(Self, u64)> {
|
||||
if bool::from(self.is_zero()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// TODO: these can probably be simplified
|
||||
let t = self.pow_vartime(&[1 << Self::S, 0, 0, 0]);
|
||||
let t = t.pow_vartime(&Self::UNROLL_T_EXPONENT);
|
||||
let t = t.pow_vartime(&Self::UNROLL_T_EXPONENT);
|
||||
let s = self.pow_vartime(&Self::T_EXPONENT);
|
||||
let mut s = s.pow_vartime(&[Self::UNROLL_S_EXPONENT, 0, 0, 0]);
|
||||
|
||||
let mut m = Self::S;
|
||||
let mut c = Self::ROOT_OF_UNITY_INV;
|
||||
|
||||
let mut extract: u64 = 0;
|
||||
|
||||
let mut cur = 1;
|
||||
while s != Self::one() {
|
||||
let mut i = 1;
|
||||
{
|
||||
let mut s2i = s;
|
||||
s2i = s2i.square();
|
||||
while s2i != Self::one() {
|
||||
i += 1;
|
||||
s2i = s2i.square();
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..(m - i) {
|
||||
c = c.square();
|
||||
cur <<= 1;
|
||||
}
|
||||
extract |= cur;
|
||||
s *= c;
|
||||
m = i;
|
||||
}
|
||||
|
||||
Some((t, extract))
|
||||
}
|
||||
|
||||
/// Exponentiates `self` by `by`, where `by` is a little-endian order
|
||||
/// integer exponent.
|
||||
fn pow(&self, by: &[u64; 4]) -> Self {
|
||||
let mut res = Self::one();
|
||||
for e in by.iter().rev() {
|
||||
for i in (0..64).rev() {
|
||||
res = res.square();
|
||||
let mut tmp = res;
|
||||
tmp *= self;
|
||||
res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into());
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Exponentiates `self` by `by`, where `by` is a little-endian order
|
||||
/// integer exponent.
|
||||
///
|
||||
/// **This operation is variable time with respect to the exponent.** If the
|
||||
/// exponent is fixed, this operation is effectively constant time.
|
||||
fn pow_vartime(&self, by: &[u64; 4]) -> Self {
|
||||
let mut res = Self::one();
|
||||
let mut found_one = false;
|
||||
for e in by.iter().rev() {
|
||||
for i in (0..64).rev() {
|
||||
if found_one {
|
||||
res = res.square();
|
||||
}
|
||||
|
||||
if ((*e >> i) & 1) == 1 {
|
||||
found_one = true;
|
||||
res.mul_assign(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Gets the lower 128 bits of this field element when expressed
|
||||
/// canonically.
|
||||
fn get_lower_128(&self) -> u128;
|
||||
|
||||
/// Performs a batch inversion using Montgomery's trick, returns the product
|
||||
/// of every inverse. Zero inputs are ignored.
|
||||
fn batch_invert(v: &mut [Self]) -> Self {
|
||||
let mut tmp = Vec::with_capacity(v.len());
|
||||
|
||||
let mut acc = Self::one();
|
||||
for p in v.iter() {
|
||||
tmp.push(acc);
|
||||
acc = Self::conditional_select(&(acc * p), &acc, p.is_zero());
|
||||
}
|
||||
|
||||
acc = acc.invert().unwrap();
|
||||
let allinv = acc;
|
||||
|
||||
for (p, tmp) in v.iter_mut().rev().zip(tmp.into_iter().rev()) {
|
||||
let skip = p.is_zero();
|
||||
|
||||
let tmp = tmp * acc;
|
||||
acc = Self::conditional_select(&(acc * *p), &acc, skip);
|
||||
*p = Self::conditional_select(&tmp, p, skip);
|
||||
}
|
||||
|
||||
allinv
|
||||
}
|
||||
}
|
||||
|
||||
mod fp;
|
||||
mod fq;
|
||||
|
||||
/// Compute a + b + carry, returning the result and the new carry over.
|
||||
#[inline(always)]
|
||||
const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) {
|
||||
let ret = (a as u128) + (b as u128) + (carry as u128);
|
||||
(ret as u64, (ret >> 64) as u64)
|
||||
}
|
||||
|
||||
/// Compute a - (b + borrow), returning the result and the new borrow.
|
||||
#[inline(always)]
|
||||
const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) {
|
||||
let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128));
|
||||
(ret as u64, (ret >> 64) as u64)
|
||||
}
|
||||
|
||||
/// Compute a + (b * c) + carry, returning the result and the new carry over.
|
||||
#[inline(always)]
|
||||
const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) {
|
||||
let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128);
|
||||
(ret as u64, (ret >> 64) as u64)
|
||||
}
|
||||
|
||||
pub use fp::*;
|
||||
pub use fq::*;
|
||||
|
||||
#[test]
|
||||
fn test_extract() {
|
||||
let a = Fq::random();
|
||||
let a = a.square();
|
||||
let (t, s) = a.extract_radix2_vartime().unwrap();
|
||||
assert_eq!(
|
||||
t.pow_vartime(&[1 << Fq::S, 0, 0, 0]) * Fq::ROOT_OF_UNITY.pow_vartime(&[s, 0, 0, 0]),
|
||||
a
|
||||
);
|
||||
assert_eq!(a.deterministic_sqrt().unwrap().square(), a);
|
||||
}
|
|
@ -0,0 +1,642 @@
|
|||
use super::{Field, Group};
|
||||
|
||||
use core::convert::TryInto;
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use super::{adc, mac, sbb};
|
||||
|
||||
/// This represents an element of $\mathbb{F}_p$ where
|
||||
///
|
||||
/// `p = 0x40000000000000000000000000000000038aa1276c3f59b9a14064e200000001`
|
||||
///
|
||||
/// is the base field of the Tweedledum 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)]
|
||||
pub struct Fp(pub(crate) [u64; 4]);
|
||||
|
||||
impl fmt::Debug for Fp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let tmp = self.to_bytes();
|
||||
write!(f, "0x")?;
|
||||
for &b in tmp.iter().rev() {
|
||||
write!(f, "{:02x}", b)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Fp {
|
||||
fn from(bit: bool) -> Fp {
|
||||
if bit {
|
||||
Fp::one()
|
||||
} else {
|
||||
Fp::zero()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Fp {
|
||||
fn from(val: u64) -> Fp {
|
||||
Fp([val, 0, 0, 0]) * R2
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.0[0].ct_eq(&other.0[0])
|
||||
& self.0[1].ct_eq(&other.0[1])
|
||||
& self.0[2].ct_eq(&other.0[2])
|
||||
& self.0[3].ct_eq(&other.0[3])
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Fp {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ct_eq(other).unwrap_u8() == 1
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fp([
|
||||
u64::conditional_select(&a.0[0], &b.0[0], choice),
|
||||
u64::conditional_select(&a.0[1], &b.0[1], choice),
|
||||
u64::conditional_select(&a.0[2], &b.0[2], choice),
|
||||
u64::conditional_select(&a.0[3], &b.0[3], choice),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/// Constant representing the modulus
|
||||
/// p = 0x40000000000000000000000000000000038aa1276c3f59b9a14064e200000001
|
||||
const MODULUS: Fp = Fp([
|
||||
0xa14064e200000001,
|
||||
0x38aa1276c3f59b9,
|
||||
0x0,
|
||||
0x4000000000000000,
|
||||
]);
|
||||
|
||||
impl<'a> Neg for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp {
|
||||
self.neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp> for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp) -> Fp {
|
||||
self.sub(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp> for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp) -> Fp {
|
||||
self.add(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp> for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b Fp) -> Fp {
|
||||
self.mul(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp, Fp);
|
||||
impl_binops_multiplicative!(Fp, Fp);
|
||||
|
||||
/// INV = -(p^{-1} mod 2^64) mod 2^64
|
||||
const INV: u64 = 0xa14064e1ffffffff;
|
||||
|
||||
/// R = 2^256 mod p
|
||||
const R: Fp = Fp([
|
||||
0x1c3ed159fffffffd,
|
||||
0xf5601c89bb41f2d3,
|
||||
0xffffffffffffffff,
|
||||
0x3fffffffffffffff,
|
||||
]);
|
||||
|
||||
/// R^2 = 2^512 mod p
|
||||
const R2: Fp = Fp([
|
||||
0x280c9c4000000010,
|
||||
0x91a4409b5400af74,
|
||||
0xdd7b28e19094c659,
|
||||
0xc8ad9107ccca0e,
|
||||
]);
|
||||
|
||||
/// R^3 = 2^768 mod p
|
||||
const R3: Fp = Fp([
|
||||
0x98fb3d144380a737,
|
||||
0xf9fdbeb55b7eb87c,
|
||||
0x63f75cb999eafa89,
|
||||
0x217cb214ebb8fc72,
|
||||
]);
|
||||
|
||||
const S: u32 = 33;
|
||||
|
||||
/// GENERATOR^t where t * 2^s + 1 = p
|
||||
/// with t odd. In other words, this
|
||||
/// is a 2^s root of unity.
|
||||
///
|
||||
/// `GENERATOR = 5 mod p` is a generator
|
||||
/// of the p - 1 order multiplicative
|
||||
/// subgroup.
|
||||
const ROOT_OF_UNITY: Fp = Fp::from_raw([
|
||||
0x53de9f31b88837ce,
|
||||
0xff46e8f3f3ea99d6,
|
||||
0xf624f2eaaf8c2d57,
|
||||
0x2ae45117890ee2fc,
|
||||
]);
|
||||
|
||||
impl Default for Fp {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl Fp {
|
||||
/// Returns zero, the additive identity.
|
||||
#[inline]
|
||||
pub const fn zero() -> Fp {
|
||||
Fp([0, 0, 0, 0])
|
||||
}
|
||||
|
||||
/// Returns one, the multiplicative identity.
|
||||
#[inline]
|
||||
pub const fn one() -> Fp {
|
||||
R
|
||||
}
|
||||
|
||||
/// Doubles this field element.
|
||||
#[inline]
|
||||
pub const fn double(&self) -> Fp {
|
||||
// TODO: This can be achieved more efficiently with a bitshift.
|
||||
self.add(self)
|
||||
}
|
||||
|
||||
fn from_u512(limbs: [u64; 8]) -> Fp {
|
||||
// We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits
|
||||
// with the higher bits multiplied by 2^256. Thus, we perform two reductions
|
||||
//
|
||||
// 1. the lower bits are multiplied by R^2, as normal
|
||||
// 2. the upper bits are multiplied by R^2 * 2^256 = R^3
|
||||
//
|
||||
// and computing their sum in the field. It remains to see that arbitrary 256-bit
|
||||
// numbers can be placed into Montgomery form safely using the reduction. The
|
||||
// reduction works so long as the product is less than R=2^256 multipled by
|
||||
// the modulus. This holds because for any `c` smaller than the modulus, we have
|
||||
// that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the
|
||||
// reduction always works so long as `c` is in the field; in this case it is either the
|
||||
// constant `R2` or `R3`.
|
||||
let d0 = Fp([limbs[0], limbs[1], limbs[2], limbs[3]]);
|
||||
let d1 = Fp([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) `Fp` representation.
|
||||
pub const fn from_raw(val: [u64; 4]) -> Self {
|
||||
(&Fp(val)).mul(&R2)
|
||||
}
|
||||
|
||||
/// Squares this element.
|
||||
#[inline]
|
||||
pub const fn square(&self) -> Fp {
|
||||
let (r1, carry) = mac(0, self.0[0], self.0[1], 0);
|
||||
let (r2, carry) = mac(0, self.0[0], self.0[2], carry);
|
||||
let (r3, r4) = mac(0, self.0[0], self.0[3], carry);
|
||||
|
||||
let (r3, carry) = mac(r3, self.0[1], self.0[2], 0);
|
||||
let (r4, r5) = mac(r4, self.0[1], self.0[3], carry);
|
||||
|
||||
let (r5, r6) = mac(r5, self.0[2], self.0[3], 0);
|
||||
|
||||
let r7 = r6 >> 63;
|
||||
let r6 = (r6 << 1) | (r5 >> 63);
|
||||
let r5 = (r5 << 1) | (r4 >> 63);
|
||||
let r4 = (r4 << 1) | (r3 >> 63);
|
||||
let r3 = (r3 << 1) | (r2 >> 63);
|
||||
let r2 = (r2 << 1) | (r1 >> 63);
|
||||
let r1 = r1 << 1;
|
||||
|
||||
let (r0, carry) = mac(0, self.0[0], self.0[0], 0);
|
||||
let (r1, carry) = adc(0, r1, carry);
|
||||
let (r2, carry) = mac(r2, self.0[1], self.0[1], carry);
|
||||
let (r3, carry) = adc(0, r3, carry);
|
||||
let (r4, carry) = mac(r4, self.0[2], self.0[2], carry);
|
||||
let (r5, carry) = adc(0, r5, carry);
|
||||
let (r6, carry) = mac(r6, self.0[3], self.0[3], carry);
|
||||
let (r7, _) = adc(0, r7, carry);
|
||||
|
||||
Fp::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn montgomery_reduce(
|
||||
r0: u64,
|
||||
r1: u64,
|
||||
r2: u64,
|
||||
r3: u64,
|
||||
r4: u64,
|
||||
r5: u64,
|
||||
r6: u64,
|
||||
r7: u64,
|
||||
) -> Self {
|
||||
// The Montgomery reduction here is based on Algorithm 14.32 in
|
||||
// Handbook of Applied Cryptography
|
||||
// <http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
|
||||
|
||||
let k = r0.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r0, k, MODULUS.0[0], 0);
|
||||
let (r1, carry) = mac(r1, k, MODULUS.0[1], carry);
|
||||
let (r2, carry) = mac(r2, k, MODULUS.0[2], carry);
|
||||
let (r3, carry) = mac(r3, k, MODULUS.0[3], carry);
|
||||
let (r4, carry2) = adc(r4, 0, carry);
|
||||
|
||||
let k = r1.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r1, k, MODULUS.0[0], 0);
|
||||
let (r2, carry) = mac(r2, k, MODULUS.0[1], carry);
|
||||
let (r3, carry) = mac(r3, k, MODULUS.0[2], carry);
|
||||
let (r4, carry) = mac(r4, k, MODULUS.0[3], carry);
|
||||
let (r5, carry2) = adc(r5, carry2, carry);
|
||||
|
||||
let k = r2.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r2, k, MODULUS.0[0], 0);
|
||||
let (r3, carry) = mac(r3, k, MODULUS.0[1], carry);
|
||||
let (r4, carry) = mac(r4, k, MODULUS.0[2], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS.0[3], carry);
|
||||
let (r6, carry2) = adc(r6, carry2, carry);
|
||||
|
||||
let k = r3.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r3, k, MODULUS.0[0], 0);
|
||||
let (r4, carry) = mac(r4, k, MODULUS.0[1], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS.0[2], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS.0[3], carry);
|
||||
let (r7, _) = adc(r7, carry2, carry);
|
||||
|
||||
// Result may be within MODULUS of the correct value
|
||||
(&Fp([r4, r5, r6, r7])).sub(&MODULUS)
|
||||
}
|
||||
|
||||
/// Multiplies `rhs` by `self`, returning the result.
|
||||
#[inline]
|
||||
pub const fn mul(&self, rhs: &Self) -> Self {
|
||||
// Schoolbook multiplication
|
||||
|
||||
let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0);
|
||||
let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry);
|
||||
let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry);
|
||||
let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry);
|
||||
|
||||
let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0);
|
||||
let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry);
|
||||
let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry);
|
||||
let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry);
|
||||
|
||||
let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0);
|
||||
let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry);
|
||||
let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry);
|
||||
let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry);
|
||||
|
||||
let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0);
|
||||
let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry);
|
||||
let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry);
|
||||
let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry);
|
||||
|
||||
Fp::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7)
|
||||
}
|
||||
|
||||
/// Subtracts `rhs` from `self`, returning the result.
|
||||
#[inline]
|
||||
pub const fn sub(&self, rhs: &Self) -> Self {
|
||||
let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0);
|
||||
let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow);
|
||||
let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow);
|
||||
let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow);
|
||||
|
||||
// If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise
|
||||
// borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus.
|
||||
let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0);
|
||||
let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry);
|
||||
let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry);
|
||||
let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry);
|
||||
|
||||
Fp([d0, d1, d2, d3])
|
||||
}
|
||||
|
||||
/// Adds `rhs` to `self`, returning the result.
|
||||
#[inline]
|
||||
pub const fn add(&self, rhs: &Self) -> Self {
|
||||
let (d0, carry) = adc(self.0[0], rhs.0[0], 0);
|
||||
let (d1, carry) = adc(self.0[1], rhs.0[1], carry);
|
||||
let (d2, carry) = adc(self.0[2], rhs.0[2], carry);
|
||||
let (d3, _) = adc(self.0[3], rhs.0[3], carry);
|
||||
|
||||
// Attempt to subtract the modulus, to ensure the value
|
||||
// is smaller than the modulus.
|
||||
(&Fp([d0, d1, d2, d3])).sub(&MODULUS)
|
||||
}
|
||||
|
||||
/// Negates `self`.
|
||||
#[inline]
|
||||
pub const fn neg(&self) -> Self {
|
||||
// Subtract `self` from `MODULUS` to negate. Ignore the final
|
||||
// borrow because it cannot underflow; self is guaranteed to
|
||||
// be in the field.
|
||||
let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0);
|
||||
let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow);
|
||||
let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow);
|
||||
let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow);
|
||||
|
||||
// `tmp` could be `MODULUS` if `self` was zero. Create a mask that is
|
||||
// zero if `self` was zero, and `u64::max_value()` if self was nonzero.
|
||||
let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1);
|
||||
|
||||
Fp([d0 & mask, d1 & mask, d2 & mask, d3 & mask])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Fp> for [u8; 32] {
|
||||
fn from(value: &'a Fp) -> [u8; 32] {
|
||||
value.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Group for Fp {
|
||||
type Scalar = Fp;
|
||||
|
||||
fn group_zero() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
fn group_add(&mut self, rhs: &Self) {
|
||||
*self = *self + *rhs;
|
||||
}
|
||||
fn group_sub(&mut self, rhs: &Self) {
|
||||
*self = *self - *rhs;
|
||||
}
|
||||
fn group_scale(&mut self, by: &Self::Scalar) {
|
||||
*self = *self * (*by);
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fp {
|
||||
const NUM_BITS: u32 = 255;
|
||||
const CAPACITY: u32 = 254;
|
||||
const S: u32 = S;
|
||||
const ROOT_OF_UNITY: Self = ROOT_OF_UNITY;
|
||||
const ROOT_OF_UNITY_INV: Self = Fp::from_raw([
|
||||
0x9246674078fa45bb,
|
||||
0xd822ebd60888c5ea,
|
||||
0x56d579133a11731f,
|
||||
0x1c88fa9e942120bb,
|
||||
]);
|
||||
const UNROLL_T_EXPONENT: [u64; 4] = [
|
||||
0x3b3a6633d1897d83,
|
||||
0x0000000000c93d5b,
|
||||
0xf000000000000000,
|
||||
0xe34ab16,
|
||||
];
|
||||
const T_EXPONENT: [u64; 4] = [
|
||||
0xb61facdcd0a03271,
|
||||
0x0000000001c55093,
|
||||
0x0000000000000000,
|
||||
0x20000000,
|
||||
];
|
||||
const UNROLL_S_EXPONENT: u64 = 0x11cb54e91;
|
||||
const TWO_INV: Self = Fp::from_raw([
|
||||
0xd0a0327100000001,
|
||||
0x01c55093b61facdc,
|
||||
0x0000000000000000,
|
||||
0x2000000000000000,
|
||||
]);
|
||||
const RESCUE_ALPHA: u64 = 5;
|
||||
const RESCUE_INVALPHA: [u64; 4] = [
|
||||
0x810050b4cccccccd,
|
||||
0x360880ec56991494,
|
||||
0x3333333333333333,
|
||||
0x3333333333333333,
|
||||
];
|
||||
const ZETA: Self = Fp::from_raw([
|
||||
0x8598abb3a410c9c8,
|
||||
0x7881fb239ba41a26,
|
||||
0x9bebc9146ef83d9a,
|
||||
0x1508415ab5e97c94,
|
||||
]);
|
||||
|
||||
fn is_zero(&self) -> Choice {
|
||||
self.ct_eq(&Self::zero())
|
||||
}
|
||||
|
||||
fn zero() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
Self::one()
|
||||
}
|
||||
|
||||
fn from_u64(v: u64) -> Self {
|
||||
Fp::from_raw([v as u64, 0, 0, 0])
|
||||
}
|
||||
|
||||
fn from_u128(v: u128) -> Self {
|
||||
Fp::from_raw([v as u64, (v >> 64) as u64, 0, 0])
|
||||
}
|
||||
|
||||
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<Self> {
|
||||
// Tonelli-Shank's algorithm for p mod 16 = 1
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||
|
||||
// w = self^((t - 1) // 2)
|
||||
let w = self.pow_vartime(&[0xdb0fd66e68501938, 0xe2a849, 0x0, 0x10000000]);
|
||||
|
||||
let mut v = S;
|
||||
let mut x = self * w;
|
||||
let mut b = x * w;
|
||||
|
||||
// Initialize z as the 2^S root of unity.
|
||||
let mut z = ROOT_OF_UNITY;
|
||||
|
||||
for max_v in (1..=S).rev() {
|
||||
let mut k = 1;
|
||||
let mut tmp = b.square();
|
||||
let mut j_less_than_v: Choice = 1.into();
|
||||
|
||||
for j in 2..max_v {
|
||||
let tmp_is_one = tmp.ct_eq(&Fp::one());
|
||||
let squared = Fp::conditional_select(&tmp, &z, tmp_is_one).square();
|
||||
tmp = Fp::conditional_select(&squared, &tmp, tmp_is_one);
|
||||
let new_z = Fp::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 = Fp::conditional_select(&z, &new_z, j_less_than_v);
|
||||
}
|
||||
|
||||
let result = x * z;
|
||||
x = Fp::conditional_select(&result, &x, b.ct_eq(&Fp::one()));
|
||||
z = z.square();
|
||||
b *= z;
|
||||
v = k;
|
||||
}
|
||||
|
||||
CtOption::new(
|
||||
x,
|
||||
(x * x).ct_eq(self), // Only return Some if it's the square root.
|
||||
)
|
||||
}
|
||||
|
||||
/// Computes the multiplicative inverse of this element,
|
||||
/// failing if the element is zero.
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
let tmp = self.pow_vartime(&[
|
||||
0xa14064e1ffffffff,
|
||||
0x38aa1276c3f59b9,
|
||||
0x0,
|
||||
0x4000000000000000,
|
||||
]);
|
||||
|
||||
CtOption::new(tmp, !self.ct_eq(&Self::zero()))
|
||||
}
|
||||
|
||||
/// Attempts to convert a little-endian byte representation of
|
||||
/// a scalar into a `Fp`, failing if the input is not canonical.
|
||||
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Fp> {
|
||||
let mut tmp = Fp([0, 0, 0, 0]);
|
||||
|
||||
tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
|
||||
tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
|
||||
tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap());
|
||||
tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap());
|
||||
|
||||
// Try to subtract the modulus
|
||||
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
|
||||
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
|
||||
|
||||
// If the element is smaller than MODULUS then the
|
||||
// subtraction will underflow, producing a borrow value
|
||||
// of 0xffff...ffff. Otherwise, it'll be zero.
|
||||
let is_some = (borrow as u8) & 1;
|
||||
|
||||
// Convert to Montgomery form by computing
|
||||
// (a.R^0 * R^2) / R = a.R
|
||||
tmp *= &R2;
|
||||
|
||||
CtOption::new(tmp, Choice::from(is_some))
|
||||
}
|
||||
|
||||
/// Converts an element of `Fp` into a byte representation in
|
||||
/// little-endian byte order.
|
||||
fn to_bytes(&self) -> [u8; 32] {
|
||||
// Turn into canonical form by computing
|
||||
// (a.R) / R = a
|
||||
let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
|
||||
|
||||
let mut res = [0; 32];
|
||||
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
|
||||
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
|
||||
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
|
||||
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Converts a 512-bit little endian integer into
|
||||
/// a `Fp` by reducing by the modulus.
|
||||
fn from_bytes_wide(bytes: &[u8; 64]) -> Fp {
|
||||
Fp::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 = Fp::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)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inv() {
|
||||
// Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating
|
||||
// by totient(2**64) - 1
|
||||
|
||||
let mut inv = 1u64;
|
||||
for _ in 0..63 {
|
||||
inv = inv.wrapping_mul(inv);
|
||||
inv = inv.wrapping_mul(MODULUS.0[0]);
|
||||
}
|
||||
inv = inv.wrapping_neg();
|
||||
|
||||
assert_eq!(inv, INV);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zeta() {
|
||||
assert_eq!(
|
||||
format!("{:?}", Fp::ZETA),
|
||||
"0x1508415ab5e97c949bebc9146ef83d9a7881fb239ba41a268598abb3a410c9c8"
|
||||
);
|
||||
|
||||
let a = Fp::ZETA;
|
||||
assert!(bool::from(a != Fp::one()));
|
||||
let b = a * a;
|
||||
assert!(bool::from(b != Fp::one()));
|
||||
let c = b * a;
|
||||
assert!(bool::from(c == Fp::one()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inv_root_of_unity() {
|
||||
assert_eq!(Fp::ROOT_OF_UNITY_INV, Fp::ROOT_OF_UNITY.invert().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inv_2() {
|
||||
assert_eq!(Fp::TWO_INV, Fp::from(2).invert().unwrap());
|
||||
}
|
|
@ -0,0 +1,656 @@
|
|||
use super::{Field, Group};
|
||||
|
||||
use core::convert::TryInto;
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use super::{adc, mac, sbb};
|
||||
|
||||
/// This represents an element of $\mathbb{F}_q$ where
|
||||
///
|
||||
/// `q = 0x40000000000000000000000000000000038aa127696286c9842cafd400000001`
|
||||
///
|
||||
/// is the base field of the Tweedledee 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)]
|
||||
pub struct Fq(pub(crate) [u64; 4]);
|
||||
|
||||
impl fmt::Debug for Fq {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let tmp = self.to_bytes();
|
||||
write!(f, "0x")?;
|
||||
for &b in tmp.iter().rev() {
|
||||
write!(f, "{:02x}", b)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Fq {
|
||||
fn from(bit: bool) -> Fq {
|
||||
if bit {
|
||||
Fq::one()
|
||||
} else {
|
||||
Fq::zero()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Fq {
|
||||
fn from(val: u64) -> Fq {
|
||||
Fq([val, 0, 0, 0]) * R2
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fq {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.0[0].ct_eq(&other.0[0])
|
||||
& self.0[1].ct_eq(&other.0[1])
|
||||
& self.0[2].ct_eq(&other.0[2])
|
||||
& self.0[3].ct_eq(&other.0[3])
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Fq {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ct_eq(other).unwrap_u8() == 1
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fq {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fq([
|
||||
u64::conditional_select(&a.0[0], &b.0[0], choice),
|
||||
u64::conditional_select(&a.0[1], &b.0[1], choice),
|
||||
u64::conditional_select(&a.0[2], &b.0[2], choice),
|
||||
u64::conditional_select(&a.0[3], &b.0[3], choice),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/// Constant representing the modulus
|
||||
/// q = 0x40000000000000000000000000000000038aa127696286c9842cafd400000001
|
||||
const MODULUS: Fq = Fq([
|
||||
0x842cafd400000001,
|
||||
0x38aa127696286c9,
|
||||
0x0,
|
||||
0x4000000000000000,
|
||||
]);
|
||||
|
||||
impl<'a> Neg for &'a Fq {
|
||||
type Output = Fq;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fq {
|
||||
self.neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fq {
|
||||
type Output = Fq;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fq {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fq> for &'a Fq {
|
||||
type Output = Fq;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fq) -> Fq {
|
||||
self.sub(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fq> for &'a Fq {
|
||||
type Output = Fq;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fq) -> Fq {
|
||||
self.add(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fq> for &'a Fq {
|
||||
type Output = Fq;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b Fq) -> Fq {
|
||||
self.mul(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fq, Fq);
|
||||
impl_binops_multiplicative!(Fq, Fq);
|
||||
|
||||
/// INV = -(q^{-1} mod 2^64) mod 2^64
|
||||
const INV: u64 = 0x842cafd3ffffffff;
|
||||
|
||||
/// R = 2^256 mod q
|
||||
const R: Fq = Fq([
|
||||
0x7379f083fffffffd,
|
||||
0xf5601c89c3d86ba3,
|
||||
0xffffffffffffffff,
|
||||
0x3fffffffffffffff,
|
||||
]);
|
||||
|
||||
/// R^2 = 2^512 mod q
|
||||
const R2: Fq = Fq([
|
||||
0x8595fa8000000010,
|
||||
0x7e16a565c6895230,
|
||||
0xf4c0e6fcb03aa0a2,
|
||||
0xc8ad9106886013,
|
||||
]);
|
||||
|
||||
/// R^3 = 2^768 mod q
|
||||
const R3: Fq = Fq([
|
||||
0xa624f338075cdb5e,
|
||||
0x57964eacb8fe21f2,
|
||||
0xcb266d18c0413bc2,
|
||||
0xa42cdf95f959577,
|
||||
]);
|
||||
|
||||
const S: u32 = 34;
|
||||
|
||||
/// GENERATOR^t where t * 2^s + 1 = q
|
||||
/// with t odd. In other words, this
|
||||
/// is a 2^s root of unity.
|
||||
///
|
||||
/// `GENERATOR = 5 mod q` is a generator
|
||||
/// of the q - 1 order multiplicative
|
||||
/// subgroup.
|
||||
const ROOT_OF_UNITY: Fq = Fq::from_raw([
|
||||
0x1cbd3234869d57ec,
|
||||
0xa287dd1b8084fbf,
|
||||
0xf1dbcb645a987293,
|
||||
0x113efc510dc03c0b,
|
||||
]);
|
||||
|
||||
impl Default for Fq {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl Fq {
|
||||
/// Returns zero, the additive identity.
|
||||
#[inline]
|
||||
pub const fn zero() -> Fq {
|
||||
Fq([0, 0, 0, 0])
|
||||
}
|
||||
|
||||
/// Returns one, the multiplicative identity.
|
||||
#[inline]
|
||||
pub const fn one() -> Fq {
|
||||
R
|
||||
}
|
||||
|
||||
/// Doubles this field element.
|
||||
#[inline]
|
||||
pub const fn double(&self) -> Fq {
|
||||
// TODO: This can be achieved more efficiently with a bitshift.
|
||||
self.add(self)
|
||||
}
|
||||
|
||||
/// Converts a 512-bit little endian integer into
|
||||
/// a `Fq` by reducing by the modulus.
|
||||
pub fn from_bytes_wide(bytes: &[u8; 64]) -> Fq {
|
||||
Fq::from_u512([
|
||||
u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
|
||||
u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
|
||||
u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
|
||||
u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
|
||||
u64::from_le_bytes(bytes[32..40].try_into().unwrap()),
|
||||
u64::from_le_bytes(bytes[40..48].try_into().unwrap()),
|
||||
u64::from_le_bytes(bytes[48..56].try_into().unwrap()),
|
||||
u64::from_le_bytes(bytes[56..64].try_into().unwrap()),
|
||||
])
|
||||
}
|
||||
|
||||
fn from_u512(limbs: [u64; 8]) -> Fq {
|
||||
// We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits
|
||||
// with the higher bits multiplied by 2^256. Thus, we perform two reductions
|
||||
//
|
||||
// 1. the lower bits are multiplied by R^2, as normal
|
||||
// 2. the upper bits are multiplied by R^2 * 2^256 = R^3
|
||||
//
|
||||
// and computing their sum in the field. It remains to see that arbitrary 256-bit
|
||||
// numbers can be placed into Montgomery form safely using the reduction. The
|
||||
// reduction works so long as the product is less than R=2^256 multipled by
|
||||
// the modulus. This holds because for any `c` smaller than the modulus, we have
|
||||
// that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the
|
||||
// reduction always works so long as `c` is in the field; in this case it is either the
|
||||
// constant `R2` or `R3`.
|
||||
let d0 = Fq([limbs[0], limbs[1], limbs[2], limbs[3]]);
|
||||
let d1 = Fq([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) `Fq` representation.
|
||||
pub const fn from_raw(val: [u64; 4]) -> Self {
|
||||
(&Fq(val)).mul(&R2)
|
||||
}
|
||||
|
||||
/// Squares this element.
|
||||
#[inline]
|
||||
pub const fn square(&self) -> Fq {
|
||||
let (r1, carry) = mac(0, self.0[0], self.0[1], 0);
|
||||
let (r2, carry) = mac(0, self.0[0], self.0[2], carry);
|
||||
let (r3, r4) = mac(0, self.0[0], self.0[3], carry);
|
||||
|
||||
let (r3, carry) = mac(r3, self.0[1], self.0[2], 0);
|
||||
let (r4, r5) = mac(r4, self.0[1], self.0[3], carry);
|
||||
|
||||
let (r5, r6) = mac(r5, self.0[2], self.0[3], 0);
|
||||
|
||||
let r7 = r6 >> 63;
|
||||
let r6 = (r6 << 1) | (r5 >> 63);
|
||||
let r5 = (r5 << 1) | (r4 >> 63);
|
||||
let r4 = (r4 << 1) | (r3 >> 63);
|
||||
let r3 = (r3 << 1) | (r2 >> 63);
|
||||
let r2 = (r2 << 1) | (r1 >> 63);
|
||||
let r1 = r1 << 1;
|
||||
|
||||
let (r0, carry) = mac(0, self.0[0], self.0[0], 0);
|
||||
let (r1, carry) = adc(0, r1, carry);
|
||||
let (r2, carry) = mac(r2, self.0[1], self.0[1], carry);
|
||||
let (r3, carry) = adc(0, r3, carry);
|
||||
let (r4, carry) = mac(r4, self.0[2], self.0[2], carry);
|
||||
let (r5, carry) = adc(0, r5, carry);
|
||||
let (r6, carry) = mac(r6, self.0[3], self.0[3], carry);
|
||||
let (r7, _) = adc(0, r7, carry);
|
||||
|
||||
Fq::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn montgomery_reduce(
|
||||
r0: u64,
|
||||
r1: u64,
|
||||
r2: u64,
|
||||
r3: u64,
|
||||
r4: u64,
|
||||
r5: u64,
|
||||
r6: u64,
|
||||
r7: u64,
|
||||
) -> Self {
|
||||
// The Montgomery reduction here is based on Algorithm 14.32 in
|
||||
// Handbook of Applied Cryptography
|
||||
// <http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
|
||||
|
||||
let k = r0.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r0, k, MODULUS.0[0], 0);
|
||||
let (r1, carry) = mac(r1, k, MODULUS.0[1], carry);
|
||||
let (r2, carry) = mac(r2, k, MODULUS.0[2], carry);
|
||||
let (r3, carry) = mac(r3, k, MODULUS.0[3], carry);
|
||||
let (r4, carry2) = adc(r4, 0, carry);
|
||||
|
||||
let k = r1.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r1, k, MODULUS.0[0], 0);
|
||||
let (r2, carry) = mac(r2, k, MODULUS.0[1], carry);
|
||||
let (r3, carry) = mac(r3, k, MODULUS.0[2], carry);
|
||||
let (r4, carry) = mac(r4, k, MODULUS.0[3], carry);
|
||||
let (r5, carry2) = adc(r5, carry2, carry);
|
||||
|
||||
let k = r2.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r2, k, MODULUS.0[0], 0);
|
||||
let (r3, carry) = mac(r3, k, MODULUS.0[1], carry);
|
||||
let (r4, carry) = mac(r4, k, MODULUS.0[2], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS.0[3], carry);
|
||||
let (r6, carry2) = adc(r6, carry2, carry);
|
||||
|
||||
let k = r3.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r3, k, MODULUS.0[0], 0);
|
||||
let (r4, carry) = mac(r4, k, MODULUS.0[1], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS.0[2], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS.0[3], carry);
|
||||
let (r7, _) = adc(r7, carry2, carry);
|
||||
|
||||
// Result may be within MODULUS of the correct value
|
||||
(&Fq([r4, r5, r6, r7])).sub(&MODULUS)
|
||||
}
|
||||
|
||||
/// Multiplies `rhs` by `self`, returning the result.
|
||||
#[inline]
|
||||
pub const fn mul(&self, rhs: &Self) -> Self {
|
||||
// Schoolbook multiplication
|
||||
|
||||
let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0);
|
||||
let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry);
|
||||
let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry);
|
||||
let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry);
|
||||
|
||||
let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0);
|
||||
let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry);
|
||||
let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry);
|
||||
let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry);
|
||||
|
||||
let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0);
|
||||
let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry);
|
||||
let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry);
|
||||
let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry);
|
||||
|
||||
let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0);
|
||||
let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry);
|
||||
let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry);
|
||||
let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry);
|
||||
|
||||
Fq::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7)
|
||||
}
|
||||
|
||||
/// Subtracts `rhs` from `self`, returning the result.
|
||||
#[inline]
|
||||
pub const fn sub(&self, rhs: &Self) -> Self {
|
||||
let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0);
|
||||
let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow);
|
||||
let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow);
|
||||
let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow);
|
||||
|
||||
// If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise
|
||||
// borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus.
|
||||
let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0);
|
||||
let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry);
|
||||
let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry);
|
||||
let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry);
|
||||
|
||||
Fq([d0, d1, d2, d3])
|
||||
}
|
||||
|
||||
/// Adds `rhs` to `self`, returning the result.
|
||||
#[inline]
|
||||
pub const fn add(&self, rhs: &Self) -> Self {
|
||||
let (d0, carry) = adc(self.0[0], rhs.0[0], 0);
|
||||
let (d1, carry) = adc(self.0[1], rhs.0[1], carry);
|
||||
let (d2, carry) = adc(self.0[2], rhs.0[2], carry);
|
||||
let (d3, _) = adc(self.0[3], rhs.0[3], carry);
|
||||
|
||||
// Attempt to subtract the modulus, to ensure the value
|
||||
// is smaller than the modulus.
|
||||
(&Fq([d0, d1, d2, d3])).sub(&MODULUS)
|
||||
}
|
||||
|
||||
/// Negates `self`.
|
||||
#[inline]
|
||||
pub const fn neg(&self) -> Self {
|
||||
// Subtract `self` from `MODULUS` to negate. Ignore the final
|
||||
// borrow because it cannot underflow; self is guaranteed to
|
||||
// be in the field.
|
||||
let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0);
|
||||
let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow);
|
||||
let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow);
|
||||
let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow);
|
||||
|
||||
// `tmp` could be `MODULUS` if `self` was zero. Create a mask that is
|
||||
// zero if `self` was zero, and `u64::max_value()` if self was nonzero.
|
||||
let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1);
|
||||
|
||||
Fq([d0 & mask, d1 & mask, d2 & mask, d3 & mask])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Fq> for [u8; 32] {
|
||||
fn from(value: &'a Fq) -> [u8; 32] {
|
||||
value.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Group for Fq {
|
||||
type Scalar = Fq;
|
||||
|
||||
fn group_zero() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
fn group_add(&mut self, rhs: &Self) {
|
||||
*self = *self + *rhs;
|
||||
}
|
||||
fn group_sub(&mut self, rhs: &Self) {
|
||||
*self = *self - *rhs;
|
||||
}
|
||||
fn group_scale(&mut self, by: &Self::Scalar) {
|
||||
*self = *self * (*by);
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fq {
|
||||
const NUM_BITS: u32 = 255;
|
||||
const CAPACITY: u32 = 254;
|
||||
const S: u32 = S;
|
||||
const ROOT_OF_UNITY: Self = ROOT_OF_UNITY;
|
||||
const ROOT_OF_UNITY_INV: Self = Fq::from_raw([
|
||||
0x824860d3eb30de02,
|
||||
0xad9f0afd0ea63acc,
|
||||
0xd250318c11a16fe1,
|
||||
0x12a9f5e1dd62dabc,
|
||||
]);
|
||||
const UNROLL_T_EXPONENT: [u64; 4] = [
|
||||
0x9b71de17e6d2d5a0,
|
||||
0x0000000000296ee0,
|
||||
0x8c00000000000000,
|
||||
0x2ecc05e,
|
||||
];
|
||||
const T_EXPONENT: [u64; 4] = [
|
||||
0xda58a1b2610b2bf5,
|
||||
0x0000000000e2a849,
|
||||
0x0000000000000000,
|
||||
0x10000000,
|
||||
];
|
||||
const UNROLL_S_EXPONENT: u64 = 0x344cfe85d;
|
||||
const TWO_INV: Self = Fq::from_raw([
|
||||
0xc21657ea00000001,
|
||||
0x01c55093b4b14364,
|
||||
0x0000000000000000,
|
||||
0x2000000000000000,
|
||||
]);
|
||||
const RESCUE_ALPHA: u64 = 5;
|
||||
const RESCUE_INVALPHA: [u64; 4] = [
|
||||
0xd023bfdccccccccd,
|
||||
0x360880ec544ed23a,
|
||||
0x3333333333333333,
|
||||
0x3333333333333333,
|
||||
];
|
||||
const ZETA: Self = Fq::from_raw([
|
||||
0x4394c2bd148fa4fd,
|
||||
0x69cf8de720e52ec1,
|
||||
0x87ad8b5ff9731ffe,
|
||||
0x36c66d3a1e049a58,
|
||||
]);
|
||||
|
||||
fn is_zero(&self) -> Choice {
|
||||
self.ct_eq(&Self::zero())
|
||||
}
|
||||
|
||||
fn zero() -> Self {
|
||||
Self::zero()
|
||||
}
|
||||
|
||||
fn one() -> Self {
|
||||
Self::one()
|
||||
}
|
||||
|
||||
fn from_u64(v: u64) -> Self {
|
||||
Fq::from_raw([v as u64, 0, 0, 0])
|
||||
}
|
||||
|
||||
fn from_u128(v: u128) -> Self {
|
||||
Fq::from_raw([v as u64, (v >> 64) as u64, 0, 0])
|
||||
}
|
||||
|
||||
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<Self> {
|
||||
// Tonelli-Shank's algorithm for q mod 16 = 1
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||
|
||||
// w = self^((t - 1) // 2)
|
||||
let w = self.pow_vartime(&[0xed2c50d9308595fa, 0x715424, 0x0, 0x8000000]);
|
||||
|
||||
let mut v = S;
|
||||
let mut x = self * w;
|
||||
let mut b = x * w;
|
||||
|
||||
// Initialize z as the 2^S root of unity.
|
||||
let mut z = ROOT_OF_UNITY;
|
||||
|
||||
for max_v in (1..=S).rev() {
|
||||
let mut k = 1;
|
||||
let mut tmp = b.square();
|
||||
let mut j_less_than_v: Choice = 1.into();
|
||||
|
||||
for j in 2..max_v {
|
||||
let tmp_is_one = tmp.ct_eq(&Fq::one());
|
||||
let squared = Fq::conditional_select(&tmp, &z, tmp_is_one).square();
|
||||
tmp = Fq::conditional_select(&squared, &tmp, tmp_is_one);
|
||||
let new_z = Fq::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 = Fq::conditional_select(&z, &new_z, j_less_than_v);
|
||||
}
|
||||
|
||||
let result = x * z;
|
||||
x = Fq::conditional_select(&result, &x, b.ct_eq(&Fq::one()));
|
||||
z = z.square();
|
||||
b *= z;
|
||||
v = k;
|
||||
}
|
||||
|
||||
CtOption::new(
|
||||
x,
|
||||
(x * x).ct_eq(self), // Only return Some if it's the square root.
|
||||
)
|
||||
}
|
||||
|
||||
/// Computes the multiplicative inverse of this element,
|
||||
/// failing if the element is zero.
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
let tmp = self.pow_vartime(&[
|
||||
0x842cafd3ffffffff,
|
||||
0x38aa127696286c9,
|
||||
0x0,
|
||||
0x4000000000000000,
|
||||
]);
|
||||
|
||||
CtOption::new(tmp, !self.ct_eq(&Self::zero()))
|
||||
}
|
||||
|
||||
/// Attempts to convert a little-endian byte representation of
|
||||
/// a scalar into a `Fq`, failing if the input is not canonical.
|
||||
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Fq> {
|
||||
let mut tmp = Fq([0, 0, 0, 0]);
|
||||
|
||||
tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
|
||||
tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
|
||||
tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap());
|
||||
tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap());
|
||||
|
||||
// Try to subtract the modulus
|
||||
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
|
||||
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
|
||||
|
||||
// If the element is smaller than MODULUS then the
|
||||
// subtraction will underflow, producing a borrow value
|
||||
// of 0xffff...ffff. Otherwise, it'll be zero.
|
||||
let is_some = (borrow as u8) & 1;
|
||||
|
||||
// Convert to Montgomery form by computing
|
||||
// (a.R^0 * R^2) / R = a.R
|
||||
tmp *= &R2;
|
||||
|
||||
CtOption::new(tmp, Choice::from(is_some))
|
||||
}
|
||||
|
||||
/// Converts an element of `Fq` into a byte representation in
|
||||
/// little-endian byte order.
|
||||
fn to_bytes(&self) -> [u8; 32] {
|
||||
// Turn into canonical form by computing
|
||||
// (a.R) / R = a
|
||||
let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
|
||||
|
||||
let mut res = [0; 32];
|
||||
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
|
||||
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
|
||||
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
|
||||
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Converts a 512-bit little endian integer into
|
||||
/// a `Fq` by reducing by the modulus.
|
||||
fn from_bytes_wide(bytes: &[u8; 64]) -> Fq {
|
||||
Fq::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 = Fq::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)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inv() {
|
||||
// Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating
|
||||
// by totient(2**64) - 1
|
||||
|
||||
let mut inv = 1u64;
|
||||
for _ in 0..63 {
|
||||
inv = inv.wrapping_mul(inv);
|
||||
inv = inv.wrapping_mul(MODULUS.0[0]);
|
||||
}
|
||||
inv = inv.wrapping_neg();
|
||||
|
||||
assert_eq!(inv, INV);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zeta() {
|
||||
assert_eq!(
|
||||
format!("{:?}", Fq::ZETA),
|
||||
"0x36c66d3a1e049a5887ad8b5ff9731ffe69cf8de720e52ec14394c2bd148fa4fd"
|
||||
);
|
||||
let a = Fq::ZETA;
|
||||
assert!(bool::from(a != Fq::one()));
|
||||
let b = a * a;
|
||||
assert!(bool::from(b != Fq::one()));
|
||||
let c = b * a;
|
||||
assert!(bool::from(c == 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_inv_2() {
|
||||
assert_eq!(Fq::TWO_INV, Fq::from(2).invert().unwrap());
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
macro_rules! impl_add_binop_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Add<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b $rhs) -> $output {
|
||||
&self + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: $rhs) -> $output {
|
||||
self + &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<$rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: $rhs) -> $output {
|
||||
&self + &rhs
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_sub_binop_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Sub<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b $rhs) -> $output {
|
||||
&self - rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
self - &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<$rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
&self - &rhs
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_additive_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl_add_binop_specify_output!($lhs, $rhs, $output);
|
||||
impl_sub_binop_specify_output!($lhs, $rhs, $output);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_multiplicative_mixed {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Mul<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b $rhs) -> $output {
|
||||
&self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: $rhs) -> $output {
|
||||
self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<$rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: $rhs) -> $output {
|
||||
&self * &rhs
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_additive {
|
||||
($lhs:ident, $rhs:ident) => {
|
||||
impl_binops_additive_specify_output!($lhs, $rhs, $lhs);
|
||||
|
||||
impl SubAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self - &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self + &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> SubAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> AddAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self + rhs;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binops_multiplicative {
|
||||
($lhs:ident, $rhs:ident) => {
|
||||
impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs);
|
||||
|
||||
impl MulAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self * &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> MulAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self * rhs;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
//! # halo2
|
||||
|
||||
#![allow(
|
||||
clippy::op_ref,
|
||||
clippy::assign_op_pattern,
|
||||
clippy::too_many_arguments,
|
||||
clippy::suspicious_arithmetic_impl,
|
||||
clippy::many_single_char_names,
|
||||
clippy::same_item_push
|
||||
)]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
pub mod arithmetic;
|
||||
pub mod plonk;
|
||||
pub mod polycommit;
|
||||
pub mod transcript;
|
|
@ -0,0 +1,145 @@
|
|||
//! This module provides an implementation of a variant of (Turbo)[PLONK][plonk]
|
||||
//! that is designed specifically for the polynomial commitment scheme described
|
||||
//! in the [Halo][halo] paper.
|
||||
//!
|
||||
//! [halo]: https://eprint.iacr.org/2019/1021
|
||||
//! [plonk]: https://eprint.iacr.org/2019/953
|
||||
|
||||
use crate::arithmetic::CurveAffine;
|
||||
use crate::polycommit::OpeningProof;
|
||||
use crate::transcript::Hasher;
|
||||
|
||||
#[macro_use]
|
||||
mod circuit;
|
||||
mod domain;
|
||||
mod prover;
|
||||
mod srs;
|
||||
mod verifier;
|
||||
|
||||
pub use circuit::*;
|
||||
pub use prover::*;
|
||||
pub use srs::*;
|
||||
pub use verifier::*;
|
||||
|
||||
use domain::EvaluationDomain;
|
||||
|
||||
// TODO: remove this
|
||||
const GATE_DEGREE: u32 = 3;
|
||||
|
||||
/// This is a structured reference string (SRS) that is (deterministically)
|
||||
/// computed from a specific circuit and parameters for the polynomial
|
||||
/// commitment scheme.
|
||||
#[derive(Debug)]
|
||||
pub struct SRS<C: CurveAffine> {
|
||||
sa: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sb: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sc: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sd: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sm: (Vec<C::Scalar>, Vec<C::Scalar>),
|
||||
sa_commitment: C,
|
||||
sb_commitment: C,
|
||||
sc_commitment: C,
|
||||
sd_commitment: C,
|
||||
sm_commitment: C,
|
||||
domain: EvaluationDomain<C::Scalar>,
|
||||
}
|
||||
|
||||
/// This is an object which represents a (Turbo)PLONK proof.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Proof<C: CurveAffine> {
|
||||
a_commitment: C,
|
||||
b_commitment: C,
|
||||
c_commitment: C,
|
||||
d_commitment: C,
|
||||
h_commitments: Vec<C>,
|
||||
a_eval_x: C::Scalar,
|
||||
b_eval_x: C::Scalar,
|
||||
c_eval_x: C::Scalar,
|
||||
d_eval_x: C::Scalar,
|
||||
sa_eval_x: C::Scalar,
|
||||
sb_eval_x: C::Scalar,
|
||||
sc_eval_x: C::Scalar,
|
||||
sd_eval_x: C::Scalar,
|
||||
sm_eval_x: C::Scalar,
|
||||
h_evals_x: Vec<C::Scalar>,
|
||||
opening: OpeningProof<C>,
|
||||
}
|
||||
|
||||
/// This is an error that could occur during proving or circuit synthesis.
|
||||
// TODO: these errors need to be cleaned up
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// This is an error that can occur during synthesis of the circuit, for
|
||||
/// example, when the witness is not present.
|
||||
SynthesisError,
|
||||
/// The structured reference string or the parameters are not compatible
|
||||
/// with the circuit being synthesized.
|
||||
IncompatibleParams,
|
||||
/// The constraint system is not satisfied.
|
||||
ConstraintSystemFailure,
|
||||
}
|
||||
|
||||
fn hash_point<C: CurveAffine, H: Hasher<C::Base>>(
|
||||
transcript: &mut H,
|
||||
point: &C,
|
||||
) -> Result<(), Error> {
|
||||
let tmp = point.get_xy();
|
||||
if bool::from(tmp.is_none()) {
|
||||
return Err(Error::SynthesisError);
|
||||
};
|
||||
let tmp = tmp.unwrap();
|
||||
transcript.absorb(tmp.0);
|
||||
transcript.absorb(tmp.1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_proving() {
|
||||
use crate::arithmetic::{EqAffine, Field, Fp, Fq};
|
||||
use crate::polycommit::Params;
|
||||
use crate::transcript::DummyHash;
|
||||
const K: u32 = 5;
|
||||
|
||||
// Initialize the polynomial commitment parameters
|
||||
let params: Params<EqAffine> = Params::new::<DummyHash<Fq>>(K);
|
||||
|
||||
struct MyCircuit<F: Field> {
|
||||
a: Option<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> Circuit<F> for MyCircuit<F> {
|
||||
fn synthesize(&self, cs: &mut impl ConstraintSystem<F>) -> Result<(), Error> {
|
||||
for _ in 0..10 {
|
||||
let (_, _, _, _) = cs.multiply(|| {
|
||||
let a = self.a.ok_or(Error::SynthesisError)?;
|
||||
let a2 = a.square();
|
||||
Ok((a, a, a2))
|
||||
})?;
|
||||
//cs.copy(a, b);
|
||||
let (_, _, _, _) = cs.add(|| {
|
||||
let a = self.a.ok_or(Error::SynthesisError)?;
|
||||
let a2 = a.square();
|
||||
let a3 = a + a2;
|
||||
Ok((a, a2, a3))
|
||||
})?;
|
||||
//cs.copy(a, d);
|
||||
//cs.copy(c, e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let circuit: MyCircuit<Fp> = MyCircuit {
|
||||
a: Some((-Fp::from_u64(2) + Fp::ROOT_OF_UNITY).pow(&[100, 0, 0, 0])),
|
||||
};
|
||||
|
||||
// Initialize the SRS
|
||||
let srs = SRS::generate(¶ms, &circuit).expect("SRS generation should not fail");
|
||||
|
||||
// Create a proof
|
||||
let proof = Proof::create::<DummyHash<Fq>, DummyHash<Fp>, _>(¶ms, &srs, &circuit)
|
||||
.expect("proof generation should not fail");
|
||||
|
||||
assert!(proof.verify::<DummyHash<Fq>, DummyHash<Fp>>(¶ms, &srs));
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
use super::Error;
|
||||
|
||||
use crate::arithmetic::Field;
|
||||
|
||||
/// This represents a PLONK wire, which could be a fixed (selector) wire or an
|
||||
/// advice wire.
|
||||
#[derive(Debug)]
|
||||
pub enum Wire {
|
||||
/// A wires
|
||||
A(usize),
|
||||
/// B wires
|
||||
B(usize),
|
||||
/// C wires
|
||||
C(usize),
|
||||
/// D wires
|
||||
D(usize),
|
||||
}
|
||||
|
||||
/// This trait allows a [`Circuit`] to direct some backend to assign a witness
|
||||
/// for a constraint system.
|
||||
pub trait ConstraintSystem<F: Field> {
|
||||
/// Creates a gate.
|
||||
fn create_gate(
|
||||
&mut self,
|
||||
sa: F,
|
||||
sb: F,
|
||||
sc: F,
|
||||
sd: F,
|
||||
sm: F,
|
||||
f: impl Fn() -> Result<(F, F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error>;
|
||||
|
||||
/// a * b - c = 0
|
||||
fn multiply(
|
||||
&mut self,
|
||||
f: impl Fn() -> Result<(F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
||||
self.create_gate(F::zero(), F::zero(), F::one(), F::zero(), F::one(), || {
|
||||
let (a, b, c) = f()?;
|
||||
Ok((a, b, c, F::zero()))
|
||||
})
|
||||
}
|
||||
|
||||
/// a + b - c = 0
|
||||
fn add(
|
||||
&mut self,
|
||||
f: impl Fn() -> Result<(F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
||||
self.create_gate(F::one(), F::one(), F::one(), F::zero(), F::zero(), || {
|
||||
let (a, b, c) = f()?;
|
||||
Ok((a, b, c, F::zero()))
|
||||
})
|
||||
}
|
||||
|
||||
// fn copy(&mut self, left: Wire, right: Wire);
|
||||
}
|
||||
|
||||
/// This is a trait that circuits provide implementations for so that the
|
||||
/// backend prover can ask the circuit to synthesize using some given
|
||||
/// [`ConstraintSystem`] implementation.
|
||||
pub trait Circuit<F: Field> {
|
||||
/// Given the provided `cs`, synthesize the circuit. The concrete type of
|
||||
/// the caller will be different depending on the context, and they may or
|
||||
/// may not expect to have a witness present.
|
||||
fn synthesize(&self, cs: &mut impl ConstraintSystem<F>) -> Result<(), Error>;
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
use crate::arithmetic::{best_fft, parallelize, Field, Group};
|
||||
|
||||
/// This structure contains precomputed constants and other details needed for
|
||||
/// performing operations on an evaluation domain of size $2^k$ in the context
|
||||
/// of PLONK.
|
||||
#[derive(Debug)]
|
||||
pub struct EvaluationDomain<G: Group> {
|
||||
n: u64,
|
||||
k: u32,
|
||||
extended_k: u32,
|
||||
omega_inv: G::Scalar,
|
||||
extended_omega: G::Scalar,
|
||||
extended_omega_inv: G::Scalar,
|
||||
g_coset: G::Scalar,
|
||||
g_coset_inv: G::Scalar,
|
||||
quotient_poly_degree: u64,
|
||||
ifft_divisor: G::Scalar,
|
||||
extended_ifft_divisor: G::Scalar,
|
||||
t_evaluations: Vec<G::Scalar>,
|
||||
}
|
||||
|
||||
impl<G: Group> EvaluationDomain<G> {
|
||||
/// This constructs a new evaluation domain object (containing precomputed
|
||||
/// constants) for operating on an evaluation domain of size $2^k$ and for
|
||||
/// some operations over an extended domain of size $2^{k + j}$ where $j$ is
|
||||
/// sufficiently large to describe the quotient polynomial depending on the
|
||||
/// maximum degree of all PLONK gates.
|
||||
pub fn new(gate_degree: u32, k: u32) -> Self {
|
||||
// quotient_poly_degree * params.n - 1 is the degree of the quotient polynomial
|
||||
let quotient_poly_degree = (gate_degree - 1) as u64;
|
||||
|
||||
let n = 1u64 << k;
|
||||
|
||||
// We need to work within an extended domain, not params.k but params.k + j
|
||||
// such that 2^(params.k + j) is sufficiently large to describe the quotient
|
||||
// polynomial.
|
||||
let mut extended_k = k;
|
||||
while (1 << extended_k) < (n * quotient_poly_degree) {
|
||||
extended_k += 1;
|
||||
}
|
||||
|
||||
let mut extended_omega = G::Scalar::ROOT_OF_UNITY;
|
||||
for _ in extended_k..G::Scalar::S {
|
||||
extended_omega = extended_omega.square();
|
||||
}
|
||||
let extended_omega = extended_omega; // 2^{j+k}'th root of unity
|
||||
let extended_omega_inv = extended_omega.invert().unwrap();
|
||||
|
||||
let mut omega = extended_omega;
|
||||
for _ in k..extended_k {
|
||||
omega = omega.square();
|
||||
}
|
||||
let omega = omega; // 2^{k}'th root of unity
|
||||
let omega_inv = omega.invert().unwrap();
|
||||
|
||||
// We use zeta here because we know it generates a coset, and it's available
|
||||
// already.
|
||||
let g_coset = G::Scalar::ZETA;
|
||||
let g_coset_inv = g_coset.square();
|
||||
|
||||
// TODO: merge these inversions together with t_evaluations batch inversion?
|
||||
let ifft_divisor = G::Scalar::from_u64(1 << k).invert().unwrap();
|
||||
let extended_ifft_divisor = G::Scalar::from_u64(1 << extended_k).invert().unwrap();
|
||||
|
||||
let mut t_evaluations = Vec::with_capacity(1 << (extended_k - k));
|
||||
{
|
||||
// Compute the evaluations of t(X) 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 as u64, 0, 0, 0]);
|
||||
let step = extended_omega.pow_vartime(&[n as u64, 0, 0, 0]);
|
||||
let mut cur = orig;
|
||||
loop {
|
||||
t_evaluations.push(cur);
|
||||
cur *= &step;
|
||||
if cur == orig {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert_eq!(t_evaluations.len(), 1 << (extended_k - k));
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Invert, because we're dividing by this polynomial.
|
||||
G::Scalar::batch_invert(&mut t_evaluations);
|
||||
}
|
||||
|
||||
EvaluationDomain {
|
||||
n,
|
||||
k,
|
||||
extended_k,
|
||||
omega_inv,
|
||||
extended_omega,
|
||||
extended_omega_inv,
|
||||
g_coset,
|
||||
g_coset_inv,
|
||||
quotient_poly_degree,
|
||||
ifft_divisor,
|
||||
extended_ifft_divisor,
|
||||
t_evaluations,
|
||||
}
|
||||
}
|
||||
|
||||
/// This takes us from an n-length vector into the coset evaluation domain.
|
||||
/// Also returns the polynomial.
|
||||
///
|
||||
/// This function will panic if the provided vector is not the correct
|
||||
/// length.
|
||||
pub fn obtain_coset(&self, mut a: Vec<G>) -> (Vec<G>, Vec<G>) {
|
||||
assert_eq!(a.len(), 1 << self.k);
|
||||
|
||||
// Perform inverse FFT to obtain the polynomial in coefficient form
|
||||
Self::ifft(&mut a, self.omega_inv, self.k, self.ifft_divisor);
|
||||
|
||||
// Keep this polynomial around; we'll need to evaluate it at arbitrary
|
||||
// points later.
|
||||
let old = a.clone();
|
||||
|
||||
// Distributes powers so that an FFT will move us into the coset
|
||||
// evaluation domain.
|
||||
Self::distribute_powers(&mut a, self.g_coset);
|
||||
|
||||
// Resize to account for the quotient polynomial's size
|
||||
a.resize(1 << self.extended_k, G::group_zero());
|
||||
|
||||
// Move into coset evaluation domain
|
||||
best_fft(&mut a, self.extended_omega, self.extended_k);
|
||||
|
||||
(a, old)
|
||||
}
|
||||
|
||||
/// This takes us from the coset evaluation domain and gets us the quotient
|
||||
/// polynomial coefficients.
|
||||
///
|
||||
/// This function will panic if the provided vector is not the correct
|
||||
/// length.
|
||||
pub fn from_coset(&self, mut a: Vec<G>) -> Vec<G> {
|
||||
assert_eq!(a.len(), 1 << self.extended_k);
|
||||
|
||||
// Inverse FFT
|
||||
Self::ifft(
|
||||
&mut a,
|
||||
self.extended_omega_inv,
|
||||
self.extended_k,
|
||||
self.extended_ifft_divisor,
|
||||
);
|
||||
|
||||
// Distribute powers to move from coset; opposite from the
|
||||
// transformation we performed earlier.
|
||||
Self::distribute_powers(&mut a, self.g_coset_inv);
|
||||
|
||||
// Truncate it to match the size of the quotient polynomial; the
|
||||
// evaluation domain might be slightly larger than necessary because
|
||||
// it always lies on a power-of-two boundary.
|
||||
a.truncate((&self.n * self.quotient_poly_degree) as usize);
|
||||
|
||||
a
|
||||
}
|
||||
|
||||
/// This divides the polynomial (in the coset domain) by the vanishing
|
||||
/// polynomial.
|
||||
pub fn divide_by_vanishing_poly(&self, mut h_poly: Vec<G>) -> Vec<G> {
|
||||
assert_eq!(h_poly.len(), 1 << self.extended_k);
|
||||
|
||||
// Divide to obtain the quotient polynomial in the coset evaluation
|
||||
// domain.
|
||||
parallelize(&mut h_poly, |h, mut index| {
|
||||
for h in h {
|
||||
h.group_scale(&self.t_evaluations[index % self.t_evaluations.len()]);
|
||||
index += 1;
|
||||
}
|
||||
});
|
||||
|
||||
h_poly
|
||||
}
|
||||
|
||||
fn distribute_powers(mut a: &mut [G], g: G::Scalar) {
|
||||
let coset_powers = [g, g.square()];
|
||||
parallelize(&mut a, |a, mut index| {
|
||||
for a in a {
|
||||
// Distribute powers to move into coset
|
||||
let i = index % (coset_powers.len() + 1);
|
||||
if i != 0 {
|
||||
a.group_scale(&coset_powers[i - 1]);
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) {
|
||||
best_fft(a, omega_inv, log_n);
|
||||
parallelize(a, |a, _| {
|
||||
for a in a {
|
||||
// Finish iFFT
|
||||
a.group_scale(&divisor);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
use super::{
|
||||
circuit::{Circuit, ConstraintSystem, Wire},
|
||||
hash_point, Error, Proof, SRS,
|
||||
};
|
||||
use crate::arithmetic::{
|
||||
eval_polynomial, get_challenge_scalar, Challenge, Curve, CurveAffine, Field,
|
||||
};
|
||||
use crate::polycommit::Params;
|
||||
use crate::transcript::Hasher;
|
||||
|
||||
impl<C: CurveAffine> Proof<C> {
|
||||
/// This creates a proof for the provided `circuit` when given the public
|
||||
/// parameters `params` and the structured reference string `srs` that was
|
||||
/// previously computed for the same circuit.
|
||||
pub fn create<
|
||||
HBase: Hasher<C::Base>,
|
||||
HScalar: Hasher<C::Scalar>,
|
||||
ConcreteCircuit: Circuit<C::Scalar>,
|
||||
>(
|
||||
params: &Params<C>,
|
||||
srs: &SRS<C>,
|
||||
circuit: &ConcreteCircuit,
|
||||
) -> Result<Self, Error> {
|
||||
struct WitnessCollection<F: Field> {
|
||||
a: Vec<F>,
|
||||
b: Vec<F>,
|
||||
c: Vec<F>,
|
||||
d: Vec<F>,
|
||||
sa: Vec<F>,
|
||||
sb: Vec<F>,
|
||||
sc: Vec<F>,
|
||||
sd: Vec<F>,
|
||||
sm: Vec<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> ConstraintSystem<F> for WitnessCollection<F> {
|
||||
fn create_gate(
|
||||
&mut self,
|
||||
sa: F,
|
||||
sb: F,
|
||||
sc: F,
|
||||
sd: F,
|
||||
sm: F,
|
||||
f: impl Fn() -> Result<(F, F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
||||
let (a, b, c, d) = f()?;
|
||||
let tmp = Ok((
|
||||
Wire::A(self.a.len()),
|
||||
Wire::B(self.a.len()),
|
||||
Wire::C(self.a.len()),
|
||||
Wire::D(self.a.len()),
|
||||
));
|
||||
self.a.push(a);
|
||||
self.b.push(b);
|
||||
self.c.push(c);
|
||||
self.d.push(d);
|
||||
self.sa.push(sa);
|
||||
self.sb.push(sb);
|
||||
self.sc.push(sc);
|
||||
self.sd.push(sd);
|
||||
self.sm.push(sm);
|
||||
tmp
|
||||
}
|
||||
// fn copy(&mut self, left: Wire, right: Wire) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
}
|
||||
|
||||
let mut witness = WitnessCollection {
|
||||
a: vec![],
|
||||
b: vec![],
|
||||
c: vec![],
|
||||
d: vec![],
|
||||
sa: vec![],
|
||||
sb: vec![],
|
||||
sc: vec![],
|
||||
sd: vec![],
|
||||
sm: vec![],
|
||||
};
|
||||
|
||||
// Synthesize the circuit to obtain the witness and other information.
|
||||
circuit.synthesize(&mut witness)?;
|
||||
|
||||
// Create a transcript for obtaining Fiat-Shamir challenges.
|
||||
let mut transcript = HBase::init(C::Base::one());
|
||||
|
||||
if witness.a.len() > params.n as usize {
|
||||
// The polynomial commitment does not support a high enough degree
|
||||
// polynomial to commit to our wires because this circuit has too
|
||||
// many gates.
|
||||
return Err(Error::IncompatibleParams);
|
||||
}
|
||||
|
||||
witness.a.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.b.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.c.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.d.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sa.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sb.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sc.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sd.resize(params.n as usize, C::Scalar::zero());
|
||||
witness.sm.resize(params.n as usize, C::Scalar::zero());
|
||||
|
||||
// Compute commitments to the various wire values
|
||||
let a_blind = C::Scalar::one(); // TODO: not random
|
||||
let b_blind = C::Scalar::one(); // TODO: not random
|
||||
let c_blind = C::Scalar::one(); // TODO: not random
|
||||
let d_blind = C::Scalar::one(); // TODO: not random
|
||||
let a_commitment = params.commit_lagrange(&witness.a, a_blind).to_affine();
|
||||
let b_commitment = params.commit_lagrange(&witness.b, b_blind).to_affine();
|
||||
let c_commitment = params.commit_lagrange(&witness.c, c_blind).to_affine();
|
||||
let d_commitment = params.commit_lagrange(&witness.d, d_blind).to_affine();
|
||||
|
||||
hash_point(&mut transcript, &a_commitment)?;
|
||||
hash_point(&mut transcript, &b_commitment)?;
|
||||
hash_point(&mut transcript, &c_commitment)?;
|
||||
hash_point(&mut transcript, &d_commitment)?;
|
||||
|
||||
let domain = &srs.domain;
|
||||
|
||||
let (a_coset, a_poly) = domain.obtain_coset(witness.a);
|
||||
let (b_coset, b_poly) = domain.obtain_coset(witness.b);
|
||||
let (c_coset, c_poly) = domain.obtain_coset(witness.c);
|
||||
let (d_coset, d_poly) = domain.obtain_coset(witness.d);
|
||||
|
||||
// (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc)
|
||||
let mut h_poly = Vec::with_capacity(a_coset.len());
|
||||
for ((((((((a, b), c), d), sa), sb), sc), sd), sm) in a_coset
|
||||
.iter()
|
||||
.zip(b_coset.iter())
|
||||
.zip(c_coset.iter())
|
||||
.zip(d_coset.iter())
|
||||
.zip(srs.sa.0.iter())
|
||||
.zip(srs.sb.0.iter())
|
||||
.zip(srs.sc.0.iter())
|
||||
.zip(srs.sd.0.iter())
|
||||
.zip(srs.sm.0.iter())
|
||||
{
|
||||
h_poly.push((*a) * sa + &((*b) * sb) + &((*a) * sm * b) + &((*d) * sd) - &((*c) * sc));
|
||||
}
|
||||
|
||||
// Divide by t(X) = X^{params.n} - 1.
|
||||
let h_poly = domain.divide_by_vanishing_poly(h_poly);
|
||||
|
||||
// Obtain final h(X) polynomial
|
||||
let h_poly = domain.from_coset(h_poly);
|
||||
|
||||
// Split h(X) up into pieces
|
||||
let h_pieces = h_poly
|
||||
.chunks_exact(params.n as usize)
|
||||
.map(|v| v.to_vec())
|
||||
.collect::<Vec<_>>();
|
||||
drop(h_poly);
|
||||
let h_blinds = vec![C::Scalar::one(); h_pieces.len()]; // TODO: not random
|
||||
|
||||
// Compute commitments to each h(X) piece
|
||||
let h_commitments: Vec<_> = h_pieces
|
||||
.iter()
|
||||
.zip(h_blinds.iter())
|
||||
.map(|(h_piece, blind)| params.commit(&h_piece, *blind).to_affine())
|
||||
.collect();
|
||||
|
||||
// Hash each h(X) piece
|
||||
for c in h_commitments.iter() {
|
||||
hash_point(&mut transcript, c)?;
|
||||
}
|
||||
|
||||
let x: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// Evaluate polynomials at x
|
||||
let a_eval_x = eval_polynomial(&a_poly, x);
|
||||
let b_eval_x = eval_polynomial(&b_poly, x);
|
||||
let c_eval_x = eval_polynomial(&c_poly, x);
|
||||
let d_eval_x = eval_polynomial(&d_poly, x);
|
||||
let sa_eval_x = eval_polynomial(&srs.sa.1, x);
|
||||
let sb_eval_x = eval_polynomial(&srs.sb.1, x);
|
||||
let sc_eval_x = eval_polynomial(&srs.sc.1, x);
|
||||
let sd_eval_x = eval_polynomial(&srs.sd.1, x);
|
||||
let sm_eval_x = eval_polynomial(&srs.sm.1, x);
|
||||
|
||||
let h_evals_x: Vec<_> = h_pieces
|
||||
.iter()
|
||||
.map(|poly| eval_polynomial(poly, x))
|
||||
.collect();
|
||||
|
||||
// We set up a second transcript on the scalar field to hash in openings of
|
||||
// our polynomial commitments.
|
||||
let mut transcript_scalar = HScalar::init(C::Scalar::one());
|
||||
transcript_scalar.absorb(a_eval_x);
|
||||
transcript_scalar.absorb(b_eval_x);
|
||||
transcript_scalar.absorb(c_eval_x);
|
||||
transcript_scalar.absorb(d_eval_x);
|
||||
transcript_scalar.absorb(sa_eval_x);
|
||||
transcript_scalar.absorb(sb_eval_x);
|
||||
transcript_scalar.absorb(sc_eval_x);
|
||||
transcript_scalar.absorb(sd_eval_x);
|
||||
transcript_scalar.absorb(sm_eval_x);
|
||||
|
||||
// Hash each h(x) piece
|
||||
for eval in h_evals_x.iter() {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
let transcript_scalar_point =
|
||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
let y: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut q_commitment = h_commitments[0].clone().to_projective();
|
||||
let mut q_poly = h_pieces[0].clone();
|
||||
let mut q_blind = h_blinds[0];
|
||||
{
|
||||
let mut accumulate = |poly: &[_], blind: &C::Scalar, commitment: C| {
|
||||
for (a, q) in poly.iter().zip(q_poly.iter_mut()) {
|
||||
*q = (*q * &y) + a;
|
||||
}
|
||||
q_commitment = (q_commitment * y) + &commitment.to_projective();
|
||||
q_blind = (q_blind * &y) + blind;
|
||||
};
|
||||
|
||||
for ((poly, blind), commitment) in h_pieces
|
||||
.iter()
|
||||
.zip(h_blinds.iter())
|
||||
.zip(h_commitments.iter())
|
||||
.skip(1)
|
||||
{
|
||||
accumulate(&poly, blind, *commitment);
|
||||
}
|
||||
|
||||
accumulate(&a_poly, &a_blind, a_commitment);
|
||||
accumulate(&b_poly, &b_blind, b_commitment);
|
||||
accumulate(&c_poly, &c_blind, c_commitment);
|
||||
accumulate(&d_poly, &d_blind, d_commitment);
|
||||
accumulate(&srs.sa.1, &Field::one(), srs.sa_commitment);
|
||||
accumulate(&srs.sb.1, &Field::one(), srs.sb_commitment);
|
||||
accumulate(&srs.sc.1, &Field::one(), srs.sc_commitment);
|
||||
accumulate(&srs.sd.1, &Field::one(), srs.sd_commitment);
|
||||
accumulate(&srs.sm.1, &Field::one(), srs.sm_commitment);
|
||||
}
|
||||
|
||||
// Let's prove that the q_commitment opens at x to the expected value.
|
||||
let opening = params
|
||||
.create_proof(&mut transcript, &q_poly, q_blind, x)
|
||||
.map_err(|_| Error::ConstraintSystemFailure)?;
|
||||
|
||||
Ok(Proof {
|
||||
a_commitment,
|
||||
b_commitment,
|
||||
c_commitment,
|
||||
d_commitment,
|
||||
h_commitments,
|
||||
a_eval_x,
|
||||
b_eval_x,
|
||||
c_eval_x,
|
||||
d_eval_x,
|
||||
sa_eval_x,
|
||||
sb_eval_x,
|
||||
sc_eval_x,
|
||||
sd_eval_x,
|
||||
sm_eval_x,
|
||||
h_evals_x,
|
||||
opening,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
use super::{
|
||||
circuit::{Circuit, ConstraintSystem, Wire},
|
||||
domain::EvaluationDomain,
|
||||
Error, GATE_DEGREE, SRS,
|
||||
};
|
||||
use crate::arithmetic::{Curve, CurveAffine, Field};
|
||||
use crate::polycommit::Params;
|
||||
|
||||
impl<C: CurveAffine> SRS<C> {
|
||||
/// This generates a structured reference string for the provided `circuit`
|
||||
/// and `params`.
|
||||
pub fn generate<ConcreteCircuit: Circuit<C::Scalar>>(
|
||||
params: &Params<C>,
|
||||
circuit: &ConcreteCircuit,
|
||||
) -> Result<Self, Error> {
|
||||
struct Assembly<F: Field> {
|
||||
sa: Vec<F>,
|
||||
sb: Vec<F>,
|
||||
sc: Vec<F>,
|
||||
sd: Vec<F>,
|
||||
sm: Vec<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> ConstraintSystem<F> for Assembly<F> {
|
||||
fn create_gate(
|
||||
&mut self,
|
||||
sa: F,
|
||||
sb: F,
|
||||
sc: F,
|
||||
sd: F,
|
||||
sm: F,
|
||||
_: impl Fn() -> Result<(F, F, F, F), Error>,
|
||||
) -> Result<(Wire, Wire, Wire, Wire), Error> {
|
||||
let tmp = Ok((
|
||||
Wire::A(self.sa.len()),
|
||||
Wire::B(self.sa.len()),
|
||||
Wire::C(self.sa.len()),
|
||||
Wire::D(self.sa.len()),
|
||||
));
|
||||
self.sa.push(sa);
|
||||
self.sb.push(sb);
|
||||
self.sc.push(sc);
|
||||
self.sd.push(sd);
|
||||
self.sm.push(sm);
|
||||
tmp
|
||||
}
|
||||
}
|
||||
|
||||
let mut assembly: Assembly<C::Scalar> = Assembly {
|
||||
sa: vec![],
|
||||
sb: vec![],
|
||||
sc: vec![],
|
||||
sd: vec![],
|
||||
sm: vec![],
|
||||
};
|
||||
|
||||
// Synthesize the circuit to obtain SRS
|
||||
circuit.synthesize(&mut assembly)?;
|
||||
|
||||
assembly.sa.resize(params.n as usize, C::Scalar::zero());
|
||||
assembly.sb.resize(params.n as usize, C::Scalar::zero());
|
||||
assembly.sc.resize(params.n as usize, C::Scalar::zero());
|
||||
assembly.sd.resize(params.n as usize, C::Scalar::zero());
|
||||
assembly.sm.resize(params.n as usize, C::Scalar::zero());
|
||||
|
||||
// Compute commitments to the fixed wire values
|
||||
let sa_commitment = params
|
||||
.commit_lagrange(&assembly.sa, C::Scalar::one())
|
||||
.to_affine();
|
||||
let sb_commitment = params
|
||||
.commit_lagrange(&assembly.sb, C::Scalar::one())
|
||||
.to_affine();
|
||||
let sc_commitment = params
|
||||
.commit_lagrange(&assembly.sc, C::Scalar::one())
|
||||
.to_affine();
|
||||
let sd_commitment = params
|
||||
.commit_lagrange(&assembly.sd, C::Scalar::one())
|
||||
.to_affine();
|
||||
let sm_commitment = params
|
||||
.commit_lagrange(&assembly.sm, C::Scalar::one())
|
||||
.to_affine();
|
||||
|
||||
let domain = EvaluationDomain::new(GATE_DEGREE, params.k);
|
||||
|
||||
let sa = domain.obtain_coset(assembly.sa);
|
||||
let sb = domain.obtain_coset(assembly.sb);
|
||||
let sc = domain.obtain_coset(assembly.sc);
|
||||
let sd = domain.obtain_coset(assembly.sd);
|
||||
let sm = domain.obtain_coset(assembly.sm);
|
||||
|
||||
Ok(SRS {
|
||||
sa,
|
||||
sb,
|
||||
sc,
|
||||
sd,
|
||||
sm,
|
||||
sa_commitment,
|
||||
sb_commitment,
|
||||
sc_commitment,
|
||||
sd_commitment,
|
||||
sm_commitment,
|
||||
domain,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
use super::{hash_point, Proof, SRS};
|
||||
use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field};
|
||||
use crate::polycommit::Params;
|
||||
use crate::transcript::Hasher;
|
||||
|
||||
impl<C: CurveAffine> Proof<C> {
|
||||
/// Returns
|
||||
pub fn verify<HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
|
||||
&self,
|
||||
params: &Params<C>,
|
||||
srs: &SRS<C>,
|
||||
) -> bool {
|
||||
// Create a transcript for obtaining Fiat-Shamir challenges.
|
||||
let mut transcript = HBase::init(C::Base::one());
|
||||
|
||||
hash_point(&mut transcript, &self.a_commitment)
|
||||
.expect("proof cannot contain points at infinity");
|
||||
hash_point(&mut transcript, &self.b_commitment)
|
||||
.expect("proof cannot contain points at infinity");
|
||||
hash_point(&mut transcript, &self.c_commitment)
|
||||
.expect("proof cannot contain points at infinity");
|
||||
hash_point(&mut transcript, &self.d_commitment)
|
||||
.expect("proof cannot contain points at infinity");
|
||||
|
||||
for c in &self.h_commitments {
|
||||
hash_point(&mut transcript, c).expect("proof cannot contain points at infinity");
|
||||
}
|
||||
|
||||
let x: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
// We set up a second transcript on the scalar field to hash in openings of
|
||||
// our polynomial commitments.
|
||||
let mut transcript_scalar = HScalar::init(C::Scalar::one());
|
||||
transcript_scalar.absorb(self.a_eval_x);
|
||||
transcript_scalar.absorb(self.b_eval_x);
|
||||
transcript_scalar.absorb(self.c_eval_x);
|
||||
transcript_scalar.absorb(self.d_eval_x);
|
||||
transcript_scalar.absorb(self.sa_eval_x);
|
||||
transcript_scalar.absorb(self.sb_eval_x);
|
||||
transcript_scalar.absorb(self.sc_eval_x);
|
||||
transcript_scalar.absorb(self.sd_eval_x);
|
||||
transcript_scalar.absorb(self.sm_eval_x);
|
||||
|
||||
for eval in &self.h_evals_x {
|
||||
transcript_scalar.absorb(*eval);
|
||||
}
|
||||
|
||||
let transcript_scalar_point =
|
||||
C::Base::from_bytes(&(transcript_scalar.squeeze()).to_bytes()).unwrap();
|
||||
transcript.absorb(transcript_scalar_point);
|
||||
|
||||
let y: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128()));
|
||||
|
||||
let mut q_commitment = self.h_commitments[0].clone().to_projective();
|
||||
let mut expected_opening = self.h_evals_x[0];
|
||||
{
|
||||
let mut accumulate = |commitment: C, opening: C::Scalar| {
|
||||
q_commitment = commitment.to_projective() + &(q_commitment * y);
|
||||
expected_opening = opening + &(expected_opening * &y);
|
||||
};
|
||||
|
||||
for (commitment, eval) in self.h_commitments.iter().zip(self.h_evals_x.iter()).skip(1) {
|
||||
accumulate(*commitment, *eval);
|
||||
}
|
||||
|
||||
accumulate(self.a_commitment, self.a_eval_x);
|
||||
accumulate(self.b_commitment, self.b_eval_x);
|
||||
accumulate(self.c_commitment, self.c_eval_x);
|
||||
accumulate(self.d_commitment, self.d_eval_x);
|
||||
accumulate(srs.sa_commitment, self.sa_eval_x);
|
||||
accumulate(srs.sb_commitment, self.sb_eval_x);
|
||||
accumulate(srs.sc_commitment, self.sc_eval_x);
|
||||
accumulate(srs.sd_commitment, self.sd_eval_x);
|
||||
accumulate(srs.sm_commitment, self.sm_eval_x);
|
||||
}
|
||||
let q_commitment = q_commitment.to_affine();
|
||||
|
||||
let xn = x.pow(&[params.n as u64, 0, 0, 0]);
|
||||
|
||||
// Compute the expected h(x) value
|
||||
let mut h_eval_x = C::Scalar::zero();
|
||||
let mut cur = C::Scalar::one();
|
||||
for eval in &self.h_evals_x {
|
||||
h_eval_x += &(cur * eval);
|
||||
cur *= &xn;
|
||||
}
|
||||
|
||||
// Check that the circuit is satisfied.
|
||||
// (a * sa) + (b * sb) + (a * sm * b) + (d * sd) - (c * sc)
|
||||
if self.a_eval_x * &self.sa_eval_x
|
||||
+ &(self.b_eval_x * &self.sb_eval_x)
|
||||
+ &(self.a_eval_x * &self.sm_eval_x * &self.b_eval_x)
|
||||
+ &(self.d_eval_x * &self.sd_eval_x)
|
||||
- &(self.c_eval_x * &self.sc_eval_x)
|
||||
!= h_eval_x * &(xn - &C::Scalar::one())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
params.verify_proof(
|
||||
&self.opening,
|
||||
&mut transcript,
|
||||
x,
|
||||
&q_commitment,
|
||||
expected_opening,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,600 @@
|
|||
//! This module contains an implementation of the polynomial commitment scheme
|
||||
//! described in the [Halo][halo] paper.
|
||||
//!
|
||||
//! [halo]: https://eprint.iacr.org/2019/1021
|
||||
|
||||
use crate::arithmetic::{
|
||||
best_fft, best_multiexp, compute_inner_product, get_challenge_scalar, parallelize, Challenge,
|
||||
Curve, CurveAffine, Field,
|
||||
};
|
||||
use crate::transcript::Hasher;
|
||||
|
||||
/// This is a proof object for the polynomial commitment scheme opening.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OpeningProof<C: CurveAffine> {
|
||||
fork: u8,
|
||||
rounds: Vec<(C, C)>,
|
||||
delta: C,
|
||||
z1: C::Scalar,
|
||||
z2: C::Scalar,
|
||||
}
|
||||
|
||||
/// These are the public parameters for the polynomial commitment scheme.
|
||||
#[derive(Debug)]
|
||||
pub struct Params<C: CurveAffine> {
|
||||
pub(crate) k: u32,
|
||||
pub(crate) n: u64,
|
||||
pub(crate) g: Vec<C>,
|
||||
pub(crate) g_lagrange: Vec<C>,
|
||||
pub(crate) h: C,
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> Params<C> {
|
||||
/// Initializes parameters for the curve, given a random oracle to draw
|
||||
/// points from.
|
||||
pub fn new<H: Hasher<C::Base>>(k: u32) -> Self {
|
||||
// This is usually a limitation on the curve, but we also want 32-bit
|
||||
// architectures to be supported.
|
||||
assert!(k < 32);
|
||||
// No goofy hardware please.
|
||||
assert!(core::mem::size_of::<usize>() >= 4);
|
||||
|
||||
let n: u64 = 1 << k;
|
||||
|
||||
let g = {
|
||||
let hasher = &H::init(C::Base::zero());
|
||||
|
||||
let mut g = Vec::with_capacity(n as usize);
|
||||
g.resize(n as usize, C::zero());
|
||||
|
||||
parallelize(&mut g, move |g, start| {
|
||||
let mut cur_value = C::Base::from(start as u64);
|
||||
for g in g.iter_mut() {
|
||||
let mut hasher = hasher.clone();
|
||||
hasher.absorb(cur_value);
|
||||
cur_value += &C::Base::one();
|
||||
loop {
|
||||
let x = hasher.squeeze().to_bytes();
|
||||
let p = C::from_bytes(&x);
|
||||
if bool::from(p.is_some()) {
|
||||
*g = p.unwrap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
g
|
||||
};
|
||||
|
||||
// Let's evaluate all of the Lagrange basis polynomials
|
||||
// using an inverse FFT.
|
||||
let mut alpha_inv = C::Scalar::ROOT_OF_UNITY_INV;
|
||||
for _ in k..C::Scalar::S {
|
||||
alpha_inv = alpha_inv.square();
|
||||
}
|
||||
let mut g_lagrange_projective = g.iter().map(|g| g.to_projective()).collect::<Vec<_>>();
|
||||
best_fft(&mut g_lagrange_projective, alpha_inv, k);
|
||||
let minv = C::Scalar::TWO_INV.pow_vartime(&[k as u64, 0, 0, 0]);
|
||||
parallelize(&mut g_lagrange_projective, |g, _| {
|
||||
for g in g.iter_mut() {
|
||||
*g *= minv;
|
||||
}
|
||||
});
|
||||
|
||||
let g_lagrange = {
|
||||
let mut g_lagrange = vec![C::zero(); n as usize];
|
||||
parallelize(&mut g_lagrange, |g_lagrange, starts| {
|
||||
C::Projective::batch_to_affine(
|
||||
&g_lagrange_projective[starts..(starts + g_lagrange.len())],
|
||||
g_lagrange,
|
||||
);
|
||||
});
|
||||
drop(g_lagrange_projective);
|
||||
g_lagrange
|
||||
};
|
||||
|
||||
let h = {
|
||||
let mut hasher = H::init(C::Base::zero());
|
||||
hasher.absorb(-C::Base::one());
|
||||
let x = hasher.squeeze().to_bytes();
|
||||
let p = C::from_bytes(&x);
|
||||
p.unwrap()
|
||||
};
|
||||
|
||||
Params {
|
||||
k,
|
||||
n,
|
||||
g,
|
||||
g_lagrange,
|
||||
h,
|
||||
}
|
||||
}
|
||||
|
||||
/// This computes a commitment to a polynomial described by the provided
|
||||
/// slice of coefficients. The commitment will be blinded by the blinding
|
||||
/// factor `r`.
|
||||
pub fn commit(&self, poly: &[C::Scalar], r: C::Scalar) -> C::Projective {
|
||||
let mut tmp_scalars = Vec::with_capacity(poly.len() + 1);
|
||||
let mut tmp_bases = Vec::with_capacity(poly.len() + 1);
|
||||
|
||||
tmp_scalars.extend(poly.iter());
|
||||
tmp_scalars.push(r);
|
||||
|
||||
tmp_bases.extend(self.g.iter());
|
||||
tmp_bases.push(self.h);
|
||||
|
||||
best_multiexp::<C>(&tmp_scalars, &tmp_bases)
|
||||
}
|
||||
|
||||
/// This commits to a polynomial using its evaluations over the $2^k$ size
|
||||
/// evaluation domain. The commitment will be blinded by the blinding factor
|
||||
/// `r`.
|
||||
pub fn commit_lagrange(&self, poly: &[C::Scalar], r: C::Scalar) -> C::Projective {
|
||||
let mut tmp_scalars = Vec::with_capacity(poly.len() + 1);
|
||||
let mut tmp_bases = Vec::with_capacity(poly.len() + 1);
|
||||
|
||||
tmp_scalars.extend(poly.iter());
|
||||
tmp_scalars.push(r);
|
||||
|
||||
tmp_bases.extend(self.g_lagrange.iter());
|
||||
tmp_bases.push(self.h);
|
||||
|
||||
best_multiexp::<C>(&tmp_scalars, &tmp_bases)
|
||||
}
|
||||
|
||||
/// Create a polynomial commitment opening proof for the polynomial defined
|
||||
/// by the coefficients `px`, the blinding factor `blind` used for the
|
||||
/// polynomial commitment, and the point `x` that the polynomial is
|
||||
/// evaluated at.
|
||||
///
|
||||
/// This function will panic if the provided polynomial is too large with
|
||||
/// respect to the polynomial commitment parameters.
|
||||
///
|
||||
/// **Important:** This function assumes that the provided `transcript` has
|
||||
/// already seen the common inputs: the polynomial commitment P, the claimed
|
||||
/// opening v, and the point x. It's probably also nice for the transcript
|
||||
/// to have seen the elliptic curve description and the SRS, if you want to
|
||||
/// be rigorous.
|
||||
pub fn create_proof<H: Hasher<C::Base>>(
|
||||
&self,
|
||||
transcript: &mut H,
|
||||
px: &[C::Scalar],
|
||||
mut blind: C::Scalar,
|
||||
x: C::Scalar,
|
||||
) -> Result<OpeningProof<C>, ()> {
|
||||
// We're limited to polynomials of degree n - 1.
|
||||
assert!(px.len() <= self.n as usize);
|
||||
|
||||
let mut fork = 0;
|
||||
|
||||
// TODO: remove this hack and force the caller to deal with it
|
||||
loop {
|
||||
let mut transcript = transcript.clone();
|
||||
transcript.absorb(C::Base::from_u64(fork as u64));
|
||||
let u_x = transcript.squeeze();
|
||||
// y^2 = x^3 + B
|
||||
let u_y2 = u_x.square() * &u_x + &C::b();
|
||||
let u_y = u_y2.deterministic_sqrt();
|
||||
|
||||
if u_y.is_none() {
|
||||
fork += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
transcript.absorb(C::Base::from_u64(fork as u64));
|
||||
|
||||
// Compute U
|
||||
let u = {
|
||||
let u_x = transcript.squeeze();
|
||||
// y^2 = x^3 + B
|
||||
let u_y2 = u_x.square() * &u_x + &C::b();
|
||||
let u_y = u_y2.deterministic_sqrt().unwrap();
|
||||
|
||||
C::from_xy(u_x, u_y).unwrap()
|
||||
};
|
||||
|
||||
// Initialize the vector `a` as the coefficients of the polynomial,
|
||||
// rounding up to the parameters.
|
||||
let mut a = px.to_vec();
|
||||
a.resize(self.n as usize, C::Scalar::zero());
|
||||
|
||||
// Initialize the vector `b` as the powers of `x`. The inner product of
|
||||
// `a` and `b` is the evaluation of the polynomial at `x`.
|
||||
let mut b = Vec::with_capacity(1 << self.k);
|
||||
{
|
||||
let mut cur = C::Scalar::one();
|
||||
for _ in 0..(1 << self.k) {
|
||||
b.push(cur);
|
||||
cur *= &x;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the vector `G` from the SRS. We'll be progressively
|
||||
// collapsing this vector into smaller and smaller vectors until it is
|
||||
// of length 1.
|
||||
let mut g = self.g.clone();
|
||||
|
||||
// Perform the inner product argument, round by round.
|
||||
let mut rounds = Vec::with_capacity(self.k as usize);
|
||||
for k in (1..=self.k).rev() {
|
||||
let half = 1 << (k - 1); // half the length of `a`, `b`, `G`
|
||||
|
||||
// Compute L, R
|
||||
//
|
||||
// TODO: If we modify multiexp to take "extra" bases, we could speed
|
||||
// this piece up a bit by combining the multiexps.
|
||||
let l = best_multiexp(&a[0..half], &g[half..]);
|
||||
let r = best_multiexp(&a[half..], &g[0..half]);
|
||||
let value_l = compute_inner_product(&a[0..half], &b[half..]);
|
||||
let value_r = compute_inner_product(&a[half..], &b[0..half]);
|
||||
let mut l_randomness = C::Scalar::random();
|
||||
let r_randomness = C::Scalar::random();
|
||||
let l = l + &best_multiexp(&[value_l, l_randomness], &[u, self.h]);
|
||||
let r = r + &best_multiexp(&[value_r, r_randomness], &[u, self.h]);
|
||||
let mut l = l.to_affine();
|
||||
let r = r.to_affine();
|
||||
|
||||
let challenge = loop {
|
||||
// We'll fork the transcript and adjust our randomness
|
||||
// until the challenge is a square.
|
||||
let mut transcript = transcript.clone();
|
||||
|
||||
// We expect these to not be points at infinity due to the randomness.
|
||||
let (l_x, l_y) = l.get_xy().unwrap();
|
||||
let (r_x, r_y) = r.get_xy().unwrap();
|
||||
|
||||
// Feed L and R into the cloned transcript...
|
||||
transcript.absorb(l_x);
|
||||
transcript.absorb(l_y);
|
||||
transcript.absorb(r_x);
|
||||
transcript.absorb(r_y);
|
||||
|
||||
// ... and get the squared challenge.
|
||||
let challenge_sq_packed = transcript.squeeze().get_lower_128();
|
||||
let challenge_sq: C::Scalar = get_challenge_scalar(Challenge(challenge_sq_packed));
|
||||
|
||||
// There might be no square root, in which case we'll fork the
|
||||
// transcript.
|
||||
let challenge = challenge_sq.deterministic_sqrt();
|
||||
if let Some(challenge) = challenge {
|
||||
break challenge;
|
||||
} else {
|
||||
// Try again, with slightly different randomness
|
||||
l = (l + self.h).to_affine();
|
||||
l_randomness += &C::Scalar::one();
|
||||
}
|
||||
};
|
||||
|
||||
// Challenge is unlikely to be zero.
|
||||
let challenge_inv = challenge.invert().unwrap();
|
||||
let challenge_sq_inv = challenge_inv.square();
|
||||
let challenge_sq = challenge.square();
|
||||
|
||||
// Feed L and R into the real transcript
|
||||
let (l_x, l_y) = l.get_xy().unwrap();
|
||||
let (r_x, r_y) = r.get_xy().unwrap();
|
||||
transcript.absorb(l_x);
|
||||
transcript.absorb(l_y);
|
||||
transcript.absorb(r_x);
|
||||
transcript.absorb(r_y);
|
||||
|
||||
// And obtain the challenge, even though we already have it, since
|
||||
// squeezing affects the transcript.
|
||||
{
|
||||
let challenge_sq_packed = transcript.squeeze().get_lower_128();
|
||||
let challenge_sq_expected = get_challenge_scalar(Challenge(challenge_sq_packed));
|
||||
assert_eq!(challenge_sq, challenge_sq_expected);
|
||||
}
|
||||
|
||||
// Done with this round.
|
||||
rounds.push((l, r));
|
||||
|
||||
// Collapse `a` and `b`.
|
||||
// TODO: parallelize
|
||||
for i in 0..half {
|
||||
a[i] = (a[i] * &challenge) + &(a[i + half] * &challenge_inv);
|
||||
b[i] = (b[i] * &challenge_inv) + &(b[i + half] * &challenge);
|
||||
}
|
||||
a.truncate(half);
|
||||
b.truncate(half);
|
||||
|
||||
// Collapse `G`
|
||||
parallel_generator_collapse(&mut g, challenge, challenge_inv);
|
||||
g.truncate(half);
|
||||
|
||||
// Update randomness (the synthetic blinding factor at the end)
|
||||
blind += &(l_randomness * &challenge_sq);
|
||||
blind += &(r_randomness * &challenge_sq_inv);
|
||||
}
|
||||
|
||||
// We have fully colapsed `a`, `b`, `G`
|
||||
assert_eq!(a.len(), 1);
|
||||
let a = a[0];
|
||||
assert_eq!(b.len(), 1);
|
||||
let b = b[0];
|
||||
assert_eq!(g.len(), 1);
|
||||
let g = g[0];
|
||||
|
||||
// Random nonces for the zero-knowledge opening
|
||||
let d = C::Scalar::random();
|
||||
let s = C::Scalar::random();
|
||||
|
||||
let delta = best_multiexp(&[d, d * &b, s], &[g, u, self.h]).to_affine();
|
||||
|
||||
let (delta_x, delta_y) = delta.get_xy().unwrap();
|
||||
|
||||
// Feed delta into the transcript
|
||||
transcript.absorb(delta_x);
|
||||
transcript.absorb(delta_y);
|
||||
|
||||
// Obtain the challenge c.
|
||||
let c_packed = transcript.squeeze().get_lower_128();
|
||||
let c: C::Scalar = get_challenge_scalar(Challenge(c_packed));
|
||||
|
||||
// Compute z1 and z2 as described in the Halo paper.
|
||||
let z1 = a * &c + &d;
|
||||
let z2 = c * &blind + &s;
|
||||
|
||||
Ok(OpeningProof {
|
||||
fork,
|
||||
rounds,
|
||||
delta,
|
||||
z1,
|
||||
z2,
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks to see if an [`OpeningProof`] is valid given the current
|
||||
/// `transcript`, and a point `x` that the polynomial commitment `p` opens
|
||||
/// purportedly to the value `v`.
|
||||
pub fn verify_proof<H: Hasher<C::Base>>(
|
||||
&self,
|
||||
proof: &OpeningProof<C>,
|
||||
transcript: &mut H,
|
||||
x: C::Scalar,
|
||||
p: &C,
|
||||
v: C::Scalar,
|
||||
) -> bool {
|
||||
// Check for well-formedness
|
||||
if proof.rounds.len() != self.k as usize {
|
||||
return false;
|
||||
}
|
||||
|
||||
transcript.absorb(C::Base::from_u64(proof.fork as u64));
|
||||
|
||||
// Compute U
|
||||
let u = {
|
||||
let u_x = transcript.squeeze();
|
||||
// y^2 = x^3 + B
|
||||
let u_y2 = u_x.square() * &u_x + &C::b();
|
||||
let u_y = u_y2.deterministic_sqrt();
|
||||
if u_y.is_none() {
|
||||
return false;
|
||||
}
|
||||
let u_y = u_y.unwrap();
|
||||
|
||||
C::from_xy(u_x, u_y).unwrap()
|
||||
};
|
||||
|
||||
let mut extra_scalars = Vec::with_capacity(proof.rounds.len() * 2 + 4 + self.n as usize);
|
||||
let mut extra_bases = Vec::with_capacity(proof.rounds.len() * 2 + 4 + self.n as usize);
|
||||
|
||||
// Data about the challenges from each of the rounds.
|
||||
let mut challenges = Vec::with_capacity(proof.rounds.len());
|
||||
let mut challenges_inv = Vec::with_capacity(proof.rounds.len());
|
||||
let mut challenges_sq = Vec::with_capacity(proof.rounds.len());
|
||||
let mut allinv = Field::one();
|
||||
|
||||
for round in &proof.rounds {
|
||||
// Feed L and R into the transcript.
|
||||
let l = round.0.get_xy();
|
||||
let r = round.1.get_xy();
|
||||
if bool::from(l.is_none() | r.is_none()) {
|
||||
return false;
|
||||
}
|
||||
let l = l.unwrap();
|
||||
let r = r.unwrap();
|
||||
transcript.absorb(l.0);
|
||||
transcript.absorb(l.1);
|
||||
transcript.absorb(r.0);
|
||||
transcript.absorb(r.1);
|
||||
let challenge_sq_packed = transcript.squeeze().get_lower_128();
|
||||
let challenge_sq: C::Scalar = get_challenge_scalar(Challenge(challenge_sq_packed));
|
||||
|
||||
let challenge = challenge_sq.deterministic_sqrt();
|
||||
if challenge.is_none() {
|
||||
// We didn't sample a square.
|
||||
return false;
|
||||
}
|
||||
let challenge = challenge.unwrap();
|
||||
|
||||
let challenge_inv = challenge.invert();
|
||||
if bool::from(challenge_inv.is_none()) {
|
||||
// We sampled zero for some reason, unlikely to happen by
|
||||
// chance.
|
||||
return false;
|
||||
}
|
||||
let challenge_inv = challenge_inv.unwrap();
|
||||
allinv *= challenge_inv;
|
||||
|
||||
let challenge_sq_inv = challenge_inv.square();
|
||||
|
||||
extra_scalars.push(challenge_sq);
|
||||
extra_bases.push(round.0);
|
||||
extra_scalars.push(challenge_sq_inv);
|
||||
extra_bases.push(round.1);
|
||||
|
||||
challenges.push(challenge);
|
||||
challenges_inv.push(challenge_inv);
|
||||
challenges_sq.push(challenge_sq);
|
||||
}
|
||||
|
||||
let delta = proof.delta.get_xy();
|
||||
if bool::from(delta.is_none()) {
|
||||
return false;
|
||||
}
|
||||
let delta = delta.unwrap();
|
||||
|
||||
// Feed delta into the transcript
|
||||
transcript.absorb(delta.0);
|
||||
transcript.absorb(delta.1);
|
||||
|
||||
// Get the challenge `c`
|
||||
let c_packed = transcript.squeeze().get_lower_128();
|
||||
let c: C::Scalar = get_challenge_scalar(Challenge(c_packed));
|
||||
|
||||
// Check
|
||||
// [c] P + [c * v] U + [c] sum(L_i * u_i^2) + [c] sum(R_i * u_i^-2) + delta - [z1] G - [z1 * b] U - [z2] H
|
||||
// = 0
|
||||
|
||||
for scalar in &mut extra_scalars {
|
||||
*scalar *= &c;
|
||||
}
|
||||
|
||||
let b = compute_b(x, &challenges, &challenges_inv);
|
||||
|
||||
let neg_z1 = -proof.z1;
|
||||
|
||||
// [c] P
|
||||
extra_bases.push(*p);
|
||||
extra_scalars.push(c);
|
||||
|
||||
// [c * v] U - [z1 * b] U
|
||||
extra_bases.push(u);
|
||||
extra_scalars.push((c * &v) + &(neg_z1 * &b));
|
||||
|
||||
// delta
|
||||
extra_bases.push(proof.delta);
|
||||
extra_scalars.push(Field::one());
|
||||
|
||||
// - [z2] H
|
||||
extra_bases.push(self.h);
|
||||
extra_scalars.push(-proof.z2);
|
||||
|
||||
// - [z1] G
|
||||
extra_bases.extend(&self.g);
|
||||
let mut s = compute_s(&challenges_sq, allinv);
|
||||
// TODO: parallelize
|
||||
for s in &mut s {
|
||||
*s *= &neg_z1;
|
||||
}
|
||||
extra_scalars.extend(s);
|
||||
|
||||
bool::from(best_multiexp(&extra_scalars, &extra_bases).is_zero())
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_b<F: Field>(x: F, challenges: &[F], challenges_inv: &[F]) -> F {
|
||||
assert!(!challenges.is_empty());
|
||||
assert_eq!(challenges.len(), challenges_inv.len());
|
||||
if challenges.len() == 1 {
|
||||
*challenges_inv.last().unwrap() + *challenges.last().unwrap() * x
|
||||
} else {
|
||||
(*challenges_inv.last().unwrap() + *challenges.last().unwrap() * x)
|
||||
* compute_b(
|
||||
x.square(),
|
||||
&challenges[0..(challenges.len() - 1)],
|
||||
&challenges_inv[0..(challenges.len() - 1)],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: parallelize
|
||||
fn compute_s<F: Field>(challenges_sq: &[F], allinv: F) -> Vec<F> {
|
||||
let lg_n = challenges_sq.len();
|
||||
let n = 1 << lg_n;
|
||||
|
||||
let mut s = Vec::with_capacity(n);
|
||||
s.push(allinv);
|
||||
for i in 1..n {
|
||||
let lg_i = (32 - 1 - (i as u32).leading_zeros()) as usize;
|
||||
let k = 1 << lg_i;
|
||||
let u_lg_i_sq = challenges_sq[(lg_n - 1) - lg_i];
|
||||
s.push(s[i - k] * u_lg_i_sq);
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn parallel_generator_collapse<C: CurveAffine>(
|
||||
g: &mut [C],
|
||||
challenge: C::Scalar,
|
||||
challenge_inv: C::Scalar,
|
||||
) {
|
||||
let len = g.len() / 2;
|
||||
let (mut g_lo, g_hi) = g.split_at_mut(len);
|
||||
|
||||
parallelize(&mut g_lo, |g_lo, start| {
|
||||
let g_hi = &g_hi[start..];
|
||||
let mut tmp = Vec::with_capacity(g_lo.len());
|
||||
for (g_lo, g_hi) in g_lo.iter().zip(g_hi.iter()) {
|
||||
// TODO: could use multiexp
|
||||
tmp.push(((*g_lo) * challenge_inv) + &((*g_hi) * challenge));
|
||||
}
|
||||
C::Projective::batch_to_affine(&tmp, g_lo);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_commit_lagrange() {
|
||||
const K: u32 = 6;
|
||||
|
||||
use crate::arithmetic::{EpAffine, Fp, Fq};
|
||||
use crate::transcript::DummyHash;
|
||||
let params = Params::<EpAffine>::new::<DummyHash<Fp>>(K);
|
||||
|
||||
let a = (0..(1 << K)).map(|l| Fq::from(l)).collect::<Vec<_>>();
|
||||
let mut b = a.clone();
|
||||
let mut alpha = Fq::ROOT_OF_UNITY;
|
||||
for _ in K..Fq::S {
|
||||
alpha = alpha.square();
|
||||
}
|
||||
best_fft(&mut b, alpha, K);
|
||||
|
||||
assert_eq!(params.commit(&a, alpha), params.commit_lagrange(&b, alpha));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opening_proof() {
|
||||
const K: u32 = 6;
|
||||
|
||||
use crate::arithmetic::{eval_polynomial, EpAffine, Fp, Fq};
|
||||
use crate::transcript::DummyHash;
|
||||
let params = Params::<EpAffine>::new::<DummyHash<Fp>>(K);
|
||||
|
||||
let px = (0..(1 << K))
|
||||
.map(|l| Fq::from(l + 1) * Fq::ZETA)
|
||||
.collect::<Vec<_>>();
|
||||
let blind = Fq::random();
|
||||
|
||||
let p = params.commit(&px, blind).to_affine();
|
||||
|
||||
let mut transcript = DummyHash::init(Field::one());
|
||||
let (p_x, p_y) = p.get_xy().unwrap();
|
||||
transcript.absorb(p_x);
|
||||
transcript.absorb(p_y);
|
||||
let x_packed = transcript.squeeze().get_lower_128();
|
||||
let x: Fq = get_challenge_scalar(Challenge(x_packed));
|
||||
|
||||
// Evaluate the polynomial
|
||||
let v = eval_polynomial(&px, x);
|
||||
|
||||
transcript.absorb(Fp::from_bytes(&v.to_bytes()).unwrap()); // unlikely to fail since p ~ q
|
||||
|
||||
loop {
|
||||
let mut transcript_dup = transcript.clone();
|
||||
|
||||
let opening_proof = params.create_proof(&mut transcript, &px, blind, x);
|
||||
if opening_proof.is_err() {
|
||||
transcript = transcript_dup;
|
||||
transcript.absorb(Field::one());
|
||||
} else {
|
||||
let opening_proof = opening_proof.unwrap();
|
||||
assert!(params.verify_proof(&opening_proof, &mut transcript_dup, x, &p, v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//! This module contains utilities and traits for dealing with Fiat-Shamir
|
||||
//! transcripts.
|
||||
|
||||
use crate::arithmetic::Field;
|
||||
|
||||
/// This is a generic interface for a sponge function that can be used for
|
||||
/// Fiat-Shamir transformations.
|
||||
pub trait Hasher<F: Field>: Clone + Send + Sync + 'static {
|
||||
/// Initialize the sponge with some key.
|
||||
fn init(key: F) -> Self;
|
||||
/// Absorb a field element into the sponge.
|
||||
fn absorb(&mut self, value: F);
|
||||
/// Square a field element out of the sponge.
|
||||
fn squeeze(&mut self) -> F;
|
||||
}
|
||||
|
||||
/// This is just a simple (and completely broken) hash function, standing in for
|
||||
/// some algebraic hash function that we'll switch to later.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DummyHash<F: Field> {
|
||||
power: F,
|
||||
state: F,
|
||||
}
|
||||
|
||||
impl<F: Field> Hasher<F> for DummyHash<F> {
|
||||
fn init(value: F) -> Self {
|
||||
DummyHash {
|
||||
power: F::ZETA + F::one() + value,
|
||||
state: F::ZETA,
|
||||
}
|
||||
}
|
||||
fn absorb(&mut self, value: F) {
|
||||
for _ in 0..10 {
|
||||
self.state += value;
|
||||
self.state *= self.power;
|
||||
self.power += self.power.square();
|
||||
self.state += self.power;
|
||||
}
|
||||
}
|
||||
fn squeeze(&mut self) -> F {
|
||||
let tmp = self.state;
|
||||
self.absorb(tmp);
|
||||
tmp
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue