bellman/
lib.rs

1//! `bellman` is a crate for building zk-SNARK circuits. It provides circuit
2//! traits and and primitive structures, as well as basic gadget implementations
3//! such as booleans and number abstractions.
4//!
5//! # Example circuit
6//!
7//! Say we want to write a circuit that proves we know the preimage to some hash
8//! computed using SHA-256d (calling SHA-256 twice). The preimage must have a
9//! fixed length known in advance (because the circuit parameters will depend on
10//! it), but can otherwise have any value. We take the following strategy:
11//!
12//! - Witness each bit of the preimage.
13//! - Compute `hash = SHA-256d(preimage)` inside the circuit.
14//! - Expose `hash` as a public input using multiscalar packing.
15//!
16//! ```
17//! use bellman::{
18//!     gadgets::{
19//!         boolean::{AllocatedBit, Boolean},
20//!         multipack,
21//!         sha256::sha256,
22//!     },
23//!     groth16, Circuit, ConstraintSystem, SynthesisError,
24//! };
25//! use bls12_381::Bls12;
26//! use ff::PrimeField;
27//! use pairing::Engine;
28//! use rand::rngs::OsRng;
29//! use sha2::{Digest, Sha256};
30//!
31//! /// Our own SHA-256d gadget. Input and output are in little-endian bit order.
32//! fn sha256d<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
33//!     mut cs: CS,
34//!     data: &[Boolean],
35//! ) -> Result<Vec<Boolean>, SynthesisError> {
36//!     // Flip endianness of each input byte
37//!     let input: Vec<_> = data
38//!         .chunks(8)
39//!         .map(|c| c.iter().rev())
40//!         .flatten()
41//!         .cloned()
42//!         .collect();
43//!
44//!     let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
45//!     let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;
46//!
47//!     // Flip endianness of each output byte
48//!     Ok(res
49//!         .chunks(8)
50//!         .map(|c| c.iter().rev())
51//!         .flatten()
52//!         .cloned()
53//!         .collect())
54//! }
55//!
56//! struct MyCircuit {
57//!     /// The input to SHA-256d we are proving that we know. Set to `None` when we
58//!     /// are verifying a proof (and do not have the witness data).
59//!     preimage: Option<[u8; 80]>,
60//! }
61//!
62//! impl<Scalar: PrimeField> Circuit<Scalar> for MyCircuit {
63//!     fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
64//!         // Compute the values for the bits of the preimage. If we are verifying a proof,
65//!         // we still need to create the same constraints, so we return an equivalent-size
66//!         // Vec of None (indicating that the value of each bit is unknown).
67//!         let bit_values = if let Some(preimage) = self.preimage {
68//!             preimage
69//!                 .into_iter()
70//!                 .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
71//!                 .flatten()
72//!                 .map(|b| Some(b))
73//!                 .collect()
74//!         } else {
75//!             vec![None; 80 * 8]
76//!         };
77//!         assert_eq!(bit_values.len(), 80 * 8);
78//!
79//!         // Witness the bits of the preimage.
80//!         let preimage_bits = bit_values
81//!             .into_iter()
82//!             .enumerate()
83//!             // Allocate each bit.
84//!             .map(|(i, b)| {
85//!                 AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b)
86//!             })
87//!             // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
88//!             .map(|b| b.map(Boolean::from))
89//!             .collect::<Result<Vec<_>, _>>()?;
90//!
91//!         // Compute hash = SHA-256d(preimage).
92//!         let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;
93//!
94//!         // Expose the vector of 32 boolean variables as compact public inputs.
95//!         multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
96//!     }
97//! }
98//!
99//! // Create parameters for our circuit. In a production deployment these would
100//! // be generated securely using a multiparty computation.
101//! let params = {
102//!     let c = MyCircuit { preimage: None };
103//!     groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
104//! };
105//!
106//! // Prepare the verification key (for proof verification).
107//! let pvk = groth16::prepare_verifying_key(&params.vk);
108//!
109//! // Pick a preimage and compute its hash.
110//! let preimage = [42; 80];
111//! let hash = Sha256::digest(&Sha256::digest(&preimage));
112//!
113//! // Create an instance of our circuit (with the preimage as a witness).
114//! let c = MyCircuit {
115//!     preimage: Some(preimage),
116//! };
117//!
118//! // Create a Groth16 proof with our parameters.
119//! let proof = groth16::create_random_proof(c, &params, &mut OsRng).unwrap();
120//!
121//! // Pack the hash as inputs for proof verification.
122//! let hash_bits = multipack::bytes_to_bits_le(&hash);
123//! let inputs = multipack::compute_multipacking(&hash_bits);
124//!
125//! // Check the proof!
126//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).is_ok());
127//! ```
128//!
129//! # Roadmap
130//!
131//! `bellman` is being refactored into a generic proving library. Currently it
132//! is pairing-specific, and different types of proving systems need to be
133//! implemented as sub-modules. After the refactor, `bellman` will be generic
134//! using the [`ff`] and [`group`] crates, while specific proving systems will
135//! be separate crates that pull in the dependencies they require.
136
137// Catch documentation errors caused by code changes.
138#![deny(rustdoc::broken_intra_doc_links)]
139
140pub mod domain;
141pub mod gadgets;
142#[cfg(feature = "groth16")]
143pub mod groth16;
144pub mod multicore;
145pub mod multiexp;
146
147use ff::PrimeField;
148
149use std::error::Error;
150use std::fmt;
151use std::io;
152use std::marker::PhantomData;
153use std::ops::{Add, Sub};
154
155/// Computations are expressed in terms of arithmetic circuits, in particular
156/// rank-1 quadratic constraint systems. The `Circuit` trait represents a
157/// circuit that can be synthesized. The `synthesize` method is called during
158/// CRS generation and during proving.
159pub trait Circuit<Scalar: PrimeField> {
160    /// Synthesize the circuit into a rank-1 quadratic constraint system
161    fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError>;
162}
163
164/// Represents a variable in our constraint system.
165#[derive(Copy, Clone, Debug)]
166pub struct Variable(Index);
167
168impl Variable {
169    /// This constructs a variable with an arbitrary index.
170    /// Circuit implementations are not recommended to use this.
171    pub fn new_unchecked(idx: Index) -> Variable {
172        Variable(idx)
173    }
174
175    /// This returns the index underlying the variable.
176    /// Circuit implementations are not recommended to use this.
177    pub fn get_unchecked(&self) -> Index {
178        self.0
179    }
180}
181
182/// Represents the index of either an input variable or
183/// auxiliary variable.
184#[derive(Copy, Clone, PartialEq, Debug)]
185pub enum Index {
186    Input(usize),
187    Aux(usize),
188}
189
190/// This represents a linear combination of some variables, with coefficients
191/// in the scalar field of a pairing-friendly elliptic curve group.
192#[derive(Clone)]
193pub struct LinearCombination<Scalar: PrimeField>(Vec<(Variable, Scalar)>);
194
195impl<Scalar: PrimeField> AsRef<[(Variable, Scalar)]> for LinearCombination<Scalar> {
196    fn as_ref(&self) -> &[(Variable, Scalar)] {
197        &self.0
198    }
199}
200
201impl<Scalar: PrimeField> LinearCombination<Scalar> {
202    pub fn zero() -> LinearCombination<Scalar> {
203        LinearCombination(vec![])
204    }
205}
206
207impl<Scalar: PrimeField> Add<(Scalar, Variable)> for LinearCombination<Scalar> {
208    type Output = LinearCombination<Scalar>;
209
210    fn add(mut self, (coeff, var): (Scalar, Variable)) -> LinearCombination<Scalar> {
211        self.0.push((var, coeff));
212
213        self
214    }
215}
216
217impl<Scalar: PrimeField> Sub<(Scalar, Variable)> for LinearCombination<Scalar> {
218    type Output = LinearCombination<Scalar>;
219
220    #[allow(clippy::suspicious_arithmetic_impl)]
221    fn sub(self, (coeff, var): (Scalar, Variable)) -> LinearCombination<Scalar> {
222        self + (coeff.neg(), var)
223    }
224}
225
226impl<Scalar: PrimeField> Add<Variable> for LinearCombination<Scalar> {
227    type Output = LinearCombination<Scalar>;
228
229    fn add(self, other: Variable) -> LinearCombination<Scalar> {
230        self + (Scalar::ONE, other)
231    }
232}
233
234impl<Scalar: PrimeField> Sub<Variable> for LinearCombination<Scalar> {
235    type Output = LinearCombination<Scalar>;
236
237    fn sub(self, other: Variable) -> LinearCombination<Scalar> {
238        self - (Scalar::ONE, other)
239    }
240}
241
242impl<'a, Scalar: PrimeField> Add<&'a LinearCombination<Scalar>> for LinearCombination<Scalar> {
243    type Output = LinearCombination<Scalar>;
244
245    fn add(mut self, other: &'a LinearCombination<Scalar>) -> LinearCombination<Scalar> {
246        for s in &other.0 {
247            self = self + (s.1, s.0);
248        }
249
250        self
251    }
252}
253
254impl<'a, Scalar: PrimeField> Sub<&'a LinearCombination<Scalar>> for LinearCombination<Scalar> {
255    type Output = LinearCombination<Scalar>;
256
257    fn sub(mut self, other: &'a LinearCombination<Scalar>) -> LinearCombination<Scalar> {
258        for s in &other.0 {
259            self = self - (s.1, s.0);
260        }
261
262        self
263    }
264}
265
266impl<'a, Scalar: PrimeField> Add<(Scalar, &'a LinearCombination<Scalar>)>
267    for LinearCombination<Scalar>
268{
269    type Output = LinearCombination<Scalar>;
270
271    fn add(
272        mut self,
273        (coeff, other): (Scalar, &'a LinearCombination<Scalar>),
274    ) -> LinearCombination<Scalar> {
275        for s in &other.0 {
276            let mut tmp = s.1;
277            tmp.mul_assign(&coeff);
278            self = self + (tmp, s.0);
279        }
280
281        self
282    }
283}
284
285impl<'a, Scalar: PrimeField> Sub<(Scalar, &'a LinearCombination<Scalar>)>
286    for LinearCombination<Scalar>
287{
288    type Output = LinearCombination<Scalar>;
289
290    fn sub(
291        mut self,
292        (coeff, other): (Scalar, &'a LinearCombination<Scalar>),
293    ) -> LinearCombination<Scalar> {
294        for s in &other.0 {
295            let mut tmp = s.1;
296            tmp.mul_assign(&coeff);
297            self = self - (tmp, s.0);
298        }
299
300        self
301    }
302}
303
304/// This is an error that could occur during circuit synthesis contexts,
305/// such as CRS generation or proving.
306#[derive(Debug)]
307pub enum SynthesisError {
308    /// During synthesis, we lacked knowledge of a variable assignment.
309    AssignmentMissing,
310    /// During synthesis, we divided by zero.
311    DivisionByZero,
312    /// During synthesis, we constructed an unsatisfiable constraint system.
313    Unsatisfiable,
314    /// During synthesis, our polynomials ended up being too high of degree
315    PolynomialDegreeTooLarge,
316    /// During proof generation, we encountered an identity in the CRS
317    UnexpectedIdentity,
318    /// During proof generation, we encountered an I/O error with the CRS
319    IoError(io::Error),
320    /// During CRS generation, we observed an unconstrained auxiliary variable
321    UnconstrainedVariable,
322}
323
324impl From<io::Error> for SynthesisError {
325    fn from(e: io::Error) -> SynthesisError {
326        SynthesisError::IoError(e)
327    }
328}
329
330impl Error for SynthesisError {}
331
332impl fmt::Display for SynthesisError {
333    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
334        let msg = match *self {
335            SynthesisError::AssignmentMissing => {
336                "an assignment for a variable could not be computed"
337            }
338            SynthesisError::DivisionByZero => "division by zero",
339            SynthesisError::Unsatisfiable => "unsatisfiable constraint system",
340            SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large",
341            SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS",
342            SynthesisError::IoError(_) => "encountered an I/O error",
343            SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained",
344        };
345        if let SynthesisError::IoError(ref e) = *self {
346            write!(f, "I/O error: ")?;
347            e.fmt(f)
348        } else {
349            write!(f, "{}", msg)
350        }
351    }
352}
353
354/// An error during verification.
355#[derive(Debug, Clone)]
356pub enum VerificationError {
357    /// Verification was attempted with a malformed verifying key.
358    InvalidVerifyingKey,
359    /// Proof verification failed.
360    InvalidProof,
361}
362
363impl Error for VerificationError {}
364
365impl fmt::Display for VerificationError {
366    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
367        let msg = match *self {
368            VerificationError::InvalidVerifyingKey => "malformed verifying key",
369            VerificationError::InvalidProof => "proof verification failed",
370        };
371        write!(f, "{}", msg)
372    }
373}
374
375/// Represents a constraint system which can have new variables
376/// allocated and constrains between them formed.
377pub trait ConstraintSystem<Scalar: PrimeField>: Sized {
378    /// Represents the type of the "root" of this constraint system
379    /// so that nested namespaces can minimize indirection.
380    type Root: ConstraintSystem<Scalar>;
381
382    /// Return the "one" input variable
383    fn one() -> Variable {
384        Variable::new_unchecked(Index::Input(0))
385    }
386
387    /// Allocate a private variable in the constraint system. The provided function is used to
388    /// determine the assignment of the variable. The given `annotation` function is invoked
389    /// in testing contexts in order to derive a unique name for this variable in the current
390    /// namespace.
391    fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
392    where
393        F: FnOnce() -> Result<Scalar, SynthesisError>,
394        A: FnOnce() -> AR,
395        AR: Into<String>;
396
397    /// Allocate a public variable in the constraint system. The provided function is used to
398    /// determine the assignment of the variable.
399    fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
400    where
401        F: FnOnce() -> Result<Scalar, SynthesisError>,
402        A: FnOnce() -> AR,
403        AR: Into<String>;
404
405    /// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts
406    /// in order to derive a unique name for the constraint in the current namespace.
407    fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
408    where
409        A: FnOnce() -> AR,
410        AR: Into<String>,
411        LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
412        LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
413        LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>;
414
415    /// Create a new (sub)namespace and enter into it. Not intended
416    /// for downstream use; use `namespace` instead.
417    fn push_namespace<NR, N>(&mut self, name_fn: N)
418    where
419        NR: Into<String>,
420        N: FnOnce() -> NR;
421
422    /// Exit out of the existing namespace. Not intended for
423    /// downstream use; use `namespace` instead.
424    fn pop_namespace(&mut self);
425
426    /// Gets the "root" constraint system, bypassing the namespacing.
427    /// Not intended for downstream use; use `namespace` instead.
428    fn get_root(&mut self) -> &mut Self::Root;
429
430    /// Begin a namespace for this constraint system.
431    fn namespace<NR, N>(&mut self, name_fn: N) -> Namespace<'_, Scalar, Self::Root>
432    where
433        NR: Into<String>,
434        N: FnOnce() -> NR,
435    {
436        self.get_root().push_namespace(name_fn);
437
438        Namespace(self.get_root(), PhantomData)
439    }
440}
441
442/// This is a "namespaced" constraint system which borrows a constraint system (pushing
443/// a namespace context) and, when dropped, pops out of the namespace context.
444pub struct Namespace<'a, Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
445    &'a mut CS,
446    PhantomData<Scalar>,
447);
448
449impl<'cs, Scalar: PrimeField, CS: ConstraintSystem<Scalar>> ConstraintSystem<Scalar>
450    for Namespace<'cs, Scalar, CS>
451{
452    type Root = CS::Root;
453
454    fn one() -> Variable {
455        CS::one()
456    }
457
458    fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
459    where
460        F: FnOnce() -> Result<Scalar, SynthesisError>,
461        A: FnOnce() -> AR,
462        AR: Into<String>,
463    {
464        self.0.alloc(annotation, f)
465    }
466
467    fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
468    where
469        F: FnOnce() -> Result<Scalar, SynthesisError>,
470        A: FnOnce() -> AR,
471        AR: Into<String>,
472    {
473        self.0.alloc_input(annotation, f)
474    }
475
476    fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
477    where
478        A: FnOnce() -> AR,
479        AR: Into<String>,
480        LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
481        LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
482        LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
483    {
484        self.0.enforce(annotation, a, b, c)
485    }
486
487    // Downstream users who use `namespace` will never interact with these
488    // functions and they will never be invoked because the namespace is
489    // never a root constraint system.
490
491    fn push_namespace<NR, N>(&mut self, _: N)
492    where
493        NR: Into<String>,
494        N: FnOnce() -> NR,
495    {
496        panic!("only the root's push_namespace should be called");
497    }
498
499    fn pop_namespace(&mut self) {
500        panic!("only the root's pop_namespace should be called");
501    }
502
503    fn get_root(&mut self) -> &mut Self::Root {
504        self.0.get_root()
505    }
506}
507
508impl<'a, Scalar: PrimeField, CS: ConstraintSystem<Scalar>> Drop for Namespace<'a, Scalar, CS> {
509    fn drop(&mut self) {
510        self.get_root().pop_namespace()
511    }
512}
513
514/// Convenience implementation of ConstraintSystem<Scalar> for mutable references to
515/// constraint systems.
516impl<'cs, Scalar: PrimeField, CS: ConstraintSystem<Scalar>> ConstraintSystem<Scalar>
517    for &'cs mut CS
518{
519    type Root = CS::Root;
520
521    fn one() -> Variable {
522        CS::one()
523    }
524
525    fn alloc<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
526    where
527        F: FnOnce() -> Result<Scalar, SynthesisError>,
528        A: FnOnce() -> AR,
529        AR: Into<String>,
530    {
531        (**self).alloc(annotation, f)
532    }
533
534    fn alloc_input<F, A, AR>(&mut self, annotation: A, f: F) -> Result<Variable, SynthesisError>
535    where
536        F: FnOnce() -> Result<Scalar, SynthesisError>,
537        A: FnOnce() -> AR,
538        AR: Into<String>,
539    {
540        (**self).alloc_input(annotation, f)
541    }
542
543    fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
544    where
545        A: FnOnce() -> AR,
546        AR: Into<String>,
547        LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
548        LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
549        LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
550    {
551        (**self).enforce(annotation, a, b, c)
552    }
553
554    fn push_namespace<NR, N>(&mut self, name_fn: N)
555    where
556        NR: Into<String>,
557        N: FnOnce() -> NR,
558    {
559        (**self).push_namespace(name_fn)
560    }
561
562    fn pop_namespace(&mut self) {
563        (**self).pop_namespace()
564    }
565
566    fn get_root(&mut self) -> &mut Self::Root {
567        (**self).get_root()
568    }
569}
570
571#[cfg(test)]
572mod test {
573    use super::*;
574
575    #[test]
576    fn verification_error_string() {
577        let err = VerificationError::InvalidProof;
578
579        // Make sure it correctly returns something (i.e. it's not an endless loop)
580        assert!(!err.to_string().is_empty());
581    }
582
583    #[test]
584    fn synthesis_error_string() {
585        let err = SynthesisError::PolynomialDegreeTooLarge;
586
587        // Make sure it correctly returns something (i.e. it's not an endless loop)
588        assert!(!err.to_string().is_empty());
589
590        let err = SynthesisError::IoError(io::Error::new(io::ErrorKind::Other, "other"));
591        assert!(
592            err.to_string().contains("other"),
593            "\"{}\" does not contain the underlying error",
594            err
595        );
596    }
597}