#![deny(intra_doc_link_resolution_failure)]
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
pub mod domain;
pub mod gadgets;
pub mod gpu;
#[cfg(feature = "groth16")]
pub mod groth16;
pub mod multicore;
pub mod multiexp;
pub mod util_cs;
#[cfg(feature = "gpu")]
pub use gpu::GPU_NVIDIA_DEVICES;
use ff::{Field, ScalarEngine};
use ahash::AHashMap as HashMap;
use std::io;
use std::marker::PhantomData;
use std::ops::{Add, Sub};
const BELLMAN_VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub trait Circuit<E: ScalarEngine> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError>;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
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, Eq, Hash)]
pub enum Index {
Input(usize),
Aux(usize),
}
#[derive(Clone)]
pub struct LinearCombination<E: ScalarEngine>(HashMap<Variable, E::Fr>);
impl<E: ScalarEngine> Default for LinearCombination<E> {
fn default() -> Self {
Self::zero()
}
}
impl<E: ScalarEngine> LinearCombination<E> {
pub fn zero() -> LinearCombination<E> {
LinearCombination(HashMap::new())
}
pub fn iter(&self) -> impl Iterator<Item = (&Variable, &E::Fr)> + '_ {
self.0.iter()
}
pub fn add_unsimplified(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
self.0
.entry(var)
.or_insert(E::Fr::zero())
.add_assign(&coeff);
self
}
}
impl<E: ScalarEngine> Add<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
self.0
.entry(var)
.or_insert(E::Fr::zero())
.add_assign(&coeff);
self
}
}
impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
coeff.negate();
self + (coeff, var)
}
}
impl<E: ScalarEngine> Add<Variable> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(self, other: Variable) -> LinearCombination<E> {
self + (E::Fr::one(), other)
}
}
impl<E: ScalarEngine> Sub<Variable> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(self, other: Variable) -> LinearCombination<E> {
self - (E::Fr::one(), other)
}
}
impl<'a, E: ScalarEngine> Add<&'a LinearCombination<E>> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
for (var, val) in &other.0 {
self.0.entry(*var).or_insert(E::Fr::zero()).add_assign(val);
}
self
}
}
impl<'a, E: ScalarEngine> Sub<&'a LinearCombination<E>> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(mut self, other: &'a LinearCombination<E>) -> LinearCombination<E> {
for (var, val) in &other.0 {
self = self - (*val, *var);
}
self
}
}
impl<'a, E: ScalarEngine> Add<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
for s in &other.0 {
let mut tmp = *s.1;
tmp.mul_assign(&coeff);
self = self + (tmp, *s.0);
}
self
}
}
impl<'a, E: ScalarEngine> Sub<(E::Fr, &'a LinearCombination<E>)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination<E>)) -> LinearCombination<E> {
for s in &other.0 {
let mut tmp = *s.1;
tmp.mul_assign(&coeff);
self = self - (tmp, *s.0);
}
self
}
}
#[derive(thiserror::Error, Debug)]
pub enum SynthesisError {
#[error("an assignment for a variable could not be computed")]
AssignmentMissing,
#[error("division by zero")]
DivisionByZero,
#[error("unsatisfiable constraint system")]
Unsatisfiable,
#[error("polynomial degree is too large")]
PolynomialDegreeTooLarge,
#[error("encountered an identity element in the CRS")]
UnexpectedIdentity,
#[error("encountered an I/O error: {0}")]
IoError(#[from] io::Error),
#[error("malformed verifying key")]
MalformedVerifyingKey,
#[error("auxiliary variable was unconstrained")]
UnconstrainedVariable,
#[error("encountered a GPU error: {0}")]
GPUError(#[from] gpu::GPUError),
}
pub trait ConstraintSystem<E: ScalarEngine>: Sized + Send {
type Root: ConstraintSystem<E>;
fn new() -> Self {
unimplemented!(
"ConstraintSystem::new must be implemented for extensible types implementing ConstraintSystem"
);
}
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<E::Fr, 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<E::Fr, 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<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>;
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<'_, E, Self::Root>
where
NR: Into<String>,
N: FnOnce() -> NR,
{
self.get_root().push_namespace(name_fn);
Namespace(self.get_root(), Default::default())
}
fn is_extensible() -> bool {
false
}
fn extend(&mut self, _other: Self) {
unimplemented!(
"ConstraintSystem::extend must be implemented for types implementing ConstraintSystem"
);
}
}
pub struct Namespace<'a, E: ScalarEngine, CS: ConstraintSystem<E>>(&'a mut CS, SendMarker<E>);
struct SendMarker<E: ScalarEngine>(PhantomData<E>);
impl<E: ScalarEngine> Default for SendMarker<E> {
fn default() -> Self {
Self(PhantomData)
}
}
unsafe impl<E: ScalarEngine> Send for SendMarker<E> {}
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> for Namespace<'cs, E, 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<E::Fr, 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<E::Fr, 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<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
{
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, E: ScalarEngine, CS: ConstraintSystem<E>> Drop for Namespace<'a, E, CS> {
fn drop(&mut self) {
self.get_root().pop_namespace()
}
}
impl<'cs, E: ScalarEngine, CS: ConstraintSystem<E>> ConstraintSystem<E> 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<E::Fr, 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<E::Fr, 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<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
{
(**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 tests {
use super::*;
#[test]
fn test_add_simplify() {
use paired::bls12_381::Bls12;
let n = 5;
let mut lc = LinearCombination::<Bls12>::zero();
let mut expected_sums = vec![<Bls12 as ScalarEngine>::Fr::zero(); n];
let mut total_additions = 0;
for i in 0..n {
for _ in 0..i + 1 {
let coeff = <Bls12 as ScalarEngine>::Fr::one();
lc = lc + (coeff, Variable::new_unchecked(Index::Aux(i)));
let mut tmp = expected_sums[i];
tmp.add_assign(&coeff);
expected_sums[i] = tmp;
total_additions += 1;
}
}
assert_eq!(n, lc.0.len());
assert!(lc.0.len() != total_additions);
lc.0.iter().for_each(|(var, coeff)| match var.0 {
Index::Aux(i) => assert_eq!(expected_sums[i], *coeff),
_ => panic!("unexpected variable type"),
});
}
}