ysbell/
lib.rs

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