Browse Source

Document `r1cs-std`

master
Pratyush Mishra 4 years ago
parent
commit
370fbcdd3b
61 changed files with 691 additions and 211 deletions
  1. +18
    -1
      r1cs-std/src/alloc.rs
  2. +37
    -14
      r1cs-std/src/bits/boolean.rs
  3. +9
    -3
      r1cs-std/src/bits/mod.rs
  4. +37
    -11
      r1cs-std/src/bits/uint.rs
  5. +11
    -4
      r1cs-std/src/bits/uint8.rs
  6. +29
    -2
      r1cs-std/src/eq.rs
  7. +13
    -2
      r1cs-std/src/fields/cubic_extension.rs
  8. +1
    -1
      r1cs-std/src/fields/fp/cmp.rs
  9. +68
    -8
      r1cs-std/src/fields/fp/mod.rs
  10. +7
    -3
      r1cs-std/src/fields/fp12.rs
  11. +2
    -0
      r1cs-std/src/fields/fp2.rs
  12. +2
    -0
      r1cs-std/src/fields/fp3.rs
  13. +3
    -0
      r1cs-std/src/fields/fp4.rs
  14. +3
    -0
      r1cs-std/src/fields/fp6_2over3.rs
  15. +5
    -1
      r1cs-std/src/fields/fp6_3over2.rs
  16. +46
    -6
      r1cs-std/src/fields/mod.rs
  17. +12
    -1
      r1cs-std/src/fields/quadratic_extension.rs
  18. +7
    -0
      r1cs-std/src/groups/curves/mod.rs
  19. +15
    -0
      r1cs-std/src/groups/curves/short_weierstrass/bls12/mod.rs
  20. +25
    -0
      r1cs-std/src/groups/curves/short_weierstrass/mnt4/mod.rs
  21. +26
    -0
      r1cs-std/src/groups/curves/short_weierstrass/mnt6/mod.rs
  22. +12
    -0
      r1cs-std/src/groups/curves/short_weierstrass/mod.rs
  23. +120
    -107
      r1cs-std/src/groups/curves/twisted_edwards/mod.rs
  24. +17
    -18
      r1cs-std/src/groups/mod.rs
  25. +6
    -0
      r1cs-std/src/instantiated/bls12_377/curves.rs
  26. +4
    -0
      r1cs-std/src/instantiated/bls12_377/fields.rs
  27. +1
    -0
      r1cs-std/src/instantiated/bls12_377/pairing.rs
  28. +1
    -0
      r1cs-std/src/instantiated/ed_on_bls12_377/curves.rs
  29. +1
    -0
      r1cs-std/src/instantiated/ed_on_bls12_377/fields.rs
  30. +1
    -0
      r1cs-std/src/instantiated/ed_on_bls12_381/curves.rs
  31. +1
    -0
      r1cs-std/src/instantiated/ed_on_bls12_381/fields.rs
  32. +1
    -0
      r1cs-std/src/instantiated/ed_on_bn254/curves.rs
  33. +1
    -0
      r1cs-std/src/instantiated/ed_on_bn254/fields.rs
  34. +0
    -11
      r1cs-std/src/instantiated/ed_on_bw6_761/curves.rs
  35. +0
    -9
      r1cs-std/src/instantiated/ed_on_bw6_761/fields.rs
  36. +1
    -0
      r1cs-std/src/instantiated/ed_on_cp6_782/curves.rs
  37. +1
    -0
      r1cs-std/src/instantiated/ed_on_cp6_782/fields.rs
  38. +1
    -0
      r1cs-std/src/instantiated/ed_on_mnt4_298/curves.rs
  39. +1
    -0
      r1cs-std/src/instantiated/ed_on_mnt4_298/fields.rs
  40. +1
    -0
      r1cs-std/src/instantiated/ed_on_mnt4_753/curves.rs
  41. +1
    -0
      r1cs-std/src/instantiated/ed_on_mnt4_753/fields.rs
  42. +6
    -0
      r1cs-std/src/instantiated/mnt4_298/curves.rs
  43. +3
    -0
      r1cs-std/src/instantiated/mnt4_298/fields.rs
  44. +1
    -0
      r1cs-std/src/instantiated/mnt4_298/pairing.rs
  45. +6
    -0
      r1cs-std/src/instantiated/mnt4_753/curves.rs
  46. +3
    -0
      r1cs-std/src/instantiated/mnt4_753/fields.rs
  47. +1
    -0
      r1cs-std/src/instantiated/mnt4_753/pairing.rs
  48. +6
    -0
      r1cs-std/src/instantiated/mnt6_298/curves.rs
  49. +3
    -0
      r1cs-std/src/instantiated/mnt6_298/fields.rs
  50. +1
    -0
      r1cs-std/src/instantiated/mnt6_298/pairing.rs
  51. +6
    -0
      r1cs-std/src/instantiated/mnt6_753/curves.rs
  52. +3
    -0
      r1cs-std/src/instantiated/mnt6_753/fields.rs
  53. +1
    -0
      r1cs-std/src/instantiated/mnt6_753/pairing.rs
  54. +12
    -0
      r1cs-std/src/instantiated/mod.rs
  55. +20
    -0
      r1cs-std/src/lib.rs
  56. +8
    -1
      r1cs-std/src/macros.rs
  57. +1
    -0
      r1cs-std/src/pairing/bls12/mod.rs
  58. +4
    -2
      r1cs-std/src/pairing/mnt4/mod.rs
  59. +4
    -2
      r1cs-std/src/pairing/mnt6/mod.rs
  60. +22
    -1
      r1cs-std/src/pairing/mod.rs
  61. +32
    -3
      r1cs-std/src/select.rs

