From 06bf1f1f625333f63d9cca9692a1733369fe7c28 Mon Sep 17 00:00:00 2001 From: Ryan Lehmkuhl Date: Mon, 5 Oct 2020 20:11:46 -0700 Subject: [PATCH] Add polynomial interfaces --- .github/workflows/ci.yml | 7 +- algebra-core/src/lib.rs | 2 + algebra-core/src/serialize/mod.rs | 58 ++- .../src/merkle_tree/constraints.rs | 2 +- .../src/prf/blake2s/constraints.rs | 2 +- ff-fft/src/evaluations.rs | 4 +- ff-fft/src/lib.rs | 2 +- ff-fft/src/polynomial/mod.rs | 87 ++++ ff-fft/src/polynomial/multivariate/mod.rs | 145 ++++++- ff-fft/src/polynomial/multivariate/sparse.rs | 378 ++++++++---------- ff-fft/src/polynomial/univariate/dense.rs | 206 +++++----- ff-fft/src/polynomial/univariate/mod.rs | 2 +- ff-fft/src/polynomial/univariate/sparse.rs | 2 +- r1cs-core/src/constraint_system.rs | 23 +- r1cs-core/src/lib.rs | 3 +- r1cs-std/src/alloc.rs | 23 +- r1cs-std/src/bits/boolean.rs | 341 +++++++++++++--- r1cs-std/src/bits/mod.rs | 30 +- r1cs-std/src/bits/uint.rs | 52 ++- r1cs-std/src/bits/uint8.rs | 117 +++++- r1cs-std/src/eq.rs | 41 +- r1cs-std/src/fields/cubic_extension.rs | 63 ++- r1cs-std/src/fields/fp/cmp.rs | 7 +- r1cs-std/src/fields/fp/mod.rs | 214 ++++++---- r1cs-std/src/fields/fp12.rs | 10 +- r1cs-std/src/fields/fp2.rs | 2 + r1cs-std/src/fields/fp3.rs | 2 + r1cs-std/src/fields/fp4.rs | 3 + r1cs-std/src/fields/fp6_2over3.rs | 3 + r1cs-std/src/fields/fp6_3over2.rs | 6 +- r1cs-std/src/fields/mod.rs | 54 ++- r1cs-std/src/fields/quadratic_extension.rs | 58 ++- r1cs-std/src/groups/curves/mod.rs | 7 + .../curves/short_weierstrass/bls12/mod.rs | 15 + .../curves/short_weierstrass/mnt4/mod.rs | 25 ++ .../curves/short_weierstrass/mnt6/mod.rs | 26 ++ .../groups/curves/short_weierstrass/mod.rs | 44 +- .../src/groups/curves/twisted_edwards/mod.rs | 277 +++++++------ r1cs-std/src/groups/mod.rs | 35 +- r1cs-std/src/instantiated/bls12_377/curves.rs | 6 + r1cs-std/src/instantiated/bls12_377/fields.rs | 5 + r1cs-std/src/instantiated/bls12_377/mod.rs | 150 +++++++ .../src/instantiated/bls12_377/pairing.rs | 1 + .../instantiated/ed_on_bls12_377/curves.rs | 1 + .../instantiated/ed_on_bls12_377/fields.rs | 1 + .../src/instantiated/ed_on_bls12_377/mod.rs | 102 +++++ .../instantiated/ed_on_bls12_381/curves.rs | 1 + .../instantiated/ed_on_bls12_381/fields.rs | 1 + .../src/instantiated/ed_on_bls12_381/mod.rs | 102 +++++ .../src/instantiated/ed_on_bn254/curves.rs | 1 + .../src/instantiated/ed_on_bn254/fields.rs | 1 + r1cs-std/src/instantiated/ed_on_bn254/mod.rs | 102 +++++ .../src/instantiated/ed_on_bw6_761/curves.rs | 11 - .../src/instantiated/ed_on_bw6_761/fields.rs | 9 - .../src/instantiated/ed_on_bw6_761/mod.rs | 102 +++++ .../src/instantiated/ed_on_cp6_782/curves.rs | 1 + .../src/instantiated/ed_on_cp6_782/fields.rs | 1 + .../src/instantiated/ed_on_cp6_782/mod.rs | 101 +++++ .../src/instantiated/ed_on_mnt4_298/curves.rs | 1 + .../src/instantiated/ed_on_mnt4_298/fields.rs | 1 + .../src/instantiated/ed_on_mnt4_298/mod.rs | 102 +++++ .../src/instantiated/ed_on_mnt4_753/curves.rs | 1 + .../src/instantiated/ed_on_mnt4_753/fields.rs | 1 + .../src/instantiated/ed_on_mnt4_753/mod.rs | 102 +++++ r1cs-std/src/instantiated/mnt4_298/curves.rs | 6 + r1cs-std/src/instantiated/mnt4_298/fields.rs | 3 + r1cs-std/src/instantiated/mnt4_298/mod.rs | 150 +++++++ r1cs-std/src/instantiated/mnt4_298/pairing.rs | 1 + r1cs-std/src/instantiated/mnt4_753/curves.rs | 6 + r1cs-std/src/instantiated/mnt4_753/fields.rs | 3 + r1cs-std/src/instantiated/mnt4_753/mod.rs | 150 +++++++ r1cs-std/src/instantiated/mnt4_753/pairing.rs | 1 + r1cs-std/src/instantiated/mnt6_298/curves.rs | 6 + r1cs-std/src/instantiated/mnt6_298/fields.rs | 3 + r1cs-std/src/instantiated/mnt6_298/mod.rs | 150 +++++++ r1cs-std/src/instantiated/mnt6_298/pairing.rs | 1 + r1cs-std/src/instantiated/mnt6_753/curves.rs | 6 + r1cs-std/src/instantiated/mnt6_753/fields.rs | 3 + r1cs-std/src/instantiated/mnt6_753/mod.rs | 150 +++++++ r1cs-std/src/instantiated/mnt6_753/pairing.rs | 1 + r1cs-std/src/lib.rs | 43 +- r1cs-std/src/macros.rs | 9 +- r1cs-std/src/pairing/bls12/mod.rs | 1 + r1cs-std/src/pairing/mnt4/mod.rs | 6 +- r1cs-std/src/pairing/mnt6/mod.rs | 6 +- r1cs-std/src/pairing/mod.rs | 23 +- r1cs-std/src/select.rs | 35 +- 87 files changed, 3351 insertions(+), 697 deletions(-) delete mode 100644 r1cs-std/src/instantiated/ed_on_bw6_761/curves.rs delete mode 100644 r1cs-std/src/instantiated/ed_on_bw6_761/fields.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c8a93ab0..d7b887b4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,12 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --all --all-features -- --skip dpc --skip integration_test + args: "--all \ + --all-features \ + --exclude cp-benches \ + --exclude algebra-benches \ + --exclude ff-fft-benches \ + -- --skip dpc --skip integration_test" check_no_std: name: Check no_std diff --git a/algebra-core/src/lib.rs b/algebra-core/src/lib.rs index 083d4d0a0..6dc6b0fdc 100644 --- a/algebra-core/src/lib.rs +++ b/algebra-core/src/lib.rs @@ -28,6 +28,7 @@ pub extern crate alloc; pub use alloc::{ borrow::{Cow, ToOwned}, boxed::Box, + collections::btree_map::BTreeMap, format, string::String, vec, @@ -40,6 +41,7 @@ pub use alloc::{ pub use std::{ borrow::{Cow, ToOwned}, boxed::Box, + collections::btree_map::BTreeMap, format, string::String, vec, diff --git a/algebra-core/src/serialize/mod.rs b/algebra-core/src/serialize/mod.rs index b81b6da0c..1ab1de689 100644 --- a/algebra-core/src/serialize/mod.rs +++ b/algebra-core/src/serialize/mod.rs @@ -11,7 +11,7 @@ pub use flags::*; #[doc(hidden)] pub use algebra_core_derive::*; -use crate::{Cow, ToOwned, Vec}; +use crate::{BTreeMap, Cow, ToOwned, Vec}; use core::convert::TryFrom; /// Serializer in little endian format allowing to encode flags. @@ -61,9 +61,9 @@ pub trait CanonicalSerialize { self.serialize(writer) } - /// Serializes `self` into `writer` without compression, and without performing - /// validity checks. Should be used *only* when there is no danger of - /// adversarial manipulation of the output. + /// Serializes `self` into `writer` without compression, and without + /// performing validity checks. Should be used *only* when there is no + /// danger of adversarial manipulation of the output. #[inline] fn serialize_unchecked(&self, writer: W) -> Result<(), SerializationError> { self.serialize_uncompressed(writer) @@ -793,6 +793,44 @@ impl CanonicalDeserialize for bool { } } +impl CanonicalSerialize for BTreeMap +where + K: CanonicalSerialize, + V: CanonicalSerialize, +{ + fn serialize(&self, mut writer: W) -> Result<(), SerializationError> { + let len = self.len() as u64; + len.serialize(&mut writer)?; + for (k, v) in self.iter() { + k.serialize(&mut writer)?; + v.serialize(&mut writer)?; + } + Ok(()) + } + + fn serialized_size(&self) -> usize { + 8 + self + .iter() + .map(|(k, v)| k.serialized_size() + v.serialized_size()) + .sum::() + } +} + +impl CanonicalDeserialize for BTreeMap +where + K: Ord + CanonicalDeserialize, + V: CanonicalDeserialize, +{ + fn deserialize(mut reader: R) -> Result { + let len = u64::deserialize(&mut reader)?; + let mut map = BTreeMap::new(); + for _ in 0..len { + map.insert(K::deserialize(&mut reader)?, V::deserialize(&mut reader)?); + } + Ok(map) + } +} + #[cfg(test)] mod test { use super::*; @@ -859,6 +897,18 @@ mod test { test_serialize(false); } + #[test] + fn test_btreemap() { + let mut map = BTreeMap::new(); + map.insert(0u64, true); + map.insert(5u64, false); + test_serialize(map); + let mut map = BTreeMap::new(); + map.insert(10u64, vec![1u8, 2u8, 3u8]); + map.insert(50u64, vec![4u8, 5u8, 6u8]); + test_serialize(map); + } + #[test] fn test_phantomdata() { test_serialize(core::marker::PhantomData::); diff --git a/crypto-primitives/src/merkle_tree/constraints.rs b/crypto-primitives/src/merkle_tree/constraints.rs index 873d3c6c4..0c5bdd3de 100644 --- a/crypto-primitives/src/merkle_tree/constraints.rs +++ b/crypto-primitives/src/merkle_tree/constraints.rs @@ -37,7 +37,7 @@ where // proof. let leaf_bits = leaf.to_bytes()?; let leaf_hash = CRHGadget::evaluate(parameters, &leaf_bits)?; - let cs = leaf_hash.cs().or(root.cs()).unwrap(); + let cs = leaf_hash.cs().or(root.cs()); // Check if leaf is one of the bottom-most siblings. let leaf_is_left = Boolean::new_witness(r1cs_core::ns!(cs, "leaf_is_left"), || { diff --git a/crypto-primitives/src/prf/blake2s/constraints.rs b/crypto-primitives/src/prf/blake2s/constraints.rs index 4a4cde99c..ce07bd24b 100644 --- a/crypto-primitives/src/prf/blake2s/constraints.rs +++ b/crypto-primitives/src/prf/blake2s/constraints.rs @@ -350,7 +350,7 @@ impl AllocVar<[u8; 32], ConstraintF> for OutputVar R1CSVar for OutputVar { type Value = [u8; 32]; - fn cs(&self) -> Option> { + fn cs(&self) -> ConstraintSystemRef { self.0.cs() } diff --git a/ff-fft/src/evaluations.rs b/ff-fft/src/evaluations.rs index 7b7c5826f..1ceb2e869 100644 --- a/ff-fft/src/evaluations.rs +++ b/ff-fft/src/evaluations.rs @@ -1,6 +1,8 @@ //! A polynomial represented in evaluations form. -use crate::{univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, Vec}; +use crate::{ + univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, Vec, +}; use algebra_core::FftField; use core::ops::{Add, AddAssign, Div, DivAssign, Index, Mul, MulAssign, Sub, SubAssign}; diff --git a/ff-fft/src/lib.rs b/ff-fft/src/lib.rs index 4d687ae16..457f7bd4a 100644 --- a/ff-fft/src/lib.rs +++ b/ff-fft/src/lib.rs @@ -102,7 +102,7 @@ pub use domain::{ EvaluationDomain, GeneralEvaluationDomain, MixedRadixEvaluationDomain, Radix2EvaluationDomain, }; pub use evaluations::Evaluations; -pub use polynomial::{multivariate, univariate}; +pub use polynomial::{multivariate, univariate, MVPolynomial, Polynomial, UVPolynomial}; #[cfg(test)] mod test; diff --git a/ff-fft/src/polynomial/mod.rs b/ff-fft/src/polynomial/mod.rs index 3e152df21..ce5ae0021 100644 --- a/ff-fft/src/polynomial/mod.rs +++ b/ff-fft/src/polynomial/mod.rs @@ -1,4 +1,91 @@ //! Modules for working with univariate or multivariate polynomials. +use crate::Vec; +use algebra_core::Field; +use core::{ + fmt::Debug, + hash::Hash, + ops::{AddAssign, Index, SubAssign}, +}; +use rand::Rng; pub mod multivariate; pub mod univariate; + +/// Describes the common interface for univariate and multivariate polynomials +pub trait Polynomial: + Sized + + Clone + + Debug + + Default + + Hash + + PartialEq + + Eq + + Send + + Sync + + for<'a> AddAssign<&'a Self> + + for<'a> AddAssign<(F, &'a Self)> + + for<'a> SubAssign<&'a Self> +{ + /// The domain of the polynomial. + type Domain: Sized + Clone + Ord + Debug; + + /// Returns the zero polynomial. + fn zero() -> Self; + + /// Checks if the given polynomial is zero. + fn is_zero(&self) -> bool; + + /// Returns the total degree of the polynomial + fn degree(&self) -> usize; + + /// Evaluates `self` at the given `point` in `Self::Domain`. + fn evaluate(&self, point: &Self::Domain) -> F; + + /// If `num_vars` is `None`, outputs a polynomial a univariate polynomial + /// of degree `d` where each coefficient is sampled uniformly at random. + /// + /// If `num_vars` is `Some(l)`, outputs an `l`-variate polynomial which + /// is the sum of `l` `d`-degree univariate polynomials where each coefficient + /// is sampled uniformly at random. + fn rand(d: usize, num_vars: Option, rng: &mut R) -> Self; + + /// Sample a random point from `Self::Domain`. + fn rand_domain_point(domain_size: Option, rng: &mut R) -> Self::Domain; +} + +/// Describes the interface for univariate polynomials +pub trait UVPolynomial: Polynomial { + /// Constructs a new polynomial from a list of coefficients. + fn from_coefficients_slice(coeffs: &[F]) -> Self; + + /// Constructs a new polynomial from a list of coefficients. + fn from_coefficients_vec(coeffs: Vec) -> Self; + + /// Returns the coefficients of `self` + fn coeffs(&self) -> &[F]; +} + +/// Describes the interface for univariate polynomials +pub trait MVPolynomial: Polynomial +where + Self::Domain: Index, +{ + /// The type of the terms of `self` + type Term: multivariate::Term; + + /// Constructs a new polynomial from a list of tuples of the form `(Self::Term, coeff)` + fn from_coefficients_slice(num_vars: usize, terms: &[(Self::Term, F)]) -> Self; + + /// Constructs a new polynomial from a list of tuples of the form `(Self::Term, coeff)` + fn from_coefficients_vec(num_vars: usize, terms: Vec<(Self::Term, F)>) -> Self; + + /// Returns the terms of a `self` as a list of tuples of the form `(Self::Term, coeff)` + fn terms(&self) -> &[(Self::Term, F)]; + + /// Given some point `z`, compute the quotients `w_i(X)` s.t + /// + /// `p(X) - p(z) = (X_1-z_1)*w_1(X) + (X_2-z_2)*w_2(X) + ... + (X_l-z_l)*w_l(X)` + /// + /// These quotients can always be found with no remainder. + fn divide_at_point(&self, point: &Self::Domain) -> Vec; +} diff --git a/ff-fft/src/polynomial/multivariate/mod.rs b/ff-fft/src/polynomial/multivariate/mod.rs index c785f1b96..cf46d3fb2 100644 --- a/ff-fft/src/polynomial/multivariate/mod.rs +++ b/ff-fft/src/polynomial/multivariate/mod.rs @@ -1,6 +1,145 @@ -//! Work with multivariate polynomials. +//! Work with sparse multivariate polynomials. +use crate::Vec; +use algebra_core::Field; +use core::{ + cmp::Ordering, + fmt::{Debug, Error, Formatter}, + hash::Hash, + ops::Deref, +}; -mod sparse; +#[cfg(feature = "parallel")] +use rayon::prelude::*; -pub use sparse::PolyVars; +mod sparse; pub use sparse::SparsePolynomial; + +/// Describes the interface for a term of a multivariate polynomial. +pub trait Term: + Clone + PartialOrd + Ord + PartialEq + Eq + Hash + Default + Debug + Deref + Send + Sync +{ + /// Create a new Term from a list of tuples of the form `(variable, power)` + fn new(term: Vec<(usize, usize)>) -> Self; + + /// Returns the sum of all variable powers in `self` + fn degree(&self) -> usize; + + /// Returns a list of variables in `self` + fn vars(&self) -> Vec; + + /// Returns whether `self` is a constant + fn is_constant(&self) -> bool; + + /// Evaluates `self` at the given `point` in the field. + fn evaluate(&self, point: &[F]) -> F; +} + +/// Stores a single term in a multivariate polynomial. Each element +/// is of the form `(variable, power)`. +#[derive(Clone, PartialOrd, PartialEq, Eq, Hash, Default)] +pub struct SparseTerm(Vec<(usize, usize)>); + +impl SparseTerm { + /// Sums the powers of any duplicated variables. Assumes `term` is sorted. + fn combine(term: &[(usize, usize)]) -> Vec<(usize, usize)> { + let mut term_dedup: Vec<(usize, usize)> = Vec::new(); + for (var, pow) in term { + match term_dedup.last_mut() { + Some(prev) => { + if prev.0 == *var { + prev.1 += pow; + continue; + } + } + _ => {} + }; + term_dedup.push((*var, *pow)); + } + term_dedup + } +} + +impl Term for SparseTerm { + /// Create a new Term from a list of tuples of the form `(variable, power)` + fn new(mut term: Vec<(usize, usize)>) -> Self { + // Remove any terms with power 0 + term.retain(|(_, pow)| *pow != 0); + // If there are more than one variables, make sure they are + // in order and combine any duplicates + if term.len() > 1 { + term.sort_by(|(v1, _), (v2, _)| v1.cmp(v2)); + term = Self::combine(&term); + } + Self(term) + } + + /// Returns the sum of all variable powers in `self` + fn degree(&self) -> usize { + self.iter().fold(0, |sum, acc| sum + acc.1) + } + + /// Returns a list of variables in `self` + fn vars(&self) -> Vec { + self.iter().map(|(v, _)| *v).collect() + } + + /// Returns whether `self` is a constant + fn is_constant(&self) -> bool { + self.len() == 0 + } + + /// Evaluates `self` at the given `point` in the field. + fn evaluate(&self, point: &[F]) -> F { + cfg_into_iter!(self) + .map(|var| { + let term = point.get(var.0).unwrap(); + term.pow(&[var.1 as u64]) + }) + .product() + } +} + +impl Debug for SparseTerm { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + for variable in self.iter() { + if variable.1 == 1 { + write!(f, " * x_{}", variable.0)?; + } else { + write!(f, " * x_{}^{}", variable.0, variable.1)?; + } + } + Ok(()) + } +} + +impl Deref for SparseTerm { + type Target = [(usize, usize)]; + + fn deref(&self) -> &[(usize, usize)] { + &self.0 + } +} + +impl Ord for SparseTerm { + /// Sort by total degree. If total degree is equal then ordering + /// is given by exponent weight in lower-numbered variables + /// ie. `x_1 > x_2`, `x_1^2 > x_1 * x_2`, etc. + fn cmp(&self, other: &Self) -> Ordering { + if self.degree() != other.degree() { + self.degree().cmp(&other.degree()) + } else { + // Iterate through all variables and return the corresponding ordering + // if they differ in variable numbering or power + for (cur, other) in self.iter().zip(other.iter()) { + if other.0 == cur.0 { + if cur.1 != other.1 { + return (cur.1).cmp(&other.1); + } + } else { + return (other.0).cmp(&cur.0); + } + } + Ordering::Equal + } + } +} diff --git a/ff-fft/src/polynomial/multivariate/sparse.rs b/ff-fft/src/polynomial/multivariate/sparse.rs index 92e6f2f14..e48e040bd 100644 --- a/ff-fft/src/polynomial/multivariate/sparse.rs +++ b/ff-fft/src/polynomial/multivariate/sparse.rs @@ -1,44 +1,41 @@ //! A sparse multivariate polynomial represented in coefficient form. -use crate::Vec; +use crate::{ + multivariate::{SparseTerm, Term}, + MVPolynomial, Polynomial, Vec, +}; use algebra_core::Field; use core::{ cmp::Ordering, fmt, - ops::{Add, AddAssign, Deref, Neg, Sub}, + ops::{Add, AddAssign, Neg, Sub, SubAssign}, }; use rand::Rng; #[cfg(feature = "parallel")] use rayon::prelude::*; -/// Represents a single term in a multivariate polynomial. -/// Each element is of the form `(variable, power)`. -#[derive(Clone, PartialOrd, PartialEq, Eq, Hash, Default)] -pub struct PolyVars(Vec<(usize, usize)>); - -/// Stores a sparse multi-variate polynomial in coefficient form. +/// Stores a sparse multivariate polynomial in coefficient form. #[derive(Derivative)] #[derivative(Clone, PartialEq, Eq, Hash, Default)] -pub struct SparsePolynomial { +pub struct SparsePolynomial { /// The number of variables the polynomial supports #[derivative(PartialEq = "ignore")] pub num_vars: usize, /// List of every term along with its coefficient - pub terms: Vec<(PolyVars, F)>, + pub terms: Vec<(T, F)>, } -impl SparsePolynomial { - /// Returns the total degree of the polynomial - pub fn degree(&self) -> usize { - self.terms - .iter() - .map(|(term, _)| (*term).degree()) - .max() - .unwrap_or(0) +impl SparsePolynomial { + fn remove_zeros(&mut self) { + self.terms.retain(|(_, c)| !c.is_zero()); } +} + +impl Polynomial for SparsePolynomial { + type Domain = Vec; /// Returns the zero polynomial. - pub fn zero() -> Self { + fn zero() -> Self { Self { num_vars: 0, terms: Vec::new(), @@ -46,24 +43,68 @@ impl SparsePolynomial { } /// Checks if the given polynomial is zero. - pub fn is_zero(&self) -> bool { + fn is_zero(&self) -> bool { self.terms.is_empty() || self.terms.iter().all(|(_, c)| c.is_zero()) } - /// Constructs a new polynomial from a list of coefficients. - pub fn from_coefficients_slice(num_vars: usize, terms: &[(Vec<(usize, usize)>, F)]) -> Self { - let terms: Vec<(PolyVars, F)> = terms - .into_iter() - .map(|c| (PolyVars::new((*c.0).to_vec()), c.1)) - .collect(); - Self::from_coefficients_vec(num_vars, terms) + /// Returns the total degree of the polynomial + fn degree(&self) -> usize { + self.terms + .iter() + .map(|(term, _)| (*term).degree()) + .max() + .unwrap_or(0) + } + + /// Evaluates `self` at the given `point` in `Self::Domain`. + fn evaluate(&self, point: &Vec) -> F { + assert!(point.len() >= self.num_vars, "Invalid evaluation domain"); + if self.is_zero() { + return F::zero(); + } + cfg_into_iter!(&self.terms) + .map(|(term, coeff)| *coeff * term.evaluate(point)) + .sum() } - fn from_coefficients_vec(num_vars: usize, mut terms: Vec<(PolyVars, F)>) -> Self { + /// Outputs an `l`-variate polynomial which is the sum of `l` `d`-degree + /// univariate polynomials where each coefficient is sampled uniformly at random. + fn rand(d: usize, l: Option, rng: &mut R) -> Self { + let l = l.unwrap(); + let mut random_terms = Vec::new(); + random_terms.push((SparseTerm::new(vec![]), F::rand(rng))); + for var in 0..l { + for deg in 1..=d { + random_terms.push((SparseTerm::new(vec![(var, deg)]), F::rand(rng))); + } + } + Self::from_coefficients_vec(l, random_terms) + } + + /// Sample a random point from `Self::Domain`. + fn rand_domain_point(domain_size: Option, rng: &mut R) -> Vec { + let mut point = Vec::with_capacity(domain_size.unwrap()); + for _ in 0..domain_size.unwrap() { + point.push(F::rand(rng)) + } + point + } +} + +impl MVPolynomial for SparsePolynomial { + type Term = SparseTerm; + + /// Constructs a new polynomial from a list of tuples of the form `(Self::Term, coeff)` + fn from_coefficients_slice(num_vars: usize, terms: &[(SparseTerm, F)]) -> Self { + Self::from_coefficients_vec(num_vars, terms.to_vec()) + } + + /// Constructs a new polynomial from a list of tuples of the form `(Self::Term, coeff)` + fn from_coefficients_vec(num_vars: usize, mut terms: Vec<(SparseTerm, F)>) -> Self { // Ensure that terms are in ascending order. terms.sort_by(|(c1, _), (c2, _)| c1.cmp(c2)); // If any terms are duplicated, add them together - let mut terms_dedup: Vec<(PolyVars, F)> = Vec::new(); + let mut terms_dedup: Vec<(SparseTerm, F)> = Vec::new(); for term in terms { match terms_dedup.last_mut() { Some(prev) => { @@ -90,40 +131,17 @@ impl SparsePolynomial { result } - fn remove_zeros(&mut self) { - self.terms.retain(|(_, c)| !c.is_zero()); - } - - /// Evaluates `self` at the given `point` in the field. - pub fn evaluate(&self, point: &[F]) -> F { - assert!(point.len() >= self.num_vars, "Invalid evaluation domain"); - if self.is_zero() { - return F::zero(); - } - cfg_into_iter!(&self.terms) - .map(|(term, coeff)| *coeff * term.evaluate(point)) - .sum() - } - - /// Outputs a polynomial which is the sum of `l` `d`-degree univariate - /// polynomials where each coefficient is sampled uniformly at random - /// from the field `F`. - pub fn rand(l: usize, d: usize, rng: &mut R) -> Self { - let mut random_terms = Vec::new(); - random_terms.push((PolyVars(vec![]), F::rand(rng))); - for var in 0..l { - for deg in 1..=d { - random_terms.push((PolyVars(vec![(var, deg)]), F::rand(rng))); - } - } - Self::from_coefficients_vec(l, random_terms) + /// Returns the terms of a `self` as a list of tuples of the form `(Self::Term, coeff)` + fn terms(&self) -> &[(Self::Term, F)] { + self.terms.as_slice() } /// Given some point `z`, compute the quotients `w_i(X)` s.t + /// /// `p(X) - p(z) = (X_1-z_1)*w_1(X) + (X_2-z_2)*w_2(X) + ... + (X_l-z_l)*w_l(X)` /// /// These quotients can always be found with no remainder. - pub fn divide_at_point(&self, point: &[F]) -> Vec> { + fn divide_at_point(&self, point: &Self::Domain) -> Vec { if self.is_zero() { return vec![SparsePolynomial::zero(); self.num_vars]; } @@ -138,7 +156,7 @@ impl SparsePolynomial { for (mut term, mut coeff) in cur.terms { // Since the final remainder is guaranteed to be 0, all the constant terms // cancel out so we don't need to keep track of them - if term.is_empty() { + if term.is_constant() { continue; } // If the current term contains `X_i` then divide appropiately, @@ -173,58 +191,10 @@ impl SparsePolynomial { } } -impl PolyVars { - /// Create a new `PolyVars` object from a Vec of variables and powers - pub fn new(mut term: Vec<(usize, usize)>) -> Self { - // Remove any terms with power 0 - term.retain(|(_, pow)| *pow != 0); - // If there are more than one variables, make sure they are - // in order and combine any duplicates - if term.len() > 1 { - term.sort_by(|(v1, _), (v2, _)| v1.cmp(v2)); - term = Self::combine(&term); - } - Self(term) - } +impl<'a, 'b, F: Field, T: Term> Add<&'a SparsePolynomial> for &'b SparsePolynomial { + type Output = SparsePolynomial; - /// Returns the sum of all variable powers in `self` - pub fn degree(&self) -> usize { - self.iter().fold(0, |sum, acc| sum + acc.1) - } - - /// Sums the powers of any duplicated variables. Assumes Vec is sorted - fn combine(term: &[(usize, usize)]) -> Vec<(usize, usize)> { - let mut term_dedup: Vec<(usize, usize)> = Vec::new(); - for (var, pow) in term { - match term_dedup.last_mut() { - Some(prev) => { - if prev.0 == *var { - prev.1 += pow; - continue; - } - } - _ => {} - }; - term_dedup.push((*var, *pow)); - } - term_dedup - } - - /// Evaluates `self` at the given `point` in the field. - fn evaluate(&self, point: &[F]) -> F { - cfg_into_iter!(self) - .map(|var| { - let term = point.get(var.0).unwrap(); - term.pow(&[var.1 as u64]) - }) - .product() - } -} - -impl<'a, 'b, F: Field> Add<&'a SparsePolynomial> for &'b SparsePolynomial { - type Output = SparsePolynomial; - - fn add(self, other: &'a SparsePolynomial) -> SparsePolynomial { + fn add(self, other: &'a SparsePolynomial) -> SparsePolynomial { let mut result = Vec::new(); let mut cur_iter = self.terms.iter().peekable(); let mut other_iter = other.terms.iter().peekable(); @@ -250,21 +220,25 @@ impl<'a, 'b, F: Field> Add<&'a SparsePolynomial> for &'b SparsePolynomial None => break, }); } - return SparsePolynomial::from_coefficients_vec( - core::cmp::max(self.num_vars, other.num_vars), - result, - ); + // Remove any zero terms + result.retain(|(_, c)| !c.is_zero()); + SparsePolynomial { + num_vars: core::cmp::max(self.num_vars, other.num_vars), + terms: result, + } } } -impl<'a, 'b, F: Field> AddAssign<&'a SparsePolynomial> for SparsePolynomial { - fn add_assign(&mut self, other: &'a SparsePolynomial) { +impl<'a, 'b, F: Field, T: Term> AddAssign<&'a SparsePolynomial> for SparsePolynomial { + fn add_assign(&mut self, other: &'a SparsePolynomial) { *self = &*self + other; } } -impl<'a, 'b, F: Field> AddAssign<(F, &'a SparsePolynomial)> for SparsePolynomial { - fn add_assign(&mut self, (f, other): (F, &'a SparsePolynomial)) { +impl<'a, 'b, F: Field, T: Term> AddAssign<(F, &'a SparsePolynomial)> + for SparsePolynomial +{ + fn add_assign(&mut self, (f, other): (F, &'a SparsePolynomial)) { let other = Self { num_vars: other.num_vars, terms: other @@ -278,11 +252,11 @@ impl<'a, 'b, F: Field> AddAssign<(F, &'a SparsePolynomial)> for SparsePolynom } } -impl Neg for SparsePolynomial { - type Output = SparsePolynomial; +impl Neg for SparsePolynomial { + type Output = SparsePolynomial; #[inline] - fn neg(mut self) -> SparsePolynomial { + fn neg(mut self) -> SparsePolynomial { for coeff in &mut self.terms { (*coeff).1 = -coeff.1; } @@ -290,87 +264,51 @@ impl Neg for SparsePolynomial { } } -impl<'a, 'b, F: Field> Sub<&'a SparsePolynomial> for &'b SparsePolynomial { - type Output = SparsePolynomial; +impl<'a, 'b, F: Field, T: Term> Sub<&'a SparsePolynomial> for &'b SparsePolynomial { + type Output = SparsePolynomial; #[inline] - fn sub(self, other: &'a SparsePolynomial) -> SparsePolynomial { + fn sub(self, other: &'a SparsePolynomial) -> SparsePolynomial { let neg_other = other.clone().neg(); self + &neg_other } } -impl fmt::Debug for SparsePolynomial { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - for (term, coeff) in self.terms.iter().filter(|(_, c)| !c.is_zero()) { - if term.len() == 0 { - write!(f, "\n{:?}", coeff)?; - } else { - write!(f, "\n{:?} {:?}", coeff, term)?; - } - } - Ok(()) +impl<'a, 'b, F: Field, T: Term> SubAssign<&'a SparsePolynomial> for SparsePolynomial { + #[inline] + fn sub_assign(&mut self, other: &'a SparsePolynomial) { + *self = &*self - other; } } -impl fmt::Debug for PolyVars { +impl fmt::Debug for SparsePolynomial { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - for variable in self.iter() { - if variable.1 == 1 { - write!(f, " * x_{}", variable.0)?; + for (term, coeff) in self.terms.iter().filter(|(_, c)| !c.is_zero()) { + if term.is_constant() { + write!(f, "\n{:?}", coeff)?; } else { - write!(f, " * x_{}^{}", variable.0, variable.1)?; + write!(f, "\n{:?} {:?}", coeff, term)?; } } Ok(()) } } -impl Deref for PolyVars { - type Target = [(usize, usize)]; - - fn deref(&self) -> &[(usize, usize)] { - &self.0 - } -} - -impl Ord for PolyVars { - /// Sort by total degree. If total degree is equal then ordering - /// is given by exponent weight in lower-numbered variables - /// ie. `x_1 > x_2`, `x_1^2 > x_1 * x_2`, etc. - fn cmp(&self, other: &Self) -> Ordering { - if self.degree() != other.degree() { - self.degree().cmp(&other.degree()) - } else { - // Iterate through all variables and return the corresponding ordering - // if they differ in variable numbering or power - for (cur, other) in self.iter().zip(other.iter()) { - if other.0 == cur.0 { - if cur.1 != other.1 { - return (cur.1).cmp(&other.1); - } - } else { - return (other.0).cmp(&cur.0); - } - } - Ordering::Equal - } - } -} - #[cfg(test)] mod tests { use super::*; use algebra::bls12_381::fr::Fr; use algebra_core::{test_rng, Field, One, UniformRand, Zero}; + // TODO: Make tests generic over term type + /// Generate random `l`-variate polynomial of maximum individual degree `d` - fn rand_poly(l: usize, d: usize, rng: &mut R) -> SparsePolynomial { + fn rand_poly(l: usize, d: usize, rng: &mut R) -> SparsePolynomial { let mut random_terms = Vec::new(); let num_terms = rng.gen_range(1, 1000); // For each term, randomly select up to `l` variables with degree // in [1,d] and random coefficient - random_terms.push((vec![], Fr::rand(rng))); + random_terms.push((SparseTerm::new(vec![]), Fr::rand(rng))); for _ in 1..num_terms { let term = (0..l) .map(|i| { @@ -384,13 +322,16 @@ mod tests { .map(|t| t.unwrap()) .collect(); let coeff = Fr::rand(rng); - random_terms.push((term, coeff)); + random_terms.push((SparseTerm::new(term), coeff)); } SparsePolynomial::from_coefficients_slice(l, &random_terms) } /// Perform a naive n^2 multiplication of `self` by `other`. - fn naive_mul(cur: &SparsePolynomial, other: &SparsePolynomial) -> SparsePolynomial { + fn naive_mul( + cur: &SparsePolynomial, + other: &SparsePolynomial, + ) -> SparsePolynomial { if cur.is_zero() || other.is_zero() { SparsePolynomial::zero() } else { @@ -399,7 +340,7 @@ mod tests { for (other_term, other_coeff) in other.terms.iter() { let mut term = cur_term.0.clone(); term.extend(other_term.0.clone()); - result_terms.push((term, *cur_coeff * *other_coeff)); + result_terms.push((SparseTerm::new(term), *cur_coeff * *other_coeff)); } } SparsePolynomial::from_coefficients_slice(cur.num_vars, result_terms.as_slice()) @@ -432,7 +373,7 @@ mod tests { let res1 = &p1 - &p2; let res2 = &p2 - &p1; assert_eq!(&res1 + &p2, p1); - assert_eq!(res1, -res2.clone()); + assert_eq!(res1, -res2); } } } @@ -490,33 +431,39 @@ mod tests { let a = SparsePolynomial::from_coefficients_slice( 4, &[ - (vec![], "2".parse().unwrap()), - (vec![(0, 1), (1, 2)], "4".parse().unwrap()), - (vec![(0, 1), (0, 1)], "8".parse().unwrap()), - (vec![(3, 0)], "1".parse().unwrap()), + (SparseTerm(vec![]), "2".parse().unwrap()), + (SparseTerm(vec![(0, 1), (1, 2)]), "4".parse().unwrap()), + (SparseTerm(vec![(0, 1), (0, 1)]), "8".parse().unwrap()), + (SparseTerm(vec![(3, 0)]), "1".parse().unwrap()), ], ); let b = SparsePolynomial::from_coefficients_slice( 4, &[ - (vec![(0, 1), (1, 2)], "1".parse().unwrap()), - (vec![(2, 1)], "2".parse().unwrap()), - (vec![(3, 1)], "1".parse().unwrap()), + (SparseTerm(vec![(0, 1), (1, 2)]), "1".parse().unwrap()), + (SparseTerm(vec![(2, 1)]), "2".parse().unwrap()), + (SparseTerm(vec![(3, 1)]), "1".parse().unwrap()), ], ); let result = naive_mul(&a, &b); let expected = SparsePolynomial::from_coefficients_slice( 4, &[ - (vec![(0, 1), (1, 2)], "3".parse().unwrap()), - (vec![(2, 1)], "6".parse().unwrap()), - (vec![(3, 1)], "3".parse().unwrap()), - (vec![(0, 2), (1, 4)], "4".parse().unwrap()), - (vec![(0, 1), (1, 2), (2, 1)], "8".parse().unwrap()), - (vec![(0, 1), (1, 2), (3, 1)], "4".parse().unwrap()), - (vec![(0, 3), (1, 2)], "8".parse().unwrap()), - (vec![(0, 2), (2, 1)], "16".parse().unwrap()), - (vec![(0, 2), (3, 1)], "8".parse().unwrap()), + (SparseTerm(vec![(0, 1), (1, 2)]), "3".parse().unwrap()), + (SparseTerm(vec![(2, 1)]), "6".parse().unwrap()), + (SparseTerm(vec![(3, 1)]), "3".parse().unwrap()), + (SparseTerm(vec![(0, 2), (1, 4)]), "4".parse().unwrap()), + ( + SparseTerm(vec![(0, 1), (1, 2), (2, 1)]), + "8".parse().unwrap(), + ), + ( + SparseTerm(vec![(0, 1), (1, 2), (3, 1)]), + "4".parse().unwrap(), + ), + (SparseTerm(vec![(0, 3), (1, 2)]), "8".parse().unwrap()), + (SparseTerm(vec![(0, 2), (2, 1)]), "16".parse().unwrap()), + (SparseTerm(vec![(0, 2), (3, 1)]), "8".parse().unwrap()), ], ); assert_eq!(expected, result); @@ -524,16 +471,19 @@ mod tests { #[test] fn divide_at_point_fixed() { - let dividend = SparsePolynomial::::from_coefficients_slice( + let dividend = SparsePolynomial::::from_coefficients_slice( 3, &[ - (vec![], "4".parse().unwrap()), - (vec![(0, 1)], "1".parse().unwrap()), - (vec![(0, 1), (1, 1)], "3".parse().unwrap()), - (vec![(0, 2), (1, 2), (2, 3)], "5".parse().unwrap()), + (SparseTerm(vec![]), "4".parse().unwrap()), + (SparseTerm(vec![(0, 1)]), "1".parse().unwrap()), + (SparseTerm(vec![(0, 1), (1, 1)]), "3".parse().unwrap()), + ( + SparseTerm(vec![(0, 2), (1, 2), (2, 3)]), + "5".parse().unwrap(), + ), ], ); - let point = [ + let point = vec![ "4".parse().unwrap(), "2".parse().unwrap(), "1".parse().unwrap(), @@ -543,26 +493,29 @@ mod tests { SparsePolynomial::from_coefficients_slice( 3, &[ - (vec![], "1".parse().unwrap()), - (vec![(1, 1)], "3".parse().unwrap()), - (vec![(1, 2), (2, 3)], "20".parse().unwrap()), - (vec![(0, 1), (1, 2), (2, 3)], "5".parse().unwrap()), + (SparseTerm(vec![]), "1".parse().unwrap()), + (SparseTerm(vec![(1, 1)]), "3".parse().unwrap()), + (SparseTerm(vec![(1, 2), (2, 3)]), "20".parse().unwrap()), + ( + SparseTerm(vec![(0, 1), (1, 2), (2, 3)]), + "5".parse().unwrap(), + ), ], ), SparsePolynomial::from_coefficients_slice( 3, &[ - (vec![], "12".parse().unwrap()), - (vec![(2, 3)], "160".parse().unwrap()), - (vec![(1, 1), (2, 3)], "80".parse().unwrap()), + (SparseTerm(vec![]), "12".parse().unwrap()), + (SparseTerm(vec![(2, 3)]), "160".parse().unwrap()), + (SparseTerm(vec![(1, 1), (2, 3)]), "80".parse().unwrap()), ], ), SparsePolynomial::from_coefficients_slice( 3, &[ - (vec![], "320".parse().unwrap()), - (vec![(2, 1)], "320".parse().unwrap()), - (vec![(2, 2)], "320".parse().unwrap()), + (SparseTerm(vec![]), "320".parse().unwrap()), + (SparseTerm(vec![(2, 1)]), "320".parse().unwrap()), + (SparseTerm(vec![(2, 2)]), "320".parse().unwrap()), ], ), ]; @@ -574,19 +527,22 @@ mod tests { let rng = &mut test_rng(); let max_degree = 10; for var_count in 0..20 { - let dividend = SparsePolynomial::rand(var_count, max_degree, rng); + let dividend = SparsePolynomial::rand(max_degree, Some(var_count), rng); let mut point = Vec::new(); for _ in 0..var_count { point.push(Fr::rand(rng)); } let dividend_eval = SparsePolynomial::from_coefficients_slice( 0, - &vec![(vec![], dividend.evaluate(&point))], + &vec![(SparseTerm(vec![]), dividend.evaluate(&point))], ); let quotients = dividend.divide_at_point(&point); let mut result = SparsePolynomial::zero(); for (i, q) in quotients.iter().enumerate() { - let test_terms = vec![(vec![(i, 1)], Fr::one()), (vec![], -point[i])]; + let test_terms = vec![ + (SparseTerm(vec![(i, 1)]), Fr::one()), + (SparseTerm(vec![]), -point[i]), + ]; let test = SparsePolynomial::from_coefficients_slice(var_count, &test_terms); result += &naive_mul(q, &test); } diff --git a/ff-fft/src/polynomial/univariate/dense.rs b/ff-fft/src/polynomial/univariate/dense.rs index 4aab86e2c..b84bde0de 100644 --- a/ff-fft/src/polynomial/univariate/dense.rs +++ b/ff-fft/src/polynomial/univariate/dense.rs @@ -1,6 +1,5 @@ -//! A polynomial represented in coefficient form. - -use crate::{GeneralEvaluationDomain, Vec}; +//! A dense univariate polynomial represented in coefficient form. +use crate::{GeneralEvaluationDomain, Polynomial, UVPolynomial, Vec}; use core::{ fmt, ops::{Add, AddAssign, Deref, DerefMut, Div, Mul, Neg, Sub, SubAssign}, @@ -21,65 +20,21 @@ pub struct DensePolynomial { pub coeffs: Vec, } -impl fmt::Debug for DensePolynomial { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - for (i, coeff) in self.coeffs.iter().enumerate().filter(|(_, c)| !c.is_zero()) { - if i == 0 { - write!(f, "\n{:?}", coeff)?; - } else if i == 1 { - write!(f, " + \n{:?} * x", coeff)?; - } else { - write!(f, " + \n{:?} * x^{}", coeff, i)?; - } - } - Ok(()) - } -} - -impl Deref for DensePolynomial { - type Target = [F]; +impl Polynomial for DensePolynomial { + type Domain = F; - fn deref(&self) -> &[F] { - &self.coeffs - } -} - -impl DerefMut for DensePolynomial { - fn deref_mut(&mut self) -> &mut [F] { - &mut self.coeffs - } -} - -impl DensePolynomial { /// Returns the zero polynomial. - pub fn zero() -> Self { + fn zero() -> Self { Self { coeffs: Vec::new() } } /// Checks if the given polynomial is zero. - pub fn is_zero(&self) -> bool { + fn is_zero(&self) -> bool { self.coeffs.is_empty() || self.coeffs.iter().all(|coeff| coeff.is_zero()) } - /// Constructs a new polynomial from a list of coefficients. - pub fn from_coefficients_slice(coeffs: &[F]) -> Self { - Self::from_coefficients_vec(coeffs.to_vec()) - } - - /// Constructs a new polynomial from a list of coefficients. - pub fn from_coefficients_vec(coeffs: Vec) -> Self { - let mut result = Self { coeffs }; - // While there are zeros at the end of the coefficient vector, pop them off. - result.truncate_leading_zeros(); - // Check that either the coefficients vec is empty or that the last coeff is - // non-zero. - assert!(result.coeffs.last().map_or(true, |coeff| !coeff.is_zero())); - - result - } - - /// Returns the degree of the polynomial. - pub fn degree(&self) -> usize { + /// Returns the total degree of the polynomial + fn degree(&self) -> usize { if self.is_zero() { 0 } else { @@ -88,22 +43,16 @@ impl DensePolynomial { } } - fn truncate_leading_zeros(&mut self) { - while self.coeffs.last().map_or(false, |c| c.is_zero()) { - self.coeffs.pop(); - } - } - - /// Evaluates `self` at the given `point` in the field. - pub fn evaluate(&self, point: F) -> F { + /// Evaluates `self` at the given `point` in `Self::Domain`. + fn evaluate(&self, point: &F) -> F { if self.is_zero() { return F::zero(); } let mut powers_of_point = vec![F::one()]; - let mut cur = point; + let mut cur = *point; for _ in 0..self.degree() { powers_of_point.push(cur); - cur *= &point; + cur *= point; } assert_eq!(powers_of_point.len(), self.coeffs.len()); cfg_into_iter!(powers_of_point) @@ -112,30 +61,43 @@ impl DensePolynomial { .sum() } - /// Perform a naive n^2 multiplication of `self` by `other`. - pub fn naive_mul(&self, other: &Self) -> Self { - if self.is_zero() || other.is_zero() { - DensePolynomial::zero() - } else { - let mut result = vec![F::zero(); self.degree() + other.degree() + 1]; - for (i, self_coeff) in self.coeffs.iter().enumerate() { - for (j, other_coeff) in other.coeffs.iter().enumerate() { - result[i + j] += &(*self_coeff * other_coeff); - } - } - DensePolynomial::from_coefficients_vec(result) - } - } - - /// Outputs a polynomial of degree `d` where each coefficient is sampled - /// uniformly at random from the field `F`. - pub fn rand(d: usize, rng: &mut R) -> Self { + /// Outputs a polynomial a univariate polynomial of degree `d` where + /// each coefficient is sampled uniformly at random. + fn rand(d: usize, _: Option, rng: &mut R) -> Self { let mut random_coeffs = Vec::new(); for _ in 0..=d { random_coeffs.push(F::rand(rng)); } Self::from_coefficients_vec(random_coeffs) } + + /// Sample a random point from `Self::Domain`. + fn rand_domain_point(_: Option, rng: &mut R) -> F { + F::rand(rng) + } +} + +impl UVPolynomial for DensePolynomial { + /// Constructs a new polynomial from a list of coefficients. + fn from_coefficients_slice(coeffs: &[F]) -> Self { + Self::from_coefficients_vec(coeffs.to_vec()) + } + + /// Constructs a new polynomial from a list of coefficients. + fn from_coefficients_vec(coeffs: Vec) -> Self { + let mut result = Self { coeffs }; + // While there are zeros at the end of the coefficient vector, pop them off. + result.truncate_leading_zeros(); + // Check that either the coefficients vec is empty or that the last coeff is + // non-zero. + assert!(result.coeffs.last().map_or(true, |coeff| !coeff.is_zero())); + result + } + + /// Returns the coefficients of `self` + fn coeffs(&self) -> &[F] { + &self.coeffs + } } impl DensePolynomial { @@ -162,6 +124,58 @@ impl DensePolynomial { } } +impl DensePolynomial { + fn truncate_leading_zeros(&mut self) { + while self.coeffs.last().map_or(false, |c| c.is_zero()) { + self.coeffs.pop(); + } + } + + /// Perform a naive n^2 multiplication of `self` by `other`. + pub fn naive_mul(&self, other: &Self) -> Self { + if self.is_zero() || other.is_zero() { + DensePolynomial::zero() + } else { + let mut result = vec![F::zero(); self.degree() + other.degree() + 1]; + for (i, self_coeff) in self.coeffs.iter().enumerate() { + for (j, other_coeff) in other.coeffs.iter().enumerate() { + result[i + j] += &(*self_coeff * other_coeff); + } + } + DensePolynomial::from_coefficients_vec(result) + } + } +} + +impl fmt::Debug for DensePolynomial { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + for (i, coeff) in self.coeffs.iter().enumerate().filter(|(_, c)| !c.is_zero()) { + if i == 0 { + write!(f, "\n{:?}", coeff)?; + } else if i == 1 { + write!(f, " + \n{:?} * x", coeff)?; + } else { + write!(f, " + \n{:?} * x^{}", coeff, i)?; + } + } + Ok(()) + } +} + +impl Deref for DensePolynomial { + type Target = [F]; + + fn deref(&self) -> &[F] { + &self.coeffs + } +} + +impl DerefMut for DensePolynomial { + fn deref_mut(&mut self) -> &mut [F] { + &mut self.coeffs + } +} + impl<'a, 'b, F: Field> Add<&'a DensePolynomial> for &'b DensePolynomial { type Output = DensePolynomial; @@ -357,7 +371,7 @@ mod tests { fn double_polynomials_random() { let rng = &mut test_rng(); for degree in 0..70 { - let p = DensePolynomial::::rand(degree, rng); + let p = DensePolynomial::::rand(degree, None, rng); let p_double = &p + &p; let p_quad = &p_double + &p_double; assert_eq!(&(&(&p + &p) + &p) + &p, p_quad); @@ -369,8 +383,8 @@ mod tests { let rng = &mut test_rng(); for a_degree in 0..70 { for b_degree in 0..70 { - let p1 = DensePolynomial::::rand(a_degree, rng); - let p2 = DensePolynomial::::rand(b_degree, rng); + let p1 = DensePolynomial::::rand(a_degree, None, rng); + let p2 = DensePolynomial::::rand(b_degree, None, rng); let res1 = &p1 + &p2; let res2 = &p2 + &p1; assert_eq!(res1, res2); @@ -383,8 +397,8 @@ mod tests { let rng = &mut test_rng(); for a_degree in 0..70 { for b_degree in 0..70 { - let mut p1 = DensePolynomial::rand(a_degree, rng); - let p2 = DensePolynomial::rand(b_degree, rng); + let mut p1 = DensePolynomial::rand(a_degree, None, rng); + let p2 = DensePolynomial::rand(b_degree, None, rng); let f = Fr::rand(rng); let f_p2 = DensePolynomial::from_coefficients_vec( p2.coeffs.iter().map(|c| f * c).collect(), @@ -400,8 +414,8 @@ mod tests { #[test] fn sub_polynomials() { let rng = &mut test_rng(); - let p1 = DensePolynomial::::rand(5, rng); - let p2 = DensePolynomial::::rand(3, rng); + let p1 = DensePolynomial::::rand(5, None, rng); + let p2 = DensePolynomial::::rand(3, None, rng); let res1 = &p1 - &p2; let res2 = &p2 - &p1; assert_eq!( @@ -436,8 +450,8 @@ mod tests { for a_degree in 0..70 { for b_degree in 0..70 { - let dividend = DensePolynomial::::rand(a_degree, rng); - let divisor = DensePolynomial::::rand(b_degree, rng); + let dividend = DensePolynomial::::rand(a_degree, None, rng); + let divisor = DensePolynomial::::rand(b_degree, None, rng); if let Some((quotient, remainder)) = DenseOrSparsePolynomial::divide_with_q_and_r( &(÷nd).into(), &(&divisor).into(), @@ -452,13 +466,13 @@ mod tests { fn evaluate_polynomials() { let rng = &mut test_rng(); for a_degree in 0..70 { - let p = DensePolynomial::rand(a_degree, rng); + let p = DensePolynomial::rand(a_degree, None, rng); let point: Fr = Fr::from(10u64); let mut total = Fr::zero(); for (i, coeff) in p.coeffs.iter().enumerate() { total += &(point.pow(&[i as u64]) * coeff); } - assert_eq!(p.evaluate(point), total); + assert_eq!(p.evaluate(&point), total); } } @@ -467,8 +481,8 @@ mod tests { let rng = &mut test_rng(); for a_degree in 0..70 { for b_degree in 0..70 { - let a = DensePolynomial::::rand(a_degree, rng); - let b = DensePolynomial::::rand(b_degree, rng); + let a = DensePolynomial::::rand(a_degree, None, rng); + let b = DensePolynomial::::rand(b_degree, None, rng); assert_eq!(&a * &b, a.naive_mul(&b)) } } @@ -480,7 +494,7 @@ mod tests { for size in 1..10 { let domain = GeneralEvaluationDomain::new(1 << size).unwrap(); for degree in 0..70 { - let p = DensePolynomial::::rand(degree, rng); + let p = DensePolynomial::::rand(degree, None, rng); let ans1 = p.mul_by_vanishing_poly(domain); let ans2 = &p * &domain.vanishing_polynomial().into(); assert_eq!(ans1, ans2); @@ -491,7 +505,7 @@ mod tests { #[test] fn test_leading_zero() { let n = 10; - let rand_poly = DensePolynomial::rand(n, &mut test_rng()); + let rand_poly = DensePolynomial::rand(n, None, &mut test_rng()); let coefficients = rand_poly.coeffs.clone(); let leading_coefficient: Fr = coefficients[n]; diff --git a/ff-fft/src/polynomial/univariate/mod.rs b/ff-fft/src/polynomial/univariate/mod.rs index 52a1b45c0..9179abd61 100644 --- a/ff-fft/src/polynomial/univariate/mod.rs +++ b/ff-fft/src/polynomial/univariate/mod.rs @@ -1,6 +1,6 @@ //! Work with sparse and dense univariate polynomials. -use crate::{Cow, EvaluationDomain, Evaluations, Vec}; +use crate::{Cow, EvaluationDomain, Evaluations, Polynomial, UVPolynomial, Vec}; use algebra_core::{FftField, Field}; use core::convert::TryInto; use DenseOrSparsePolynomial::*; diff --git a/ff-fft/src/polynomial/univariate/sparse.rs b/ff-fft/src/polynomial/univariate/sparse.rs index 4f8619eaa..ae6f8888d 100644 --- a/ff-fft/src/polynomial/univariate/sparse.rs +++ b/ff-fft/src/polynomial/univariate/sparse.rs @@ -2,7 +2,7 @@ use core::fmt; -use crate::univariate::{DenseOrSparsePolynomial, DensePolynomial}; +use crate::univariate::{DenseOrSparsePolynomial, DensePolynomial, UVPolynomial}; use crate::{BTreeMap, EvaluationDomain, Evaluations, Vec}; use algebra_core::{FftField, Field}; diff --git a/r1cs-core/src/constraint_system.rs b/r1cs-core/src/constraint_system.rs index c490e5ff0..7efe73c81 100644 --- a/r1cs-core/src/constraint_system.rs +++ b/r1cs-core/src/constraint_system.rs @@ -381,7 +381,9 @@ impl ConstraintSystem { } } } - +/// The A, B and C matrices of a Rank-One `ConstraintSystem`. +/// Also contains metadata on the structure of the constraint system +/// and the matrices. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ConstraintMatrices { /// The number of variables that are "public instances" to the constraint system. @@ -413,7 +415,7 @@ pub struct ConstraintMatrices { #[derive(Debug, Clone)] pub enum ConstraintSystemRef { /// Represents the case where we *don't* need to allocate variables or enforce - /// constraints. + /// constraints. Encountered when operating over constant values. None, /// Represents the case where we *do* allocate variables or enforce constraints. CS(Rc>>), @@ -477,13 +479,28 @@ impl Drop for Namespace { } impl ConstraintSystemRef { + /// Returns `self` if `!self.is_none()`, otherwise returns `other`. + pub fn or(self, other: Self) -> Self { + match self { + ConstraintSystemRef::None => other, + _ => self, + } + } + + /// Returns `true` is `self == ConstraintSystemRef::None`. + pub fn is_none(&self) -> bool { + match self { + ConstraintSystemRef::None => true, + _ => false, + } + } + /// Construct a `ConstraintSystemRef` from a `ConstraintSystem`. #[inline] pub fn new(inner: ConstraintSystem) -> Self { Self::CS(Rc::new(RefCell::new(inner))) } - // TODO: make this and all callers use `#[track_caller]` fn inner(&self) -> Option<&Rc>>> { match self { Self::CS(a) => Some(a), diff --git a/r1cs-core/src/lib.rs b/r1cs-core/src/lib.rs index 2951d3088..8014b5ce2 100644 --- a/r1cs-core/src/lib.rs +++ b/r1cs-core/src/lib.rs @@ -45,7 +45,8 @@ pub use tracing::info_span; pub use algebra_core::{Field, ToConstraintField}; pub use constraint_system::{ - ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, SynthesisMode, + ConstraintMatrices, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, + SynthesisMode, }; pub use error::SynthesisError; diff --git a/r1cs-std/src/alloc.rs b/r1cs-std/src/alloc.rs index 1ffc6df8d..8a5a87052 100644 --- a/r1cs-std/src/alloc.rs +++ b/r1cs-std/src/alloc.rs @@ -3,15 +3,26 @@ use algebra::Field; use core::borrow::Borrow; use r1cs_core::{Namespace, SynthesisError}; +/// Describes the mode that a variable should be allocated in within +/// a `ConstraintSystem`. #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone)] pub enum AllocationMode { + /// Indicate to the `ConstraintSystem` that the high-level variable should + /// be allocated as a constant. That is, no `Variable`s should be + /// generated. Constant = 0, + + /// Indicate to the `ConstraintSystem` that the high-level variable should + /// be allocated as a public input to the `ConstraintSystem`. Input = 1, + + /// Indicate to the `ConstraintSystem` that the high-level variable should + /// be allocated as a private witness to the `ConstraintSystem`. Witness = 2, } impl AllocationMode { - // Outputs the maximum according to the relation `Constant < Input < Witness`. + /// Outputs the maximum according to the relation `Constant < Input < Witness`. pub fn max(&self, other: Self) -> Self { use AllocationMode::*; match (self, other) { @@ -23,17 +34,23 @@ impl AllocationMode { } } +/// Specifies how variables of type `Self` should be allocated in a `ConstraintSystem`. pub trait AllocVar where Self: Sized, V: ?Sized, { + /// Allocates a new variable of type `Self` in the `ConstraintSystem` `cs`. + /// The mode of allocation is decided by `mode`. fn new_variable>( cs: impl Into>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result; + /// Allocates a new constant of type `Self` in the `ConstraintSystem` `cs`. + /// + /// This should *not* allocate any new variables or constraints in `cs`. #[tracing::instrument(target = "r1cs", skip(cs, t))] fn new_constant( cs: impl Into>, @@ -42,6 +59,7 @@ where Self::new_variable(cs, || Ok(t), AllocationMode::Constant) } + /// Allocates a new public input of type `Self` in the `ConstraintSystem` `cs`. #[tracing::instrument(target = "r1cs", skip(cs, f))] fn new_input>( cs: impl Into>, @@ -50,6 +68,7 @@ where Self::new_variable(cs, f, AllocationMode::Input) } + /// Allocates a new private witness of type `Self` in the `ConstraintSystem` `cs`. #[tracing::instrument(target = "r1cs", skip(cs, f))] fn new_witness>( cs: impl Into>, @@ -59,6 +78,8 @@ where } } +/// This blanket implementation just allocates variables in `Self` +/// element by element. impl> AllocVar<[I], F> for Vec { fn new_variable>( cs: impl Into>, diff --git a/r1cs-std/src/bits/boolean.rs b/r1cs-std/src/bits/boolean.rs index 46ec3b0bd..f78762ee4 100644 --- a/r1cs-std/src/bits/boolean.rs +++ b/r1cs-std/src/bits/boolean.rs @@ -1,11 +1,15 @@ -use algebra::{BitIteratorBE, Field}; +use algebra::{BitIteratorBE, Field, PrimeField}; -use crate::{prelude::*, Assignment, Vec}; +use crate::{fields::fp::FpVar, prelude::*, Assignment, ToConstraintFieldGadget, Vec}; use core::borrow::Borrow; use r1cs_core::{lc, ConstraintSystemRef, LinearCombination, Namespace, SynthesisError, Variable}; /// Represents a variable in the constraint system which is guaranteed /// to be either zero or one. +/// +/// In general, one should prefer using `Boolean` instead of `AllocatedBit`, +/// as `Boolean` offers better support for constant values, and implements +/// more traits. #[derive(Clone, Debug, Eq, PartialEq)] #[must_use] pub struct AllocatedBit { @@ -156,10 +160,7 @@ impl AllocatedBit { } impl AllocVar for AllocatedBit { - /// If `self.mode` == `AllocationMode::Constant`, this method simply outputs - /// a `Boolean::Constant`. - /// - /// Otherwise, it produces a new variable of the appropriate type + /// Produces a new variable of the appropriate kind /// (instance or witness), with a booleanity check. /// /// N.B.: we could omit the booleanity check when allocating `self` @@ -215,26 +216,26 @@ impl CondSelectGadget for AllocatedBit { } } -/// This is a boolean value which may be either a constant or -/// an interpretation of an `AllocatedBit`. +/// Represents a boolean value in the constraint system which is guaranteed +/// to be either zero or one. #[derive(Clone, Debug, Eq, PartialEq)] #[must_use] pub enum Boolean { - /// Existential view of the boolean variable + /// Existential view of the boolean variable. Is(AllocatedBit), - /// Negated view of the boolean variable + /// Negated view of the boolean variable. Not(AllocatedBit), - /// Constant (not an allocated variable) + /// Constant (not an allocated variable). Constant(bool), } impl R1CSVar for Boolean { type Value = bool; - fn cs(&self) -> Option> { + fn cs(&self) -> ConstraintSystemRef { match self { - Self::Is(a) | Self::Not(a) => Some(a.cs.clone()), - _ => None, + Self::Is(a) | Self::Not(a) => a.cs.clone(), + _ => ConstraintSystemRef::None, } } @@ -248,12 +249,19 @@ impl R1CSVar for Boolean { } impl Boolean { - /// Returns the constrant `true`. + /// The constant `true`. pub const TRUE: Self = Boolean::Constant(true); - /// Returns the constrant `false`. + /// The constant `false`. pub const FALSE: Self = Boolean::Constant(false); + /// Constructs a `LinearCombination` from `Self`'s variables according + /// to the following map. + /// + /// * `Boolean::Constant(true) => lc!() + Variable::One` + /// * `Boolean::Constant(false) => lc!()` + /// * `Boolean::Is(v) => lc!() + v.variable()` + /// * `Boolean::Not(v) => lc!() + Variable::One - v.variable()` pub fn lc(&self) -> LinearCombination { match self { Boolean::Constant(false) => lc!(), @@ -263,23 +271,85 @@ impl Boolean { } } - /// Construct a boolean vector from a vector of u8 + /// Constructs a `Boolean` vector from a slice of constant `u8`. + /// The `u8`s are decomposed in little-endian manner. + /// + /// This *does not* create any new variables or constraints. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// let t = Boolean::::TRUE; + /// let f = Boolean::::FALSE; + /// + /// let bits = vec![f, t]; + /// let generated_bits = Boolean::constant_vec_from_bytes(&[2]); + /// bits[..2].enforce_equal(&generated_bits[..2])?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn constant_vec_from_bytes(values: &[u8]) -> Vec { - let mut input_bits = vec![]; - for input_byte in values { - for bit_i in (0..8).rev() { - input_bits.push(Self::Constant(((input_byte >> bit_i) & 1u8) == 1u8)); + let mut bits = vec![]; + for byte in values { + for i in 0..8 { + bits.push(Self::Constant(((byte >> i) & 1u8) == 1u8)); } } - input_bits + bits } - /// Construct a boolean from a known constant + /// Constructs a constant `Boolean` with value `b`. + /// + /// This *does not* create any new variables or constraints. + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_std::prelude::*; + /// + /// let true_var = Boolean::::TRUE; + /// let false_var = Boolean::::FALSE; + /// + /// true_var.enforce_equal(&Boolean::constant(true))?; + /// false_var.enforce_equal(&Boolean::constant(false))?; + /// # Ok(()) + /// # } + /// ``` pub fn constant(b: bool) -> Self { Boolean::Constant(b) } - /// Return a negated interpretation of this boolean. + /// Negates `self`. + /// + /// This *does not* create any new variables or constraints. + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// a.not().enforce_equal(&b)?; + /// b.not().enforce_equal(&a)?; + /// + /// a.not().enforce_equal(&Boolean::FALSE)?; + /// b.not().enforce_equal(&Boolean::TRUE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn not(&self) -> Self { match *self { Boolean::Constant(c) => Boolean::Constant(!c), @@ -287,14 +357,38 @@ impl Boolean { Boolean::Not(ref v) => Boolean::Is(v.clone()), } } -} -impl Boolean { - /// Perform XOR over two boolean operands + /// Outputs `self ^ other`. + /// + /// If at least one of `self` and `other` are constants, then this method + /// *does not* create any constraints or variables. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// a.xor(&b)?.enforce_equal(&Boolean::TRUE)?; + /// b.xor(&a)?.enforce_equal(&Boolean::TRUE)?; + /// + /// a.xor(&a)?.enforce_equal(&Boolean::FALSE)?; + /// b.xor(&b)?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] - pub fn xor<'a>(&'a self, b: &'a Self) -> Result { + pub fn xor<'a>(&'a self, other: &'a Self) -> Result { use Boolean::*; - match (self, b) { + match (self, other) { (&Constant(false), x) | (x, &Constant(false)) => Ok(x.clone()), (&Constant(true), x) | (x, &Constant(true)) => Ok(x.not()), // a XOR (NOT b) = NOT(a XOR b) @@ -306,11 +400,37 @@ impl Boolean { } } - /// Perform OR over two boolean operands + /// Outputs `self | other`. + /// + /// If at least one of `self` and `other` are constants, then this method + /// *does not* create any constraints or variables. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// a.or(&b)?.enforce_equal(&Boolean::TRUE)?; + /// b.or(&a)?.enforce_equal(&Boolean::TRUE)?; + /// + /// a.or(&a)?.enforce_equal(&Boolean::TRUE)?; + /// b.or(&b)?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] - pub fn or<'a>(&'a self, b: &'a Self) -> Result { + pub fn or<'a>(&'a self, other: &'a Self) -> Result { use Boolean::*; - match (self, b) { + match (self, other) { (&Constant(false), x) | (x, &Constant(false)) => Ok(x.clone()), (&Constant(true), _) | (_, &Constant(true)) => Ok(Constant(true)), // a OR b = NOT ((NOT a) AND b) @@ -321,11 +441,37 @@ impl Boolean { } } - /// Perform AND over two boolean operands + /// Outputs `self & other`. + /// + /// If at least one of `self` and `other` are constants, then this method + /// *does not* create any constraints or variables. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// a.and(&a)?.enforce_equal(&Boolean::TRUE)?; + /// + /// a.and(&b)?.enforce_equal(&Boolean::FALSE)?; + /// b.and(&a)?.enforce_equal(&Boolean::FALSE)?; + /// b.and(&b)?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] - pub fn and<'a>(&'a self, b: &'a Self) -> Result { + pub fn and<'a>(&'a self, other: &'a Self) -> Result { use Boolean::*; - match (self, b) { + match (self, other) { // false AND x is always false (&Constant(false), _) | (_, &Constant(false)) => Ok(Constant(false)), // true AND x is always x @@ -339,6 +485,28 @@ impl Boolean { } } + /// Outputs `bits[0] & bits[1] & ... & bits.last().unwrap()`. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// let c = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// + /// Boolean::kary_and(&[a.clone(), b.clone(), c.clone()])?.enforce_equal(&Boolean::FALSE)?; + /// Boolean::kary_and(&[a.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn kary_and(bits: &[Self]) -> Result { assert!(!bits.is_empty()); @@ -354,6 +522,29 @@ impl Boolean { Ok(cur.expect("should not be 0")) } + /// Outputs `bits[0] | bits[1] | ... | bits.last().unwrap()`. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// let c = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// Boolean::kary_or(&[a.clone(), b.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// Boolean::kary_or(&[a.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// Boolean::kary_or(&[b.clone(), c.clone()])?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn kary_or(bits: &[Self]) -> Result { assert!(!bits.is_empty()); @@ -369,12 +560,37 @@ impl Boolean { Ok(cur.expect("should not be 0")) } + /// Outputs `(bits[0] & bits[1] & ... & bits.last().unwrap()).not()`. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// let c = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// + /// Boolean::kary_nand(&[a.clone(), b.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// Boolean::kary_nand(&[a.clone(), c.clone()])?.enforce_equal(&Boolean::FALSE)?; + /// Boolean::kary_nand(&[b.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn kary_nand(bits: &[Self]) -> Result { Ok(Self::kary_and(bits)?.not()) } - /// Assert that at least one input is false. + /// Enforces that `Self::kary_nand(bits).is_eq(&Boolean::TRUE)`. + /// + /// Informally, this means that at least one element in `bits` must be `false`. #[tracing::instrument(target = "r1cs")] fn enforce_kary_nand(bits: &[Self]) -> Result<(), SynthesisError> { use Boolean::*; @@ -382,17 +598,16 @@ impl Boolean { match r { Constant(true) => Ok(()), Constant(false) => Err(SynthesisError::AssignmentMissing), - Is(_) | Not(_) => r.cs().unwrap().enforce_constraint( - r.lc(), - lc!() + Variable::One, - lc!() + Variable::One, - ), + Is(_) | Not(_) => { + r.cs() + .enforce_constraint(r.lc(), lc!() + Variable::One, lc!() + Variable::One) + } } } /// Enforces that `bits`, when interpreted as a integer, is less than `F::characteristic()`, /// That is, interpret bits as a little-endian integer, and enforce that this integer - /// is "in the field F". + /// is "in the field Z_p", where `p = F::characteristic()` . #[tracing::instrument(target = "r1cs")] pub fn enforce_in_field_le(bits: &[Self]) -> Result<(), SynthesisError> { // `bits` < F::characteristic() <==> `bits` <= F::characteristic() -1 @@ -466,6 +681,30 @@ impl Boolean { Ok(current_run) } + /// Conditionally selects one of `first` and `second` based on the value of `self`: + /// + /// If `self.is_eq(&Boolean::TRUE)`, this outputs `first`; else, it outputs `second`. + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// + /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; + /// + /// let cond = Boolean::new_witness(cs.clone(), || Ok(true))?; + /// + /// cond.select(&a, &b)?.enforce_equal(&Boolean::TRUE)?; + /// cond.select(&b, &a)?.enforce_equal(&Boolean::FALSE)?; + /// + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs", skip(first, second))] pub fn select>( &self, @@ -538,7 +777,7 @@ impl EqGadget for Boolean { }; if condition != &Constant(false) { - let cs = self.cs().or(other.cs()).or(condition.cs()).unwrap(); + let cs = self.cs().or(other.cs()).or(condition.cs()); cs.enforce_constraint(lc!() + difference, condition.lc(), lc!())?; } Ok(()) @@ -574,11 +813,7 @@ impl EqGadget for Boolean { }; if should_enforce != &Constant(false) { - let cs = self - .cs() - .or(other.cs()) - .or(should_enforce.cs()) - .ok_or(SynthesisError::UnconstrainedVariable)?; + let cs = self.cs().or(other.cs()).or(should_enforce.cs()); cs.enforce_constraint(difference, should_enforce.lc(), should_enforce.lc())?; } Ok(()) @@ -597,6 +832,14 @@ impl ToBytesGadget for Boolean { } } +impl ToConstraintFieldGadget for Boolean { + #[tracing::instrument(target = "r1cs")] + fn to_constraint_field(&self) -> Result>, SynthesisError> { + let var = From::from(self.clone()); + Ok(vec![var]) + } +} + impl CondSelectGadget for Boolean { #[tracing::instrument(target = "r1cs")] fn conditionally_select( @@ -615,7 +858,7 @@ impl CondSelectGadget for Boolean { (&Constant(true), x) => cond.or(x), (x, &Constant(true)) => cond.not().or(x), (a, b) => { - let cs = cond.cs().unwrap(); + let cs = cond.cs(); let result: Boolean = AllocatedBit::new_witness_without_booleanity_check(cs.clone(), || { let cond = cond.value()?; diff --git a/r1cs-std/src/bits/mod.rs b/r1cs-std/src/bits/mod.rs index a0d03c6dc..745ae5c9f 100644 --- a/r1cs-std/src/bits/mod.rs +++ b/r1cs-std/src/bits/mod.rs @@ -5,28 +5,47 @@ use crate::{ use algebra::Field; use r1cs_core::SynthesisError; +/// This module contains `Boolean`, a R1CS equivalent of the `bool` type. pub mod boolean; +/// This module contains `UInt8`, a R1CS equivalent of the `u8` type. pub mod uint8; +/// This module contains a macro for generating `UIntN` types, which are R1CS equivalents of +/// `N`-bit unsigned integers. #[macro_use] pub mod uint; -make_uint!(UInt16, 16, u16, uint16); -make_uint!(UInt32, 32, u32, uint32); -make_uint!(UInt64, 64, u64, uint64); +make_uint!(UInt16, 16, u16, uint16, "16"); +make_uint!(UInt32, 32, u32, uint32, "32"); +make_uint!(UInt64, 64, u64, uint64, "64"); +/// Specifies constraints for conversion to a little-endian bit representation of `self`. pub trait ToBitsGadget { /// Outputs the canonical little-endian bit-wise representation of `self`. /// /// This is the correct default for 99% of use cases. fn to_bits_le(&self) -> Result>, SynthesisError>; - /// Outputs a possibly non-unique bit-wise representation of `self`. + /// Outputs a possibly non-unique little-endian bit-wise representation of `self`. /// /// If you're not absolutely certain that your usecase can get away with a - /// non-canonical representation, please use `self.to_bits(cs)` instead. + /// non-canonical representation, please use `self.to_bits()` instead. fn to_non_unique_bits_le(&self) -> Result>, SynthesisError> { self.to_bits_le() } + + /// Outputs the canonical big-endian bit-wise representation of `self`. + fn to_bits_be(&self) -> Result>, SynthesisError> { + let mut res = self.to_bits_le()?; + res.reverse(); + Ok(res) + } + + /// Outputs a possibly non-unique big-endian bit-wise representation of `self`. + fn to_non_unique_bits_be(&self) -> Result>, SynthesisError> { + let mut res = self.to_non_unique_bits_le()?; + res.reverse(); + Ok(res) + } } impl ToBitsGadget for Boolean { @@ -70,6 +89,7 @@ where } } +/// Specifies constraints for conversion to a little-endian byte representation of `self`. pub trait ToBytesGadget { /// Outputs a canonical, little-endian, byte decomposition of `self`. /// diff --git a/r1cs-std/src/bits/uint.rs b/r1cs-std/src/bits/uint.rs index e4dba0800..df0c06a69 100644 --- a/r1cs-std/src/bits/uint.rs +++ b/r1cs-std/src/bits/uint.rs @@ -1,5 +1,10 @@ macro_rules! make_uint { - ($name:ident, $size:expr, $native:ident, $mod_name:ident) => { + ($name:ident, $size:expr, $native:ident, $mod_name:ident, $native_doc_name:expr) => { + #[doc = "This module contains a `UInt"] + #[doc = $native_doc_name] + #[doc = "`, a R1CS equivalent of the `u"] + #[doc = $native_doc_name] + #[doc = "`type."] pub mod $mod_name { use algebra::{Field, FpParameters, PrimeField}; use core::borrow::Borrow; @@ -15,8 +20,14 @@ macro_rules! make_uint { Assignment, Vec, }; - /// Represents an interpretation of `Boolean` objects as an - /// unsigned integer. + #[doc = "This struct represent an unsigned"] + #[doc = $native_doc_name] + #[doc = "-bit integer as a sequence of "] + #[doc = $native_doc_name] + #[doc = " `Boolean`s\n"] + #[doc = "This is the R1CS equivalent of the native `u"] + #[doc = $native_doc_name] + #[doc = "` unsigned integer type."] #[derive(Clone, Debug)] pub struct $name { // Least significant bit first @@ -27,7 +38,7 @@ macro_rules! make_uint { impl R1CSVar for $name { type Value = $native; - fn cs(&self) -> Option> { + fn cs(&self) -> ConstraintSystemRef { self.bits.as_slice().cs() } @@ -46,7 +57,11 @@ macro_rules! make_uint { } impl $name { - /// Construct a constant `$name` from a `$native` + #[doc = "Construct a constant `UInt"] + #[doc = $native_doc_name] + #[doc = "` from the native `u"] + #[doc = $native_doc_name] + #[doc = "` type."] pub fn constant(value: $native) -> Self { let mut bits = Vec::with_capacity($size); @@ -67,13 +82,18 @@ macro_rules! make_uint { } } - /// Turns this `$name` into its little-endian byte order representation. + /// Turns `self` into the underlying little-endian bits. pub fn to_bits_le(&self) -> Vec> { self.bits.clone() } - /// Converts a little-endian byte order representation of bits into a - /// `$name`. + /// Construct `Self` from a slice of `Boolean`s. + /// + /// # Panics + /// + /// This method panics if `bits.len() != u + #[doc($native_doc_name)] + #[doc("`.")] pub fn from_bits_le(bits: &[Boolean]) -> Self { assert_eq!(bits.len(), $size); @@ -105,6 +125,7 @@ macro_rules! make_uint { Self { value, bits } } + /// Rotates `self` to the right by `by` steps, wrapping around. #[tracing::instrument(target = "r1cs", skip(self))] pub fn rotr(&self, by: usize) -> Self { let by = by % $size; @@ -126,8 +147,11 @@ macro_rules! make_uint { } } - /// XOR this `$name` with another `$name` - #[tracing::instrument(target = "r1cs", skip(self))] + /// Outputs `self ^ other`. + /// + /// If at least one of `self` and `other` are constants, then this method + /// *does not* create any constraints or variables. + #[tracing::instrument(target = "r1cs", skip(self, other))] pub fn xor(&self, other: &Self) -> Result { let new_value = match (self.value, other.value) { (Some(a), Some(b)) => Some(a ^ b), @@ -147,8 +171,10 @@ macro_rules! make_uint { }) } - /// Perform modular addition of several `$name` objects. - #[tracing::instrument(target = "r1cs")] + /// Perform modular addition of `operands`. + /// + /// The user must ensure that overflow does not occur. + #[tracing::instrument(target = "r1cs", skip(operands))] pub fn addmany(operands: &[Self]) -> Result where F: PrimeField, @@ -228,7 +254,7 @@ macro_rules! make_uint { return Ok($name::constant(modular_value.unwrap())); } - let cs = operands.cs().unwrap(); + let cs = operands.cs(); // Storage area for the resulting bits let mut result_bits = vec![]; diff --git a/r1cs-std/src/bits/uint8.rs b/r1cs-std/src/bits/uint8.rs index ccc8ea46f..6facd9bc5 100644 --- a/r1cs-std/src/bits/uint8.rs +++ b/r1cs-std/src/bits/uint8.rs @@ -12,14 +12,13 @@ use core::borrow::Borrow; pub struct UInt8 { /// Little-endian representation: least significant bit first pub(crate) bits: Vec>, - /// Little-endian representation: least significant bit first pub(crate) value: Option, } impl R1CSVar for UInt8 { type Value = u8; - fn cs(&self) -> Option> { + fn cs(&self) -> ConstraintSystemRef { self.bits.as_slice().cs() } @@ -39,6 +38,24 @@ impl R1CSVar for UInt8 { impl UInt8 { /// Construct a constant vector of `UInt8` from a vector of `u8` + /// + /// This *does not* create any new variables or constraints. + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// let var = vec![UInt8::new_witness(cs.clone(), || Ok(2))?]; + /// + /// let constant = UInt8::constant_vec(&[2]); + /// var.enforce_equal(&constant)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn constant_vec(values: &[u8]) -> Vec { let mut result = Vec::new(); for value in values { @@ -48,6 +65,25 @@ impl UInt8 { } /// Construct a constant `UInt8` from a `u8` + /// + /// This *does not* create new variables or constraints. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// let var = UInt8::new_witness(cs.clone(), || Ok(2))?; + /// + /// let constant = UInt8::constant(2); + /// var.enforce_equal(&constant)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn constant(value: u8) -> Self { let mut bits = Vec::with_capacity(8); @@ -64,6 +100,7 @@ impl UInt8 { } } + /// Allocates a slice of `u8`'s as private witnesses. pub fn new_witness_vec( cs: impl Into>, values: &[impl Into> + Copy], @@ -78,10 +115,31 @@ impl UInt8 { Ok(output_vec) } - /// Allocates a vector of `u8`'s by first converting (chunks of) them to - /// `ConstraintF` elements, (thus reducing the number of input allocations), - /// and then converts this list of `ConstraintF` gadgets back into - /// bytes. + /// Allocates a slice of `u8`'s as public inputs by first packing them into + /// elements of `F`, (thus reducing the number of input allocations), allocating + /// these elements as public inputs, and then converting these field variables + /// `FpVar` variables back into bytes. + /// + /// From a user perspective, this trade-off adds constraints, but improves + /// verifier time and verification key size. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// let two = UInt8::new_witness(cs.clone(), || Ok(2))?; + /// let var = vec![two.clone(); 32]; + /// + /// let c = UInt8::new_input_vec(cs.clone(), &[2; 32])?; + /// var.enforce_equal(&c)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` pub fn new_input_vec( cs: impl Into>, values: &[u8], @@ -117,6 +175,30 @@ impl UInt8 { /// Converts a little-endian byte order representation of bits into a /// `UInt8`. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// let var = UInt8::new_witness(cs.clone(), || Ok(128))?; + /// + /// let f = Boolean::FALSE; + /// let t = Boolean::TRUE; + /// + /// // Construct [0, 0, 0, 0, 0, 0, 0, 1] + /// let mut bits = vec![f.clone(); 7]; + /// bits.push(t); + /// + /// let mut c = UInt8::from_bits_le(&bits); + /// var.enforce_equal(&c)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn from_bits_le(bits: &[Boolean]) -> Self { assert_eq!(bits.len(), 8); @@ -134,7 +216,28 @@ impl UInt8 { Self { value, bits } } - /// XOR this `UInt8` with another `UInt8` + /// Outputs `self ^ other`. + /// + /// If at least one of `self` and `other` are constants, then this method + /// *does not* create any constraints or variables. + /// + /// ``` + /// # fn main() -> Result<(), r1cs_core::SynthesisError> { + /// // We'll use the BLS12-381 scalar field for our constraints. + /// use algebra::bls12_381::Fr; + /// use r1cs_core::*; + /// use r1cs_std::prelude::*; + /// + /// let cs = ConstraintSystem::::new_ref(); + /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?; + /// let b = UInt8::new_witness(cs.clone(), || Ok(17))?; + /// let c = UInt8::new_witness(cs.clone(), || Ok(1))?; + /// + /// a.xor(&b)?.enforce_equal(&c)?; + /// assert!(cs.is_satisfied().unwrap()); + /// # Ok(()) + /// # } + /// ``` #[tracing::instrument(target = "r1cs")] pub fn xor(&self, other: &Self) -> Result { let new_value = match (self.value, other.value) { diff --git a/r1cs-std/src/eq.rs b/r1cs-std/src/eq.rs index 364b5e233..80798aefd 100644 --- a/r1cs-std/src/eq.rs +++ b/r1cs-std/src/eq.rs @@ -2,17 +2,26 @@ use crate::{prelude::*, Vec}; use algebra::Field; use r1cs_core::SynthesisError; +/// Specifies how to generate constraints that check for equality for two variables of type `Self`. pub trait EqGadget { /// Output a `Boolean` value representing whether `self.value() == other.value()`. fn is_eq(&self, other: &Self) -> Result, SynthesisError>; /// Output a `Boolean` value representing whether `self.value() != other.value()`. + /// + /// By default, this is defined as `self.is_eq(other)?.not()`. fn is_neq(&self, other: &Self) -> Result, SynthesisError> { Ok(self.is_eq(other)?.not()) } /// If `should_enforce == true`, enforce that `self` and `other` are equal; else, /// enforce a vacuously true statement. + /// + /// A safe default implementation is provided that generates the following constraints: + /// `self.is_eq(other)?.conditional_enforce_equal(&Boolean::TRUE, should_enforce)`. + /// + /// More efficient specialized implementation may be possible; implementors + /// are encouraged to carefully analyze the efficiency and safety of these. #[tracing::instrument(target = "r1cs", skip(self, other))] fn conditional_enforce_equal( &self, @@ -24,13 +33,25 @@ pub trait EqGadget { } /// Enforce that `self` and `other` are equal. + /// + /// A safe default implementation is provided that generates the following constraints: + /// `self.conditional_enforce_equal(other, &Boolean::TRUE)`. + /// + /// More efficient specialized implementation may be possible; implementors + /// are encouraged to carefully analyze the efficiency and safety of these. #[tracing::instrument(target = "r1cs", skip(self, other))] fn enforce_equal(&self, other: &Self) -> Result<(), SynthesisError> { self.conditional_enforce_equal(other, &Boolean::constant(true)) } - /// If `should_enforce == true`, enforce that `self` and `other` are not equal; else, + /// If `should_enforce == true`, enforce that `self` and `other` are *not* equal; else, /// enforce a vacuously true statement. + /// + /// A safe default implementation is provided that generates the following constraints: + /// `self.is_neq(other)?.conditional_enforce_equal(&Boolean::TRUE, should_enforce)`. + /// + /// More efficient specialized implementation may be possible; implementors + /// are encouraged to carefully analyze the efficiency and safety of these. #[tracing::instrument(target = "r1cs", skip(self, other))] fn conditional_enforce_not_equal( &self, @@ -41,7 +62,13 @@ pub trait EqGadget { .conditional_enforce_equal(&Boolean::constant(true), should_enforce) } - /// Enforce that `self` and `other` are not equal. + /// Enforce that `self` and `other` are *not* equal. + /// + /// A safe default implementation is provided that generates the following constraints: + /// `self.conditional_enforce_not_equal(other, &Boolean::TRUE)`. + /// + /// More efficient specialized implementation may be possible; implementors + /// are encouraged to carefully analyze the efficiency and safety of these. #[tracing::instrument(target = "r1cs", skip(self, other))] fn enforce_not_equal(&self, other: &Self) -> Result<(), SynthesisError> { self.conditional_enforce_not_equal(other, &Boolean::constant(true)) @@ -81,16 +108,16 @@ impl + R1CSVar, F: Field> EqGadget for [T] { ) -> Result<(), SynthesisError> { assert_eq!(self.len(), other.len()); let some_are_different = self.is_neq(other)?; - if let Some(cs) = some_are_different.cs().or(should_enforce.cs()) { + if [&some_are_different, should_enforce].is_constant() { + assert!(some_are_different.value().unwrap()); + Ok(()) + } else { + let cs = [&some_are_different, should_enforce].cs(); cs.enforce_constraint( some_are_different.lc(), should_enforce.lc(), should_enforce.lc(), ) - } else { - // `some_are_different` and `should_enforce` are both constants - assert!(some_are_different.value().unwrap()); - Ok(()) } } } diff --git a/r1cs-std/src/fields/cubic_extension.rs b/r1cs-std/src/fields/cubic_extension.rs index d2ceb8782..0a96bf244 100644 --- a/r1cs-std/src/fields/cubic_extension.rs +++ b/r1cs-std/src/fields/cubic_extension.rs @@ -1,16 +1,19 @@ use algebra::{ fields::{CubicExtField, CubicExtParameters, Field}, - One, Zero, + Zero, }; use core::{borrow::Borrow, marker::PhantomData}; use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError}; +use crate::fields::fp::FpVar; use crate::{ fields::{FieldOpsBounds, FieldVar}, prelude::*, - Assignment, Vec, + ToConstraintFieldGadget, Vec, }; +/// This struct is the `R1CS` equivalent of the cubic extension field type +/// in `algebra-core`, i.e. `algebra_core::CubicExtField`. #[derive(Derivative)] #[derivative(Debug(bound = "BF: core::fmt::Debug"), Clone(bound = "BF: Clone"))] #[must_use] @@ -18,18 +21,24 @@ pub struct CubicExtVar, P: CubicEx where for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, { + /// The zero-th coefficient of this field element. pub c0: BF, + /// The first coefficient of this field element. pub c1: BF, + /// The second coefficient of this field element. pub c2: BF, #[derivative(Debug = "ignore")] _params: PhantomData

, } +/// This trait describes parameters that are used to implement arithmetic for `CubicExtVar`. pub trait CubicExtVarParams>: CubicExtParameters where for<'a> &'a BF: FieldOpsBounds<'a, Self::BaseField, BF>, { + /// Multiply the base field of the `CubicExtVar` by the appropriate Frobenius coefficient. + /// This is equivalent to `Self::mul_base_field_by_frob_coeff(c1, c2, power)`. fn mul_base_field_vars_by_frob_coeff(c1: &mut BF, c2: &mut BF, power: usize); } @@ -37,6 +46,7 @@ impl, P: CubicExtVarParams> Cu where for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, { + /// Constructs a `CubicExtVar` from the underlying coefficients. #[inline] pub fn new(c0: BF, c1: BF, c2: BF) -> Self { let _params = PhantomData; @@ -48,13 +58,14 @@ where } } - /// Multiply a BF by cubic nonresidue P::NONRESIDUE. + /// Multiplies a variable of the base field by the cubic nonresidue `P::NONRESIDUE` that + /// is used to construct the extension field. #[inline] pub fn mul_base_field_by_nonresidue(fe: &BF) -> Result { Ok(fe * P::NONRESIDUE) } - /// Multiply a CubicExtVar by an element of `P::BaseField`. + /// Multiplies `self` by a constant from the base field. #[inline] pub fn mul_by_base_field_constant(&self, fe: P::BaseField) -> Self { let c0 = &self.c0 * fe; @@ -63,6 +74,7 @@ where Self::new(c0, c1, c2) } + /// Sets `self = self.mul_by_base_field_constant(fe)`. #[inline] pub fn mul_assign_by_base_field_constant(&mut self, fe: P::BaseField) { *self = (&*self).mul_by_base_field_constant(fe); @@ -77,7 +89,7 @@ where { type Value = CubicExtField

; - fn cs(&self) -> Option> { + fn cs(&self) -> ConstraintSystemRef { [&self.c0, &self.c1, &self.c2].cs() } @@ -254,14 +266,20 @@ where #[tracing::instrument(target = "r1cs")] fn inverse(&self) -> Result { - let cs = self.cs().get()?.clone(); - let one = Self::new_constant(cs.clone(), CubicExtField::one())?; - - let inverse = Self::new_witness(self.cs().get()?.clone(), || { - self.value() - .map(|f| f.inverse().unwrap_or(CubicExtField::zero())) - })?; - self.mul_equals(&inverse, &one)?; + let mode = if self.is_constant() { + AllocationMode::Constant + } else { + AllocationMode::Witness + }; + let inverse = Self::new_variable( + self.cs(), + || { + self.value() + .map(|f| f.inverse().unwrap_or(CubicExtField::zero())) + }, + mode, + )?; + self.mul_equals(&inverse, &Self::one())?; Ok(inverse) } } @@ -440,6 +458,25 @@ where } } +impl ToConstraintFieldGadget for CubicExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, + BF: ToConstraintFieldGadget, +{ + #[tracing::instrument(target = "r1cs")] + fn to_constraint_field(&self) -> Result>, SynthesisError> { + let mut res = Vec::new(); + + res.extend_from_slice(&self.c0.to_constraint_field()?); + res.extend_from_slice(&self.c1.to_constraint_field()?); + res.extend_from_slice(&self.c2.to_constraint_field()?); + + Ok(res) + } +} + impl CondSelectGadget for CubicExtVar where BF: FieldVar, diff --git a/r1cs-std/src/fields/fp/cmp.rs b/r1cs-std/src/fields/fp/cmp.rs index 69aa6154a..e346f9b54 100644 --- a/r1cs-std/src/fields/fp/cmp.rs +++ b/r1cs-std/src/fields/fp/cmp.rs @@ -95,7 +95,7 @@ impl FpVar { Ok((left.clone(), right_for_check)) } - // Helper function to enforce `self <= (p-1)/2`. + /// Helper function to enforce that `self <= (p-1)/2`. #[tracing::instrument(target = "r1cs")] pub fn enforce_smaller_or_equal_than_mod_minus_one_div_two( &self, @@ -140,10 +140,11 @@ impl FpVar { /// Helper function to enforce `self < other`. This function assumes `self` and `other` /// are `<= (p-1)/2` and does not generate constraints to verify that. fn enforce_smaller_than_unchecked(&self, other: &FpVar) -> Result<(), SynthesisError> { - let cs = [self, other].cs().unwrap(); let is_smaller_than = self.is_smaller_than_unchecked(other)?; let lc_one = lc!() + Variable::One; - cs.enforce_constraint(is_smaller_than.lc(), lc_one.clone(), lc_one) + [self, other] + .cs() + .enforce_constraint(is_smaller_than.lc(), lc_one.clone(), lc_one) } } diff --git a/r1cs-std/src/fields/fp/mod.rs b/r1cs-std/src/fields/fp/mod.rs index 356d499af..1fa22662a 100644 --- a/r1cs-std/src/fields/fp/mod.rs +++ b/r1cs-std/src/fields/fp/mod.rs @@ -4,19 +4,25 @@ use r1cs_core::{lc, ConstraintSystemRef, LinearCombination, Namespace, Synthesis use core::borrow::Borrow; use crate::fields::{FieldOpsBounds, FieldVar}; -use crate::{prelude::*, Assignment, Vec}; +use crate::{prelude::*, Assignment, ToConstraintFieldGadget, Vec}; -pub mod cmp; +mod cmp; +/// Represents a variable in the constraint system whose +/// value can be an arbitrary field element. #[derive(Debug, Clone)] #[must_use] pub struct AllocatedFp { pub(crate) value: Option, + /// The allocated variable corresponding to `self` in `self.cs`. pub variable: Variable, + /// The constraint system that `self` was allocated in. pub cs: ConstraintSystemRef, } impl AllocatedFp { + /// Constructs a new `AllocatedFp` from a (optional) value, a low-level Variable, + /// and a `ConstraintSystemRef`. pub fn new(value: Option, variable: Variable, cs: ConstraintSystemRef) -> Self { Self { value, @@ -26,21 +32,24 @@ impl AllocatedFp { } } -/// Represent variables corresponding to the field `F`. +/// Represent variables corresponding to a field element in `F`. #[derive(Clone, Debug)] #[must_use] pub enum FpVar { + /// Represents a constant in the constraint system, which means that + /// it does not have a corresponding variable. Constant(F), + /// Represents an allocated variable constant in the constraint system. Var(AllocatedFp), } impl R1CSVar for FpVar { type Value = F; - fn cs(&self) -> Option> { + fn cs(&self) -> ConstraintSystemRef { match self { - Self::Constant(_) => None, - Self::Var(a) => Some(a.cs.clone()), + Self::Constant(_) => ConstraintSystemRef::None, + Self::Var(a) => a.cs.clone(), } } @@ -58,7 +67,7 @@ impl From> for FpVar { Self::Constant(F::from(b as u8)) } else { // `other` is a variable - let cs = other.cs().unwrap(); + let cs = other.cs(); let variable = cs.new_lc(other.lc()).unwrap(); Self::Var(AllocatedFp::new( other.value().ok().map(|b| F::from(b as u8)), @@ -79,19 +88,22 @@ impl<'a, F: PrimeField> FieldOpsBounds<'a, F, Self> for FpVar {} impl<'a, F: PrimeField> FieldOpsBounds<'a, F, FpVar> for &'a FpVar {} impl AllocatedFp { + /// Constructs `Self` from a `Boolean`: if `other` is false, this outputs `zero`, else it outputs `one`. pub fn from(other: Boolean) -> Self { - if let Some(cs) = other.cs() { - let variable = cs.new_lc(other.lc()).unwrap(); - Self::new(other.value().ok().map(|b| F::from(b as u8)), variable, cs) - } else { - unreachable!("Cannot create a constant value") - } + let cs = other.cs(); + let variable = cs.new_lc(other.lc()).unwrap(); + Self::new(other.value().ok().map(|b| F::from(b as u8)), variable, cs) } + /// Returns the value assigned to `self` in the underlying constraint system + /// (if a value was assigned). pub fn value(&self) -> Result { self.cs.assigned_value(self.variable).get() } + /// Outputs `self + other`. + /// + /// This does not create any constraints. #[tracing::instrument(target = "r1cs")] pub fn add(&self, other: &Self) -> Self { let value = match (self.value, other.value) { @@ -106,6 +118,9 @@ impl AllocatedFp { AllocatedFp::new(value, variable, self.cs.clone()) } + /// Outputs `self - other`. + /// + /// This does not create any constraints. #[tracing::instrument(target = "r1cs")] pub fn sub(&self, other: &Self) -> Self { let value = match (self.value, other.value) { @@ -120,6 +135,9 @@ impl AllocatedFp { AllocatedFp::new(value, variable, self.cs.clone()) } + /// Outputs `self * other`. + /// + /// This requires *one* constraint. #[tracing::instrument(target = "r1cs")] pub fn mul(&self, other: &Self) -> Self { let product = AllocatedFp::new_witness(self.cs.clone(), || { @@ -136,6 +154,9 @@ impl AllocatedFp { product } + /// Output `self + other` + /// + /// This does not create any constraints. #[tracing::instrument(target = "r1cs")] pub fn add_constant(&self, other: F) -> Self { if other.is_zero() { @@ -150,11 +171,17 @@ impl AllocatedFp { } } + /// Output `self - other` + /// + /// This does not create any constraints. #[tracing::instrument(target = "r1cs")] pub fn sub_constant(&self, other: F) -> Self { self.add_constant(-other) } + /// Output `self * other` + /// + /// This does not create any constraints. #[tracing::instrument(target = "r1cs")] pub fn mul_constant(&self, other: F) -> Self { if other.is_one() { @@ -166,6 +193,9 @@ impl AllocatedFp { } } + /// Output `self + self` + /// + /// This does not create any constraints. #[tracing::instrument(target = "r1cs")] pub fn double(&self) -> Result { let value = self.value.map(|val| val.double()); @@ -173,6 +203,9 @@ impl AllocatedFp { Ok(Self::new(value, variable, self.cs.clone())) } + /// Output `-self` + /// + /// This does not create any constraints. #[tracing::instrument(target = "r1cs")] pub fn negate(&self) -> Self { let mut result = self.clone(); @@ -180,6 +213,9 @@ impl AllocatedFp { result } + /// Sets `self = -self` + /// + /// This does not create any constraints. #[tracing::instrument(target = "r1cs")] pub fn negate_in_place(&mut self) -> &mut Self { self.value.as_mut().map(|val| *val = -(*val)); @@ -187,11 +223,17 @@ impl AllocatedFp { self } + /// Outputs `self * self` + /// + /// This requires *one* constraint. #[tracing::instrument(target = "r1cs")] pub fn square(&self) -> Result { Ok(self.mul(self)) } + /// Outputs `result` such that `result * self = 1`. + /// + /// This requires *one* constraint. #[tracing::instrument(target = "r1cs")] pub fn inverse(&self) -> Result { let inverse = Self::new_witness(self.cs.clone(), || { @@ -206,11 +248,15 @@ impl AllocatedFp { Ok(inverse) } + /// This is a no-op for prime fields. #[tracing::instrument(target = "r1cs")] pub fn frobenius_map(&self, _: usize) -> Result { Ok(self.clone()) } + /// Enforces that `self * other = result`. + /// + /// This requires *one* constraint. #[tracing::instrument(target = "r1cs")] pub fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> { self.cs.enforce_constraint( @@ -220,6 +266,9 @@ impl AllocatedFp { ) } + /// Enforces that `self * self = result`. + /// + /// This requires *one* constraint. #[tracing::instrument(target = "r1cs")] pub fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> { self.cs.enforce_constraint( @@ -231,9 +280,7 @@ impl AllocatedFp { /// Outputs the bit `self == other`. /// - /// # Constraint cost - /// - /// Consumes three constraints + /// This requires three constraints. #[tracing::instrument(target = "r1cs")] pub fn is_eq(&self, other: &Self) -> Result, SynthesisError> { Ok(self.is_neq(other)?.not()) @@ -241,9 +288,7 @@ impl AllocatedFp { /// Outputs the bit `self != other`. /// - /// # Constraint cost - /// - /// Consumes three constraints + /// This requires three constraints. #[tracing::instrument(target = "r1cs")] pub fn is_neq(&self, other: &Self) -> Result, SynthesisError> { let is_not_equal = Boolean::new_witness(self.cs.clone(), || { @@ -311,6 +356,9 @@ impl AllocatedFp { Ok(is_not_equal) } + /// Enforces that self == other if `should_enforce.is_eq(&Boolean::TRUE)`. + /// + /// This requires one constraint. #[tracing::instrument(target = "r1cs")] pub fn conditional_enforce_equal( &self, @@ -324,6 +372,9 @@ impl AllocatedFp { ) } + /// Enforces that self != other if `should_enforce.is_eq(&Boolean::TRUE)`. + /// + /// This requires one constraint. #[tracing::instrument(target = "r1cs")] pub fn conditional_enforce_not_equal( &self, @@ -353,6 +404,9 @@ impl AllocatedFp { impl ToBitsGadget for AllocatedFp { /// Outputs the unique bit-wise decomposition of `self` in *little-endian* /// form. + /// + /// This method enforces that the output is in the field, i.e. + /// it invokes `Boolean::enforce_in_field_le` on the bit decomposition. #[tracing::instrument(target = "r1cs")] fn to_bits_le(&self) -> Result>, SynthesisError> { let bits = self.to_non_unique_bits_le()?; @@ -405,6 +459,9 @@ impl ToBitsGadget for AllocatedFp { impl ToBytesGadget for AllocatedFp { /// Outputs the unique byte decomposition of `self` in *little-endian* /// form. + /// + /// This method enforces that the decomposition represents + /// an integer that is less than `F::MODULUS`. #[tracing::instrument(target = "r1cs")] fn to_bytes(&self) -> Result>, SynthesisError> { let num_bits = F::BigInt::NUM_LIMBS * 64; @@ -432,6 +489,13 @@ impl ToBytesGadget for AllocatedFp { } } +impl ToConstraintFieldGadget for AllocatedFp { + #[tracing::instrument(target = "r1cs")] + fn to_constraint_field(&self) -> Result>, SynthesisError> { + Ok(vec![self.clone().into()]) + } +} + impl CondSelectGadget for AllocatedFp { #[inline] #[tracing::instrument(target = "r1cs")] @@ -444,7 +508,7 @@ impl CondSelectGadget for AllocatedFp { Boolean::Constant(true) => Ok(true_val.clone()), Boolean::Constant(false) => Ok(false_val.clone()), _ => { - let cs = cond.cs().unwrap(); + let cs = cond.cs(); let result = Self::new_witness(cs.clone(), || { cond.value() .and_then(|c| if c { true_val } else { false_val }.value.get()) @@ -474,24 +538,20 @@ impl TwoBitLookupGadget for AllocatedFp { fn two_bit_lookup(b: &[Boolean], c: &[Self::TableConstant]) -> Result { debug_assert_eq!(b.len(), 2); debug_assert_eq!(c.len(), 4); - if let Some(cs) = b.cs() { - let result = Self::new_witness(cs.clone(), || { - let lsb = usize::from(b[0].value()?); - let msb = usize::from(b[1].value()?); - let index = lsb + (msb << 1); - Ok(c[index]) - })?; - let one = Variable::One; - cs.enforce_constraint( - lc!() + b[1].lc() * (c[3] - &c[2] - &c[1] + &c[0]) + (c[1] - &c[0], one), - lc!() + b[0].lc(), - lc!() + result.variable - (c[0], one) + b[1].lc() * (c[0] - &c[2]), - )?; - - Ok(result) - } else { - unreachable!("must provide a way to obtain a ConstraintSystemRef") - } + let result = Self::new_witness(b.cs(), || { + let lsb = usize::from(b[0].value()?); + let msb = usize::from(b[1].value()?); + let index = lsb + (msb << 1); + Ok(c[index]) + })?; + let one = Variable::One; + b.cs().enforce_constraint( + lc!() + b[1].lc() * (c[3] - &c[2] - &c[1] + &c[0]) + (c[1] - &c[0], one), + lc!() + b[0].lc(), + lc!() + result.variable - (c[0], one) + b[1].lc() * (c[0] - &c[2]), + )?; + + Ok(result) } } @@ -506,37 +566,32 @@ impl ThreeBitCondNegLookupGadget for AllocatedFp { ) -> Result { debug_assert_eq!(b.len(), 3); debug_assert_eq!(c.len(), 4); + let result = Self::new_witness(b.cs(), || { + let lsb = usize::from(b[0].value()?); + let msb = usize::from(b[1].value()?); + let index = lsb + (msb << 1); + let intermediate = c[index]; - if let Some(cs) = b.cs() { - let result = Self::new_witness(cs.clone(), || { - let lsb = usize::from(b[0].value()?); - let msb = usize::from(b[1].value()?); - let index = lsb + (msb << 1); - let intermediate = c[index]; - - let is_negative = b[2].value()?; - let y = if is_negative { - -intermediate - } else { - intermediate - }; - Ok(y) - })?; - - let y_lc = b0b1.lc() * (c[3] - &c[2] - &c[1] + &c[0]) - + b[0].lc() * (c[1] - &c[0]) - + b[1].lc() * (c[2] - &c[0]) - + (c[0], Variable::One); - cs.enforce_constraint( - y_lc.clone() + y_lc.clone(), - b[2].lc(), - y_lc.clone() - result.variable, - )?; - - Ok(result) - } else { - unreachable!("must provide a way to obtain a ConstraintSystemRef") - } + let is_negative = b[2].value()?; + let y = if is_negative { + -intermediate + } else { + intermediate + }; + Ok(y) + })?; + + let y_lc = b0b1.lc() * (c[3] - &c[2] - &c[1] + &c[0]) + + b[0].lc() * (c[1] - &c[0]) + + b[1].lc() * (c[2] - &c[0]) + + (c[0], Variable::One); + b.cs().enforce_constraint( + y_lc.clone() + y_lc.clone(), + b[2].lc(), + y_lc.clone() - result.variable, + )?; + + Ok(result) } } @@ -845,6 +900,13 @@ impl ToBytesGadget for FpVar { } } +impl ToConstraintFieldGadget for FpVar { + #[tracing::instrument(target = "r1cs")] + fn to_constraint_field(&self) -> Result>, SynthesisError> { + Ok(vec![self.clone()]) + } +} + impl CondSelectGadget for FpVar { #[tracing::instrument(target = "r1cs")] fn conditionally_select( @@ -864,7 +926,7 @@ impl CondSelectGadget for FpVar { Ok(is.mul_constant(*t).add(¬.mul_constant(*f)).into()) } (_, _) => { - let cs = cond.cs().unwrap(); + let cs = cond.cs(); let true_value = match true_value { Self::Constant(f) => AllocatedFp::new_constant(cs.clone(), f)?, Self::Var(v) => v.clone(), @@ -890,13 +952,13 @@ impl TwoBitLookupGadget for FpVar { fn two_bit_lookup(b: &[Boolean], c: &[Self::TableConstant]) -> Result { debug_assert_eq!(b.len(), 2); debug_assert_eq!(c.len(), 4); - if b.cs().is_some() { - AllocatedFp::two_bit_lookup(b, c).map(Self::Var) - } else { + if b.is_constant() { let lsb = usize::from(b[0].value()?); let msb = usize::from(b[1].value()?); let index = lsb + (msb << 1); Ok(Self::Constant(c[index])) + } else { + AllocatedFp::two_bit_lookup(b, c).map(Self::Var) } } } @@ -913,9 +975,9 @@ impl ThreeBitCondNegLookupGadget for FpVar { debug_assert_eq!(b.len(), 3); debug_assert_eq!(c.len(), 4); - if b.cs().or(b0b1.cs()).is_some() { - AllocatedFp::three_bit_cond_neg_lookup(b, b0b1, c).map(Self::Var) - } else { + if !b.cs().or(b0b1.cs()).is_none() { + // We only have constants + let lsb = usize::from(b[0].value()?); let msb = usize::from(b[1].value()?); let index = lsb + (msb << 1); @@ -928,6 +990,8 @@ impl ThreeBitCondNegLookupGadget for FpVar { intermediate }; Ok(Self::Constant(y)) + } else { + AllocatedFp::three_bit_cond_neg_lookup(b, b0b1, c).map(Self::Var) } } } diff --git a/r1cs-std/src/fields/fp12.rs b/r1cs-std/src/fields/fp12.rs index 914553c13..aa5a95f30 100644 --- a/r1cs-std/src/fields/fp12.rs +++ b/r1cs-std/src/fields/fp12.rs @@ -2,6 +2,9 @@ use crate::fields::{fp2::Fp2Var, fp6_3over2::Fp6Var, quadratic_extension::*, Fie use algebra::fields::{fp12_2over3over2::*, fp6_3over2::Fp6Parameters, Field, QuadExtParameters}; use r1cs_core::SynthesisError; +/// A degree-12 extension field constructed as the tower of a +/// quadratic extension over a cubic extension over a quadratic extension field. +/// This is the R1CS equivalent of `algebra_core::fp12_2over3over2::Fp12

`. pub type Fp12Var

= QuadExtVar::Fp6Params>, Fp12ParamsWrapper

>; type Fp2Params

= <

::Fp6Params as Fp6Parameters>::Fp2Params; @@ -15,7 +18,7 @@ impl QuadExtVarParams> for Fp12ParamsWra } impl Fp12Var

{ - /// Multiplies by an element of the form (c0 = (c0, c1, 0), c1 = (0, d1, 0)) + /// Multiplies by a sparse element of the form `(c0 = (c0, c1, 0), c1 = (0, d1, 0))`. #[inline] pub fn mul_by_014( &self, @@ -31,7 +34,7 @@ impl Fp12Var

{ Ok(Self::new(new_c0, new_c1)) } - /// Multiplies by an element of the form (c0 = (c0, 0, 0), c1 = (d0, d1, 0)) + /// Multiplies by a sparse element of the form `(c0 = (c0, 0, 0), c1 = (d0, d1, 0))`. #[inline] pub fn mul_by_034( &self, @@ -54,6 +57,7 @@ impl Fp12Var

{ Ok(Self::new(new_c0, new_c1)) } + /// Squares `self` when `self` is in the cyclotomic subgroup. pub fn cyclotomic_square(&self) -> Result { if characteristic_square_mod_6_is_one(Fp12::

::characteristic()) { let fp2_nr = ::NONRESIDUE; @@ -132,7 +136,7 @@ impl Fp12Var

{ } } - /// Like `Self::cyclotomic_exp, but additionally uses cyclotomic squaring. + /// Like `Self::cyclotomic_exp`, but additionally uses cyclotomic squaring. pub fn optimized_cyclotomic_exp( &self, exponent: impl AsRef<[u64]>, diff --git a/r1cs-std/src/fields/fp2.rs b/r1cs-std/src/fields/fp2.rs index 3bf8e45bb..7a358ad60 100644 --- a/r1cs-std/src/fields/fp2.rs +++ b/r1cs-std/src/fields/fp2.rs @@ -1,6 +1,8 @@ use crate::fields::{fp::FpVar, quadratic_extension::*}; use algebra::fields::{Fp2Parameters, Fp2ParamsWrapper, QuadExtParameters}; +/// A quadratic extension field constructed over a prime field. +/// This is the R1CS equivalent of `algebra_core::Fp2

`. pub type Fp2Var

= QuadExtVar::Fp>, Fp2ParamsWrapper

>; impl QuadExtVarParams> for Fp2ParamsWrapper

{ diff --git a/r1cs-std/src/fields/fp3.rs b/r1cs-std/src/fields/fp3.rs index 24f14cc2b..b2feb3908 100644 --- a/r1cs-std/src/fields/fp3.rs +++ b/r1cs-std/src/fields/fp3.rs @@ -1,6 +1,8 @@ use crate::fields::{cubic_extension::*, fp::FpVar}; use algebra::fields::{CubicExtParameters, Fp3Parameters, Fp3ParamsWrapper}; +/// A cubic extension field constructed over a prime field. +/// This is the R1CS equivalent of `algebra_core::Fp3

`. pub type Fp3Var

= CubicExtVar::Fp>, Fp3ParamsWrapper

>; impl CubicExtVarParams> for Fp3ParamsWrapper

{ diff --git a/r1cs-std/src/fields/fp4.rs b/r1cs-std/src/fields/fp4.rs index 2f9da2b2d..e0a82be30 100644 --- a/r1cs-std/src/fields/fp4.rs +++ b/r1cs-std/src/fields/fp4.rs @@ -1,6 +1,9 @@ use crate::fields::{fp2::Fp2Var, quadratic_extension::*}; use algebra::fields::{Fp4Parameters, Fp4ParamsWrapper, QuadExtParameters}; +/// A quartic extension field constructed as the tower of a +/// quadratic extension over a quadratic extension field. +/// This is the R1CS equivalent of `algebra_core::Fp4

`. pub type Fp4Var

= QuadExtVar::Fp2Params>, Fp4ParamsWrapper

>; impl QuadExtVarParams> for Fp4ParamsWrapper

{ diff --git a/r1cs-std/src/fields/fp6_2over3.rs b/r1cs-std/src/fields/fp6_2over3.rs index 147e239ab..8a12aa6fe 100644 --- a/r1cs-std/src/fields/fp6_2over3.rs +++ b/r1cs-std/src/fields/fp6_2over3.rs @@ -1,6 +1,9 @@ use crate::fields::{fp3::Fp3Var, quadratic_extension::*}; use algebra::fields::{fp6_2over3::*, QuadExtParameters}; +/// A sextic extension field constructed as the tower of a +/// quadratic extension over a cubic extension field. +/// This is the R1CS equivalent of `algebra_core::fp6_2over3::Fp6

`. pub type Fp6Var

= QuadExtVar::Fp3Params>, Fp6ParamsWrapper

>; impl QuadExtVarParams> for Fp6ParamsWrapper

{ diff --git a/r1cs-std/src/fields/fp6_3over2.rs b/r1cs-std/src/fields/fp6_3over2.rs index 34da0bc98..709e33dd8 100644 --- a/r1cs-std/src/fields/fp6_3over2.rs +++ b/r1cs-std/src/fields/fp6_3over2.rs @@ -3,6 +3,9 @@ use algebra::fields::{fp6_3over2::*, CubicExtParameters, Fp2}; use core::ops::MulAssign; use r1cs_core::SynthesisError; +/// A sextic extension field constructed as the tower of a +/// cubic extension over a quadratic extension field. +/// This is the R1CS equivalent of `algebra_core::fp6_3over3::Fp6

`. pub type Fp6Var

= CubicExtVar::Fp2Params>, Fp6ParamsWrapper

>; impl CubicExtVarParams> for Fp6ParamsWrapper

{ @@ -17,6 +20,7 @@ impl CubicExtVarParams> for Fp6ParamsWrap } impl Fp6Var

{ + /// Multiplies `self` by a sparse element which has `c0 == c2 == zero`. pub fn mul_by_0_c1_0(&self, c1: &Fp2Var) -> Result { // Karatsuba multiplication // v0 = a0 * b0 = 0 @@ -44,7 +48,7 @@ impl Fp6Var

{ Ok(Self::new(c0, c1, c2)) } - // #[inline] + /// Multiplies `self` by a sparse element which has `c2 == zero`. pub fn mul_by_c0_c1_0( &self, c0: &Fp2Var, diff --git a/r1cs-std/src/fields/mod.rs b/r1cs-std/src/fields/mod.rs index 71159c0d2..5320e1e6d 100644 --- a/r1cs-std/src/fields/mod.rs +++ b/r1cs-std/src/fields/mod.rs @@ -7,18 +7,37 @@ use r1cs_core::SynthesisError; use crate::{prelude::*, Assignment}; +/// This module contains a generic implementation of cubic extension field variables. +/// That is, it implements the R1CS equivalent of `algebra_core::CubicExtField`. pub mod cubic_extension; +/// This module contains a generic implementation of quadratic extension field variables. +/// That is, it implements the R1CS equivalent of `algebra_core::QuadExtField`. pub mod quadratic_extension; +/// This module contains a generic implementation of prime field variables. +/// That is, it implements the R1CS equivalent of `algebra_core::Fp*`. pub mod fp; + +/// This module contains a generic implementation of the degree-12 tower extension field. +/// That is, it implements the R1CS equivalent of `algebra_core::Fp12` pub mod fp12; +/// This module contains a generic implementation of the degree-2 tower extension field. +/// That is, it implements the R1CS equivalent of `algebra_core::Fp2` pub mod fp2; +/// This module contains a generic implementation of the degree-3 tower extension field. +/// That is, it implements the R1CS equivalent of `algebra_core::Fp3` pub mod fp3; +/// This module contains a generic implementation of the degree-4 tower extension field. +/// That is, it implements the R1CS equivalent of `algebra_core::Fp4` pub mod fp4; +/// This module contains a generic implementation of the degree-6 tower extension field. +/// That is, it implements the R1CS equivalent of `algebra_core::fp6_2over3::Fp6` pub mod fp6_2over3; +/// This module contains a generic implementation of the degree-6 tower extension field. +/// That is, it implements the R1CS equivalent of `algebra_core::fp6_3over2::Fp6` pub mod fp6_3over2; -/// A hack used to work around the lack of implied bounds. +/// This trait is a hack used to work around the lack of implied bounds. pub trait FieldOpsBounds<'a, F, T: 'a>: Sized + Add<&'a T, Output = T> @@ -56,66 +75,84 @@ pub trait FieldVar: + MulAssign + Debug { + /// Returns the constant `F::zero()`. fn zero() -> Self; + /// Returns a `Boolean` representing whether `self == Self::zero()`. fn is_zero(&self) -> Result, SynthesisError> { self.is_eq(&Self::zero()) } + /// Returns the constant `F::one()`. fn one() -> Self; + /// Returns a `Boolean` representing whether `self == Self::one()`. fn is_one(&self) -> Result, SynthesisError> { self.is_eq(&Self::one()) } + /// Returns a constant with value `v`. + /// + /// This *should not* allocate any variables. fn constant(v: F) -> Self; + /// Computes `self + self`. fn double(&self) -> Result { Ok(self.clone() + self) } + /// Sets `self = self + self`. fn double_in_place(&mut self) -> Result<&mut Self, SynthesisError> { *self += self.double()?; Ok(self) } + /// Coputes `-self`. fn negate(&self) -> Result; + /// Sets `self = -self`. #[inline] fn negate_in_place(&mut self) -> Result<&mut Self, SynthesisError> { *self = self.negate()?; Ok(self) } + /// Computes `self * self`. + /// + /// A default implementation is provided which just invokes the underlying + /// multiplication routine. However, this method should be specialized + /// for extension fields, where faster algorithms exist for squaring. fn square(&self) -> Result { Ok(self.clone() * self) } + /// Sets `self = self.square()`. fn square_in_place(&mut self) -> Result<&mut Self, SynthesisError> { *self = self.square()?; Ok(self) } - /// Enforce that `self * other == result`. + /// Enforces that `self * other == result`. fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> { let actual_result = self.clone() * other; result.enforce_equal(&actual_result) } - /// Enforce that `self * self == result`. + /// Enforces that `self * self == result`. fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> { let actual_result = self.square()?; result.enforce_equal(&actual_result) } + /// Computes `result` such that `self * result == Self::one()`. fn inverse(&self) -> Result; - /// Returns (self / denominator), but requires fewer constraints than - /// self * denominator.inverse() + /// Returns `(self / denominator)`. but requires fewer constraints than + /// `self * denominator.inverse()`. /// It is up to the caller to ensure that denominator is non-zero, /// since in that case the result is unconstrained. fn mul_by_inverse(&self, denominator: &Self) -> Result { - let result = Self::new_witness(self.cs().unwrap(), || { + let result = Self::new_witness(self.cs(), || { let denominator_inv_native = denominator.value()?.inverse().get()?; let result = self.value()? * &denominator_inv_native; Ok(result) @@ -125,8 +162,10 @@ pub trait FieldVar: Ok(result) } + /// Computes the frobenius map over `self`. fn frobenius_map(&self, power: usize) -> Result; + /// Sets `self = self.frobenius_map()`. fn frobenius_map_in_place(&mut self, power: usize) -> Result<&mut Self, SynthesisError> { *self = self.frobenius_map(power)?; Ok(self) @@ -145,7 +184,8 @@ pub trait FieldVar: Ok(res) } - /// Computes `self^S`, where S is interpreted as an integer. + /// Computes `self^S`, where S is interpreted as an little-endian u64-decomposition of + /// an integer. fn pow_by_constant>(&self, exp: S) -> Result { let mut res = Self::one(); for i in BitIteratorBE::without_leading_zeros(exp) { diff --git a/r1cs-std/src/fields/quadratic_extension.rs b/r1cs-std/src/fields/quadratic_extension.rs index f37802b7a..4b1cb304a 100644 --- a/r1cs-std/src/fields/quadratic_extension.rs +++ b/r1cs-std/src/fields/quadratic_extension.rs @@ -1,16 +1,19 @@ use algebra::{ fields::{Field, QuadExtField, QuadExtParameters}, - One, Zero, + Zero, }; use core::{borrow::Borrow, marker::PhantomData}; use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError}; +use crate::fields::fp::FpVar; use crate::{ fields::{FieldOpsBounds, FieldVar}, prelude::*, - Assignment, Vec, + ToConstraintFieldGadget, Vec, }; +/// This struct is the `R1CS` equivalent of the quadratic extension field type +/// in `algebra-core`, i.e. `algebra_core::QuadExtField`. #[derive(Derivative)] #[derivative(Debug(bound = "BF: core::fmt::Debug"), Clone(bound = "BF: Clone"))] #[must_use] @@ -18,17 +21,22 @@ pub struct QuadExtVar, P: QuadExtV where for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, { + /// The zero-th coefficient of this field element. pub c0: BF, + /// The first coefficient of this field element. pub c1: BF, #[derivative(Debug = "ignore")] _params: PhantomData

, } +/// This trait describes parameters that are used to implement arithmetic for `QuadExtVar`. pub trait QuadExtVarParams>: QuadExtParameters where for<'a> &'a BF: FieldOpsBounds<'a, Self::BaseField, BF>, { + /// Multiply the base field of the `QuadExtVar` by the appropriate Frobenius coefficient. + /// This is equivalent to `Self::mul_base_field_by_frob_coeff(power)`. fn mul_base_field_var_by_frob_coeff(fe: &mut BF, power: usize); } @@ -36,6 +44,7 @@ impl, P: QuadExtVarParams> Qua where for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, { + /// Constructs a `QuadExtVar` from the underlying coefficients. pub fn new(c0: BF, c1: BF) -> Self { Self { c0, @@ -44,12 +53,14 @@ where } } - /// Multiply a BF by quadratic nonresidue P::NONRESIDUE. + /// Multiplies a variable of the base field by the quadratic nonresidue `P::NONRESIDUE` that + /// is used to construct the extension field. #[inline] pub fn mul_base_field_by_nonresidue(fe: &BF) -> Result { Ok(fe * P::NONRESIDUE) } + /// Multiplies `self` by a constant from the base field. #[inline] pub fn mul_by_base_field_constant(&self, fe: P::BaseField) -> Self { let c0 = self.c0.clone() * fe; @@ -57,6 +68,7 @@ where QuadExtVar::new(c0, c1) } + /// Sets `self = self.mul_by_base_field_constant(fe)`. #[inline] pub fn mul_assign_by_base_field_constant(&mut self, fe: P::BaseField) { *self = (&*self).mul_by_base_field_constant(fe); @@ -110,7 +122,7 @@ where { type Value = QuadExtField

; - fn cs(&self) -> Option> { + fn cs(&self) -> ConstraintSystemRef { [&self.c0, &self.c1].cs() } @@ -261,12 +273,20 @@ where #[tracing::instrument(target = "r1cs")] fn inverse(&self) -> Result { - let one = Self::new_constant(self.cs().get()?.clone(), QuadExtField::one())?; - let inverse = Self::new_witness(self.cs().get()?.clone(), || { - self.value() - .map(|f| f.inverse().unwrap_or(QuadExtField::zero())) - })?; - self.mul_equals(&inverse, &one)?; + let mode = if self.is_constant() { + AllocationMode::Constant + } else { + AllocationMode::Witness + }; + let inverse = Self::new_variable( + self.cs(), + || { + self.value() + .map(|f| f.inverse().unwrap_or(QuadExtField::zero())) + }, + mode, + )?; + self.mul_equals(&inverse, &Self::one())?; Ok(inverse) } } @@ -430,6 +450,24 @@ where } } +impl ToConstraintFieldGadget for QuadExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: QuadExtVarParams, + BF: ToConstraintFieldGadget, +{ + #[tracing::instrument(target = "r1cs")] + fn to_constraint_field(&self) -> Result>, SynthesisError> { + let mut res = Vec::new(); + + res.extend_from_slice(&self.c0.to_constraint_field()?); + res.extend_from_slice(&self.c1.to_constraint_field()?); + + Ok(res) + } +} + impl CondSelectGadget for QuadExtVar where BF: FieldVar, diff --git a/r1cs-std/src/groups/curves/mod.rs b/r1cs-std/src/groups/curves/mod.rs index 11cea997b..b0c12e97d 100644 --- a/r1cs-std/src/groups/curves/mod.rs +++ b/r1cs-std/src/groups/curves/mod.rs @@ -1,2 +1,9 @@ +/// This module generically implements arithmetic for Short +/// Weierstrass elliptic curves by following the complete formulae of +/// [[Renes, Costello, Batina 2015]](https://eprint.iacr.org/2015/1060). pub mod short_weierstrass; + +/// This module generically implements arithmetic for Twisted +/// Edwards elliptic curves by following the complete formulae described in the +/// [EFD](https://www.hyperelliptic.org/EFD/g1p/auto-twisted.html). pub mod twisted_edwards; diff --git a/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs index 0a4a8482a..384ac7607 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs @@ -16,19 +16,29 @@ use crate::{ use core::fmt::Debug; +/// Represents a projective point in G1. pub type G1Var

= ProjectiveVar<

::G1Parameters, FpVar<

::Fp>>; + +/// Represents an affine point on G1. Should be used only for comparison and when +/// a canonical representation of a point is required, and not for arithmetic. pub type G1AffineVar

= AffineVar<

::G1Parameters, FpVar<

::Fp>>; +/// Represents a projective point in G2. pub type G2Var

= ProjectiveVar<

::G2Parameters, Fp2G

>; +/// Represents an affine point on G2. Should be used only for comparison and when +/// a canonical representation of a point is required, and not for arithmetic. pub type G2AffineVar

= AffineVar<

::G2Parameters, Fp2G

>; +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. #[derive(Derivative)] #[derivative(Clone(bound = "G1Var

: Clone"), Debug(bound = "G1Var

: Debug"))] pub struct G1PreparedVar(pub AffineVar>); impl G1PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint system. pub fn value(&self) -> Result, SynthesisError> { let x = self.0.x.value()?; let y = self.0.y.value()?; @@ -37,6 +47,7 @@ impl G1PreparedVar

{ Ok(g.into()) } + /// Constructs `Self` from a `G1Var`. pub fn from_group_var(q: &G1Var

) -> Result { let g = q.to_affine()?; Ok(Self(g)) @@ -90,12 +101,15 @@ impl ToBytesGadget for G1PreparedVar

{ type Fp2G

= Fp2Var<

::Fp2Params>; type LCoeff

= (Fp2G

, Fp2G

); +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. #[derive(Derivative)] #[derivative( Clone(bound = "Fp2Var: Clone"), Debug(bound = "Fp2Var: Debug") )] pub struct G2PreparedVar { + #[doc(hidden)] pub ell_coeffs: Vec>, } @@ -165,6 +179,7 @@ impl ToBytesGadget for G2PreparedVar

{ } impl G2PreparedVar

{ + /// Constructs `Self` from a `G2Var`. #[tracing::instrument(target = "r1cs")] pub fn from_group_var(q: &G2Var

) -> Result { let q = q.to_affine()?; diff --git a/r1cs-std/src/groups/curves/short_weierstrass/mnt4/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/mnt4/mod.rs index c8f244540..e78d96752 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/mnt4/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/mnt4/mod.rs @@ -16,17 +16,25 @@ use crate::{ }; use core::borrow::Borrow; +/// Represents a projective point in G1. pub type G1Var

= ProjectiveVar<

::G1Parameters, FpVar<

::Fp>>; +/// Represents a projective point in G2. pub type G2Var

= ProjectiveVar<

::G2Parameters, Fp2G

>; +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] pub struct G1PreparedVar { + #[doc(hidden)] pub x: FpVar, + #[doc(hidden)] pub y: FpVar, + #[doc(hidden)] pub x_twist: Fp2Var, + #[doc(hidden)] pub y_twist: Fp2Var, } @@ -64,6 +72,7 @@ impl AllocVar, P::Fp> for G1PreparedVar

{ } impl G1PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint system. pub fn value(&self) -> Result, SynthesisError> { let (x, y, x_twist, y_twist) = ( self.x.value()?, @@ -79,6 +88,7 @@ impl G1PreparedVar

{ }) } + /// Constructs `Self` from a `G1Var`. #[tracing::instrument(target = "r1cs")] pub fn from_group_var(q: &G1Var

) -> Result { let q = q.to_affine()?; @@ -124,14 +134,22 @@ impl ToBytesGadget for G1PreparedVar

{ type Fp2G

= Fp2Var<

::Fp2Params>; +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] pub struct G2PreparedVar { + #[doc(hidden)] pub x: Fp2Var, + #[doc(hidden)] pub y: Fp2Var, + #[doc(hidden)] pub x_over_twist: Fp2Var, + #[doc(hidden)] pub y_over_twist: Fp2Var, + #[doc(hidden)] pub double_coefficients: Vec>, + #[doc(hidden)] pub addition_coefficients: Vec>, } @@ -225,6 +243,7 @@ impl ToBytesGadget for G2PreparedVar

{ } impl G2PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint system. pub fn value(&self) -> Result, SynthesisError> { let x = self.x.value()?; let y = self.y.value()?; @@ -250,6 +269,7 @@ impl G2PreparedVar

{ }) } + /// Constructs `Self` from a `G2Var`. #[tracing::instrument(target = "r1cs")] pub fn from_group_var(q: &G2Var

) -> Result { let twist_inv = P::TWIST.inverse().unwrap(); @@ -320,6 +340,7 @@ impl G2PreparedVar

{ } } +#[doc(hidden)] #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] pub struct AteDoubleCoefficientsVar { @@ -385,6 +406,7 @@ impl ToBytesGadget for AteDoubleCoefficientsVar

{ } impl AteDoubleCoefficientsVar

{ + /// Returns the value assigned to `self` in the underlying constraint system. pub fn value(&self) -> Result, SynthesisError> { let (c_h, c_4c, c_j, c_l) = ( self.c_l.value()?, @@ -401,6 +423,7 @@ impl AteDoubleCoefficientsVar

{ } } +#[doc(hidden)] #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT4Parameters"), Debug(bound = "P: MNT4Parameters"))] pub struct AteAdditionCoefficientsVar { @@ -451,12 +474,14 @@ impl ToBytesGadget for AteAdditionCoefficientsVar

{ } impl AteAdditionCoefficientsVar

{ + /// Returns the value assigned to `self` in the underlying constraint system. pub fn value(&self) -> Result, SynthesisError> { let (c_l1, c_rz) = (self.c_l1.value()?, self.c_rz.value()?); Ok(AteAdditionCoefficients { c_l1, c_rz }) } } +#[doc(hidden)] pub struct G2ProjectiveExtendedVar { pub x: Fp2Var, pub y: Fp2Var, diff --git a/r1cs-std/src/groups/curves/short_weierstrass/mnt6/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/mnt6/mod.rs index a0d9616f7..6562928f0 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/mnt6/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/mnt6/mod.rs @@ -16,21 +16,30 @@ use crate::{ }; use core::borrow::Borrow; +/// Represents a projective point in G1. pub type G1Var

= ProjectiveVar<

::G1Parameters, FpVar<

::Fp>>; +/// Represents a projective point in G2. pub type G2Var

= ProjectiveVar<

::G2Parameters, Fp3G

>; +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] pub struct G1PreparedVar { + #[doc(hidden)] pub x: FpVar, + #[doc(hidden)] pub y: FpVar, + #[doc(hidden)] pub x_twist: Fp3Var, + #[doc(hidden)] pub y_twist: Fp3Var, } impl G1PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint system. pub fn value(&self) -> Result, SynthesisError> { let x = self.x.value()?; let y = self.y.value()?; @@ -44,6 +53,7 @@ impl G1PreparedVar

{ }) } + /// Constructs `Self` from a `G1Var`. #[tracing::instrument(target = "r1cs")] pub fn from_group_var(q: &G1Var

) -> Result { let q = q.to_affine()?; @@ -123,14 +133,23 @@ impl ToBytesGadget for G1PreparedVar

{ } type Fp3G

= Fp3Var<

::Fp3Params>; + +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] pub struct G2PreparedVar { + #[doc(hidden)] pub x: Fp3Var, + #[doc(hidden)] pub y: Fp3Var, + #[doc(hidden)] pub x_over_twist: Fp3Var, + #[doc(hidden)] pub y_over_twist: Fp3Var, + #[doc(hidden)] pub double_coefficients: Vec>, + #[doc(hidden)] pub addition_coefficients: Vec>, } @@ -224,6 +243,7 @@ impl ToBytesGadget for G2PreparedVar

{ } impl G2PreparedVar

{ + /// Returns the value assigned to `self` in the underlying constraint system. pub fn value(&self) -> Result, SynthesisError> { let x = self.x.value()?; let y = self.y.value()?; @@ -249,6 +269,7 @@ impl G2PreparedVar

{ }) } + /// Constructs `Self` from a `G2Var`. #[tracing::instrument(target = "r1cs")] pub fn from_group_var(q: &G2Var

) -> Result { let q = q.to_affine()?; @@ -319,6 +340,7 @@ impl G2PreparedVar

{ } } +#[doc(hidden)] #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] pub struct AteDoubleCoefficientsVar { @@ -384,6 +406,7 @@ impl ToBytesGadget for AteDoubleCoefficientsVar

{ } impl AteDoubleCoefficientsVar

{ + /// Returns the value assigned to `self` in the underlying constraint system. pub fn value(&self) -> Result, SynthesisError> { let c_h = self.c_h.value()?; let c_4c = self.c_4c.value()?; @@ -398,6 +421,7 @@ impl AteDoubleCoefficientsVar

{ } } +#[doc(hidden)] #[derive(Derivative)] #[derivative(Clone(bound = "P: MNT6Parameters"), Debug(bound = "P: MNT6Parameters"))] pub struct AteAdditionCoefficientsVar { @@ -448,6 +472,7 @@ impl ToBytesGadget for AteAdditionCoefficientsVar

{ } impl AteAdditionCoefficientsVar

{ + /// Returns the value assigned to `self` in the underlying constraint system. pub fn value(&self) -> Result, SynthesisError> { let c_l1 = self.c_l1.value()?; let c_rz = self.c_rz.value()?; @@ -455,6 +480,7 @@ impl AteAdditionCoefficientsVar

{ } } +#[doc(hidden)] pub struct G2ProjectiveExtendedVar { pub x: Fp3Var, pub y: Fp3Var, diff --git a/r1cs-std/src/groups/curves/short_weierstrass/mod.rs b/r1cs-std/src/groups/curves/short_weierstrass/mod.rs index 70be5403f..b73470e1e 100644 --- a/r1cs-std/src/groups/curves/short_weierstrass/mod.rs +++ b/r1cs-std/src/groups/curves/short_weierstrass/mod.rs @@ -8,10 +8,20 @@ use algebra::{ use core::{borrow::Borrow, marker::PhantomData}; use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError}; -use crate::{prelude::*, Vec}; +use crate::fields::fp::FpVar; +use crate::{prelude::*, ToConstraintFieldGadget, Vec}; +/// This module provides a generic implementation of G1 and G2 for +/// the [[BLS12]](https://eprint.iacr.org/2002/088.pdf) family of bilinear groups. pub mod bls12; + +/// This module provides a generic implementation of G1 and G2 for +/// the [[MNT4]](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.20.8113&rep=rep1&type=pdf) +/// family of bilinear groups. pub mod mnt4; +/// This module provides a generic implementation of G1 and G2 for +/// the [[MNT6]](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.20.8113&rep=rep1&type=pdf) +/// family of bilinear groups. pub mod mnt6; /// An implementation of arithmetic for Short Weierstrass curves that relies on @@ -71,6 +81,8 @@ where } } + /// Returns the value assigned to `self` in the underlying + /// constraint system. pub fn value(&self) -> Result, SynthesisError> { Ok(SWAffine::new( self.x.value()?, @@ -80,6 +92,25 @@ where } } +impl ToConstraintFieldGadget<::BasePrimeField> for AffineVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, + F: ToConstraintFieldGadget<::BasePrimeField>, +{ + fn to_constraint_field( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + let mut res = Vec::::BasePrimeField>>::new(); + + res.extend_from_slice(&self.x.to_constraint_field()?); + res.extend_from_slice(&self.y.to_constraint_field()?); + + Ok(res) + } +} + impl R1CSVar<::BasePrimeField> for ProjectiveVar where P: SWModelParameters, @@ -88,7 +119,7 @@ where { type Value = SWProjective

; - fn cs(&self) -> Option::BasePrimeField>> { + fn cs(&self) -> ConstraintSystemRef<::BasePrimeField> { self.x.cs().or(self.y.cs()).or(self.z.cs()) } @@ -108,6 +139,7 @@ impl::Ba where for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { + /// Constructs `Self` from an `(x, y, z)` coordinate triple. pub fn new(x: F, y: F, z: F) -> Self { Self { x, @@ -120,9 +152,13 @@ where /// Convert this point into affine form. #[tracing::instrument(target = "r1cs")] pub fn to_affine(&self) -> Result, SynthesisError> { - let cs = self.cs().unwrap_or(ConstraintSystemRef::None); + let cs = self.cs(); let mode = if self.is_constant() { - AllocationMode::Constant + let point = self.value()?.into_affine(); + let x = F::new_constant(ConstraintSystemRef::None, point.x)?; + let y = F::new_constant(ConstraintSystemRef::None, point.y)?; + let infinity = Boolean::constant(point.infinity); + return Ok(AffineVar::new(x, y, infinity)); } else { AllocationMode::Witness }; diff --git a/r1cs-std/src/groups/curves/twisted_edwards/mod.rs b/r1cs-std/src/groups/curves/twisted_edwards/mod.rs index 30a1b3849..f8dc5ce27 100644 --- a/r1cs-std/src/groups/curves/twisted_edwards/mod.rs +++ b/r1cs-std/src/groups/curves/twisted_edwards/mod.rs @@ -8,10 +8,17 @@ use algebra::{ use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError}; -use crate::{prelude::*, Vec}; +use crate::{prelude::*, ToConstraintFieldGadget, Vec}; +use crate::fields::fp::FpVar; use core::{borrow::Borrow, marker::PhantomData}; +/// An implementation of arithmetic for Montgomery curves that relies on +/// incomplete addition formulae for the affine model, as outlined in the +/// [EFD](https://www.hyperelliptic.org/EFD/g1p/auto-montgom.html). +/// +/// This is intended for use primarily for implementing efficient +/// multi-scalar-multiplication in the Bowe-Hopwood-Pedersen hash. #[derive(Derivative)] #[derivative(Debug, Clone)] #[must_use] @@ -21,7 +28,9 @@ pub struct MontgomeryAffineVar< > where for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { + /// The x-coordinate. pub x: F, + /// The y-coordinate. pub y: F, #[derivative(Debug = "ignore")] _params: PhantomData

, @@ -40,7 +49,7 @@ mod montgomery_affine_impl { { type Value = (P::BaseField, P::BaseField); - fn cs(&self) -> Option::BasePrimeField>> { + fn cs(&self) -> ConstraintSystemRef<::BasePrimeField> { self.x.cs().or(self.y.cs()) } @@ -58,6 +67,7 @@ mod montgomery_affine_impl { where for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { + /// Constructs `Self` from an `(x, y)` coordinate pair. pub fn new(x: F, y: F) -> Self { Self { x, @@ -66,6 +76,8 @@ mod montgomery_affine_impl { } } + /// Converts a Twisted Edwards curve point to coordinates for the corresponding affine + /// Montgomery curve point. #[tracing::instrument(target = "r1cs")] pub fn from_edwards_to_coords( p: &TEAffine

, @@ -84,6 +96,8 @@ mod montgomery_affine_impl { Ok((montgomery_point.x, montgomery_point.y)) } + /// Converts a Twisted Edwards curve point to coordinates for the corresponding affine + /// Montgomery curve point. #[tracing::instrument(target = "r1cs")] pub fn new_witness_from_edwards( cs: ConstraintSystemRef<::BasePrimeField>, @@ -95,9 +109,10 @@ mod montgomery_affine_impl { Ok(Self::new(u, v)) } + /// Converts `self` into a Twisted Edwards curve point variable. #[tracing::instrument(target = "r1cs")] pub fn into_edwards(&self) -> Result, SynthesisError> { - let cs = self.cs().unwrap_or(ConstraintSystemRef::None); + let cs = self.cs(); // Compute u = x / y let u = F::new_witness(r1cs_core::ns!(cs, "u"), || { let y_inv = self @@ -138,12 +153,11 @@ mod montgomery_affine_impl { #[tracing::instrument(target = "r1cs")] fn add(self, other: &'a Self) -> Self::Output { let cs = [&self, other].cs(); - let mode = if cs.is_none() || matches!(cs, Some(ConstraintSystemRef::None)) { + let mode = if cs.is_none() { AllocationMode::Constant } else { AllocationMode::Witness }; - let cs = cs.unwrap_or(ConstraintSystemRef::None); let coeff_b = P::MontgomeryModelParameters::COEFF_B; let coeff_a = P::MontgomeryModelParameters::COEFF_A; @@ -198,6 +212,9 @@ mod montgomery_affine_impl { } } +/// An implementation of arithmetic for Twisted Edwards curves that relies on +/// the complete formulae for the affine model, as outlined in the +/// [EFD](https://www.hyperelliptic.org/EFD/g1p/auto-twisted.html). #[derive(Derivative)] #[derivative(Debug, Clone)] #[must_use] @@ -207,7 +224,9 @@ pub struct AffineVar< > where for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { + /// The x-coordinate. pub x: F, + /// The y-coordinate. pub y: F, #[derivative(Debug = "ignore")] _params: PhantomData

, @@ -218,6 +237,7 @@ impl::Ba where for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { + /// Constructs `Self` from an `(x, y)` coordinate triple. pub fn new(x: F, y: F) -> Self { Self { x, @@ -256,6 +276,99 @@ where } } +impl::BasePrimeField>> + AffineVar +where + P: TEModelParameters, + F: FieldVar::BasePrimeField> + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> + + ThreeBitCondNegLookupGadget< + ::BasePrimeField, + TableConstant = P::BaseField, + >, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, +{ + /// Compute a scalar multiplication of `bases` with respect to `scalars`, + /// where the elements of `scalars` are length-three slices of bits, and which + /// such that the first two bits are use to select one of the bases, + /// while the third bit is used to conditionally negate the selection. + #[tracing::instrument(target = "r1cs", skip(bases, scalars))] + pub fn precomputed_base_3_bit_signed_digit_scalar_mul( + bases: &[impl Borrow<[TEProjective

]>], + scalars: &[impl Borrow<[J]>], + ) -> Result + where + J: Borrow<[Boolean<::BasePrimeField>]>, + { + const CHUNK_SIZE: usize = 3; + let mut ed_result: Option> = None; + let mut result: Option> = None; + + let mut process_segment_result = |result: &MontgomeryAffineVar| { + let sgmt_result = result.into_edwards()?; + ed_result = match ed_result.as_ref() { + None => Some(sgmt_result), + Some(r) => Some(sgmt_result + r), + }; + Ok::<(), SynthesisError>(()) + }; + + // Compute ∏(h_i^{m_i}) for all i. + for (segment_bits_chunks, segment_powers) in scalars.iter().zip(bases) { + for (bits, base_power) in segment_bits_chunks + .borrow() + .iter() + .zip(segment_powers.borrow()) + { + let base_power = base_power.borrow(); + let mut acc_power = *base_power; + let mut coords = vec![]; + for _ in 0..4 { + coords.push(acc_power); + acc_power += base_power; + } + + let bits = bits.borrow().to_bits_le()?; + if bits.len() != CHUNK_SIZE { + return Err(SynthesisError::Unsatisfiable); + } + + let coords = coords + .iter() + .map(|p| MontgomeryAffineVar::from_edwards_to_coords(&p.into_affine())) + .collect::, _>>()?; + + let x_coeffs = coords.iter().map(|p| p.0).collect::>(); + let y_coeffs = coords.iter().map(|p| p.1).collect::>(); + + let precomp = bits[0].and(&bits[1])?; + + let x = F::zero() + + x_coeffs[0] + + F::from(bits[0].clone()) * (x_coeffs[1] - &x_coeffs[0]) + + F::from(bits[1].clone()) * (x_coeffs[2] - &x_coeffs[0]) + + F::from(precomp.clone()) + * (x_coeffs[3] - &x_coeffs[2] - &x_coeffs[1] + &x_coeffs[0]); + + let y = F::three_bit_cond_neg_lookup(&bits, &precomp, &y_coeffs)?; + + let tmp = MontgomeryAffineVar::new(x, y); + result = match result.as_ref() { + None => Some(tmp), + Some(r) => Some(tmp + r), + }; + } + + process_segment_result(&result.unwrap())?; + result = None; + } + if result.is_some() { + process_segment_result(&result.unwrap())?; + } + Ok(ed_result.unwrap()) + } +} + impl R1CSVar<::BasePrimeField> for AffineVar where P: TEModelParameters, @@ -264,7 +377,7 @@ where { type Value = TEProjective

; - fn cs(&self) -> Option::BasePrimeField>> { + fn cs(&self) -> ConstraintSystemRef<::BasePrimeField> { self.x.cs().or(self.y.cs()) } @@ -280,11 +393,7 @@ impl CurveVar, ::BasePrimeField> fo where P: TEModelParameters, F: FieldVar::BasePrimeField> - + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> - + ThreeBitCondNegLookupGadget< - ::BasePrimeField, - TableConstant = P::BaseField, - >, + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { fn constant(g: TEProjective

) -> Self { @@ -351,7 +460,11 @@ where #[inline] #[tracing::instrument(target = "r1cs")] fn double_in_place(&mut self) -> Result<(), SynthesisError> { - if let Some(cs) = self.cs() { + if self.is_constant() { + let value = self.value()?; + *self = Self::constant(value.double()); + } else { + let cs = self.cs(); let a = P::COEFF_A; // xy @@ -386,9 +499,6 @@ where y3.mul_equals(&two_minus_ax2_minus_y2, &y2_minus_a_x2)?; self.x = x3; self.y = y3; - } else { - let value = self.value()?; - *self = Self::constant(value.double()); } Ok(()) } @@ -440,95 +550,13 @@ where Ok(()) } - - #[tracing::instrument(target = "r1cs", skip(bases, scalars))] - fn precomputed_base_3_bit_signed_digit_scalar_mul<'a, I, J, B>( - bases: &[B], - scalars: &[J], - ) -> Result - where - I: Borrow<[Boolean<::BasePrimeField>]>, - J: Borrow<[I]>, - B: Borrow<[TEProjective

]>, - { - const CHUNK_SIZE: usize = 3; - let mut ed_result: Option> = None; - let mut result: Option> = None; - - let mut process_segment_result = |result: &MontgomeryAffineVar| { - let sgmt_result = result.into_edwards()?; - ed_result = match ed_result.as_ref() { - None => Some(sgmt_result), - Some(r) => Some(sgmt_result + r), - }; - Ok::<(), SynthesisError>(()) - }; - - // Compute ∏(h_i^{m_i}) for all i. - for (segment_bits_chunks, segment_powers) in scalars.iter().zip(bases) { - for (bits, base_power) in segment_bits_chunks - .borrow() - .iter() - .zip(segment_powers.borrow()) - { - let base_power = base_power.borrow(); - let mut acc_power = *base_power; - let mut coords = vec![]; - for _ in 0..4 { - coords.push(acc_power); - acc_power += base_power; - } - - let bits = bits.borrow().to_bits_le()?; - if bits.len() != CHUNK_SIZE { - return Err(SynthesisError::Unsatisfiable); - } - - let coords = coords - .iter() - .map(|p| MontgomeryAffineVar::from_edwards_to_coords(&p.into_affine())) - .collect::, _>>()?; - - let x_coeffs = coords.iter().map(|p| p.0).collect::>(); - let y_coeffs = coords.iter().map(|p| p.1).collect::>(); - - let precomp = bits[0].and(&bits[1])?; - - let x = F::zero() - + x_coeffs[0] - + F::from(bits[0].clone()) * (x_coeffs[1] - &x_coeffs[0]) - + F::from(bits[1].clone()) * (x_coeffs[2] - &x_coeffs[0]) - + F::from(precomp.clone()) - * (x_coeffs[3] - &x_coeffs[2] - &x_coeffs[1] + &x_coeffs[0]); - - let y = F::three_bit_cond_neg_lookup(&bits, &precomp, &y_coeffs)?; - - let tmp = MontgomeryAffineVar::new(x, y); - result = match result.as_ref() { - None => Some(tmp), - Some(r) => Some(tmp + r), - }; - } - - process_segment_result(&result.unwrap())?; - result = None; - } - if result.is_some() { - process_segment_result(&result.unwrap())?; - } - Ok(ed_result.unwrap()) - } } impl AllocVar, ::BasePrimeField> for AffineVar where P: TEModelParameters, F: FieldVar::BasePrimeField> - + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> - + ThreeBitCondNegLookupGadget< - ::BasePrimeField, - TableConstant = P::BaseField, - >, + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { #[tracing::instrument(target = "r1cs", skip(cs, f))] @@ -629,11 +657,7 @@ impl AllocVar, ::BasePrimeField> for Af where P: TEModelParameters, F: FieldVar::BasePrimeField> - + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> - + ThreeBitCondNegLookupGadget< - ::BasePrimeField, - TableConstant = P::BaseField, - >, + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, { #[tracing::instrument(target = "r1cs", skip(cs, f))] @@ -646,6 +670,25 @@ where } } +impl ToConstraintFieldGadget<::BasePrimeField> for AffineVar +where + P: TEModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, + F: ToConstraintFieldGadget<::BasePrimeField>, +{ + fn to_constraint_field( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + let mut res = Vec::new(); + + res.extend_from_slice(&self.x.to_constraint_field()?); + res.extend_from_slice(&self.y.to_constraint_field()?); + + Ok(res) + } +} + #[inline] fn div2(limbs: &mut [u64]) { let mut t = 0; @@ -665,7 +708,12 @@ impl_bounded_ops!( AddAssign, add_assign, |this: &'a AffineVar, other: &'a AffineVar| { - if let Some(cs) = [this, other].cs() { + + if [this, other].is_constant() { + assert!(this.is_constant() && other.is_constant()); + AffineVar::constant(this.value().unwrap() + &other.value().unwrap()) + } else { + let cs = [this, other].cs(); let a = P::COEFF_A; let d = P::COEFF_D; @@ -709,16 +757,12 @@ impl_bounded_ops!( y3.mul_equals(&one_minus_v2, &u_plus_a_v0_minus_v1).unwrap(); AffineVar::new(x3, y3) - } else { - assert!(this.is_constant() && other.is_constant()); - AffineVar::constant(this.value().unwrap() + &other.value().unwrap()) } }, |this: &'a AffineVar, other: TEProjective

| this + AffineVar::constant(other), ( F :FieldVar::BasePrimeField> - + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> - + ThreeBitCondNegLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, P: TEModelParameters, ), for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, @@ -735,8 +779,7 @@ impl_bounded_ops!( |this: &'a AffineVar, other: TEProjective

| this - AffineVar::constant(other), ( F :FieldVar::BasePrimeField> - + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> - + ThreeBitCondNegLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, P: TEModelParameters, ), for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F> @@ -746,11 +789,7 @@ impl<'a, P, F> GroupOpsBounds<'a, TEProjective

, AffineVar> for AffineVa where P: TEModelParameters, F: FieldVar::BasePrimeField> - + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> - + ThreeBitCondNegLookupGadget< - ::BasePrimeField, - TableConstant = P::BaseField, - >, + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, { } @@ -759,11 +798,7 @@ impl<'a, P, F> GroupOpsBounds<'a, TEProjective

, AffineVar> for &'a Affi where P: TEModelParameters, F: FieldVar::BasePrimeField> - + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField> - + ThreeBitCondNegLookupGadget< - ::BasePrimeField, - TableConstant = P::BaseField, - >, + + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, { } diff --git a/r1cs-std/src/groups/mod.rs b/r1cs-std/src/groups/mod.rs index 13e81dabd..a2682ef3e 100644 --- a/r1cs-std/src/groups/mod.rs +++ b/r1cs-std/src/groups/mod.rs @@ -5,6 +5,7 @@ use r1cs_core::{Namespace, SynthesisError}; use core::{borrow::Borrow, fmt::Debug}; +/// This module contains implementations of arithmetic for various curve models. pub mod curves; pub use self::curves::short_weierstrass::bls12; @@ -23,6 +24,8 @@ pub trait GroupOpsBounds<'a, F, T: 'a>: { } +/// A variable that represents a curve point for +/// the curve `C`. pub trait CurveVar: 'static + Sized @@ -43,17 +46,23 @@ pub trait CurveVar: + AddAssign + SubAssign { - fn constant(other: C) -> Self; - + /// Returns the constant `F::zero()`. This is the identity + /// of the group. fn zero() -> Self; + /// Returns a `Boolean` representing whether `self == Self::zero()`. #[tracing::instrument(target = "r1cs")] fn is_zero(&self) -> Result, SynthesisError> { self.is_eq(&Self::zero()) } - /// Allocate a variable in the subgroup without checking if it's in the - /// prime-order subgroup + /// Returns a constant with value `v`. + /// + /// This *should not* allocate any variables. + fn constant(other: C) -> Self; + + /// Allocates a variable in the subgroup without checking if it's in the + /// prime-order subgroup. fn new_variable_omit_prime_order_check( cs: impl Into>, f: impl FnOnce() -> Result, @@ -63,6 +72,7 @@ pub trait CurveVar: /// Enforce that `self` is in the prime-order subgroup. fn enforce_prime_order(&self) -> Result<(), SynthesisError>; + /// Computes `self + self`. #[tracing::instrument(target = "r1cs")] fn double(&self) -> Result { let mut result = self.clone(); @@ -70,8 +80,10 @@ pub trait CurveVar: Ok(result) } + /// Sets `self = self + self`. fn double_in_place(&mut self) -> Result<(), SynthesisError>; + /// Coputes `-self`. fn negate(&self) -> Result; /// Computes `bits * self`, where `bits` is a little-endian @@ -113,20 +125,7 @@ pub trait CurveVar: Ok(()) } - #[tracing::instrument(target = "r1cs")] - fn precomputed_base_3_bit_signed_digit_scalar_mul<'a, I, J, B>( - _: &[B], - _: &[J], - ) -> Result - where - I: Borrow<[Boolean]>, - J: Borrow<[I]>, - B: Borrow<[C]>, - { - Err(SynthesisError::AssignmentMissing) - } - - /// Computes a `\sum I_j * B_j`, where `I_j` is a `Boolean` + /// Computes a `\sum_j I_j * B_j`, where `I_j` is a `Boolean` /// representation of the j-th scalar. #[tracing::instrument(target = "r1cs", skip(bases, scalars))] fn precomputed_base_multiscalar_mul_le<'a, T, I, B>( diff --git a/r1cs-std/src/instantiated/bls12_377/curves.rs b/r1cs-std/src/instantiated/bls12_377/curves.rs index 0cba94ffc..e0b86d37d 100644 --- a/r1cs-std/src/instantiated/bls12_377/curves.rs +++ b/r1cs-std/src/instantiated/bls12_377/curves.rs @@ -1,10 +1,16 @@ use crate::groups::bls12; use algebra::bls12_377::Parameters; +/// An element of G1 in the BLS12-377 bilinear group. pub type G1Var = bls12::G1Var; +/// An element of G2 in the BLS12-377 bilinear group. pub type G2Var = bls12::G2Var; +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. pub type G1PreparedVar = bls12::G1PreparedVar; +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. pub type G2PreparedVar = bls12::G2PreparedVar; #[test] diff --git a/r1cs-std/src/instantiated/bls12_377/fields.rs b/r1cs-std/src/instantiated/bls12_377/fields.rs index 127e362fc..f64a281f6 100644 --- a/r1cs-std/src/instantiated/bls12_377/fields.rs +++ b/r1cs-std/src/instantiated/bls12_377/fields.rs @@ -2,9 +2,14 @@ use algebra::bls12_377::{Fq, Fq12Parameters, Fq2Parameters, Fq6Parameters}; use crate::fields::{fp::FpVar, fp12::Fp12Var, fp2::Fp2Var, fp6_3over2::Fp6Var}; +/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq`. pub type FqVar = FpVar; + +/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq2`. pub type Fq2Var = Fp2Var; +/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq6`. pub type Fq6Var = Fp6Var; +/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq12`. pub type Fq12Var = Fp12Var; #[test] diff --git a/r1cs-std/src/instantiated/bls12_377/mod.rs b/r1cs-std/src/instantiated/bls12_377/mod.rs index 5e10f69ff..d1df08d68 100644 --- a/r1cs-std/src/instantiated/bls12_377/mod.rs +++ b/r1cs-std/src/instantiated/bls12_377/mod.rs @@ -1,3 +1,153 @@ +//! This module implements the R1CS equivalent of `algebra::bls12_377`. +//! +//! It implements field variables for `algebra::bls12_377::{Fq, Fq2, Fq6, Fq12}`, +//! group variables for `algebra::bls12_377::{G1, G2}`, and implements constraint +//! generation for computing `Bls12_377::pairing`. +//! +//! The field underlying these constraints is `algebra::bls12_377::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, bls12_377::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::bls12_377::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `G1Var` and `G2Var`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, bls12_377::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::bls12_377::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `G1` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G1Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `G1`. +//! let zero = G1Var::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! Finally, one can check pairing computations as well: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, PairingEngine, bls12_377::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::bls12_377::{self, *}; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate random `G1` and `G2` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G2Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let pairing_result_native = Bls12_377::pairing(a_native, b_native); +//! +//! // Prepare `a` and `b` for pairing. +//! let a_prep = bls12_377::PairingVar::prepare_g1(&a)?; +//! let b_prep = bls12_377::PairingVar::prepare_g2(&b)?; +//! let pairing_result = bls12_377::PairingVar::pairing(a_prep, b_prep)?; +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!(pairing_result.value()?, pairing_result_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! let a_prep_const = bls12_377::PairingVar::prepare_g1(&a_const)?; +//! let b_prep_const = bls12_377::PairingVar::prepare_g2(&b_const)?; +//! let pairing_result_const = bls12_377::PairingVar::pairing(a_prep_const, b_prep_const)?; +//! println!("Done here 3"); +//! +//! pairing_result.enforce_equal(&pairing_result_const)?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; mod pairing; diff --git a/r1cs-std/src/instantiated/bls12_377/pairing.rs b/r1cs-std/src/instantiated/bls12_377/pairing.rs index 1258aebc7..e9a80475d 100644 --- a/r1cs-std/src/instantiated/bls12_377/pairing.rs +++ b/r1cs-std/src/instantiated/bls12_377/pairing.rs @@ -1,5 +1,6 @@ use algebra::bls12_377::Parameters; +/// Specifies the constraints for computing a pairing in the BLS12-377 bilinear group. pub type PairingVar = crate::pairing::bls12::PairingVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_bls12_377/curves.rs b/r1cs-std/src/instantiated/ed_on_bls12_377/curves.rs index 16025d143..ed38d2137 100644 --- a/r1cs-std/src/instantiated/ed_on_bls12_377/curves.rs +++ b/r1cs-std/src/instantiated/ed_on_bls12_377/curves.rs @@ -3,6 +3,7 @@ use algebra::ed_on_bls12_377::*; use crate::ed_on_bls12_377::FqVar; +/// A variable that is the R1CS equivalent of `algebra::ed_on_bls12_377::EdwardsAffine`. pub type EdwardsVar = AffineVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_bls12_377/fields.rs b/r1cs-std/src/instantiated/ed_on_bls12_377/fields.rs index 18e3f938c..32011d1a8 100644 --- a/r1cs-std/src/instantiated/ed_on_bls12_377/fields.rs +++ b/r1cs-std/src/instantiated/ed_on_bls12_377/fields.rs @@ -1,6 +1,7 @@ use crate::fields::fp::FpVar; use algebra::ed_on_bls12_377::fq::Fq; +/// A variable that is the R1CS equivalent of `algebra::ed_on_bls12_377::Fq`. pub type FqVar = FpVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_bls12_377/mod.rs b/r1cs-std/src/instantiated/ed_on_bls12_377/mod.rs index cd5e75810..f7d3da3bf 100644 --- a/r1cs-std/src/instantiated/ed_on_bls12_377/mod.rs +++ b/r1cs-std/src/instantiated/ed_on_bls12_377/mod.rs @@ -1,3 +1,105 @@ +//! This module implements the R1CS equivalent of `algebra::ed_on_bls12_377`. +//! +//! It implements field variables for `algebra::ed_on_bls12_377::Fq`, +//! and group variables for `algebra::ed_on_bls12_377::GroupProjective`. +//! +//! The field underlying these constraints is `algebra::ed_on_bls12_377::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, ed_on_bls12_377::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::ed_on_bls12_377::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `EdwardsVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, ed_on_bls12_377::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::ed_on_bls12_377::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Edwards` elements. +//! let a_native = EdwardsProjective::rand(&mut rng); +//! let b_native = EdwardsProjective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity. +//! let zero = EdwardsVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; diff --git a/r1cs-std/src/instantiated/ed_on_bls12_381/curves.rs b/r1cs-std/src/instantiated/ed_on_bls12_381/curves.rs index 9ea875851..555f4ad12 100644 --- a/r1cs-std/src/instantiated/ed_on_bls12_381/curves.rs +++ b/r1cs-std/src/instantiated/ed_on_bls12_381/curves.rs @@ -3,6 +3,7 @@ use algebra::ed_on_bls12_381::*; use crate::ed_on_bls12_381::FqVar; +/// A variable that is the R1CS equivalent of `algebra::ed_on_bls12_381::EdwardsAffine`. pub type EdwardsVar = AffineVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_bls12_381/fields.rs b/r1cs-std/src/instantiated/ed_on_bls12_381/fields.rs index eb9b929a8..eaf367b54 100644 --- a/r1cs-std/src/instantiated/ed_on_bls12_381/fields.rs +++ b/r1cs-std/src/instantiated/ed_on_bls12_381/fields.rs @@ -1,5 +1,6 @@ use crate::fields::fp::FpVar; +/// A variable that is the R1CS equivalent of `algebra::ed_on_bls12_381::Fq`. pub type FqVar = FpVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_bls12_381/mod.rs b/r1cs-std/src/instantiated/ed_on_bls12_381/mod.rs index cd5e75810..8368f6dc7 100644 --- a/r1cs-std/src/instantiated/ed_on_bls12_381/mod.rs +++ b/r1cs-std/src/instantiated/ed_on_bls12_381/mod.rs @@ -1,3 +1,105 @@ +//! This module implements the R1CS equivalent of `algebra::ed_on_bls12_381`. +//! +//! It implements field variables for `algebra::ed_on_bls12_381::Fq`, +//! and group variables for `algebra::ed_on_bls12_381::GroupProjective`. +//! +//! The field underlying these constraints is `algebra::ed_on_bls12_381::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, ed_on_bls12_381::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::ed_on_bls12_381::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `EdwardsVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, ed_on_bls12_381::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::ed_on_bls12_381::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Edwards` elements. +//! let a_native = EdwardsProjective::rand(&mut rng); +//! let b_native = EdwardsProjective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `Edwards`. +//! let zero = EdwardsVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; diff --git a/r1cs-std/src/instantiated/ed_on_bn254/curves.rs b/r1cs-std/src/instantiated/ed_on_bn254/curves.rs index 386accedb..f3171a171 100644 --- a/r1cs-std/src/instantiated/ed_on_bn254/curves.rs +++ b/r1cs-std/src/instantiated/ed_on_bn254/curves.rs @@ -3,6 +3,7 @@ use algebra::ed_on_bn254::*; use crate::ed_on_bn254::FqVar; +/// A variable that is the R1CS equivalent of `algebra::ed_on_bn254::EdwardsAffine`. pub type EdwardsVar = AffineVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_bn254/fields.rs b/r1cs-std/src/instantiated/ed_on_bn254/fields.rs index 24e65be4c..45b30a031 100644 --- a/r1cs-std/src/instantiated/ed_on_bn254/fields.rs +++ b/r1cs-std/src/instantiated/ed_on_bn254/fields.rs @@ -1,5 +1,6 @@ use crate::fields::fp::FpVar; +/// A variable that is the R1CS equivalent of `algebra::ed_on_bn254::Fq`. pub type FqVar = FpVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_bn254/mod.rs b/r1cs-std/src/instantiated/ed_on_bn254/mod.rs index cd5e75810..c4facd775 100644 --- a/r1cs-std/src/instantiated/ed_on_bn254/mod.rs +++ b/r1cs-std/src/instantiated/ed_on_bn254/mod.rs @@ -1,3 +1,105 @@ +//! This module implements the R1CS equivalent of `algebra::ed_on_bn254`. +//! +//! It implements field variables for `algebra::ed_on_bn254::Fq`, +//! and group variables for `algebra::ed_on_bn254::GroupProjective`. +//! +//! The field underlying these constraints is `algebra::ed_on_bn254::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, ed_on_bn254::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::ed_on_bn254::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `EdwardsVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, ed_on_bn254::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::ed_on_bn254::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Edwards` elements. +//! let a_native = EdwardsProjective::rand(&mut rng); +//! let b_native = EdwardsProjective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `Edwards`. +//! let zero = EdwardsVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; diff --git a/r1cs-std/src/instantiated/ed_on_bw6_761/curves.rs b/r1cs-std/src/instantiated/ed_on_bw6_761/curves.rs deleted file mode 100644 index 7720aad26..000000000 --- a/r1cs-std/src/instantiated/ed_on_bw6_761/curves.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::groups::curves::twisted_edwards::AffineGadget; -use algebra::ed_on_cp6_782::*; - -use crate::ed_on_cp6_782::FqGadget; - -pub type EdwardsGadget = AffineGadget; - -#[test] -fn test() { - crate::groups::curves::twisted_edwards::test::<_, EdwardsParameters, EdwardsGadget>(); -} diff --git a/r1cs-std/src/instantiated/ed_on_bw6_761/fields.rs b/r1cs-std/src/instantiated/ed_on_bw6_761/fields.rs deleted file mode 100644 index 37b097244..000000000 --- a/r1cs-std/src/instantiated/ed_on_bw6_761/fields.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::fields::fp::FpGadget; -use algebra::ed_on_cp6_782::fq::Fq; - -pub type FqGadget = FpGadget; - -#[test] -fn test() { - crate::fields::tests::field_test::<_, _, Fq, FqGadget>(); -} diff --git a/r1cs-std/src/instantiated/ed_on_bw6_761/mod.rs b/r1cs-std/src/instantiated/ed_on_bw6_761/mod.rs index 8c475c055..73ae181a1 100644 --- a/r1cs-std/src/instantiated/ed_on_bw6_761/mod.rs +++ b/r1cs-std/src/instantiated/ed_on_bw6_761/mod.rs @@ -1 +1,103 @@ +//! This module implements the R1CS equivalent of `algebra::ed_on_bw6_761`. +//! +//! It implements field variables for `algebra::ed_on_bw6_761::Fq`, +//! and group variables for `algebra::ed_on_bw6_761::GroupProjective`. +//! +//! The field underlying these constraints is `algebra::ed_on_bw6_761::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, ed_on_bw6_761::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::ed_on_bw6_761::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `EdwardsVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, ed_on_bw6_761::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::ed_on_bw6_761::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Edwards` elements. +//! let a_native = EdwardsProjective::rand(&mut rng); +//! let b_native = EdwardsProjective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `Edwards`. +//! let zero = EdwardsVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + pub use crate::instantiated::ed_on_cp6_782::*; diff --git a/r1cs-std/src/instantiated/ed_on_cp6_782/curves.rs b/r1cs-std/src/instantiated/ed_on_cp6_782/curves.rs index df5e45ace..2c5d10130 100644 --- a/r1cs-std/src/instantiated/ed_on_cp6_782/curves.rs +++ b/r1cs-std/src/instantiated/ed_on_cp6_782/curves.rs @@ -3,6 +3,7 @@ use algebra::ed_on_cp6_782::*; use crate::instantiated::ed_on_cp6_782::FqVar; +/// A variable that is the R1CS equivalent of `algebra::ed_on_cp6_782::EdwardsAffine`. pub type EdwardsVar = AffineVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_cp6_782/fields.rs b/r1cs-std/src/instantiated/ed_on_cp6_782/fields.rs index 7722ff0f1..db4cf98ff 100644 --- a/r1cs-std/src/instantiated/ed_on_cp6_782/fields.rs +++ b/r1cs-std/src/instantiated/ed_on_cp6_782/fields.rs @@ -1,6 +1,7 @@ use crate::fields::fp::FpVar; use algebra::ed_on_cp6_782::fq::Fq; +/// A variable that is the R1CS equivalent of `algebra::ed_on_cp6_782::Fq`. pub type FqVar = FpVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_cp6_782/mod.rs b/r1cs-std/src/instantiated/ed_on_cp6_782/mod.rs index 0119106aa..24e43f68c 100644 --- a/r1cs-std/src/instantiated/ed_on_cp6_782/mod.rs +++ b/r1cs-std/src/instantiated/ed_on_cp6_782/mod.rs @@ -1,4 +1,105 @@ #![allow(unreachable_pub)] +//! This module implements the R1CS equivalent of `algebra::ed_on_cp6_782`. +//! +//! It implements field variables for `algebra::ed_on_cp6_782::Fq`, +//! and group variables for `algebra::ed_on_cp6_782::GroupProjective`. +//! +//! The field underlying these constraints is `algebra::ed_on_cp6_782::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, ed_on_cp6_782::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::ed_on_cp6_782::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `EdwardsVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, ed_on_cp6_782::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::ed_on_cp6_782::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Edwards` elements. +//! let a_native = EdwardsProjective::rand(&mut rng); +//! let b_native = EdwardsProjective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `Edwards`. +//! let zero = EdwardsVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` mod curves; mod fields; diff --git a/r1cs-std/src/instantiated/ed_on_mnt4_298/curves.rs b/r1cs-std/src/instantiated/ed_on_mnt4_298/curves.rs index 5867a0c75..bc251c687 100644 --- a/r1cs-std/src/instantiated/ed_on_mnt4_298/curves.rs +++ b/r1cs-std/src/instantiated/ed_on_mnt4_298/curves.rs @@ -3,6 +3,7 @@ use algebra::ed_on_mnt4_298::*; use crate::instantiated::ed_on_mnt4_298::fields::FqVar; +/// A variable that is the R1CS equivalent of `algebra::ed_on_mnt4_298::EdwardsAffine`. pub type EdwardsVar = AffineVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_mnt4_298/fields.rs b/r1cs-std/src/instantiated/ed_on_mnt4_298/fields.rs index 07128a202..714bd9878 100644 --- a/r1cs-std/src/instantiated/ed_on_mnt4_298/fields.rs +++ b/r1cs-std/src/instantiated/ed_on_mnt4_298/fields.rs @@ -1,6 +1,7 @@ use crate::fields::fp::FpVar; use algebra::ed_on_mnt4_298::fq::Fq; +/// A variable that is the R1CS equivalent of `algebra::ed_on_mnt4_298::Fq`. pub type FqVar = FpVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_mnt4_298/mod.rs b/r1cs-std/src/instantiated/ed_on_mnt4_298/mod.rs index cd5e75810..aa3d206e2 100644 --- a/r1cs-std/src/instantiated/ed_on_mnt4_298/mod.rs +++ b/r1cs-std/src/instantiated/ed_on_mnt4_298/mod.rs @@ -1,3 +1,105 @@ +//! This module implements the R1CS equivalent of `algebra::ed_on_mnt4_298`. +//! +//! It implements field variables for `algebra::ed_on_mnt4_298::Fq`, +//! and group variables for `algebra::ed_on_mnt4_298::GroupProjective`. +//! +//! The field underlying these constraints is `algebra::ed_on_mnt4_298::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, ed_on_mnt4_298::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::ed_on_mnt4_298::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `EdwardsVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, ed_on_mnt4_298::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::ed_on_mnt4_298::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Edwards` elements. +//! let a_native = EdwardsProjective::rand(&mut rng); +//! let b_native = EdwardsProjective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `Edwards`. +//! let zero = EdwardsVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; diff --git a/r1cs-std/src/instantiated/ed_on_mnt4_753/curves.rs b/r1cs-std/src/instantiated/ed_on_mnt4_753/curves.rs index aa9b9f004..c0b7677eb 100644 --- a/r1cs-std/src/instantiated/ed_on_mnt4_753/curves.rs +++ b/r1cs-std/src/instantiated/ed_on_mnt4_753/curves.rs @@ -3,6 +3,7 @@ use algebra::ed_on_mnt4_753::*; use crate::instantiated::ed_on_mnt4_753::fields::FqVar; +/// A variable that is the R1CS equivalent of `algebra::ed_on_mnt4_753::EdwardsAffine`. pub type EdwardsVar = AffineVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_mnt4_753/fields.rs b/r1cs-std/src/instantiated/ed_on_mnt4_753/fields.rs index f43a823b5..750b1b225 100644 --- a/r1cs-std/src/instantiated/ed_on_mnt4_753/fields.rs +++ b/r1cs-std/src/instantiated/ed_on_mnt4_753/fields.rs @@ -1,6 +1,7 @@ use crate::fields::fp::FpVar; use algebra::ed_on_mnt4_753::fq::Fq; +/// A variable that is the R1CS equivalent of `algebra::ed_on_mnt4_753::Fq`. pub type FqVar = FpVar; #[test] diff --git a/r1cs-std/src/instantiated/ed_on_mnt4_753/mod.rs b/r1cs-std/src/instantiated/ed_on_mnt4_753/mod.rs index cd5e75810..9d030acf9 100644 --- a/r1cs-std/src/instantiated/ed_on_mnt4_753/mod.rs +++ b/r1cs-std/src/instantiated/ed_on_mnt4_753/mod.rs @@ -1,3 +1,105 @@ +//! This module implements the R1CS equivalent of `algebra::ed_on_mnt4_753`. +//! +//! It implements field variables for `algebra::ed_on_mnt4_753::Fq`, +//! and group variables for `algebra::ed_on_mnt4_753::GroupProjective`. +//! +//! The field underlying these constraints is `algebra::ed_on_mnt4_753::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, ed_on_mnt4_753::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::ed_on_mnt4_753::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `EdwardsVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, ed_on_mnt4_753::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::ed_on_mnt4_753::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Edwards` elements. +//! let a_native = EdwardsProjective::rand(&mut rng); +//! let b_native = EdwardsProjective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = EdwardsVar::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = EdwardsVar::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = EdwardsVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `Edwards`. +//! let zero = EdwardsVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; diff --git a/r1cs-std/src/instantiated/mnt4_298/curves.rs b/r1cs-std/src/instantiated/mnt4_298/curves.rs index 359bdcc8a..ecb60155b 100644 --- a/r1cs-std/src/instantiated/mnt4_298/curves.rs +++ b/r1cs-std/src/instantiated/mnt4_298/curves.rs @@ -1,10 +1,16 @@ use crate::groups::mnt4; use algebra::mnt4_298::Parameters; +/// An element of G1 in the MNT4-298 bilinear group. pub type G1Var = mnt4::G1Var; +/// An element of G2 in the MNT4-298 bilinear group. pub type G2Var = mnt4::G2Var; +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. pub type G1PreparedVar = mnt4::G1PreparedVar; +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. pub type G2PreparedVar = mnt4::G2PreparedVar; #[test] diff --git a/r1cs-std/src/instantiated/mnt4_298/fields.rs b/r1cs-std/src/instantiated/mnt4_298/fields.rs index ad79f1e59..279a5c064 100644 --- a/r1cs-std/src/instantiated/mnt4_298/fields.rs +++ b/r1cs-std/src/instantiated/mnt4_298/fields.rs @@ -2,8 +2,11 @@ use algebra::mnt4_298::{Fq, Fq2Parameters, Fq4Parameters}; use crate::fields::{fp::FpVar, fp2::Fp2Var, fp4::Fp4Var}; +/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq`. pub type FqVar = FpVar; +/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq2`. pub type Fq2Var = Fp2Var; +/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq4`. pub type Fq4Var = Fp4Var; #[test] diff --git a/r1cs-std/src/instantiated/mnt4_298/mod.rs b/r1cs-std/src/instantiated/mnt4_298/mod.rs index 5e10f69ff..fe65742f4 100644 --- a/r1cs-std/src/instantiated/mnt4_298/mod.rs +++ b/r1cs-std/src/instantiated/mnt4_298/mod.rs @@ -1,3 +1,153 @@ +//! This module implements the R1CS equivalent of `algebra::mnt4_298`. +//! +//! It implements field variables for `algebra::mnt4_298::{Fq, Fq2, Fq4}`, +//! group variables for `algebra::mnt4_298::{G1, G2}`, and implements constraint +//! generation for computing `MNT4_298::pairing`. +//! +//! The field underlying these constraints is `algebra::mnt4_298::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, mnt4_298::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::mnt4_298::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `G1Var` and `G2Var`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, mnt4_298::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::mnt4_298::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `G1` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G1Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `G1`. +//! let zero = G1Var::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! Finally, one can check pairing computations as well: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, PairingEngine, mnt4_298::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::mnt4_298::{self, *}; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate random `G1` and `G2` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G2Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let pairing_result_native = MNT4_298::pairing(a_native, b_native); +//! +//! // Prepare `a` and `b` for pairing. +//! let a_prep = mnt4_298::PairingVar::prepare_g1(&a)?; +//! let b_prep = mnt4_298::PairingVar::prepare_g2(&b)?; +//! let pairing_result = mnt4_298::PairingVar::pairing(a_prep, b_prep)?; +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!(pairing_result.value()?, pairing_result_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! let a_prep_const = mnt4_298::PairingVar::prepare_g1(&a_const)?; +//! let b_prep_const = mnt4_298::PairingVar::prepare_g2(&b_const)?; +//! let pairing_result_const = mnt4_298::PairingVar::pairing(a_prep_const, b_prep_const)?; +//! println!("Done here 3"); +//! +//! pairing_result.enforce_equal(&pairing_result_const)?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; mod pairing; diff --git a/r1cs-std/src/instantiated/mnt4_298/pairing.rs b/r1cs-std/src/instantiated/mnt4_298/pairing.rs index 768e73eca..c1edc5154 100644 --- a/r1cs-std/src/instantiated/mnt4_298/pairing.rs +++ b/r1cs-std/src/instantiated/mnt4_298/pairing.rs @@ -1,5 +1,6 @@ use algebra::mnt4_298::Parameters; +/// Specifies the constraints for computing a pairing in the MNT4-298 bilinear group. pub type PairingVar = crate::pairing::mnt4::PairingVar; #[test] diff --git a/r1cs-std/src/instantiated/mnt4_753/curves.rs b/r1cs-std/src/instantiated/mnt4_753/curves.rs index 4f83b8e91..0f9223003 100644 --- a/r1cs-std/src/instantiated/mnt4_753/curves.rs +++ b/r1cs-std/src/instantiated/mnt4_753/curves.rs @@ -1,10 +1,16 @@ use crate::groups::mnt4; use algebra::mnt4_753::Parameters; +/// An element of G1 in the MNT4-753 bilinear group. pub type G1Var = mnt4::G1Var; +/// An element of G2 in the MNT4-753 bilinear group. pub type G2Var = mnt4::G2Var; +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. pub type G1PreparedVar = mnt4::G1PreparedVar; +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. pub type G2PreparedVar = mnt4::G2PreparedVar; #[test] diff --git a/r1cs-std/src/instantiated/mnt4_753/fields.rs b/r1cs-std/src/instantiated/mnt4_753/fields.rs index 5cc960ed3..9f79e0e80 100644 --- a/r1cs-std/src/instantiated/mnt4_753/fields.rs +++ b/r1cs-std/src/instantiated/mnt4_753/fields.rs @@ -2,8 +2,11 @@ use algebra::mnt4_753::{Fq, Fq2Parameters, Fq4Parameters}; use crate::fields::{fp::FpVar, fp2::Fp2Var, fp4::Fp4Var}; +/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq`. pub type FqVar = FpVar; +/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq2`. pub type Fq2Var = Fp2Var; +/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq4`. pub type Fq4Var = Fp4Var; #[test] diff --git a/r1cs-std/src/instantiated/mnt4_753/mod.rs b/r1cs-std/src/instantiated/mnt4_753/mod.rs index 5e10f69ff..c717fdcb1 100644 --- a/r1cs-std/src/instantiated/mnt4_753/mod.rs +++ b/r1cs-std/src/instantiated/mnt4_753/mod.rs @@ -1,3 +1,153 @@ +//! This module implements the R1CS equivalent of `algebra::mnt4_753`. +//! +//! It implements field variables for `algebra::mnt4_753::{Fq, Fq2, Fq4}`, +//! group variables for `algebra::mnt4_753::{G1, G2}`, and implements constraint +//! generation for computing `MNT4_753::pairing`. +//! +//! The field underlying these constraints is `algebra::mnt4_753::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, mnt4_753::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::mnt4_753::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `G1Var` and `G2Var`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, mnt4_753::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::mnt4_753::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `G1` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G1Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `G1`. +//! let zero = G1Var::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! Finally, one can check pairing computations as well: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, PairingEngine, mnt4_753::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::mnt4_753::{self, *}; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate random `G1` and `G2` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G2Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let pairing_result_native = MNT4_753::pairing(a_native, b_native); +//! +//! // Prepare `a` and `b` for pairing. +//! let a_prep = mnt4_753::PairingVar::prepare_g1(&a)?; +//! let b_prep = mnt4_753::PairingVar::prepare_g2(&b)?; +//! let pairing_result = mnt4_753::PairingVar::pairing(a_prep, b_prep)?; +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!(pairing_result.value()?, pairing_result_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! let a_prep_const = mnt4_753::PairingVar::prepare_g1(&a_const)?; +//! let b_prep_const = mnt4_753::PairingVar::prepare_g2(&b_const)?; +//! let pairing_result_const = mnt4_753::PairingVar::pairing(a_prep_const, b_prep_const)?; +//! println!("Done here 3"); +//! +//! pairing_result.enforce_equal(&pairing_result_const)?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; mod pairing; diff --git a/r1cs-std/src/instantiated/mnt4_753/pairing.rs b/r1cs-std/src/instantiated/mnt4_753/pairing.rs index 43e7a78c0..677f6447d 100644 --- a/r1cs-std/src/instantiated/mnt4_753/pairing.rs +++ b/r1cs-std/src/instantiated/mnt4_753/pairing.rs @@ -1,5 +1,6 @@ use algebra::mnt4_753::Parameters; +/// Specifies the constraints for computing a pairing in the MNT4-753 bilinear group. pub type PairingVar = crate::pairing::mnt4::PairingVar; #[test] diff --git a/r1cs-std/src/instantiated/mnt6_298/curves.rs b/r1cs-std/src/instantiated/mnt6_298/curves.rs index 4741888d8..0d7f245da 100644 --- a/r1cs-std/src/instantiated/mnt6_298/curves.rs +++ b/r1cs-std/src/instantiated/mnt6_298/curves.rs @@ -1,10 +1,16 @@ use crate::groups::mnt6; use algebra::mnt6_298::Parameters; +/// An element of G1 in the MNT6-298 bilinear group. pub type G1Var = mnt6::G1Var; +/// An element of G2 in the MNT6-298 bilinear group. pub type G2Var = mnt6::G2Var; +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. pub type G1PreparedVar = mnt6::G1PreparedVar; +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. pub type G2PreparedVar = mnt6::G2PreparedVar; #[test] diff --git a/r1cs-std/src/instantiated/mnt6_298/fields.rs b/r1cs-std/src/instantiated/mnt6_298/fields.rs index 8ef8c61a6..d864bb691 100644 --- a/r1cs-std/src/instantiated/mnt6_298/fields.rs +++ b/r1cs-std/src/instantiated/mnt6_298/fields.rs @@ -2,8 +2,11 @@ use algebra::mnt6_298::{Fq, Fq3Parameters, Fq6Parameters}; use crate::fields::{fp::FpVar, fp3::Fp3Var, fp6_2over3::Fp6Var}; +/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq`. pub type FqVar = FpVar; +/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq3`. pub type Fq3Var = Fp3Var; +/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq6`. pub type Fq6Var = Fp6Var; #[test] diff --git a/r1cs-std/src/instantiated/mnt6_298/mod.rs b/r1cs-std/src/instantiated/mnt6_298/mod.rs index 5e10f69ff..292345e57 100644 --- a/r1cs-std/src/instantiated/mnt6_298/mod.rs +++ b/r1cs-std/src/instantiated/mnt6_298/mod.rs @@ -1,3 +1,153 @@ +//! This module implements the R1CS equivalent of `algebra::mnt6_298`. +//! +//! It implements field variables for `algebra::mnt6_298::{Fq, Fq3, Fq6}`, +//! group variables for `algebra::mnt6_298::{G1, G2}`, and implements constraint +//! generation for computing `MNT6_298::pairing`. +//! +//! The field underlying these constraints is `algebra::mnt6_298::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, mnt6_298::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::mnt6_298::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `G1Var` and `G2Var`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, mnt6_298::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::mnt6_298::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `G1` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G1Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `G1`. +//! let zero = G1Var::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! Finally, one can check pairing computations as well: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, PairingEngine, mnt6_298::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::mnt6_298::{self, *}; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate random `G1` and `G2` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G2Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let pairing_result_native = MNT6_298::pairing(a_native, b_native); +//! +//! // Prepare `a` and `b` for pairing. +//! let a_prep = mnt6_298::PairingVar::prepare_g1(&a)?; +//! let b_prep = mnt6_298::PairingVar::prepare_g2(&b)?; +//! let pairing_result = mnt6_298::PairingVar::pairing(a_prep, b_prep)?; +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!(pairing_result.value()?, pairing_result_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! let a_prep_const = mnt6_298::PairingVar::prepare_g1(&a_const)?; +//! let b_prep_const = mnt6_298::PairingVar::prepare_g2(&b_const)?; +//! let pairing_result_const = mnt6_298::PairingVar::pairing(a_prep_const, b_prep_const)?; +//! println!("Done here 3"); +//! +//! pairing_result.enforce_equal(&pairing_result_const)?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; mod pairing; diff --git a/r1cs-std/src/instantiated/mnt6_298/pairing.rs b/r1cs-std/src/instantiated/mnt6_298/pairing.rs index 0d3af879c..3d7881ea8 100644 --- a/r1cs-std/src/instantiated/mnt6_298/pairing.rs +++ b/r1cs-std/src/instantiated/mnt6_298/pairing.rs @@ -1,5 +1,6 @@ use algebra::mnt6_298::Parameters; +/// Specifies the constraints for computing a pairing in the MNT6-298 bilinear group. pub type PairingVar = crate::pairing::mnt6::PairingVar; #[test] diff --git a/r1cs-std/src/instantiated/mnt6_753/curves.rs b/r1cs-std/src/instantiated/mnt6_753/curves.rs index 3b2c27346..b3b016048 100644 --- a/r1cs-std/src/instantiated/mnt6_753/curves.rs +++ b/r1cs-std/src/instantiated/mnt6_753/curves.rs @@ -1,10 +1,16 @@ use crate::groups::mnt6; use algebra::mnt6_753::Parameters; +/// An element of G1 in the MNT6-753 bilinear group. pub type G1Var = mnt6::G1Var; +/// An element of G2 in the MNT6-753 bilinear group. pub type G2Var = mnt6::G2Var; +/// Represents the cached precomputation that can be performed on a G1 element +/// which enables speeding up pairing computation. pub type G1PreparedVar = mnt6::G1PreparedVar; +/// Represents the cached precomputation that can be performed on a G2 element +/// which enables speeding up pairing computation. pub type G2PreparedVar = mnt6::G2PreparedVar; #[test] diff --git a/r1cs-std/src/instantiated/mnt6_753/fields.rs b/r1cs-std/src/instantiated/mnt6_753/fields.rs index 3cbea4c07..68d1193de 100644 --- a/r1cs-std/src/instantiated/mnt6_753/fields.rs +++ b/r1cs-std/src/instantiated/mnt6_753/fields.rs @@ -2,8 +2,11 @@ use algebra::mnt6_753::{Fq, Fq3Parameters, Fq6Parameters}; use crate::fields::{fp::FpVar, fp3::Fp3Var, fp6_2over3::Fp6Var}; +/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq`. pub type FqVar = FpVar; +/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq3`. pub type Fq3Var = Fp3Var; +/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq6`. pub type Fq6Var = Fp6Var; #[test] diff --git a/r1cs-std/src/instantiated/mnt6_753/mod.rs b/r1cs-std/src/instantiated/mnt6_753/mod.rs index 5e10f69ff..914a58256 100644 --- a/r1cs-std/src/instantiated/mnt6_753/mod.rs +++ b/r1cs-std/src/instantiated/mnt6_753/mod.rs @@ -1,3 +1,153 @@ +//! This module implements the R1CS equivalent of `algebra::mnt6_753`. +//! +//! It implements field variables for `algebra::mnt6_753::{Fq, Fq3, Fq6}`, +//! group variables for `algebra::mnt6_753::{G1, G2}`, and implements constraint +//! generation for computing `MNT6_753::pairing`. +//! +//! The field underlying these constraints is `algebra::mnt6_753::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FqVar`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! use algebra::{UniformRand, mnt6_753::*}; +//! use r1cs_core::*; +//! use r1cs_std::prelude::*; +//! use r1cs_std::mnt6_753::*; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = algebra::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FqVar::new_witness(r1cs_core::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FqVar::new_witness(r1cs_core::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FqVar::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FqVar::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FqVar::one(); +//! let zero = FqVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `G1Var` and `G2Var`: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, mnt6_753::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::mnt6_753::*; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate some random `G1` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G1Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G1Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G1Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity of `G1`. +//! let zero = G1Var::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! Finally, one can check pairing computations as well: +//! +//! ``` +//! # fn main() -> Result<(), r1cs_core::SynthesisError> { +//! # use algebra::{UniformRand, PairingEngine, mnt6_753::*}; +//! # use r1cs_core::*; +//! # use r1cs_std::prelude::*; +//! # use r1cs_std::mnt6_753::{self, *}; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = algebra::test_rng(); +//! +//! // Generate random `G1` and `G2` elements. +//! let a_native = G1Projective::rand(&mut rng); +//! let b_native = G2Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = G1Var::new_witness(r1cs_core::ns!(cs, "a"), || Ok(a_native))?; +//! let b = G2Var::new_witness(r1cs_core::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = G1Var::new_constant(r1cs_core::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = G2Var::new_constant(r1cs_core::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let pairing_result_native = MNT6_753::pairing(a_native, b_native); +//! +//! // Prepare `a` and `b` for pairing. +//! let a_prep = mnt6_753::PairingVar::prepare_g1(&a)?; +//! let b_prep = mnt6_753::PairingVar::prepare_g2(&b)?; +//! let pairing_result = mnt6_753::PairingVar::pairing(a_prep, b_prep)?; +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!(pairing_result.value()?, pairing_result_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! let a_prep_const = mnt6_753::PairingVar::prepare_g1(&a_const)?; +//! let b_prep_const = mnt6_753::PairingVar::prepare_g2(&b_const)?; +//! let pairing_result_const = mnt6_753::PairingVar::pairing(a_prep_const, b_prep_const)?; +//! println!("Done here 3"); +//! +//! pairing_result.enforce_equal(&pairing_result_const)?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + mod curves; mod fields; mod pairing; diff --git a/r1cs-std/src/instantiated/mnt6_753/pairing.rs b/r1cs-std/src/instantiated/mnt6_753/pairing.rs index 9d39c73d3..c97741b35 100644 --- a/r1cs-std/src/instantiated/mnt6_753/pairing.rs +++ b/r1cs-std/src/instantiated/mnt6_753/pairing.rs @@ -1,5 +1,6 @@ use algebra::mnt6_753::Parameters; +/// Specifies the constraints for computing a pairing in the MNT6-753 bilinear group. pub type PairingVar = crate::pairing::mnt6::PairingVar; #[test] diff --git a/r1cs-std/src/lib.rs b/r1cs-std/src/lib.rs index 1d4166da3..e017234a7 100644 --- a/r1cs-std/src/lib.rs +++ b/r1cs-std/src/lib.rs @@ -1,3 +1,5 @@ +//! This crate implements common "gadgets" that make +//! programming rank-1 constraint systems easier. #![cfg_attr(not(feature = "std"), no_std)] #![deny(unused_import_braces, unused_qualifications, trivial_casts)] #![deny(trivial_numeric_casts, variant_size_differences, unreachable_pub)] @@ -5,21 +7,27 @@ #![deny(unused_extern_crates, renamed_and_removed_lints, unused_allocation)] #![deny(unused_comparisons, bare_trait_objects, const_err, unused_must_use)] #![deny(unused_mut, unused_unsafe, private_in_public, unsafe_code)] +#![deny(missing_docs)] #![forbid(unsafe_code)] +#[doc(hidden)] #[cfg(all(test, not(feature = "std")))] #[macro_use] extern crate std; +#[doc(hidden)] #[cfg(not(feature = "std"))] extern crate alloc as ralloc; +#[doc(hidden)] #[macro_use] extern crate algebra; +#[doc(hidden)] #[macro_use] extern crate derivative; +/// Some utility macros for making downstream impls easier. #[macro_use] pub mod macros; @@ -31,11 +39,14 @@ use std::vec::Vec; use algebra::prelude::Field; +/// This module implements gadgets related to bit manipulation, such as `Boolean` and `UInt`s. pub mod bits; pub use self::bits::*; +/// This module implements gadgets related to field arithmetic. pub mod fields; +/// This module implements gadgets related to group arithmetic, and specifically elliptic curve arithmetic. pub mod groups; mod instantiated; @@ -76,12 +87,17 @@ pub use instantiated::mnt6_298; #[cfg(feature = "mnt6_753")] pub use instantiated::mnt6_753; +/// This module implements gadgets related to computing pairings in bilinear groups. pub mod pairing; +/// This module describes a trait for allocating new variables in a constraint system. pub mod alloc; +/// This module describes a trait for checking equality of variables. pub mod eq; +/// This module describes traits for conditionally selecting a variable from a list of variables. pub mod select; +#[allow(missing_docs)] pub mod prelude { pub use crate::{ alloc::*, @@ -96,16 +112,20 @@ pub mod prelude { }; } +/// This trait describes some core functionality that is common to high-level variables, +/// such as `Boolean`s, `FieldVar`s, `GroupVar`s, etc. pub trait R1CSVar { + /// The type of the "native" value that `Self` represents in the constraint system. type Value: core::fmt::Debug + Eq + Clone; /// Returns the underlying `ConstraintSystemRef`. - fn cs(&self) -> Option>; + /// + /// If `self` is a constant value, then this *must* return `r1cs_core::ConstraintSystemRef::None`. + fn cs(&self) -> r1cs_core::ConstraintSystemRef; /// Returns `true` if `self` is a circuit-generation-time constant. fn is_constant(&self) -> bool { - self.cs() - .map_or(true, |cs| cs == r1cs_core::ConstraintSystemRef::None) + self.cs().is_none() } /// Returns the value that is assigned to `self` in the underlying @@ -116,8 +136,8 @@ pub trait R1CSVar { impl> R1CSVar for [T] { type Value = Vec; - fn cs(&self) -> Option> { - let mut result = None; + fn cs(&self) -> r1cs_core::ConstraintSystemRef { + let mut result = r1cs_core::ConstraintSystemRef::None; for var in self { result = var.cs().or(result); } @@ -136,7 +156,7 @@ impl> R1CSVar for [T] { impl<'a, F: Field, T: 'a + R1CSVar> R1CSVar for &'a T { type Value = T::Value; - fn cs(&self) -> Option> { + fn cs(&self) -> r1cs_core::ConstraintSystemRef { (*self).cs() } @@ -145,7 +165,9 @@ impl<'a, F: Field, T: 'a + R1CSVar> R1CSVar for &'a T { } } +/// A utility trait to convert `Self` to `Result pub trait Assignment { + /// Converts `self` to `Result`. fn get(self) -> Result; } @@ -154,3 +176,12 @@ impl Assignment for Option { self.ok_or(r1cs_core::SynthesisError::AssignmentMissing) } } + +/// Specifies how to convert a variable of type `Self` to variables of +/// type `FpVar` +pub trait ToConstraintFieldGadget { + /// Converts `self` to `FpVar` variables. + fn to_constraint_field( + &self, + ) -> Result>, r1cs_core::SynthesisError>; +} diff --git a/r1cs-std/src/macros.rs b/r1cs-std/src/macros.rs index 35cccb914..c943a5df7 100644 --- a/r1cs-std/src/macros.rs +++ b/r1cs-std/src/macros.rs @@ -1,5 +1,7 @@ #[allow(unused_braces)] -// Implements arithmetic operations with generic bounds. +/// Implements arithmetic traits (eg: `Add`, `Sub`, `Mul`) for the given type using the impl in `$impl`. +/// +/// Used primarily for implementing these traits for `FieldVar`s and `GroupVar`s. #[macro_export] macro_rules! impl_ops { ( @@ -17,6 +19,11 @@ macro_rules! impl_ops { }; } +/// Implements arithmetic traits (eg: `Add`, `Sub`, `Mul`) for the given type using the impl in `$impl`. +/// +/// Used primarily for implementing these traits for `FieldVar`s and `GroupVar`s. +/// +/// When compared to `impl_ops`, this macro allows specifying additional trait bounds. #[macro_export] macro_rules! impl_bounded_ops { ( diff --git a/r1cs-std/src/pairing/bls12/mod.rs b/r1cs-std/src/pairing/bls12/mod.rs index 35c306089..25dbb495c 100644 --- a/r1cs-std/src/pairing/bls12/mod.rs +++ b/r1cs-std/src/pairing/bls12/mod.rs @@ -12,6 +12,7 @@ use algebra::{ }; use core::marker::PhantomData; +/// Specifies the constraints for computing a pairing in a BLS12 bilinear group. pub struct PairingVar(PhantomData

); type Fp2V

= Fp2Var<

::Fp2Params>; diff --git a/r1cs-std/src/pairing/mnt4/mod.rs b/r1cs-std/src/pairing/mnt4/mod.rs index 9baa45abd..46d75c6ea 100644 --- a/r1cs-std/src/pairing/mnt4/mod.rs +++ b/r1cs-std/src/pairing/mnt4/mod.rs @@ -15,10 +15,12 @@ use algebra::{ }; use core::marker::PhantomData; +/// Specifies the constraints for computing a pairing in a MNT4 bilinear group. pub struct PairingVar(PhantomData

); type Fp2G

= Fp2Var<

::Fp2Params>; type Fp4G

= Fp4Var<

::Fp4Params>; +/// A variable corresponding to `algebra_core::mnt4::GT`. pub type GTVar

= Fp4G

; impl PairingVar

{ @@ -92,7 +94,7 @@ impl PairingVar

{ } #[tracing::instrument(target = "r1cs", skip(p, q))] - pub fn ate_miller_loop( + pub(crate) fn ate_miller_loop( p: &G1PreparedVar

, q: &G2PreparedVar

, ) -> Result, SynthesisError> { @@ -142,7 +144,7 @@ impl PairingVar

{ } #[tracing::instrument(target = "r1cs", skip(value))] - pub fn final_exponentiation(value: &Fp4G

) -> Result, SynthesisError> { + pub(crate) fn final_exponentiation(value: &Fp4G

) -> Result, SynthesisError> { let value_inv = value.inverse()?; let value_to_first_chunk = Self::final_exponentiation_first_chunk(value, &value_inv)?; let value_inv_to_first_chunk = Self::final_exponentiation_first_chunk(&value_inv, value)?; diff --git a/r1cs-std/src/pairing/mnt6/mod.rs b/r1cs-std/src/pairing/mnt6/mod.rs index 832f54c30..accf2671e 100644 --- a/r1cs-std/src/pairing/mnt6/mod.rs +++ b/r1cs-std/src/pairing/mnt6/mod.rs @@ -15,10 +15,12 @@ use algebra::{ }; use core::marker::PhantomData; +/// Specifies the constraints for computing a pairing in a MNT6 bilinear group. pub struct PairingVar(PhantomData

); type Fp3G

= Fp3Var<

::Fp3Params>; type Fp6G

= Fp6Var<

::Fp6Params>; +/// A variable corresponding to `algebra_core::mnt6::GT`. pub type GTVar

= Fp6G

; impl PairingVar

{ @@ -87,7 +89,7 @@ impl PairingVar

{ } #[tracing::instrument(target = "r1cs", skip(p, q))] - pub fn ate_miller_loop( + pub(crate) fn ate_miller_loop( p: &G1PreparedVar

, q: &G2PreparedVar

, ) -> Result, SynthesisError> { @@ -138,7 +140,7 @@ impl PairingVar

{ } #[tracing::instrument(target = "r1cs")] - pub fn final_exponentiation(value: &Fp6G

) -> Result, SynthesisError> { + pub(crate) fn final_exponentiation(value: &Fp6G

) -> Result, SynthesisError> { let value_inv = value.inverse()?; let value_to_first_chunk = Self::final_exponentiation_first_chunk(value, &value_inv)?; let value_inv_to_first_chunk = Self::final_exponentiation_first_chunk(&value_inv, value)?; diff --git a/r1cs-std/src/pairing/mod.rs b/r1cs-std/src/pairing/mod.rs index 569223718..ee2ce8418 100644 --- a/r1cs-std/src/pairing/mod.rs +++ b/r1cs-std/src/pairing/mod.rs @@ -3,36 +3,55 @@ use algebra::{Field, PairingEngine}; use core::fmt::Debug; use r1cs_core::SynthesisError; +/// This module implements pairings for BLS12 bilinear groups. pub mod bls12; +/// This module implements pairings for MNT4 bilinear groups. pub mod mnt4; +/// This module implements pairings for MNT6 bilinear groups. pub mod mnt6; +/// Specifies the constraints for computing a pairing in the yybilinear group `E`. pub trait PairingVar::Fq> { + /// An variable representing an element of `G1`. + /// This is the R1CS equivalent of `E::G1Projective`. type G1Var: CurveVar + AllocVar + AllocVar; + + /// An variable representing an element of `G2`. + /// This is the R1CS equivalent of `E::G2Projective`. type G2Var: CurveVar + AllocVar + AllocVar; + /// An variable representing an element of `GT`. + /// This is the R1CS equivalent of `E::GT`. type GTVar: FieldVar; + /// An variable representing cached precomputation that can speed up pairings computations. + /// This is the R1CS equivalent of `E::G1Prepared`. type G1PreparedVar: ToBytesGadget + AllocVar + Clone + Debug; + /// An variable representing cached precomputation that can speed up pairings computations. + /// This is the R1CS equivalent of `E::G2Prepared`. type G2PreparedVar: ToBytesGadget + AllocVar + Clone + Debug; + /// Computes a multi-miller loop between elements + /// of `p` and `q`. fn miller_loop( p: &[Self::G1PreparedVar], q: &[Self::G2PreparedVar], ) -> Result; + /// Computes a final exponentiation over `p`. fn final_exponentiation(p: &Self::GTVar) -> Result; + /// Computes a pairing over `p` and `q`. #[tracing::instrument(target = "r1cs")] fn pairing( p: Self::G1PreparedVar, @@ -42,7 +61,7 @@ pub trait PairingVar Self::final_exponentiation(&tmp) } - /// Computes a product of pairings. + /// Computes a product of pairings over the elements in `p` and `q`. #[must_use] #[tracing::instrument(target = "r1cs")] fn product_of_pairings( @@ -53,8 +72,10 @@ pub trait PairingVar Self::final_exponentiation(&miller_result) } + /// Performs the precomputation to generate `Self::G1PreparedVar`. fn prepare_g1(q: &Self::G1Var) -> Result; + /// Performs the precomputation to generate `Self::G2PreparedVar`. fn prepare_g2(q: &Self::G2Var) -> Result; } diff --git a/r1cs-std/src/select.rs b/r1cs-std/src/select.rs index 032dcc12e..044071173 100644 --- a/r1cs-std/src/select.rs +++ b/r1cs-std/src/select.rs @@ -2,11 +2,16 @@ use crate::prelude::*; use algebra::Field; use r1cs_core::SynthesisError; -/// If condition is `true`, return `true_value`; else, select `false_value`. +/// Generates constraints for selecting between one of two values. pub trait CondSelectGadget where Self: Sized, { + /// If `cond == &Boolean::TRUE`, then this returns `true_value`; else, returns `false_value`. + /// + /// # Note + /// `Self::conditionally_select(cond, true_value, false_value)?` can be more succinctly written as + /// `cond.select(&true_value, &false_value)?`. fn conditionally_select( cond: &Boolean, true_value: &Self, @@ -14,12 +19,23 @@ where ) -> Result; } -/// Uses two bits to perform a lookup into a table +/// Performs a lookup in a 4-element table using two bits. pub trait TwoBitLookupGadget where Self: Sized, { + /// The type of values being looked up. type TableConstant; + + /// Interprets the slice `bits` as a two-bit integer `b = bits[0] + (bits[1] << 1)`, + /// and then outputs `constants[b]`. + /// + /// For example, if `bits == [0, 1]`, and `constants == [0, 1, 2, 3]`, this method + /// should output a variable corresponding to `2`. + /// + /// # Panics + /// + /// This method panics if `bits.len() != 2` or `constants.len() != 4`. fn two_bit_lookup( bits: &[Boolean], constants: &[Self::TableConstant], @@ -27,12 +43,25 @@ where } /// Uses three bits to perform a lookup into a table, where the last bit -/// performs negation +/// conditionally negates the looked-up value. pub trait ThreeBitCondNegLookupGadget where Self: Sized, { + /// The type of values being looked up. type TableConstant; + + /// Interprets the slice `bits` as a two-bit integer `b = bits[0] + (bits[1] << 1)`, + /// and then outputs `constants[b] * c`, where `c = if bits[2] { -1 } else { 1 };`. + /// + /// That is, `bits[2]` conditionally negates the looked-up value. + /// + /// For example, if `bits == [1, 0, 1]`, and `constants == [0, 1, 2, 3]`, this method + /// should output a variable corresponding to `-1`. + /// + /// # Panics + /// + /// This method panics if `bits.len() != 3` or `constants.len() != 4`. fn three_bit_cond_neg_lookup( bits: &[Boolean], b0b1: &Boolean,