diff --git a/Changelog.md b/Changelog.md index 0977ded..330667b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,14 @@ +# Version 1.2.0, 2020-06, Arithmetic circuits + +Interface definition: +- Added constraint_type to support for both R1CS and arithmetic circuits. +- *(breaking)* Position of the field `ConstraintSystem.info` (unused afaik.). + +Rust: +- *(breaking)* New field `ConstraintSystemOwned.constraint_type` +- A function to validate that a constraint system is indeed an arithmetic circuit (`validate_constraint_type`). + + # Version 1.1.0, 2020-06, Simplifications Interface definition: diff --git a/examples/example.json b/examples/example.json index 552be0d..f3b1f25 100644 --- a/examples/example.json +++ b/examples/example.json @@ -109,7 +109,8 @@ ] } } - ] + ], + "constraint_type": "R1CS" } ], "witnesses": [ diff --git a/rust/src/examples.rs b/rust/src/examples.rs index 5e5ead3..1c481a5 100644 --- a/rust/src/examples.rs +++ b/rust/src/examples.rs @@ -3,7 +3,7 @@ use std::io; use std::mem::size_of; use owned::circuit::CircuitOwned; use owned::variables::VariablesOwned; -use zkinterface_generated::zkinterface::{BilinearConstraint, BilinearConstraintArgs, Message, ConstraintSystem, ConstraintSystemArgs, Root, RootArgs, Variables, VariablesArgs, Witness, WitnessArgs}; +use zkinterface_generated::zkinterface::{BilinearConstraint, BilinearConstraintArgs, Message, ConstraintSystem, ConstraintSystemArgs, Root, RootArgs, Variables, VariablesArgs, Witness, WitnessArgs, ConstraintType}; pub fn example_circuit() -> CircuitOwned { @@ -58,6 +58,7 @@ pub fn write_example_constraints(mut writer: W) -> io::Result<()> let constraints_built = builder.create_vector(&constraints_built); let r1cs = ConstraintSystem::create(&mut builder, &ConstraintSystemArgs { constraints: Some(constraints_built), + constraint_type: ConstraintType::R1CS, info: None, }); diff --git a/rust/src/owned/constraints.rs b/rust/src/owned/constraints.rs index 7163be6..1d5ec0d 100644 --- a/rust/src/owned/constraints.rs +++ b/rust/src/owned/constraints.rs @@ -1,10 +1,12 @@ use owned::variables::VariablesOwned; use serde::{Deserialize, Serialize}; -use zkinterface_generated::zkinterface::ConstraintSystem; +use zkinterface_generated::zkinterface::{ConstraintSystem, ConstraintType}; #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] pub struct ConstraintSystemOwned { pub constraints: Vec, + + pub constraint_type: ConstraintTypeOwned, } #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] @@ -19,6 +21,7 @@ impl<'a> From> for ConstraintSystemOwned { fn from(constraints_ref: ConstraintSystem) -> ConstraintSystemOwned { let mut owned = ConstraintSystemOwned { constraints: vec![], + constraint_type: constraints_ref.constraint_type().into(), }; let cons_ref = constraints_ref.constraints().unwrap(); @@ -34,3 +37,30 @@ impl<'a> From> for ConstraintSystemOwned { owned } } + + +#[allow(non_camel_case_types)] +#[repr(i8)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum ConstraintTypeOwned { + R1CS = 0, + arithmetic = 1, +} + +impl From for ConstraintTypeOwned { + fn from(ct: ConstraintType) -> Self { + match ct { + ConstraintType::R1CS => ConstraintTypeOwned::R1CS, + ConstraintType::arithmetic => ConstraintTypeOwned::arithmetic, + } + } +} + +impl Into for ConstraintTypeOwned { + fn into(self) -> ConstraintType { + match self { + ConstraintTypeOwned::R1CS => ConstraintType::R1CS, + ConstraintTypeOwned::arithmetic => ConstraintType::arithmetic, + } + } +} \ No newline at end of file diff --git a/rust/src/reading.rs b/rust/src/reading.rs index 10a8f69..77358f0 100644 --- a/rust/src/reading.rs +++ b/rust/src/reading.rs @@ -9,6 +9,7 @@ use std::path::Path; use zkinterface_generated::zkinterface::{ BilinearConstraint, Circuit, + ConstraintType, get_size_prefixed_root_as_root, Root, Variables, @@ -299,6 +300,36 @@ impl Messages { constraints: None, } } + + pub fn validate_constraint_type(&self) -> Result<(), String> { + for message in self.into_iter() { + match message.message_as_constraint_system() { + None => continue, + Some(cs) => { + if cs.constraint_type() == ConstraintType::arithmetic { + let constraints = cs.constraints().unwrap(); + for i in 0..constraints.len() { + let constraint = constraints.get(i); + let n_a = constraint.linear_combination_a().unwrap().variable_ids().unwrap().len(); + let n_b = constraint.linear_combination_b().unwrap().variable_ids().unwrap().len(); + let n_c = constraint.linear_combination_c().unwrap().variable_ids().unwrap().len(); + + let is_pure_multiplication = + n_a == 1 && n_b == 1 && n_c == 1; + + let is_pure_linear = + n_a == 0 && n_b == 0; + + if !(is_pure_multiplication || is_pure_linear) { + return Err("The circuit should be fan-in 2 but constraints are not in the correct format.".to_string()); + } + } + } + } + }; + } + Ok(()) + } } pub type Term<'a> = Variable<'a>; diff --git a/rust/src/zkinterface_generated.rs b/rust/src/zkinterface_generated.rs index e24c071..ac1511b 100644 --- a/rust/src/zkinterface_generated.rs +++ b/rust/src/zkinterface_generated.rs @@ -87,6 +87,66 @@ pub fn enum_name_message(e: Message) -> &'static str { } pub struct MessageUnionTableOffset {} +#[allow(non_camel_case_types)] +#[repr(i8)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum ConstraintType { + R1CS = 0, + arithmetic = 1, + +} + +pub const ENUM_MIN_CONSTRAINT_TYPE: i8 = 0; +pub const ENUM_MAX_CONSTRAINT_TYPE: i8 = 1; + +impl<'a> flatbuffers::Follow<'a> for ConstraintType { + type Inner = Self; + #[inline] + fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + flatbuffers::read_scalar_at::(buf, loc) + } +} + +impl flatbuffers::EndianScalar for ConstraintType { + #[inline] + fn to_little_endian(self) -> Self { + let n = i8::to_le(self as i8); + let p = &n as *const i8 as *const ConstraintType; + unsafe { *p } + } + #[inline] + fn from_little_endian(self) -> Self { + let n = i8::from_le(self as i8); + let p = &n as *const i8 as *const ConstraintType; + unsafe { *p } + } +} + +impl flatbuffers::Push for ConstraintType { + type Output = ConstraintType; + #[inline] + fn push(&self, dst: &mut [u8], _rest: &[u8]) { + flatbuffers::emplace_scalar::(dst, *self); + } +} + +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_CONSTRAINT_TYPE:[ConstraintType; 2] = [ + ConstraintType::R1CS, + ConstraintType::arithmetic +]; + +#[allow(non_camel_case_types)] +pub const ENUM_NAMES_CONSTRAINT_TYPE:[&'static str; 2] = [ + "R1CS", + "arithmetic" +]; + +pub fn enum_name_constraint_type(e: ConstraintType) -> &'static str { + let index = e as i8; + ENUM_NAMES_CONSTRAINT_TYPE[index as usize] +} + pub enum CircuitOffset {} #[derive(Copy, Clone, Debug, PartialEq)] @@ -253,16 +313,25 @@ impl<'a> ConstraintSystem<'a> { let mut builder = ConstraintSystemBuilder::new(_fbb); if let Some(x) = args.info { builder.add_info(x); } if let Some(x) = args.constraints { builder.add_constraints(x); } + builder.add_constraint_type(args.constraint_type); builder.finish() } pub const VT_CONSTRAINTS: flatbuffers::VOffsetT = 4; - pub const VT_INFO: flatbuffers::VOffsetT = 6; + pub const VT_CONSTRAINT_TYPE: flatbuffers::VOffsetT = 6; + pub const VT_INFO: flatbuffers::VOffsetT = 8; #[inline] pub fn constraints(&self) -> Option>>> { self._tab.get::>>>>(ConstraintSystem::VT_CONSTRAINTS, None) } + /// Whether this is an R1CS or fan-in-2 arithmetic circuit. + /// A special case is a boolean circuit with XOR and AND gates, + /// then constraint_type == arithmetic and circuit.field_maximum == 1. + #[inline] + pub fn constraint_type(&self) -> ConstraintType { + self._tab.get::(ConstraintSystem::VT_CONSTRAINT_TYPE, Some(ConstraintType::R1CS)).unwrap() + } /// Optional: Any complementary info that may be useful. /// /// Example: human-readable descriptions. @@ -275,6 +344,7 @@ impl<'a> ConstraintSystem<'a> { pub struct ConstraintSystemArgs<'a> { pub constraints: Option>>>>, + pub constraint_type: ConstraintType, pub info: Option>>>>, } impl<'a> Default for ConstraintSystemArgs<'a> { @@ -282,6 +352,7 @@ impl<'a> Default for ConstraintSystemArgs<'a> { fn default() -> Self { ConstraintSystemArgs { constraints: None, + constraint_type: ConstraintType::R1CS, info: None, } } @@ -296,6 +367,10 @@ impl<'a: 'b, 'b> ConstraintSystemBuilder<'a, 'b> { self.fbb_.push_slot_always::>(ConstraintSystem::VT_CONSTRAINTS, constraints); } #[inline] + pub fn add_constraint_type(&mut self, constraint_type: ConstraintType) { + self.fbb_.push_slot::(ConstraintSystem::VT_CONSTRAINT_TYPE, constraint_type, ConstraintType::R1CS); + } + #[inline] pub fn add_info(&mut self, info: flatbuffers::WIPOffset>>>) { self.fbb_.push_slot_always::>(ConstraintSystem::VT_INFO, info); } diff --git a/zkinterface.fbs b/zkinterface.fbs index 1aef8f3..e33289c 100644 --- a/zkinterface.fbs +++ b/zkinterface.fbs @@ -44,12 +44,20 @@ table Circuit { configuration :[KeyValue]; } +// A circuit can be R1CS or arithmetic (fan-in 2 multiplications). +enum ConstraintType : byte { R1CS = 0, arithmetic = 1 } + /// ConstraintSystem represents constraints to be added to the constraint system. /// /// Multiple such messages are equivalent to the concatenation of `constraints` arrays. table ConstraintSystem { constraints :[BilinearConstraint]; + /// Whether this is an R1CS or fan-in-2 arithmetic circuit. + /// A special case is a boolean circuit with XOR and AND gates, + /// then constraint_type == arithmetic and circuit.field_maximum == 1. + constraint_type :ConstraintType; + /// Optional: Any complementary info that may be useful. /// /// Example: human-readable descriptions.