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(¶ms.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, ¶ms, &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}