Skip to content

Commit

Permalink
Circuit builder initial ideas impl (#303)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
CPerezz authored Sep 21, 2020
1 parent 89a4659 commit 5b4dee4
Show file tree
Hide file tree
Showing 18 changed files with 345 additions and 18 deletions.
303 changes: 303 additions & 0 deletions src/circuit_builder.rs
Original file line number Diff line number Diff line change
@@ -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<BlsScalar> {
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<Vec<PublicInput>, 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<BlsScalar>;

/// 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<Proof, Error>;

/// 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<Vec<PublicInput>>,
}

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<Vec<PublicInput>, 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<BlsScalar> {
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<Proof, Error> {
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,
)
}
}
2 changes: 1 addition & 1 deletion src/fft/evaluations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Scalar>,
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -53,6 +55,7 @@
mod macros;

mod bit_iterator;
pub mod circuit_builder;
pub mod commitment_scheme;
pub mod constraint_system;
pub mod fft;
Expand Down
16 changes: 14 additions & 2 deletions src/proof_system/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions src/proof_system/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion src/proof_system/widget/arithmetic/proverkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion src/proof_system/widget/arithmetic/verifierkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 5b4dee4

Please sign in to comment.