#![deny(rustdoc::broken_intra_doc_links)]
pub mod domain;
pub mod gadgets;
#[cfg(feature = "groth16")]
pub mod groth16;
pub mod multicore;
pub mod multiexp;
use ff::PrimeField;
use std::error::Error;
use std::fmt;
use std::io;
use std::marker::PhantomData;
use std::ops::{Add, Sub};
pub trait Circuit<Scalar: PrimeField> {
fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError>;
}
#[derive(Copy, Clone, Debug)]
pub struct Variable(Index);
impl Variable {
pub fn new_unchecked(idx: Index) -> Variable {
Variable(idx)
}
pub fn get_unchecked(&self) -> Index {
self.0
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Index {
Input(usize),
Aux(usize),
}
#[derive(Clone)]
pub struct LinearCombination<Scalar: PrimeField>(Vec<(Variable, Scalar)>);
impl<Scalar: PrimeField> AsRef<[(Variable, Scalar)]> for LinearCombination<Scalar> {
fn as_ref(&self) -> &[(Variable, Scalar)] {
&self.0
}
}
impl<Scalar: PrimeField> LinearCombination<Scalar> {
pub fn zero() -> LinearCombination<Scalar> {
LinearCombination(vec![])
}
}
impl<Scalar: PrimeField> Add<(Scalar, Variable)> for LinearCombination<Scalar> {
type Output = LinearCombination<Scalar>;
fn add(mut self, (coeff, var): (Scalar, Variable)) -> LinearCombination<Scalar> {
self.0.push((var, coeff));
self
}
}
impl<Scalar: PrimeField> Sub<(Scalar, Variable)> for LinearCombination<Scalar> {
type Output = LinearCombination<Scalar>;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, (coeff, var): (Scalar, Variable)) -> LinearCombination<Scalar> {
self + (coeff.neg(), var)
}
}
impl<Scalar: PrimeField> Add<Variable> for LinearCombination<Scalar> {
type Output = LinearCombination<Scalar>;
fn add(self, other: Variable) -> LinearCombination<Scalar> {
self + (Scalar::one(), other)
}
}
impl<Scalar: PrimeField> Sub<Variable> for LinearCombination<Scalar> {
type Output = LinearCombination<Scalar>;
fn sub(self, other: Variable) -> LinearCombination<Scalar> {
self - (Scalar::one(), other)
}
}
impl<'a, Scalar: PrimeField> Add<&'a LinearCombination<Scalar>> for LinearCombination<Scalar> {
type Output = LinearCombination<Scalar>;
fn add(mut self, other: &'a LinearCombination<Scalar>) -> LinearCombination<Scalar> {
for s in &other.0 {
self = self + (s.1, s.0);
}
self
}
}
impl<'a, Scalar: PrimeField> Sub<&'a LinearCombination<Scalar>> for LinearCombination<Scalar> {
type Output = LinearCombination<Scalar>;
fn sub(mut self, other: &'a LinearCombination<Scalar>) -> LinearCombination<Scalar> {
for s in &other.0 {
self = self - (s.1, s.0);
}
self
}
}
impl<'a, Scalar: PrimeField> Add<(Scalar, &'a LinearCombination<Scalar>)>
for LinearCombination<Scalar>
{
type Output = LinearCombination<Scalar>;
fn add(
mut self,
(coeff, other): (Scalar, &'a LinearCombination<Scalar>),
) -> LinearCombination<Scalar> {
for s in &other.0 {
let mut tmp = s.1;
tmp.mul_assign(&coeff);
self = self + (tmp, s.0);
}
self
}
}
impl<'a, Scalar: PrimeField> Sub<(Scalar, &'a LinearCombination<Scalar>)>
for LinearCombination<Scalar>
{
type Output = LinearCombination<Scalar>;
fn sub(
mut self,
(coeff, other): (Scalar, &'a LinearCombination<Scalar>),
) -> LinearCombination<Scalar> {
for s in &other.0 {
let mut tmp = s.1;
tmp.mul_assign(&coeff);
self = self - (tmp, s.0);
}
self
}
}
#[derive(Debug)]
pub enum SynthesisError {
AssignmentMissing,
DivisionByZero,
Unsatisfiable,
PolynomialDegreeTooLarge,
UnexpectedIdentity,
IoError(io::Error),
UnconstrainedVariable,
}
impl From<io::Error> for SynthesisError {
fn from(e: io::Error) -> SynthesisError {
SynthesisError::IoError(e)
}
}
impl Error for SynthesisError {}
impl fmt::Display for SynthesisError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let msg = match *self {
SynthesisError::AssignmentMissing => {
"an assignment for a variable could not be computed"
}
SynthesisError::DivisionByZero => "division by zero",
SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
SynthesisError::IoError(_) => "encountered an I/O error",
SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained",
};
if let SynthesisError::IoError(ref e) = *self {
write!(f, "I/O error: ")?;
e.fmt(f)
} else {
write!(f, "{}", msg)
}
}
}
#[derive(Debug, Clone)]
pub enum VerificationError {
InvalidVerifyingKey,
InvalidProof,
}
impl Error for VerificationError {}
impl fmt::Display for VerificationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let msg = match *self {
VerificationError::InvalidVerifyingKey => "malformed verifying key",
VerificationError::InvalidProof => "proof verification failed",
};
write!(f, "{}", msg)
}
}
pub trait ConstraintSystem<Scalar: PrimeField>: Sized {
type Root: ConstraintSystem<Scalar>;
fn one() -> Variable {
Variable::new_unchecked(Index::Input(0))
}
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>;
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>;
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>;
fn push_namespace<NR, N>(&mut self, name_fn: N)
where
NR: Into<String>,
N: FnOnce() -> NR;
fn pop_namespace(&mut self);
fn get_root(&mut self) -> &mut Self::Root;
fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, Scalar, Self::Root>
where
NR: Into<String>,
N: FnOnce() -> NR,
{
self.get_root().push_namespace(name_fn);
Namespace(self.get_root(), PhantomData)
}
}
pub struct Namespace<'a, Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
&'a mut CS,
PhantomData<Scalar>,
);
impl<'cs, Scalar: PrimeField, CS: ConstraintSystem<Scalar>> ConstraintSystem<Scalar>
for Namespace<'cs, Scalar, CS>
{
type Root = CS::Root;
fn one() -> Variable {
CS::one()
}
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
self.0.alloc(annotation, f)
}
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
self.0.alloc_input(annotation, f)
}
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
{
self.0.enforce(annotation, a, b, c)
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
panic!("only the root's push_namespace should be called");
}
fn pop_namespace(&mut self) {
panic!("only the root's pop_namespace should be called");
}
fn get_root(&mut self) -> &mut Self::Root {
self.0.get_root()
}
}
impl<'a, Scalar: PrimeField, CS: ConstraintSystem<Scalar>> Drop for Namespace<'a, Scalar, CS> {
fn drop(&mut self) {
self.get_root().pop_namespace()
}
}
impl<'cs, Scalar: PrimeField, CS: ConstraintSystem<Scalar>> ConstraintSystem<Scalar>
for &'cs mut CS
{
type Root = CS::Root;
fn one() -> Variable {
CS::one()
}
fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
(**self).alloc(annotation, f)
}
fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
(**self).alloc_input(annotation, f)
}
fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
{
(**self).enforce(annotation, a, b, c)
}
fn push_namespace<NR, N>(&mut self, name_fn: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
(**self).push_namespace(name_fn)
}
fn pop_namespace(&mut self) {
(**self).pop_namespace()
}
fn get_root(&mut self) -> &mut Self::Root {
(**self).get_root()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn verification_error_string() {
let err = VerificationError::InvalidProof;
assert!(!err.to_string().is_empty());
}
#[test]
fn synthesis_error_string() {
let err = SynthesisError::PolynomialDegreeTooLarge;
assert!(!err.to_string().is_empty());
let err = SynthesisError::IoError(io::Error::new(io::ErrorKind::Other, "other"));
assert!(
err.to_string().contains("other"),
"\"{}\" does not contain the underlying error",
err
);
}
}