use std::path::Path;
use std::marker::PhantomData;
use zkinterface::{ConstraintSystem, Witness, Variables, KeyValue, StatementBuilder, Sink, WorkspaceSink, BilinearConstraint};
use bellman as bl;
use bellman::{Variable, Index, LinearCombination, SynthesisError};
use ff::PrimeField;
use super::export::{write_scalar, to_zkif_constraint};
use std::mem;
const DEFAULT_CONSTRAINTS_PER_MESSAGE: usize = 100000;
pub struct ZkifCS<Scalar: PrimeField> {
pub constraints_per_message: usize,
statement: StatementBuilder<WorkspaceSink>,
constraints: ConstraintSystem,
proving: bool,
witness: Vec<u8>,
phantom: PhantomData<Scalar>,
}
impl<Scalar: PrimeField> ZkifCS<Scalar> {
/// Must call finish() to finalize the files in the workspace.
pub fn new(workspace: impl AsRef<Path>, proving: bool) -> Self {
let sink = WorkspaceSink::new(workspace).unwrap();
let statement = StatementBuilder::new(sink);
ZkifCS {
constraints_per_message: DEFAULT_CONSTRAINTS_PER_MESSAGE,
statement,
constraints: ConstraintSystem::default(),
proving,
witness: vec![],
phantom: PhantomData,
}
}
pub fn finish(mut self, name: &str) -> zkinterface::Result<()> {
if self.constraints.constraints.len() > 0 {
self.statement.push_constraints(self.constraints)?;
}
if self.proving {
let wit = Witness {
assigned_variables: Variables {
variable_ids: self.statement.header.list_witness_ids(),
values: Some(self.witness.clone()),
}
};
self.statement.push_witness(wit)?;
}
let negative_one = Scalar::one().neg();
let mut field_maximum = Vec::<u8>::new();
write_scalar(&negative_one, &mut field_maximum);
self.statement.header.field_maximum = Some(field_maximum);
self.statement.header.configuration = Some(vec![
KeyValue {
key: "name".to_string(),
text: Some(name.to_string()),
data: None,
number: 0,
}]);
self.statement.finish_header()
}
fn push_constraint(&mut self, co: BilinearConstraint) -> zkinterface::Result<()> {
self.constraints.constraints.push(co);
if self.constraints.constraints.len() >= self.constraints_per_message {
let cs = mem::replace(&mut self.constraints, ConstraintSystem::default());
self.statement.push_constraints(cs)?;
}
Ok(())
}
}
impl<Scalar: PrimeField> bl::ConstraintSystem<Scalar> for ZkifCS<Scalar> {
type Root = Self;
fn alloc<F, A, AR>(&mut self, _annotation: A, f: F) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR, AR: Into<String>
{
let zkid = self.statement.allocate_var();
if self.proving {
let fr = f()?;
write_scalar(&fr, &mut self.witness);
}
Ok(Variable::new_unchecked(Index::Aux(zkid as usize)))
}
fn alloc_input<F, A, AR>(&mut self, _annotation: A, f: F) -> Result<Variable, SynthesisError>
where F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR, AR: Into<String>
{
let value = f()?;
let mut encoded = vec![];
write_scalar(&value, &mut encoded);
let zkid = self.statement.allocate_instance_var(&encoded);
Ok(Variable::new_unchecked(Index::Input(zkid as usize)))
}
fn enforce<A, AR, LA, LB, LC>(&mut self, _annotation: A, a: LA, b: LB, c: LC)
where A: FnOnce() -> AR, AR: Into<String>,
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>
{
let a = a(LinearCombination::zero());
let b = b(LinearCombination::zero());
let c = c(LinearCombination::zero());
let co = to_zkif_constraint(a, b, c);
self.push_constraint(co).unwrap();
}
fn push_namespace<NR, N>(&mut self, _name_fn: N) where NR: Into<String>, N: FnOnce() -> NR {}
fn pop_namespace(&mut self) {}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
#[test]
fn test_zkif_cs() -> zkinterface::Result<()> {
use std::path::Path;
use std::fs::remove_dir_all;
use bellman::ConstraintSystem as BLCS;
use bls12_381::Scalar;
use zkinterface::{Messages, CircuitHeader, ConstraintSystem, Workspace};
use zkinterface::consumers::simulator::Simulator;
let dir = Path::new("local/test/");
let _ = remove_dir_all(dir);
let mut cs = ZkifCS::<Scalar>::new(dir, true);
// Create 10 constraints to store in chunks of 4.
cs.constraints_per_message = 4;
let n_constraints = 10;
let (xv, yv) = (10, 11);
let one = ZkifCS::<Scalar>::one();
let x = cs.alloc_input(
|| "x", || Ok(Scalar::from(xv)))?;
let y = cs.alloc(
|| "y", || Ok(Scalar::from(yv)))?;
let z = cs.alloc(
|| "z", || Ok(Scalar::from(xv * (xv + yv))))?;
for i in 0..n_constraints {
cs.enforce(
|| "constraint",
|zero| zero + x,
|zero| zero + x + y + (Scalar::from(i), one),
|zero| zero + z + (Scalar::from(i * xv), one),
);
}
cs.finish("test")?;
let ws = Workspace::from_dir(dir)?;
let mut simulator = Simulator::default();
for msg in ws.iter_messages() {
simulator.ingest_message(&msg);
}
assert_eq!(simulator.get_violations().len(), 0);
let messages = ws.read_all_messages();
let expected = Messages {
circuit_headers: vec![CircuitHeader {
instance_variables: Variables {
variable_ids: vec![1],
values: Some(vec![10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
},
free_variable_id: 4,
field_maximum: Some(vec![0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115]),
configuration: Some(vec![KeyValue { key: "name".to_string(), text: Some("test".to_string()), data: None, number: 0 }]),
}],
constraint_systems: vec![
// First chunk of 4 constraints.
ConstraintSystem {
constraints: vec![
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
]
},
// Second chunk of 4 constraints.
ConstraintSystem {
constraints: vec![
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
]
},
// Final chunk of 2 constraints.
ConstraintSystem {
constraints: vec![
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
BilinearConstraint { linear_combination_a: Variables { variable_ids: vec![1], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_b: Variables { variable_ids: vec![1, 2, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, linear_combination_c: Variables { variable_ids: vec![3, 0], values: Some(vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } },
]
}
],
witnesses: vec![Witness {
assigned_variables: Variables {
variable_ids: vec![2, 3],
values: Some(vec![
11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
}
}],
};
assert_eq!(messages, expected);
Ok(())
}