impl circuit synthesizer and serialized circuit

This commit is contained in:
collin 2020-08-13 01:21:39 -07:00
parent 6016f47d66
commit 76009cb843
6 changed files with 348 additions and 196 deletions

View File

@ -140,13 +140,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
/// Synthesizes the circuit without program input to verify correctness.
pub fn compile_constraints<CS: ConstraintSystem<F>>(self, cs: &mut CS) -> Result<OutputBytes, CompilerError> {
let path = self.main_file_path;
let input = self.program_input.empty();
generate_constraints::<F, G, CS>(cs, self.program, input, &self.imported_programs).map_err(|mut error| {
error.set_path(path);
generate_constraints::<F, G, CS>(cs, self.program, self.program_input, &self.imported_programs).map_err(
|mut error| {
error.set_path(path);
error
})
error
},
)
}
/// Synthesizes the circuit for test functions with program input.

View File

@ -1,4 +1,9 @@
use crate::{cli::*, cli_types::*, errors::CLIError};
use crate::{
cli::*,
cli_types::*,
errors::CLIError,
synthesizer::{CircuitSynthesizer, SerializedCircuit},
};
use leo_compiler::{compiler::Compiler, group::targets::edwards_bls12::EdwardsGroupType};
use leo_package::{
inputs::*,
@ -7,13 +12,8 @@ use leo_package::{
source::{LibFile, MainFile, LIB_FILE_NAME, MAIN_FILE_NAME, SOURCE_DIRECTORY_NAME},
};
use snarkos_algorithms::snark::groth16::KeypairAssembly;
use snarkos_curves::{bls12_377::Bls12_377, edwards_bls12::Fq};
use snarkos_models::{
curves::PairingEngine,
gadgets::r1cs::{ConstraintSystem, Index},
};
use snarkos_utilities::serialize::*;
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir};
@ -102,13 +102,12 @@ impl CLI for BuildCommand {
// Generate the program on the constraint system and verify correctness
{
let mut cs = KeypairAssembly::<Bls12_377> {
num_inputs: 0,
num_aux: 0,
num_constraints: 0,
let mut cs = CircuitSynthesizer::<Bls12_377> {
at: vec![],
bt: vec![],
ct: vec![],
input_assignment: vec![],
aux_assignment: vec![],
};
let temporary_program = program.clone();
let output = temporary_program.compile_constraints(&mut cs)?;
@ -116,8 +115,8 @@ impl CLI for BuildCommand {
log::debug!("Number of constraints - {:#?}", cs.num_constraints());
// Serialize the circuit
let keypair_object = SerializedKeypairAssembly::from(cs);
let json = keypair_object.to_json_string().unwrap();
let circuit_object = SerializedCircuit::from(cs);
let json = circuit_object.to_json_string().unwrap();
// println!("json: {}", json);
// Write serialized circuit to circuit `.json` file.
@ -128,9 +127,9 @@ impl CLI for BuildCommand {
let serialized = circuit_file.read_from(&package_path)?;
// Deserialize the circuit
let deserialized = SerializedKeypairAssembly::from_json_string(&serialized).unwrap();
let keypair_assembly = KeypairAssembly::<Bls12_377>::try_from(deserialized).unwrap();
println!("deserialized {:?}", keypair_assembly.num_constraints);
let deserialized = SerializedCircuit::from_json_string(&serialized).unwrap();
let _circuit_synthesizer = CircuitSynthesizer::<Bls12_377>::try_from(deserialized).unwrap();
// println!("deserialized {:?}", circuit_synthesizer.num_constraints());
}
// If a checksum file exists, check if it differs from the new checksum
@ -161,178 +160,3 @@ impl CLI for BuildCommand {
Ok(None)
}
}
use num_bigint::BigUint;
use serde::{Deserialize, Serialize};
use snarkos_errors::curves::FieldError;
use snarkos_models::curves::{Field, Fp256, Fp256Parameters};
use std::str::FromStr;
#[derive(Serialize, Deserialize)]
pub struct SerializedKeypairAssembly {
pub num_inputs: usize,
pub num_aux: usize,
pub num_constraints: usize,
pub at: Vec<Vec<(SerializedField, SerializedIndex)>>,
pub bt: Vec<Vec<(SerializedField, SerializedIndex)>>,
pub ct: Vec<Vec<(SerializedField, SerializedIndex)>>,
}
impl SerializedKeypairAssembly {
pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
Ok(serde_json::to_string_pretty(&self)?)
}
pub fn from_json_string(json: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(json)
}
}
impl<E: PairingEngine> From<KeypairAssembly<E>> for SerializedKeypairAssembly {
fn from(assembly: KeypairAssembly<E>) -> Self {
fn get_serialized_constraints<E: PairingEngine>(
constraints: &Vec<(E::Fr, Index)>,
) -> Vec<(SerializedField, SerializedIndex)> {
let mut serialized = vec![];
for &(ref coeff, index) in constraints.iter() {
let field = SerializedField::from(coeff);
let index = SerializedIndex::from(index);
serialized.push((field, index))
}
serialized
}
let mut result = Self {
num_inputs: assembly.num_inputs,
num_aux: assembly.num_aux,
num_constraints: assembly.num_constraints,
at: vec![],
bt: vec![],
ct: vec![],
};
for i in 0..assembly.num_constraints {
// Serialize at[i]
let a_constraints = get_serialized_constraints::<E>(&assembly.at[i]);
result.at.push(a_constraints);
// Serialize bt[i]
let b_constraints = get_serialized_constraints::<E>(&assembly.bt[i]);
result.bt.push(b_constraints);
// Serialize ct[i]
let c_constraints = get_serialized_constraints::<E>(&assembly.ct[i]);
result.ct.push(c_constraints);
}
result
}
}
impl TryFrom<SerializedKeypairAssembly> for KeypairAssembly<Bls12_377> {
type Error = FieldError;
fn try_from(serialized: SerializedKeypairAssembly) -> Result<KeypairAssembly<Bls12_377>, Self::Error> {
fn get_deserialized_constraints(
constraints: &Vec<(SerializedField, SerializedIndex)>,
) -> Result<Vec<(<Bls12_377 as PairingEngine>::Fr, Index)>, FieldError> {
let mut deserialized = vec![];
for &(ref serialized_coeff, ref serialized_index) in constraints.iter() {
let field = <Bls12_377 as PairingEngine>::Fr::try_from(serialized_coeff)?;
let index = Index::from(serialized_index);
deserialized.push((field, index));
}
Ok(deserialized)
}
let mut result = KeypairAssembly::<Bls12_377> {
num_inputs: serialized.num_inputs,
num_aux: serialized.num_aux,
num_constraints: serialized.num_constraints,
at: vec![],
bt: vec![],
ct: vec![],
};
for i in 0..serialized.num_constraints {
// Deserialize at[i]
let a_constraints = get_deserialized_constraints(&serialized.at[i])?;
result.at.push(a_constraints);
// Deserialize bt[i]
let b_constraints = get_deserialized_constraints(&serialized.bt[i])?;
result.bt.push(b_constraints);
// Deserialize ct[i]
let c_constraints = get_deserialized_constraints(&serialized.ct[i])?;
result.ct.push(c_constraints);
}
Ok(result)
}
}
#[derive(Serialize, Deserialize)]
pub struct SerializedField(pub String);
impl<F: Field> From<&F> for SerializedField {
fn from(field: &F) -> Self {
// write field to buffer
let mut buf = Vec::new();
field.write(&mut buf).unwrap();
// convert to big integer
let f_bigint = BigUint::from_bytes_le(&buf);
let f_string = f_bigint.to_str_radix(10);
Self(f_string)
}
}
impl<P: Fp256Parameters> TryFrom<&SerializedField> for Fp256<P> {
type Error = FieldError;
fn try_from(serialized: &SerializedField) -> Result<Self, Self::Error> {
Fp256::<P>::from_str(&serialized.0)
}
}
#[derive(Serialize, Deserialize)]
pub enum SerializedIndex {
Input(usize),
Aux(usize),
}
impl From<Index> for SerializedIndex {
fn from(index: Index) -> Self {
match index {
Index::Input(idx) => Self::Input(idx),
Index::Aux(idx) => Self::Aux(idx),
}
}
}
impl From<&SerializedIndex> for Index {
fn from(serialized_index: &SerializedIndex) -> Self {
match serialized_index {
SerializedIndex::Input(idx) => Index::Input(idx.clone()),
SerializedIndex::Aux(idx) => Index::Aux(idx.clone()),
}
}
}

View File

@ -7,3 +7,4 @@ pub mod cli_types;
pub mod commands;
pub mod errors;
pub mod logger;
pub mod synthesizer;

View File

@ -0,0 +1,95 @@
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, PairingEngine},
gadgets::r1cs::{ConstraintSystem, Index, LinearCombination, Variable},
};
pub struct CircuitSynthesizer<E: PairingEngine> {
// Constraints
pub(crate) at: Vec<Vec<(E::Fr, Index)>>,
pub(crate) bt: Vec<Vec<(E::Fr, Index)>>,
pub(crate) ct: Vec<Vec<(E::Fr, Index)>>,
// Assignments of variables
pub(crate) input_assignment: Vec<E::Fr>,
pub(crate) aux_assignment: Vec<E::Fr>,
}
impl<E: PairingEngine> ConstraintSystem<E::Fr> for CircuitSynthesizer<E> {
type Root = Self;
#[inline]
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
let index = self.aux_assignment.len();
self.aux_assignment.push(f()?);
Ok(Variable::new_unchecked(Index::Aux(index)))
}
#[inline]
fn alloc_input<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
let index = self.input_assignment.len();
self.input_assignment.push(f()?);
Ok(Variable::new_unchecked(Index::Input(index)))
}
#[inline]
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<E::Fr>) -> LinearCombination<E::Fr>,
LB: FnOnce(LinearCombination<E::Fr>) -> LinearCombination<E::Fr>,
LC: FnOnce(LinearCombination<E::Fr>) -> LinearCombination<E::Fr>,
{
let num_constraints = self.num_constraints();
self.at.push(Vec::new());
self.bt.push(Vec::new());
self.ct.push(Vec::new());
push_constraints(a(LinearCombination::zero()), &mut self.at, num_constraints);
push_constraints(b(LinearCombination::zero()), &mut self.bt, num_constraints);
push_constraints(c(LinearCombination::zero()), &mut self.ct, num_constraints);
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
// Do nothing; we don't care about namespaces in this context.
}
fn pop_namespace(&mut self) {
// Do nothing; we don't care about namespaces in this context.
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
fn num_constraints(&self) -> usize {
self.at.len()
}
}
fn push_constraints<F: Field>(l: LinearCombination<F>, constraints: &mut [Vec<(F, Index)>], this_constraint: usize) {
for (var, coeff) in l.as_ref() {
match var.get_unchecked() {
Index::Input(i) => constraints[this_constraint].push((*coeff, Index::Input(i))),
Index::Aux(i) => constraints[this_constraint].push((*coeff, Index::Aux(i))),
}
}
}

