From 5b4dee40a94e116c350c48137cf3ed887ed68f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20P=C3=A9rez?= <37264926+CPerezz@users.noreply.github.com> Date: Mon, 21 Sep 2020 16:00:29 +0200 Subject: [PATCH] Circuit builder initial ideas impl (#303) * Implement basic Circuit trait idea We need a way to treat the circuits on a generic way which allows us to serialize the preprocessed circuits as well as operate with them as gadgets to fill composers. --- src/circuit_builder.rs | 303 ++++++++++++++++++ src/fft/evaluations.rs | 2 +- src/lib.rs | 5 +- src/proof_system/prover.rs | 16 +- src/proof_system/verifier.rs | 9 + .../widget/arithmetic/proverkey.rs | 2 +- .../widget/arithmetic/verifierkey.rs | 2 +- .../widget/ecc/curve_addition/proverkey.rs | 2 +- .../widget/ecc/curve_addition/verifierkey.rs | 2 +- .../ecc/scalar_mul/fixed_base/proverkey.rs | 2 +- .../ecc/scalar_mul/fixed_base/verifierkey.rs | 2 +- src/proof_system/widget/logic/proverkey.rs | 2 +- src/proof_system/widget/logic/verifierkey.rs | 2 +- src/proof_system/widget/mod.rs | 4 +- .../widget/permutation/proverkey.rs | 2 +- .../widget/permutation/verifierkey.rs | 2 +- src/proof_system/widget/range/proverkey.rs | 2 +- src/proof_system/widget/range/verifierkey.rs | 2 +- 18 files changed, 345 insertions(+), 18 deletions(-) create mode 100644 src/circuit_builder.rs diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs new file mode 100644 index 00000000..ca2a93b0 --- /dev/null +++ b/src/circuit_builder.rs @@ -0,0 +1,303 @@ +//! Tools & traits for PLONK circuits + +use crate::commitment_scheme::kzg10::PublicParameters; +use crate::constraint_system::StandardComposer; +use crate::proof_system::{Proof, ProverKey, VerifierKey}; +use anyhow::{Error, Result}; +use dusk_bls12_381::Scalar as BlsScalar; +use dusk_jubjub::{AffinePoint as JubJubAffine, Scalar as JubJubScalar}; + +/// Circuit inputs +#[derive(Debug, Clone, Copy)] +pub struct CircuitInputs<'a> { + bls_scalars: &'a [BlsScalar], + jubjub_scalars: &'a [JubJubScalar], + jubjub_affines: &'a [JubJubAffine], +} + +/// Public Input +#[derive(Debug, Copy, Clone)] +pub enum PublicInput { + /// Scalar Input + BlsScalar(BlsScalar, usize), + /// Embedded Scalar Input + JubJubScalar(JubJubScalar, usize), + /// Point as Public Input + AffinePoint(JubJubAffine, usize, usize), +} + +impl PublicInput { + #[allow(dead_code)] + fn value(&self) -> Vec { + match self { + PublicInput::BlsScalar(scalar, _) => vec![*scalar], + PublicInput::JubJubScalar(scalar, _) => vec![BlsScalar::from(*scalar)], + PublicInput::AffinePoint(point, _, _) => vec![point.get_x(), point.get_y()], + } + } +} + +/// Circuit representation for a gadget with all of the tools that it +/// should implement. +pub trait Circuit<'a> +where + Self: Sized + Default, +{ + /// Gadget implementation used to fill the composer. + fn gadget( + &mut self, + composer: &mut StandardComposer, + inputs: CircuitInputs, + ) -> Result, Error>; + /// Compiles the circuit by using a function that returns a `Result` + /// with the `ProverKey`, `VerifierKey` and the circuit size. + fn compile( + &mut self, + pub_params: &PublicParameters, + inputs: CircuitInputs, + ) -> Result<(ProverKey, VerifierKey, usize), Error>; + + /// Build PI vector for Proof verifications. + fn build_pi(&self, pub_inputs: &[PublicInput]) -> Vec; + + /// Get the circuit size of the implemented circuit. + fn circuit_size(&self) -> usize; + + /// Generates a proof using the provided `CircuitInputs` & `ProverKey` instances. + fn gen_proof( + &mut self, + pub_params: &PublicParameters, + prover_key: &ProverKey, + inputs: CircuitInputs, + transcript_initialisation: &'static [u8], + ) -> Result; + + /// Verifies a proof using the provided `CircuitInputs` & `VerifierKey` instances. + fn verify_proof( + &self, + pub_params: &PublicParameters, + verifier_key: &VerifierKey, + transcript_initialisation: &'static [u8], + proof: &Proof, + pub_inputs: &[PublicInput], + ) -> Result<(), Error>; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::constraint_system::StandardComposer; + use crate::proof_system::{Prover, ProverKey, Verifier, VerifierKey}; + use anyhow::{Error, Result}; + + // Implements a circuit that checks: + // 1) a + b = c where C is a PI + // 2) a <= 2^6 + // 3) b <= 2^5 + // 4) a * b = d where D is a PI + pub struct TestCircuit { + circuit_size: usize, + pi_constructor: Option>, + } + + impl Default for TestCircuit { + fn default() -> Self { + TestCircuit { + circuit_size: 0, + pi_constructor: None, + } + } + } + + impl<'a> Circuit<'a> for TestCircuit { + fn gadget( + &mut self, + composer: &mut StandardComposer, + inputs: CircuitInputs, + ) -> Result, Error> { + let mut pi = Vec::new(); + let a = composer.add_input(inputs.bls_scalars[0]); + let b = composer.add_input(inputs.bls_scalars[1]); + // Make first constraint a + b = c + pi.push(PublicInput::BlsScalar( + -inputs.bls_scalars[2], + composer.circuit_size(), + )); + composer.poly_gate( + a, + b, + composer.zero_var, + BlsScalar::zero(), + BlsScalar::one(), + BlsScalar::one(), + BlsScalar::zero(), + BlsScalar::zero(), + -inputs.bls_scalars[2], + ); + + // Check that a and b are in range + composer.range_gate(a, 1 << 6); + composer.range_gate(b, 1 << 5); + // Make second constraint a * b = d + pi.push(PublicInput::BlsScalar( + -inputs.bls_scalars[3], + composer.circuit_size(), + )); + composer.poly_gate( + a, + b, + composer.zero_var, + BlsScalar::one(), + BlsScalar::zero(), + BlsScalar::zero(), + BlsScalar::one(), + BlsScalar::zero(), + -inputs.bls_scalars[3], + ); + + self.circuit_size = composer.circuit_size(); + Ok(pi) + } + fn compile( + &mut self, + pub_params: &PublicParameters, + compile_inputs: CircuitInputs, + ) -> Result<(ProverKey, VerifierKey, usize), Error> { + // Setup PublicParams + let (ck, _) = pub_params.trim(1 << 9)?; + // Generate & save `ProverKey` with some random values. + let mut prover = Prover::new(b"TestCircuit"); + // Set size & Pi builder + self.pi_constructor = Some(self.gadget(prover.mut_cs(), compile_inputs)?); + prover.preprocess(&ck)?; + + // Generate & save `VerifierKey` with some random values. + let mut verifier = Verifier::new(b"TestCircuit"); + self.gadget(verifier.mut_cs(), compile_inputs)?; + verifier.preprocess(&ck).unwrap(); + Ok(( + prover + .prover_key + .expect("Unexpected error. Missing VerifierKey in compilation") + .clone(), + verifier + .verifier_key + .expect("Unexpected error. Missing VerifierKey in compilation"), + self.circuit_size, + )) + } + + fn build_pi(&self, pub_inputs: &[PublicInput]) -> Vec { + let mut pi = vec![BlsScalar::zero(); self.circuit_size]; + self.pi_constructor + .as_ref() + .expect("Circuit must be compiled before building PI vectors.") + .iter() + .enumerate() + .for_each(|(idx, pi_constr)| { + match pi_constr { + PublicInput::BlsScalar(_, pos) => pi[*pos] = pub_inputs[idx].value()[0], + PublicInput::JubJubScalar(_, pos) => pi[*pos] = pub_inputs[idx].value()[0], + PublicInput::AffinePoint(_, pos_x, pos_y) => { + let (coord_x, coord_y) = + (pub_inputs[idx].value()[0], pub_inputs[idx].value()[1]); + pi[*pos_x] = coord_x; + pi[*pos_y] = coord_y; + } + }; + }); + pi + } + + fn circuit_size(&self) -> usize { + self.circuit_size + } + + fn gen_proof( + &mut self, + pub_params: &PublicParameters, + prover_key: &ProverKey, + inputs: CircuitInputs, + transcript_initialisation: &'static [u8], + ) -> Result { + let (ck, _) = pub_params.trim(1 << 9)?; + // New Prover instance + let mut prover = Prover::new(transcript_initialisation); + // Fill witnesses for Prover + self.gadget(prover.mut_cs(), inputs)?; + // Add ProverKey to Prover + prover.prover_key = Some(prover_key.clone()); + prover.prove(&ck) + } + + fn verify_proof( + &self, + pub_params: &PublicParameters, + verifier_key: &VerifierKey, + transcript_initialisation: &'static [u8], + proof: &Proof, + pub_inputs: &[PublicInput], + ) -> Result<(), Error> { + let (_, vk) = pub_params.trim(1 << 9)?; + // New Verifier instance + let mut verifier = Verifier::new(transcript_initialisation); + verifier.verifier_key = Some(*verifier_key); + verifier.verify(proof, &vk, &self.build_pi(pub_inputs)) + } + } + + #[test] + fn test_full() -> Result<(), Error> { + // Generate CRS + let pub_params = PublicParameters::setup(1 << 10, &mut rand::thread_rng())?; + // Generate circuit compilation params + let a = BlsScalar::from(25u64); + let b = BlsScalar::from(5u64); + let c = BlsScalar::from(30u64); + let d = BlsScalar::from(125u64); + let inputs = CircuitInputs { + bls_scalars: &[a, b, c, d], + jubjub_scalars: &[], + jubjub_affines: &[], + }; + // Initialize the circuit + let mut circuit = TestCircuit::default(); + { + // Compile the circuit + let (prover_key, verifier_key, _) = circuit.compile(&pub_params, inputs)?; + // Write the keys + use std::fs::File; + use std::io::Write; + let mut prover_file = File::create("pk_testcirc")?; + prover_file.write(prover_key.to_bytes()[..].as_ref())?; + let mut verifier_file = File::create("vk_testcirc")?; + verifier_file.write(verifier_key.to_bytes().as_ref())?; + }; + + // Read ProverKey + let prover_key = ProverKey::from_bytes(&std::fs::read("pk_testcirc")?[..]).unwrap(); + // Read VerifierKey + let verifier_key = VerifierKey::from_bytes(&std::fs::read("vk_testcirc")?[..]).unwrap(); + + // Generate new inputs + // Generate circuit compilation params + let a = BlsScalar::from(20u64); + let b = BlsScalar::from(5u64); + let c = BlsScalar::from(25u64); + let d = BlsScalar::from(100u64); + let inputs2 = CircuitInputs { + bls_scalars: &[a, b, c, d], + jubjub_scalars: &[], + jubjub_affines: &[], + }; + let public_inputs2 = vec![PublicInput::BlsScalar(-c, 0), PublicInput::BlsScalar(-d, 0)]; + let proof = circuit.gen_proof(&pub_params, &prover_key, inputs2, b"TestCirc")?; + circuit.verify_proof( + &pub_params, + &verifier_key, + b"TestCirc", + &proof, + &public_inputs2, + ) + } +} diff --git a/src/fft/evaluations.rs b/src/fft/evaluations.rs index 01a823a2..6b0baf14 100644 --- a/src/fft/evaluations.rs +++ b/src/fft/evaluations.rs @@ -9,7 +9,7 @@ use core::ops::{Add, AddAssign, DivAssign, Index, Mul, MulAssign, Sub, SubAssign use dusk_bls12_381::Scalar; /// Stores a polynomial in evaluation form. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Clone)] pub struct Evaluations { /// The evaluations of a polynomial over the domain `D` pub evals: Vec, diff --git a/src/lib.rs b/src/lib.rs index 892438ba..ec43bca3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,9 @@ #![allow(clippy::many_single_char_names)] // Bool expr are usually easier to read with match statements. #![allow(clippy::match_bool)] -#![deny(intra_doc_link_resolution_failure)] +// Clippy does not have `broken_intra_doc_links` as a known lint. +#![allow(unknown_lints)] +#![deny(broken_intra_doc_links)] #![deny(missing_debug_implementations)] #![deny(missing_docs)] #![deny(unsafe_code)] @@ -53,6 +55,7 @@ mod macros; mod bit_iterator; +pub mod circuit_builder; pub mod commitment_scheme; pub mod constraint_system; pub mod fft; diff --git a/src/proof_system/prover.rs b/src/proof_system/prover.rs index 840fbfb9..51434ecd 100644 --- a/src/proof_system/prover.rs +++ b/src/proof_system/prover.rs @@ -58,6 +58,16 @@ impl Prover { preprocessed_transcript: Transcript::new(label), } } + + /// Creates a new prover object with some expected size. + pub fn with_expected_size(label: &'static [u8], size: usize) -> Prover { + Prover { + prover_key: None, + cs: StandardComposer::with_expected_size(size), + preprocessed_transcript: Transcript::new(label), + } + } + /// Returns the number of gates in the circuit pub fn circuit_size(&self) -> usize { self.cs.circuit_size() @@ -205,8 +215,10 @@ impl Prover { let alpha = transcript.challenge_scalar(b"alpha"); let range_sep_challenge = transcript.challenge_scalar(b"range separation challenge"); let logic_sep_challenge = transcript.challenge_scalar(b"logic separation challenge"); - let fixed_base_sep_challenge = transcript.challenge_scalar(b"fixed base separation challenge"); - let var_base_sep_challenge = transcript.challenge_scalar(b"variable base separation challenge"); + let fixed_base_sep_challenge = + transcript.challenge_scalar(b"fixed base separation challenge"); + let var_base_sep_challenge = + transcript.challenge_scalar(b"variable base separation challenge"); let t_poly = quotient_poly::compute( &domain, diff --git a/src/proof_system/verifier.rs b/src/proof_system/verifier.rs index 4dd69177..b78471c7 100644 --- a/src/proof_system/verifier.rs +++ b/src/proof_system/verifier.rs @@ -38,6 +38,15 @@ impl Verifier { } } + /// Creates a new verifier object with some expected size. + pub fn with_expected_size(label: &'static [u8], size: usize) -> Verifier { + Verifier { + verifier_key: None, + cs: StandardComposer::with_expected_size(size), + preprocessed_transcript: Transcript::new(label), + } + } + /// Returns the number of gates in the circuit pub fn circuit_size(&self) -> usize { self.cs.circuit_size() diff --git a/src/proof_system/widget/arithmetic/proverkey.rs b/src/proof_system/widget/arithmetic/proverkey.rs index 8a4b7b7b..a4e7ffcf 100644 --- a/src/proof_system/widget/arithmetic/proverkey.rs +++ b/src/proof_system/widget/arithmetic/proverkey.rs @@ -4,7 +4,7 @@ use crate::fft::{Evaluations, Polynomial}; use dusk_bls12_381::Scalar; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub struct ProverKey { pub q_m: (Polynomial, Evaluations), pub q_l: (Polynomial, Evaluations), diff --git a/src/proof_system/widget/arithmetic/verifierkey.rs b/src/proof_system/widget/arithmetic/verifierkey.rs index 377d392b..2fe63904 100644 --- a/src/proof_system/widget/arithmetic/verifierkey.rs +++ b/src/proof_system/widget/arithmetic/verifierkey.rs @@ -5,7 +5,7 @@ use crate::commitment_scheme::kzg10::Commitment; use crate::proof_system::linearisation_poly::ProofEvaluations; use dusk_bls12_381::{G1Affine, Scalar}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct VerifierKey { pub q_m: Commitment, pub q_l: Commitment, diff --git a/src/proof_system/widget/ecc/curve_addition/proverkey.rs b/src/proof_system/widget/ecc/curve_addition/proverkey.rs index ab32159d..34731cf8 100644 --- a/src/proof_system/widget/ecc/curve_addition/proverkey.rs +++ b/src/proof_system/widget/ecc/curve_addition/proverkey.rs @@ -5,7 +5,7 @@ use crate::fft::{Evaluations, Polynomial}; use dusk_bls12_381::Scalar; use dusk_jubjub::EDWARDS_D; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub struct ProverKey { pub q_variable_group_add: (Polynomial, Evaluations), } diff --git a/src/proof_system/widget/ecc/curve_addition/verifierkey.rs b/src/proof_system/widget/ecc/curve_addition/verifierkey.rs index 2cfe801b..122e0e59 100644 --- a/src/proof_system/widget/ecc/curve_addition/verifierkey.rs +++ b/src/proof_system/widget/ecc/curve_addition/verifierkey.rs @@ -6,7 +6,7 @@ use crate::proof_system::linearisation_poly::ProofEvaluations; use dusk_bls12_381::{G1Affine, Scalar}; use dusk_jubjub::EDWARDS_D; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct VerifierKey { pub q_variable_group_add: Commitment, } diff --git a/src/proof_system/widget/ecc/scalar_mul/fixed_base/proverkey.rs b/src/proof_system/widget/ecc/scalar_mul/fixed_base/proverkey.rs index 6e9fc86d..c9bdfaf1 100644 --- a/src/proof_system/widget/ecc/scalar_mul/fixed_base/proverkey.rs +++ b/src/proof_system/widget/ecc/scalar_mul/fixed_base/proverkey.rs @@ -6,7 +6,7 @@ use crate::fft::{Evaluations, Polynomial}; use dusk_bls12_381::Scalar; use dusk_jubjub::EDWARDS_D; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub struct ProverKey { pub q_l: (Polynomial, Evaluations), pub q_r: (Polynomial, Evaluations), diff --git a/src/proof_system/widget/ecc/scalar_mul/fixed_base/verifierkey.rs b/src/proof_system/widget/ecc/scalar_mul/fixed_base/verifierkey.rs index d2bc15b9..5422867b 100644 --- a/src/proof_system/widget/ecc/scalar_mul/fixed_base/verifierkey.rs +++ b/src/proof_system/widget/ecc/scalar_mul/fixed_base/verifierkey.rs @@ -7,7 +7,7 @@ use crate::proof_system::linearisation_poly::ProofEvaluations; use dusk_bls12_381::{G1Affine, Scalar}; use dusk_jubjub::EDWARDS_D; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct VerifierKey { pub q_l: Commitment, pub q_r: Commitment, diff --git a/src/proof_system/widget/logic/proverkey.rs b/src/proof_system/widget/logic/proverkey.rs index 23c1e9e6..5766d63e 100644 --- a/src/proof_system/widget/logic/proverkey.rs +++ b/src/proof_system/widget/logic/proverkey.rs @@ -7,7 +7,7 @@ use crate::fft::{Evaluations, Polynomial}; use dusk_bls12_381::Scalar; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub struct ProverKey { pub q_c: (Polynomial, Evaluations), pub q_logic: (Polynomial, Evaluations), diff --git a/src/proof_system/widget/logic/verifierkey.rs b/src/proof_system/widget/logic/verifierkey.rs index 94183599..00e77210 100644 --- a/src/proof_system/widget/logic/verifierkey.rs +++ b/src/proof_system/widget/logic/verifierkey.rs @@ -6,7 +6,7 @@ use crate::commitment_scheme::kzg10::Commitment; use crate::proof_system::linearisation_poly::ProofEvaluations; use dusk_bls12_381::{G1Affine, Scalar}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct VerifierKey { pub q_c: Commitment, pub q_logic: Commitment, diff --git a/src/proof_system/widget/mod.rs b/src/proof_system/widget/mod.rs index f4372035..9a7aaebf 100644 --- a/src/proof_system/widget/mod.rs +++ b/src/proof_system/widget/mod.rs @@ -15,7 +15,7 @@ use serde::de::Visitor; use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; /// PLONK circuit proving key -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct ProverKey { /// Circuit size pub n: usize, @@ -39,7 +39,7 @@ pub struct ProverKey { } /// PLONK circuit verification key -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct VerifierKey { /// Circuit size pub n: usize, diff --git a/src/proof_system/widget/permutation/proverkey.rs b/src/proof_system/widget/permutation/proverkey.rs index 2c5f2b9c..a1af6f39 100644 --- a/src/proof_system/widget/permutation/proverkey.rs +++ b/src/proof_system/widget/permutation/proverkey.rs @@ -6,7 +6,7 @@ use crate::fft::{EvaluationDomain, Evaluations, Polynomial}; use crate::permutation::constants::{K1, K2, K3}; use dusk_bls12_381::Scalar; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub struct ProverKey { pub left_sigma: (Polynomial, Evaluations), pub right_sigma: (Polynomial, Evaluations), diff --git a/src/proof_system/widget/permutation/verifierkey.rs b/src/proof_system/widget/permutation/verifierkey.rs index eb33d342..67448081 100644 --- a/src/proof_system/widget/permutation/verifierkey.rs +++ b/src/proof_system/widget/permutation/verifierkey.rs @@ -8,7 +8,7 @@ use crate::permutation::constants::{K1, K2, K3}; use crate::proof_system::linearisation_poly::ProofEvaluations; use dusk_bls12_381::{G1Affine, Scalar}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct VerifierKey { pub left_sigma: Commitment, pub right_sigma: Commitment, diff --git a/src/proof_system/widget/range/proverkey.rs b/src/proof_system/widget/range/proverkey.rs index 09c82516..5a253936 100644 --- a/src/proof_system/widget/range/proverkey.rs +++ b/src/proof_system/widget/range/proverkey.rs @@ -5,7 +5,7 @@ use super::delta; use crate::fft::{Evaluations, Polynomial}; use dusk_bls12_381::Scalar; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub struct ProverKey { pub q_range: (Polynomial, Evaluations), } diff --git a/src/proof_system/widget/range/verifierkey.rs b/src/proof_system/widget/range/verifierkey.rs index 4be81035..182fa86b 100644 --- a/src/proof_system/widget/range/verifierkey.rs +++ b/src/proof_system/widget/range/verifierkey.rs @@ -6,7 +6,7 @@ use crate::commitment_scheme::kzg10::Commitment; use crate::proof_system::linearisation_poly::ProofEvaluations; use dusk_bls12_381::{G1Affine, Scalar}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct VerifierKey { pub q_range: Commitment, }