+ 18
- 1
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,21 @@ impl AllocationMode {
}
}
/// Specifies how variables of type `Self` should be allocated in a `ConstraintSystem`.
pub trait AllocVar<V, F: Field>
where
Self: Sized,
V: ?Sized,
{
/// Allocates a new variable of type `Self` in the `ConstraintSystem`.
/// The mode of allocation is decided by `mode`.
fn new_variable<T: Borrow<V>>(
cs: impl Into<Namespace<F>>,
f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode,
) -> Result<Self, SynthesisError>;
/// Allocates a new constant of type `Self` in the `ConstraintSystem`.
#[tracing::instrument(target = "r1cs", skip(cs, t))]
fn new_constant(
cs: impl Into<Namespace<F>>,
@ -42,6 +57,7 @@ where
Self::new_variable(cs, || Ok(t), AllocationMode::Constant)
}
/// Allocates a new public input of type `Self` in the `ConstraintSystem`.
#[tracing::instrument(target = "r1cs", skip(cs, f))]
fn new_input<T: Borrow<V>>(
cs: impl Into<Namespace<F>>,
@ -50,6 +66,7 @@ where
Self::new_variable(cs, f, AllocationMode::Input)
}
/// Allocates a new private witness of type `Self` in the `ConstraintSystem`.
#[tracing::instrument(target = "r1cs", skip(cs, f))]
fn new_witness<T: Borrow<V>>(
cs: impl Into<Namespace<F>>,

+ 37
- 14
r1cs-std/src/bits/boolean.rs

@ -254,6 +254,10 @@ impl Boolean {
/// Returns the constrant `false`.
pub const FALSE: Self = Boolean::Constant(false);
/// Constructs a `LinearCombination` from `Self`'s variables.
///
/// * `Boolean::Is(v) => lc!() + v.variable()`
/// * `Boolean::Not(v) => lc!() + Variable::One - v.variable()`
pub fn lc(&self) -> LinearCombination<F> {
match self {
Boolean::Constant(false) => lc!(),
@ -263,7 +267,9 @@ impl Boolean {
}
}
/// Construct a boolean vector from a vector of u8
/// Constructs a `Boolean` vector from a slice of constant `u8`.
///
/// This *does not* create any new variables.
pub fn constant_vec_from_bytes(values: &[u8]) -> Vec<Self> {
let mut input_bits = vec![];
for input_byte in values {
@ -274,12 +280,12 @@ impl Boolean {
input_bits
}
/// Construct a boolean from a known constant
/// Constructs a constant `Boolean` with value `b`.
pub fn constant(b: bool) -> Self {
Boolean::Constant(b)
}
/// Return a negated interpretation of this boolean.
/// Negates `self`.
pub fn not(&self) -> Self {
match *self {
Boolean::Constant(c) => Boolean::Constant(!c),
@ -290,11 +296,14 @@ impl Boolean {
}
impl<F: Field> Boolean<F> {
/// 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.
#[tracing::instrument(target = "r1cs")]
pub fn xor<'a>(&'a self, b: &'a Self) -> Result<Self, SynthesisError> {
pub fn xor<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> {
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 +315,14 @@ 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.
#[tracing::instrument(target = "r1cs")]
pub fn or<'a>(&'a self, b: &'a Self) -> Result<Self, SynthesisError> {
pub fn or<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> {
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 +333,14 @@ 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.
#[tracing::instrument(target = "r1cs")]
pub fn and<'a>(&'a self, b: &'a Self) -> Result<Self, SynthesisError> {
pub fn and<'a>(&'a self, other: &'a Self) -> Result<Self, SynthesisError> {
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 +354,7 @@ impl Boolean {
}
}
/// Outputs `bits[0] & bits[1] & ... & bits.last().unwrap()`.
#[tracing::instrument(target = "r1cs")]
pub fn kary_and(bits: &[Self]) -> Result<Self, SynthesisError> {
assert!(!bits.is_empty());
@ -354,6 +370,7 @@ impl Boolean {
Ok(cur.expect("should not be 0"))
}
/// Outputs `bits[0] | bits[1] | ... | bits.last().unwrap()`.
#[tracing::instrument(target = "r1cs")]
pub fn kary_or(bits: &[Self]) -> Result<Self, SynthesisError> {
assert!(!bits.is_empty());
@ -369,12 +386,15 @@ impl Boolean {
Ok(cur.expect("should not be 0"))
}
/// Outputs `(bits[0] & bits[1] & ... & bits.last().unwrap()).not()`.
#[tracing::instrument(target = "r1cs")]
pub fn kary_nand(bits: &[Self]) -> Result<Self, SynthesisError> {
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::*;
@ -392,7 +412,7 @@ impl Boolean {
/// 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 +486,9 @@ 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`.
#[tracing::instrument(target = "r1cs", skip(first, second))]
pub fn select<T: CondSelectGadget<F>>(
&self,

+ 9
- 3
r1cs-std/src/bits/mod.rs

@ -5,15 +5,20 @@ 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<F: Field> {
/// Outputs the canonical little-endian bit-wise representation of `self`.
///
@ -70,6 +75,7 @@ where
}
}
/// Specifies constraints for conversion to a little-endian byte representation of `self`.
pub trait ToBytesGadget<F: Field> {
/// Outputs a canonical, little-endian, byte decomposition of `self`.
///

+ 37
- 11
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<F: Field> {
// Least significant bit first
@ -46,7 +57,11 @@ macro_rules! make_uint {
}
impl<F: Field> $name<F> {
/// 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<Boolean<F>> {
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<F>]) -> 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<Self, SynthesisError> {
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<Self, SynthesisError>
where
F: PrimeField,

+ 11
- 4
r1cs-std/src/bits/uint8.rs

@ -64,6 +64,7 @@ impl UInt8 {
}
}
/// Allocates a slice of `u8`'s as private witnesses.
pub fn new_witness_vec(
cs: impl Into<Namespace<F>>,
values: &[impl Into<Option<u8>> + Copy],
@ -78,10 +79,13 @@ 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
/// Allocates a slice of `u8`'s as public inputs by first packing them into
/// `F` elements, (thus reducing the number of input allocations),
/// and then converts this list of `AllocatedFp<F>` variables back into
/// bytes.
///
/// From a user perspective, this trade-off adds constraints, but improves
/// verifier time and verification key size.
pub fn new_input_vec(
cs: impl Into<Namespace<F>>,
values: &[u8],
@ -134,7 +138,10 @@ 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.
#[tracing::instrument(target = "r1cs")]
pub fn xor(&self, other: &Self) -> Result<Self, SynthesisError> {
let new_value = match (self.value, other.value) {

+ 29
- 2
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<F: Field> {
/// Output a `Boolean` value representing whether `self.value() == other.value()`.
fn is_eq(&self, other: &Self) -> Result<Boolean<F>, 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<Boolean<F>, 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))

+ 13
- 2
r1cs-std/src/fields/cubic_extension.rs

@ -12,6 +12,8 @@ use crate::{
Assignment, 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]
@ -19,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<P>,
}
/// This trait describes parameters that are used to implement arithmetic for `CubicExtVar`.
pub trait CubicExtVarParams<BF: FieldVar<Self::BaseField, Self::BasePrimeField>>:
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);
}
@ -38,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;
@ -49,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<BF, SynthesisError> {
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;
@ -64,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);

+ 1
- 1
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,

+ 68
- 8
r1cs-std/src/fields/fp/mod.rs

@ -6,17 +6,23 @@ use core::borrow::Borrow;
use crate::fields::{FieldOpsBounds, FieldVar};
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<F: PrimeField> {
pub(crate) value: Option<F>,
/// The allocated variable corresponding to `self` in `self.cs`.
pub variable: Variable,
/// The constraint system that `self` was allocated in.
pub cs: ConstraintSystemRef<F>,
}
impl<F: PrimeField> AllocatedFp<F> {
/// Constructs a new `AllocatedFp` from a (optional) value, a low-level Variable,
/// and a `ConstraintSystemRef`.
pub fn new(value: Option<F>, variable: Variable, cs: ConstraintSystemRef<F>) -> Self {
Self {
value,
@ -26,11 +32,14 @@ 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<F: PrimeField> {
/// 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<F>),
}
@ -79,6 +88,7 @@ impl<'a, F: PrimeField> FieldOpsBounds<'a, F, Self> for FpVar {}
impl<'a, F: PrimeField> FieldOpsBounds<'a, F, FpVar<F>> for &'a FpVar<F> {}
impl<F: PrimeField> AllocatedFp<F> {
/// Constructs `Self` from a `Boolean`: if `other` is false, this outputs `zero`, else it outputs `one`.
pub fn from(other: Boolean<F>) -> Self {
if let Some(cs) = other.cs() {
let variable = cs.new_lc(other.lc()).unwrap();
@ -88,10 +98,15 @@ impl AllocatedFp {
}
}
/// Returns the value assigned to `self` in the underlying constraint system
/// (if a value was assigned).
pub fn value(&self) -> Result<F, SynthesisError> {
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 +121,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 +138,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 +157,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 +174,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 +196,9 @@ impl AllocatedFp {
}
}
/// Output `self + self`
///
/// This does not create any constraints.
#[tracing::instrument(target = "r1cs")]
pub fn double(&self) -> Result<Self, SynthesisError> {
let value = self.value.map(|val| val.double());
@ -173,6 +206,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 +216,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 +226,17 @@ impl AllocatedFp {
self
}
/// Outputs `self * self`
///
/// This requires *one* constraint.
#[tracing::instrument(target = "r1cs")]
pub fn square(&self) -> Result<Self, SynthesisError> {
Ok(self.mul(self))
}
/// Outputs `result` such that `result * self = 1`.
///
/// This requires *one* constraint.
#[tracing::instrument(target = "r1cs")]
pub fn inverse(&self) -> Result<Self, SynthesisError> {
let inverse = Self::new_witness(self.cs.clone(), || {
@ -206,11 +251,15 @@ impl AllocatedFp {
Ok(inverse)
}
/// This is a no-op for prime fields.
#[tracing::instrument(target = "r1cs")]
pub fn frobenius_map(&self, _: usize) -> Result<Self, SynthesisError> {
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 +269,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 +283,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<Boolean<F>, SynthesisError> {
Ok(self.is_neq(other)?.not())
@ -241,9 +291,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<Boolean<F>, SynthesisError> {
let is_not_equal = Boolean::new_witness(self.cs.clone(), || {
@ -311,6 +359,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 +375,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 +407,9 @@ impl AllocatedFp {
impl<F: PrimeField> ToBitsGadget<F> for AllocatedFp<F> {
/// 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<Vec<Boolean<F>>, SynthesisError> {
let bits = self.to_non_unique_bits_le()?;
@ -405,6 +462,9 @@ impl ToBitsGadget for AllocatedFp {
impl<F: PrimeField> ToBytesGadget<F> for AllocatedFp<F> {
/// 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<Vec<UInt8<F>>, SynthesisError> {
let num_bits = F::BigInt::NUM_LIMBS * 64;

+ 7
- 3
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<P>`.
pub type Fp12Var<P> = QuadExtVar<Fp6Var<<P as Fp12Parameters>::Fp6Params>, Fp12ParamsWrapper<P>>;
type Fp2Params<P> = <<P as Fp12Parameters>::Fp6Params as Fp6Parameters>::Fp2Params;
@ -15,7 +18,7 @@ impl QuadExtVarParams> for Fp12ParamsWra
}
impl<P: Fp12Parameters> Fp12Var<P> {
/// 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<Self, SynthesisError> {
if characteristic_square_mod_6_is_one(Fp12::<P>::characteristic()) {
let fp2_nr = <P::Fp6Params as Fp6Parameters>::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]>,

+ 2
- 0
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<P>`.
pub type Fp2Var<P> = QuadExtVar<FpVar<<P as Fp2Parameters>::Fp>, Fp2ParamsWrapper<P>>;
impl<P: Fp2Parameters> QuadExtVarParams<FpVar<P::Fp>> for Fp2ParamsWrapper<P> {

+ 2
- 0
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<P>`.
pub type Fp3Var<P> = CubicExtVar<FpVar<<P as Fp3Parameters>::Fp>, Fp3ParamsWrapper<P>>;
impl<P: Fp3Parameters> CubicExtVarParams<FpVar<P::Fp>> for Fp3ParamsWrapper<P> {

+ 3
- 0
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<P>`.
pub type Fp4Var<P> = QuadExtVar<Fp2Var<<P as Fp4Parameters>::Fp2Params>, Fp4ParamsWrapper<P>>;
impl<P: Fp4Parameters> QuadExtVarParams<Fp2Var<P::Fp2Params>> for Fp4ParamsWrapper<P> {

+ 3
- 0
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<P>`.
pub type Fp6Var<P> = QuadExtVar<Fp3Var<<P as Fp6Parameters>::Fp3Params>, Fp6ParamsWrapper<P>>;
impl<P: Fp6Parameters> QuadExtVarParams<Fp3Var<P::Fp3Params>> for Fp6ParamsWrapper<P> {

+ 5
- 1
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<P>`.
pub type Fp6Var<P> = CubicExtVar<Fp2Var<<P as Fp6Parameters>::Fp2Params>, Fp6ParamsWrapper<P>>;
impl<P: Fp6Parameters> CubicExtVarParams<Fp2Var<P::Fp2Params>> for Fp6ParamsWrapper<P> {
@ -17,6 +20,7 @@ impl CubicExtVarParams> for Fp6ParamsWrap
}
impl<P: Fp6Parameters> Fp6Var<P> {
/// Multiplies `self` by a sparse element which has `c0 == c2 == zero`.
pub fn mul_by_0_c1_0(&self, c1: &Fp2Var<P::Fp2Params>) -> Result<Self, SynthesisError> {
// 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<P::Fp2Params>,

+ 46
- 6
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,62 +75,80 @@ pub trait FieldVar:
+ MulAssign<F>
+ Debug
{
/// Returns the constant `F::zero()`.
fn zero() -> Self;
/// Returns a `Boolean` representing whether `self == Self::zero()`.
fn is_zero(&self) -> Result<Boolean<ConstraintF>, 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<Boolean<ConstraintF>, 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<Self, SynthesisError> {
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<Self, SynthesisError>;
/// 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<Self, SynthesisError> {
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<Self, SynthesisError>;
/// 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<Self, SynthesisError> {
@ -125,8 +162,10 @@ pub trait FieldVar:
Ok(result)
}
/// Computes the frobenius map over `self`.
fn frobenius_map(&self, power: usize) -> Result<Self, SynthesisError>;
/// 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<S: AsRef<[u64]>>(&self, exp: S) -> Result<Self, SynthesisError> {
let mut res = Self::one();
for i in BitIteratorBE::without_leading_zeros(exp) {

+ 12
- 1
r1cs-std/src/fields/quadratic_extension.rs

@ -12,6 +12,8 @@ use crate::{
Assignment, 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]
@ -19,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<P>,
}
/// This trait describes parameters that are used to implement arithmetic for `QuadExtVar`.
pub trait QuadExtVarParams<BF: FieldVar<Self::BaseField, Self::BasePrimeField>>:
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);
}
@ -37,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,
@ -45,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<BF, SynthesisError> {
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;
@ -58,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);

+ 7
- 0
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;

+ 15
- 0
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<P> =
ProjectiveVar<<P as Bls12Parameters>::G1Parameters, FpVar<<P as Bls12Parameters>::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<P> =
AffineVar<<P as Bls12Parameters>::G1Parameters, FpVar<<P as Bls12Parameters>::Fp>>;
/// Represents a projective point in G2.
pub type G2Var<P> = ProjectiveVar<<P as Bls12Parameters>::G2Parameters, Fp2G<P>>;
/// 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<P> = AffineVar<<P as Bls12Parameters>::G2Parameters, Fp2G<P>>;
/// Represents the cached precomputation that can be performed on a G1 element
/// which enables speeding up pairing computation.
#[derive(Derivative)]
#[derivative(Clone(bound = "G1Var<P>: Clone"), Debug(bound = "G1Var<P>: Debug"))]
pub struct G1PreparedVar<P: Bls12Parameters>(pub AffineVar<P::G1Parameters, FpVar<P::Fp>>);
impl<P: Bls12Parameters> G1PreparedVar<P> {
/// Returns the value assigned to `self` in the underlying constraint system.
pub fn value(&self) -> Result<G1Prepared<P>, 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<P>) -> Result<Self, SynthesisError> {
let g = q.to_affine()?;
Ok(Self(g))
@ -90,12 +101,15 @@ impl ToBytesGadget for G1PreparedVar

{

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

{

}
impl<P: Bls12Parameters> G2PreparedVar<P> {
/// Constructs `Self` from a `G2Var`.
#[tracing::instrument(target = "r1cs")]
pub fn from_group_var(q: &G2Var<P>) -> Result<Self, SynthesisError> {
let q = q.to_affine()?;

+ 25
- 0
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<P> =
ProjectiveVar<<P as MNT4Parameters>::G1Parameters, FpVar<<P as MNT4Parameters>::Fp>>;
/// Represents a projective point in G2.
pub type G2Var<P> = ProjectiveVar<<P as MNT4Parameters>::G2Parameters, Fp2G<P>>;
/// 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<P: MNT4Parameters> {
#[doc(hidden)]
pub x: FpVar<P::Fp>,
#[doc(hidden)]
pub y: FpVar<P::Fp>,
#[doc(hidden)]
pub x_twist: Fp2Var<P::Fp2Params>,
#[doc(hidden)]
pub y_twist: Fp2Var<P::Fp2Params>,
}
@ -64,6 +72,7 @@ impl AllocVar, P::Fp> for G1PreparedVar

{

}
impl<P: MNT4Parameters> G1PreparedVar<P> {
/// Returns the value assigned to `self` in the underlying constraint system.
pub fn value(&self) -> Result<G1Prepared<P>, 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<P>) -> Result<Self, SynthesisError> {
let q = q.to_affine()?;
@ -124,14 +134,22 @@ impl ToBytesGadget for G1PreparedVar

{

type Fp2G<P> = Fp2Var<<P as MNT4Parameters>::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<P: MNT4Parameters> {
#[doc(hidden)]
pub x: Fp2Var<P::Fp2Params>,
#[doc(hidden)]
pub y: Fp2Var<P::Fp2Params>,
#[doc(hidden)]
pub x_over_twist: Fp2Var<P::Fp2Params>,
#[doc(hidden)]
pub y_over_twist: Fp2Var<P::Fp2Params>,
#[doc(hidden)]
pub double_coefficients: Vec<AteDoubleCoefficientsVar<P>>,
#[doc(hidden)]
pub addition_coefficients: Vec<AteAdditionCoefficientsVar<P>>,
}
@ -225,6 +243,7 @@ impl ToBytesGadget for G2PreparedVar

{

}
impl<P: MNT4Parameters> G2PreparedVar<P> {
/// Returns the value assigned to `self` in the underlying constraint system.
pub fn value(&self) -> Result<G2Prepared<P>, 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<P>) -> Result<Self, SynthesisError> {
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<P: MNT4Parameters> {
@ -385,6 +406,7 @@ impl ToBytesGadget for AteDoubleCoefficientsVar

{

}
impl<P: MNT4Parameters> AteDoubleCoefficientsVar<P> {
/// Returns the value assigned to `self` in the underlying constraint system.
pub fn value(&self) -> Result<AteDoubleCoefficients<P>, 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<P: MNT4Parameters> {
@ -451,12 +474,14 @@ impl ToBytesGadget for AteAdditionCoefficientsVar

{

}
impl<P: MNT4Parameters> AteAdditionCoefficientsVar<P> {
/// Returns the value assigned to `self` in the underlying constraint system.
pub fn value(&self) -> Result<AteAdditionCoefficients<P>, 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<P: MNT4Parameters> {
pub x: Fp2Var<P::Fp2Params>,
pub y: Fp2Var<P::Fp2Params>,

+ 26
- 0
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<P> =
ProjectiveVar<<P as MNT6Parameters>::G1Parameters, FpVar<<P as MNT6Parameters>::Fp>>;
/// Represents a projective point in G2.
pub type G2Var<P> = ProjectiveVar<<P as MNT6Parameters>::G2Parameters, Fp3G<P>>;
/// 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<P: MNT6Parameters> {
#[doc(hidden)]
pub x: FpVar<P::Fp>,
#[doc(hidden)]
pub y: FpVar<P::Fp>,
#[doc(hidden)]
pub x_twist: Fp3Var<P::Fp3Params>,
#[doc(hidden)]
pub y_twist: Fp3Var<P::Fp3Params>,
}
impl<P: MNT6Parameters> G1PreparedVar<P> {
/// Returns the value assigned to `self` in the underlying constraint system.
pub fn value(&self) -> Result<G1Prepared<P>, 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<P>) -> Result<Self, SynthesisError> {
let q = q.to_affine()?;
@ -123,14 +133,23 @@ impl ToBytesGadget for G1PreparedVar

{

}
type Fp3G<P> = Fp3Var<<P as MNT6Parameters>::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<P: MNT6Parameters> {
#[doc(hidden)]
pub x: Fp3Var<P::Fp3Params>,
#[doc(hidden)]
pub y: Fp3Var<P::Fp3Params>,
#[doc(hidden)]
pub x_over_twist: Fp3Var<P::Fp3Params>,
#[doc(hidden)]
pub y_over_twist: Fp3Var<P::Fp3Params>,
#[doc(hidden)]
pub double_coefficients: Vec<AteDoubleCoefficientsVar<P>>,
#[doc(hidden)]
pub addition_coefficients: Vec<AteAdditionCoefficientsVar<P>>,
}
@ -224,6 +243,7 @@ impl ToBytesGadget for G2PreparedVar

{

}
impl<P: MNT6Parameters> G2PreparedVar<P> {
/// Returns the value assigned to `self` in the underlying constraint system.
pub fn value(&self) -> Result<G2Prepared<P>, 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<P>) -> Result<Self, SynthesisError> {
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<P: MNT6Parameters> {
@ -384,6 +406,7 @@ impl ToBytesGadget for AteDoubleCoefficientsVar

{

}
impl<P: MNT6Parameters> AteDoubleCoefficientsVar<P> {
/// Returns the value assigned to `self` in the underlying constraint system.
pub fn value(&self) -> Result<AteDoubleCoefficients<P>, 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<P: MNT6Parameters> {
@ -448,6 +472,7 @@ impl ToBytesGadget for AteAdditionCoefficientsVar

{

}
impl<P: MNT6Parameters> AteAdditionCoefficientsVar<P> {
/// Returns the value assigned to `self` in the underlying constraint system.
pub fn value(&self) -> Result<AteAdditionCoefficients<P>, 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<P: MNT6Parameters> {
pub x: Fp3Var<P::Fp3Params>,
pub y: Fp3Var<P::Fp3Params>,

+ 12
- 0
r1cs-std/src/groups/curves/short_weierstrass/mod.rs

@ -11,8 +11,17 @@ use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError};
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
@ -72,6 +81,8 @@ where
}
}
/// Returns the value assigned to `self` in the underlying
/// constraint system.
pub fn value(&self) -> Result<SWAffine<P>, SynthesisError> {
Ok(SWAffine::new(
self.x.value()?,
@ -128,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
- 107
r1cs-std/src/groups/curves/twisted_edwards/mod.rs

@ -13,6 +13,12 @@ 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]
@ -22,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<P>,
@ -59,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,
@ -67,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<P>,
@ -85,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<<P::BaseField as Field>::BasePrimeField>,
@ -96,6 +109,7 @@ 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<AffineVar<P, F>, SynthesisError> {
let cs = self.cs().unwrap_or(ConstraintSystemRef::None);
@ -199,6 +213,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]
@ -208,7 +225,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<P>,
@ -219,6 +238,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,
@ -257,6 +277,99 @@ where
}
}
impl<P: TEModelParameters, F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>>
AffineVar<P, F>
where
P: TEModelParameters,
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>
+ ThreeBitCondNegLookupGadget<
<P::BaseField as Field>::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<J>(
bases: &[impl Borrow<[TEProjective<P>]>],
scalars: &[impl Borrow<[J]>],
) -> Result<Self, SynthesisError>
where
J: Borrow<[Boolean<<P::BaseField as Field>::BasePrimeField>]>,
{
const CHUNK_SIZE: usize = 3;
let mut ed_result: Option<AffineVar<P, F>> = None;
let mut result: Option<MontgomeryAffineVar<P, F>> = None;
let mut process_segment_result = |result: &MontgomeryAffineVar<P, F>| {
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::<Result<Vec<_>, _>>()?;
let x_coeffs = coords.iter().map(|p| p.0).collect::<Vec<_>>();
let y_coeffs = coords.iter().map(|p| p.1).collect::<Vec<_>>();
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<P, F> R1CSVar<<P::BaseField as Field>::BasePrimeField> for AffineVar<P, F>
where
P: TEModelParameters,
@ -281,11 +394,7 @@ impl CurveVar, ::BasePrimeField> fo
where
P: TEModelParameters,
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>
+ ThreeBitCondNegLookupGadget<
<P::BaseField as Field>::BasePrimeField,
TableConstant = P::BaseField,
>,
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>,
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
{
fn constant(g: TEProjective<P>) -> Self {
@ -441,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<Self, SynthesisError>
where
I: Borrow<[Boolean<<P::BaseField as Field>::BasePrimeField>]>,
J: Borrow<[I]>,
B: Borrow<[TEProjective<P>]>,
{
const CHUNK_SIZE: usize = 3;
let mut ed_result: Option<AffineVar<P, F>> = None;
let mut result: Option<MontgomeryAffineVar<P, F>> = None;
let mut process_segment_result = |result: &MontgomeryAffineVar<P, F>| {
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::<Result<Vec<_>, _>>()?;
let x_coeffs = coords.iter().map(|p| p.0).collect::<Vec<_>>();
let y_coeffs = coords.iter().map(|p| p.1).collect::<Vec<_>>();
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<P, F> AllocVar<TEProjective<P>, <P::BaseField as Field>::BasePrimeField> for AffineVar<P, F>
where
P: TEModelParameters,
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>
+ ThreeBitCondNegLookupGadget<
<P::BaseField as Field>::BasePrimeField,
TableConstant = P::BaseField,
>,
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>,
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
{
#[tracing::instrument(target = "r1cs", skip(cs, f))]
@ -630,11 +657,7 @@ impl AllocVar, ::BasePrimeField> for Af
where
P: TEModelParameters,
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>
+ ThreeBitCondNegLookupGadget<
<P::BaseField as Field>::BasePrimeField,
TableConstant = P::BaseField,
>,
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>,
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
{
#[tracing::instrument(target = "r1cs", skip(cs, f))]
@ -737,8 +760,7 @@ impl_bounded_ops!(
|this: &'a AffineVar<P, F>, other: TEProjective<P>| this + AffineVar::constant(other),
(
F :FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>
+ ThreeBitCondNegLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>,
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>,
P: TEModelParameters,
),
for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
@ -755,8 +777,7 @@ impl_bounded_ops!(
|this: &'a AffineVar<P, F>, other: TEProjective<P>| this - AffineVar::constant(other),
(
F :FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>
+ ThreeBitCondNegLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>,
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>,
P: TEModelParameters,
),
for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>
@ -766,11 +787,7 @@ impl<'a, P, F> GroupOpsBounds<'a, TEProjective

, AffineVar> for AffineVa

where
P: TEModelParameters,
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>
+ ThreeBitCondNegLookupGadget<
<P::BaseField as Field>::BasePrimeField,
TableConstant = P::BaseField,
>,
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>,
for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
{
}
@ -779,11 +796,7 @@ impl<'a, P, F> GroupOpsBounds<'a, TEProjective

, AffineVar> for &'a Affi

where
P: TEModelParameters,
F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>
+ ThreeBitCondNegLookupGadget<
<P::BaseField as Field>::BasePrimeField,
TableConstant = P::BaseField,
>,
+ TwoBitLookupGadget<<P::BaseField as Field>::BasePrimeField, TableConstant = P::BaseField>,
for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
{
}

+ 17
- 18
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<C: ProjectiveCurve, ConstraintF: Field>:
'static
+ Sized
@ -43,17 +46,23 @@ pub trait CurveVar:
+ AddAssign<Self>
+ SubAssign<Self>
{
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<Boolean<ConstraintF>, 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<Namespace<ConstraintF>>,
f: impl FnOnce() -> Result<C, SynthesisError>,
@ -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<Self, SynthesisError> {
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<Self, SynthesisError>;
/// 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<Self, SynthesisError>
where
I: Borrow<[Boolean<ConstraintF>]>,
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>(

+ 6
- 0
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<Parameters>;
/// An element of G2 in the BLS12-377 bilinear group.
pub type G2Var = bls12::G2Var<Parameters>;
/// Represents the cached precomputation that can be performed on a G1 element
/// which enables speeding up pairing computation.
pub type G1PreparedVar = bls12::G1PreparedVar<Parameters>;
/// Represents the cached precomputation that can be performed on a G2 element
/// which enables speeding up pairing computation.
pub type G2PreparedVar = bls12::G2PreparedVar<Parameters>;
#[test]

+ 4
- 0
r1cs-std/src/instantiated/bls12_377/fields.rs

@ -2,9 +2,13 @@ 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<Fq>;
/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq2`.
pub type Fq2Var = Fp2Var<Fq2Parameters>;
/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq6`.
pub type Fq6Var = Fp6Var<Fq6Parameters>;
/// A variable that is the R1CS equivalent of `algebra::bls12_377::Fq12`.
pub type Fq12Var = Fp12Var<Fq12Parameters>;
#[test]

+ 1
- 0
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<Parameters>;
#[test]

+ 1
- 0
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<EdwardsParameters, FqVar>;
#[test]

+ 1
- 0
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<Fq>;
#[test]

+ 1
- 0
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<EdwardsParameters, FqVar>;
#[test]

+ 1
- 0
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<algebra::ed_on_bls12_381::Fq>;
#[test]

+ 1
- 0
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<EdwardsParameters, FqVar>;
#[test]

+ 1
- 0
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<algebra::ed_on_bn254::Fq>;
#[test]

+ 0
- 11
r1cs-std/src/instantiated/ed_on_bw6_761/curves.rs

@ -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<EdwardsParameters, Fq, FqGadget>;
#[test]
fn test() {
crate::groups::curves::twisted_edwards::test::<_, EdwardsParameters, EdwardsGadget>();
}

+ 0
- 9
r1cs-std/src/instantiated/ed_on_bw6_761/fields.rs

@ -1,9 +0,0 @@
use crate::fields::fp::FpGadget;
use algebra::ed_on_cp6_782::fq::Fq;
pub type FqGadget = FpGadget<Fq>;
#[test]
fn test() {
crate::fields::tests::field_test::<_, _, Fq, FqGadget>();
}

+ 1
- 0
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<EdwardsParameters, FqVar>;
#[test]

+ 1
- 0
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<Fq>;
#[test]

+ 1
- 0
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<EdwardsParameters, FqVar>;
#[test]

+ 1
- 0
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<Fq>;
#[test]

+ 1
- 0
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<EdwardsParameters, FqVar>;
#[test]

+ 1
- 0
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<Fq>;
#[test]

+ 6
- 0
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<Parameters>;
/// An element of G2 in the MNT4-298 bilinear group.
pub type G2Var = mnt4::G2Var<Parameters>;
/// Represents the cached precomputation that can be performed on a G1 element
/// which enables speeding up pairing computation.
pub type G1PreparedVar = mnt4::G1PreparedVar<Parameters>;
/// Represents the cached precomputation that can be performed on a G2 element
/// which enables speeding up pairing computation.
pub type G2PreparedVar = mnt4::G2PreparedVar<Parameters>;
#[test]

+ 3
- 0
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<Fq>;
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq2`.
pub type Fq2Var = Fp2Var<Fq2Parameters>;
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq4`.
pub type Fq4Var = Fp4Var<Fq4Parameters>;
#[test]

+ 1
- 0
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<Parameters>;
#[test]

+ 6
- 0
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<Parameters>;
/// An element of G2 in the MNT4-753 bilinear group.
pub type G2Var = mnt4::G2Var<Parameters>;
/// Represents the cached precomputation that can be performed on a G1 element
/// which enables speeding up pairing computation.
pub type G1PreparedVar = mnt4::G1PreparedVar<Parameters>;
/// Represents the cached precomputation that can be performed on a G2 element
/// which enables speeding up pairing computation.
pub type G2PreparedVar = mnt4::G2PreparedVar<Parameters>;
#[test]

+ 3
- 0
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<Fq>;
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq2`.
pub type Fq2Var = Fp2Var<Fq2Parameters>;
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq4`.
pub type Fq4Var = Fp4Var<Fq4Parameters>;
#[test]

+ 1
- 0
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<Parameters>;
#[test]

+ 6
- 0
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<Parameters>;
/// An element of G2 in the MNT6-298 bilinear group.
pub type G2Var = mnt6::G2Var<Parameters>;
/// Represents the cached precomputation that can be performed on a G1 element
/// which enables speeding up pairing computation.
pub type G1PreparedVar = mnt6::G1PreparedVar<Parameters>;
/// Represents the cached precomputation that can be performed on a G2 element
/// which enables speeding up pairing computation.
pub type G2PreparedVar = mnt6::G2PreparedVar<Parameters>;
#[test]

+ 3
- 0
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<Fq>;
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq3`.
pub type Fq3Var = Fp3Var<Fq3Parameters>;
/// A variable that is the R1CS equivalent of `algebra::mnt4_298::Fq6`.
pub type Fq6Var = Fp6Var<Fq6Parameters>;
#[test]

+ 1
- 0
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<Parameters>;
#[test]

+ 6
- 0
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<Parameters>;
/// An element of G2 in the MNT6-753 bilinear group.
pub type G2Var = mnt6::G2Var<Parameters>;
/// Represents the cached precomputation that can be performed on a G1 element
/// which enables speeding up pairing computation.
pub type G1PreparedVar = mnt6::G1PreparedVar<Parameters>;
/// Represents the cached precomputation that can be performed on a G2 element
/// which enables speeding up pairing computation.
pub type G2PreparedVar = mnt6::G2PreparedVar<Parameters>;
#[test]

+ 3
- 0
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<Fq>;
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq3`.
pub type Fq3Var = Fp3Var<Fq3Parameters>;
/// A variable that is the R1CS equivalent of `algebra::mnt4_753::Fq6`.
pub type Fq6Var = Fp6Var<Fq6Parameters>;
#[test]

+ 1
- 0
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<Parameters>;
#[test]

+ 12
- 0
r1cs-std/src/instantiated/mod.rs

@ -1,38 +1,50 @@
/// This module implements the R1CS equivalent of `algebra::bls12_377`.
#[cfg(feature = "bls12_377")]
pub mod bls12_377;
/// This module implements the R1CS equivalent of `algebra::ed_on_bls12_377`.
#[cfg(feature = "ed_on_bls12_377")]
pub mod ed_on_bls12_377;
/// This module implements the R1CS equivalent of `algebra::ed_on_cp6_782`.
#[cfg(feature = "ed_on_cp6_782")]
pub mod ed_on_cp6_782;
#[cfg(all(not(feature = "ed_on_cp6_782"), feature = "ed_on_bw6_761"))]
pub(crate) mod ed_on_cp6_782;
/// This module implements the R1CS equivalent of `algebra::ed_on_bw6_761`.
#[cfg(feature = "ed_on_bw6_761")]
pub mod ed_on_bw6_761;
/// This module implements the R1CS equivalent of `algebra::ed_on_bn254`.
#[cfg(feature = "ed_on_bn254")]
pub mod ed_on_bn254;
/// This module implements the R1CS equivalent of `algebra::ed_on_bls12_381`.
#[cfg(feature = "ed_on_bls12_381")]
pub mod ed_on_bls12_381;
/// This module implements the R1CS equivalent of `algebra::ed_on_mnt4_298`.
#[cfg(feature = "ed_on_mnt4_298")]
pub mod ed_on_mnt4_298;
/// This module implements the R1CS equivalent of `algebra::ed_on_mnt4_753`.
#[cfg(feature = "ed_on_mnt4_753")]
pub mod ed_on_mnt4_753;
/// This module implements the R1CS equivalent of `algebra::mnt4_298`.
#[cfg(feature = "mnt4_298")]
pub mod mnt4_298;
/// This module implements the R1CS equivalent of `algebra::mnt4_753`.
#[cfg(feature = "mnt4_753")]
pub mod mnt4_753;
/// This module implements the R1CS equivalent of `algebra::mnt6_298`.
#[cfg(feature = "mnt6_298")]
pub mod mnt6_298;
/// This module implements the R1CS equivalent of `algebra::mnt6_753`.
#[cfg(feature = "mnt6_753")]
pub mod mnt6_753;

+ 20
- 0
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,7 +112,9 @@ 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<F: Field> {
/// The type of the "native" value that `Self` represents in the constraint system.
type Value: core::fmt::Debug + Eq + Clone;
/// Returns the underlying `ConstraintSystemRef`.
@ -145,7 +163,9 @@ impl<'a, F: Field, T: 'a + R1CSVar> R1CSVar for &'a T {
}
}
/// A utility trait to convert `Self` to `Result<T, SynthesisErrorA`.>
pub trait Assignment<T> {
/// Converts `self` to `Result`.
fn get(self) -> Result<T, r1cs_core::SynthesisError>;
}

+ 8
- 1
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 {
(

+ 1
- 0
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<P: Bls12Parameters>(PhantomData<P>);
type Fp2V<P> = Fp2Var<<P as Bls12Parameters>::Fp2Params>;

+ 4
- 2
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<P: MNT4Parameters>(PhantomData<P>);
type Fp2G<P> = Fp2Var<<P as MNT4Parameters>::Fp2Params>;
type Fp4G<P> = Fp4Var<<P as MNT4Parameters>::Fp4Params>;
/// A variable corresponding to `algebra_core::mnt4::GT`.
pub type GTVar<P> = Fp4G<P>;
impl<P: MNT4Parameters> PairingVar<P> {
@ -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<P>,
q: &G2PreparedVar<P>,
) -> Result<Fp4G<P>, SynthesisError> {
@ -142,7 +144,7 @@ impl PairingVar

{

}
#[tracing::instrument(target = "r1cs", skip(value))]
pub fn final_exponentiation(value: &Fp4G<P>) -> Result<GTVar<P>, SynthesisError> {
pub(crate) fn final_exponentiation(value: &Fp4G<P>) -> Result<GTVar<P>, 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)?;

+ 4
- 2
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<P: MNT6Parameters>(PhantomData<P>);
type Fp3G<P> = Fp3Var<<P as MNT6Parameters>::Fp3Params>;
type Fp6G<P> = Fp6Var<<P as MNT6Parameters>::Fp6Params>;
/// A variable corresponding to `algebra_core::mnt6::GT`.
pub type GTVar<P> = Fp6G<P>;
impl<P: MNT6Parameters> PairingVar<P> {
@ -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<P>,
q: &G2PreparedVar<P>,
) -> Result<Fp6G<P>, SynthesisError> {
@ -138,7 +140,7 @@ impl PairingVar

{

}
#[tracing::instrument(target = "r1cs")]
pub fn final_exponentiation(value: &Fp6G<P>) -> Result<GTVar<P>, SynthesisError> {
pub(crate) fn final_exponentiation(value: &Fp6G<P>) -> Result<GTVar<P>, 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)?;

+ 22
- 1
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<E: PairingEngine, ConstraintF: Field = <E as PairingEngine>::Fq> {
/// An variable representing an element of `G1`.
/// This is the R1CS equivalent of `E::G1Projective`.
type G1Var: CurveVar<E::G1Projective, ConstraintF>
+ AllocVar<E::G1Projective, ConstraintF>
+ AllocVar<E::G1Affine, ConstraintF>;
/// An variable representing an element of `G2`.
/// This is the R1CS equivalent of `E::G2Projective`.
type G2Var: CurveVar<E::G2Projective, ConstraintF>
+ AllocVar<E::G2Projective, ConstraintF>
+ AllocVar<E::G2Affine, ConstraintF>;
/// An variable representing an element of `GT`.
/// This is the R1CS equivalent of `E::GT`.
type GTVar: FieldVar<E::Fqk, ConstraintF>;
/// An variable representing cached precomputation that can speed up pairings computations.
/// This is the R1CS equivalent of `E::G1Prepared`.
type G1PreparedVar: ToBytesGadget<ConstraintF>
+ AllocVar<E::G1Prepared, ConstraintF>
+ Clone
+ Debug;
/// An variable representing cached precomputation that can speed up pairings computations.
/// This is the R1CS equivalent of `E::G2Prepared`.
type G2PreparedVar: ToBytesGadget<ConstraintF>
+ AllocVar<E::G2Prepared, ConstraintF>
+ Clone
+ Debug;
/// Computes a multi-miller loop between elements
/// of `p` and `q`.
fn miller_loop(
p: &[Self::G1PreparedVar],
q: &[Self::G2PreparedVar],
) -> Result<Self::GTVar, SynthesisError>;
/// Computes a final exponentiation over `p`.
fn final_exponentiation(p: &Self::GTVar) -> Result<Self::GTVar, SynthesisError>;
/// 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<Self::G1PreparedVar, SynthesisError>;
/// Performs the precomputation to generate `Self::G2PreparedVar`.
fn prepare_g2(q: &Self::G2Var) -> Result<Self::G2PreparedVar, SynthesisError>;
}

+ 32
- 3
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<ConstraintF: Field>
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<ConstraintF>,
true_value: &Self,
@ -14,12 +19,23 @@ where
) -> Result<Self, SynthesisError>;
}
/// Uses two bits to perform a lookup into a table
/// Performs a lookup in a 4-element table using two bits.
pub trait TwoBitLookupGadget<ConstraintF: Field>
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<ConstraintF>],
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<ConstraintF: Field>
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<ConstraintF>],
b0b1: &Boolean<ConstraintF>,

Loading…
Cancel
Save