5
leo/synthesizer/mod.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod circuit_synthesizer;
pub use self::circuit_synthesizer::*;
pub mod serialized_circuit;
pub use self::serialized_circuit::*;

View File

@ -0,0 +1,226 @@
use crate::synthesizer::CircuitSynthesizer;
use num_bigint::BigUint;
use serde::{Deserialize, Serialize};
use snarkos_curves::bls12_377::Bls12_377;
use snarkos_errors::curves::FieldError;
use snarkos_models::{
curves::{Field, Fp256, Fp256Parameters, PairingEngine},
gadgets::r1cs::{ConstraintSystem, Index},
};
use std::{convert::TryFrom, str::FromStr};
#[derive(Serialize, Deserialize)]
pub struct SerializedCircuit {
pub num_inputs: usize,
pub num_aux: usize,
pub num_constraints: usize,
pub input_assignment: Vec<SerializedField>,
pub aux_assignment: Vec<SerializedField>,
pub at: Vec<Vec<(SerializedField, SerializedIndex)>>,
pub bt: Vec<Vec<(SerializedField, SerializedIndex)>>,
pub ct: Vec<Vec<(SerializedField, SerializedIndex)>>,
}
impl SerializedCircuit {
pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
Ok(serde_json::to_string_pretty(&self)?)
}
pub fn from_json_string(json: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(json)
}
}
impl<E: PairingEngine> From<CircuitSynthesizer<E>> for SerializedCircuit {
fn from(synthesizer: CircuitSynthesizer<E>) -> Self {
fn get_serialized_assignments<E: PairingEngine>(assignments: &Vec<E::Fr>) -> Vec<SerializedField> {
let mut serialized = vec![];
for assignment in assignments {
let field = SerializedField::from(assignment);
serialized.push(field);
}
serialized
}
fn get_serialized_constraints<E: PairingEngine>(
constraints: &Vec<(E::Fr, Index)>,
) -> Vec<(SerializedField, SerializedIndex)> {
let mut serialized = vec![];
for &(ref coeff, index) in constraints {
let field = SerializedField::from(coeff);
let index = SerializedIndex::from(index);
serialized.push((field, index))
}
serialized
}
let num_inputs = synthesizer.input_assignment.len();
let num_aux = synthesizer.aux_assignment.len();
let num_constraints = synthesizer.num_constraints();
let mut result = Self {
num_inputs,
num_aux,
num_constraints,
// Assignments
input_assignment: vec![],
aux_assignment: vec![],
// Constraints
at: vec![],
bt: vec![],
ct: vec![],
};
// Serialize assignments
result.input_assignment = get_serialized_assignments::<E>(&synthesizer.input_assignment);
result.aux_assignment = get_serialized_assignments::<E>(&synthesizer.aux_assignment);
// Serialize constraints
for i in 0..num_constraints {
// Serialize at[i]
let a_constraints = get_serialized_constraints::<E>(&synthesizer.at[i]);
result.at.push(a_constraints);
// Serialize bt[i]
let b_constraints = get_serialized_constraints::<E>(&synthesizer.bt[i]);
result.bt.push(b_constraints);
// Serialize ct[i]
let c_constraints = get_serialized_constraints::<E>(&synthesizer.ct[i]);
result.ct.push(c_constraints);
}
result
}
}
impl TryFrom<SerializedCircuit> for CircuitSynthesizer<Bls12_377> {
type Error = FieldError;
fn try_from(serialized: SerializedCircuit) -> Result<CircuitSynthesizer<Bls12_377>, Self::Error> {
fn get_deserialized_assignments(
assignments: &Vec<SerializedField>,
) -> Result<Vec<<Bls12_377 as PairingEngine>::Fr>, FieldError> {
let mut deserialized = vec![];
for serialized_assignment in assignments {
let field = <Bls12_377 as PairingEngine>::Fr::try_from(serialized_assignment)?;
deserialized.push(field);
}
Ok(deserialized)
}
fn get_deserialized_constraints(
constraints: &Vec<(SerializedField, SerializedIndex)>,
) -> Result<Vec<(<Bls12_377 as PairingEngine>::Fr, Index)>, FieldError> {
let mut deserialized = vec![];
for &(ref serialized_coeff, ref serialized_index) in constraints {
let field = <Bls12_377 as PairingEngine>::Fr::try_from(serialized_coeff)?;
let index = Index::from(serialized_index);
deserialized.push((field, index));
}
Ok(deserialized)
}
let mut result = CircuitSynthesizer::<Bls12_377> {
input_assignment: vec![],
aux_assignment: vec![],
at: vec![],
bt: vec![],
ct: vec![],
};
// Deserialize assignments
result.input_assignment = get_deserialized_assignments(&serialized.input_assignment)?;
result.aux_assignment = get_deserialized_assignments(&serialized.aux_assignment)?;
// Deserialize constraints
for i in 0..serialized.num_constraints {
// Deserialize at[i]
let a_constraints = get_deserialized_constraints(&serialized.at[i])?;
result.at.push(a_constraints);
// Deserialize bt[i]
let b_constraints = get_deserialized_constraints(&serialized.bt[i])?;
result.bt.push(b_constraints);
// Deserialize ct[i]
let c_constraints = get_deserialized_constraints(&serialized.ct[i])?;
result.ct.push(c_constraints);
}
Ok(result)
}
}
#[derive(Serialize, Deserialize)]
pub struct SerializedField(pub String);
impl<F: Field> From<&F> for SerializedField {
fn from(field: &F) -> Self {
// write field to buffer
let mut buf = Vec::new();
field.write(&mut buf).unwrap();
// convert to big integer
let f_bigint = BigUint::from_bytes_le(&buf);
let f_string = f_bigint.to_str_radix(10);
Self(f_string)
}
}
impl<P: Fp256Parameters> TryFrom<&SerializedField> for Fp256<P> {
type Error = FieldError;
fn try_from(serialized: &SerializedField) -> Result<Self, Self::Error> {
Fp256::<P>::from_str(&serialized.0)
}
}
#[derive(Serialize, Deserialize)]
pub enum SerializedIndex {
Input(usize),
Aux(usize),
}
impl From<Index> for SerializedIndex {
fn from(index: Index) -> Self {
match index {
Index::Input(idx) => Self::Input(idx),
Index::Aux(idx) => Self::Aux(idx),
}
}
}
impl From<&SerializedIndex> for Index {
fn from(serialized_index: &SerializedIndex) -> Self {
match serialized_index {
SerializedIndex::Input(idx) => Index::Input(idx.clone()),
SerializedIndex::Aux(idx) => Index::Aux(idx.clone()),
}
}
}