From e507d72c9bb14eafe1750429a0b7fe709e45c12a Mon Sep 17 00:00:00 2001 From: Victor Carrillo Redondo Date: Tue, 14 Jun 2022 13:40:08 +0200 Subject: [PATCH] plonk's custom gates in circom --- .gitignore | 1 + circom/src/main.rs | 8 +- constraint_generation/src/execute.rs | 5 +- .../src/execution_data/executed_template.rs | 49 ++++- .../src/constraint_simplification.rs | 12 +- constraint_list/src/lib.rs | 4 + constraint_list/src/r1cs_porting.rs | 71 ++++++- constraint_writers/src/r1cs_writer.rs | 198 ++++++++++++++++-- dag/src/lib.rs | 47 ++++- dag/src/map_to_constraint_list.rs | 33 ++- dag/src/r1cs_porting.rs | 64 +++++- parser/src/lang.lalrpop | 42 ++-- parser/src/lib.rs | 3 +- .../src/abstract_syntax_tree/ast.rs | 7 +- .../src/program_library/program_merger.rs | 3 +- .../src/program_library/template_data.rs | 6 + 16 files changed, 482 insertions(+), 71 deletions(-) diff --git a/.gitignore b/.gitignore index 2c94578..4934423 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ parser/target/ program_structure/target/ type_analysis/target/ .idea/ +.vscode/ .DS_Store Cargo.lock diff --git a/circom/src/main.rs b/circom/src/main.rs index 115c6a5..acb1a85 100644 --- a/circom/src/main.rs +++ b/circom/src/main.rs @@ -47,10 +47,10 @@ fn start() -> Result<(), ()> { c_flag: user_input.c_flag(), wasm_flag: user_input.wasm_flag(), wat_flag: user_input.wat_flag(), - js_folder: user_input.js_folder().to_string(), - wasm_name: user_input.wasm_name().to_string(), - c_folder: user_input.c_folder().to_string(), - c_run_name: user_input.c_run_name().to_string(), + js_folder: user_input.js_folder().to_string(), + wasm_name: user_input.wasm_name().to_string(), + c_folder: user_input.c_folder().to_string(), + c_run_name: user_input.c_run_name().to_string(), c_file: user_input.c_file().to_string(), dat_file: user_input.dat_file().to_string(), wat_file: user_input.wat_file().to_string(), diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index 89228c2..70b8c3b 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -484,6 +484,7 @@ fn execute_signal_declaration( ) { use SignalType::*; if let Option::Some(node) = actual_node { + node.add_ordered_signal(signal_name, dimensions); match signal_type { Input => { environment_shortcut_add_input(environment, signal_name, dimensions); @@ -984,6 +985,7 @@ fn execute_template_call( debug_assert!(runtime.block_type == BlockType::Known); let is_main = std::mem::replace(&mut runtime.public_inputs, vec![]); let is_parallel = program_archive.get_template_data(id).is_parallel(); + let is_custom_gate = program_archive.get_template_data(id).is_custom_gate(); let args_names = program_archive.get_template_data(id).get_name_of_params(); let template_body = program_archive.get_template_data(id).get_body_as_vec(); let mut args_to_values = BTreeMap::new(); @@ -1010,7 +1012,8 @@ fn execute_template_call( instantiation_name, args_to_values, code, - is_parallel + is_parallel, + is_custom_gate )); let ret = execute_sequence_of_statements( template_body, diff --git a/constraint_generation/src/execution_data/executed_template.rs b/constraint_generation/src/execution_data/executed_template.rs index 8299093..22ceed3 100644 --- a/constraint_generation/src/execution_data/executed_template.rs +++ b/constraint_generation/src/execution_data/executed_template.rs @@ -21,14 +21,16 @@ pub struct ExecutedTemplate { pub report_name: String, pub inputs: SignalCollector, pub outputs: SignalCollector, - pub constraints: Vec, pub intermediates: SignalCollector, + pub ordered_signals: Vec, + pub constraints: Vec, pub components: ComponentCollector, pub number_of_components: usize, pub public_inputs: HashSet, pub parameter_instances: ParameterContext, pub is_parallel: bool, pub has_parallel_sub_cmp: bool, + pub is_custom_gate: bool, connexions: Vec, } @@ -40,6 +42,7 @@ impl ExecutedTemplate { instance: ParameterContext, code: Statement, is_parallel: bool, + is_custom_gate: bool ) -> ExecutedTemplate { let public_inputs: HashSet<_> = public.iter().cloned().collect(); ExecutedTemplate { @@ -47,15 +50,17 @@ impl ExecutedTemplate { public_inputs, is_parallel, has_parallel_sub_cmp: false, + is_custom_gate, code: code.clone(), template_name: name, parameter_instances: instance, inputs: SignalCollector::new(), outputs: SignalCollector::new(), intermediates: SignalCollector::new(), + ordered_signals: Vec::new(), + constraints: Vec::new(), components: ComponentCollector::new(), number_of_components: 0, - constraints: Vec::new(), connexions: Vec::new(), } } @@ -82,6 +87,27 @@ impl ExecutedTemplate { self.intermediates.push((intermediate_name.to_string(), dimensions.to_vec())); } + pub fn add_ordered_signal(&mut self, signal_name: &str, dimensions: &[usize]) { + fn generate_symbols(name: String, current: usize, dimensions: &[usize]) -> Vec { + let symbol_name = name.clone(); + if current == dimensions.len() { + vec![name] + } else { + let mut generated_symbols = vec![]; + let mut index = 0; + while index < dimensions[current] { + let new_name = format!("{}[{}]", symbol_name, index); + generated_symbols.append(&mut generate_symbols(new_name, current + 1, dimensions)); + index += 1; + } + generated_symbols + } + } + for signal in generate_symbols(signal_name.to_string(), 0, dimensions) { + self.ordered_signals.push(signal); + } + } + pub fn add_component(&mut self, component_name: &str, dimensions: &[usize]) { self.components.push((component_name.to_string(), dimensions.to_vec())); self.number_of_components += dimensions.iter().fold(1, |p, c| p * (*c)); @@ -112,7 +138,24 @@ impl ExecutedTemplate { } pub fn insert_in_dag(&mut self, dag: &mut DAG) { - dag.add_node(self.report_name.clone(), self.is_parallel); + let parameters = { + let mut parameters = vec![]; + for (_, data) in self.parameter_instances.clone() { + let (_, values) = data.destruct(); + for value in as_big_int(values) { + parameters.push(value); + } + } + parameters + }; // repeated code from function build_arguments in export_to_circuit + + dag.add_node( + self.report_name.clone(), + parameters, + self.ordered_signals.clone(), // pensar si calcularlo en este momento para no hacer clone + self.is_parallel, + self.is_custom_gate + ); self.build_signals(dag); self.build_connexions(dag); self.build_constraints(dag); diff --git a/constraint_list/src/constraint_simplification.rs b/constraint_list/src/constraint_simplification.rs index 3b4c5c6..96bd42a 100644 --- a/constraint_list/src/constraint_simplification.rs +++ b/constraint_list/src/constraint_simplification.rs @@ -517,7 +517,7 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { relevant }; - let linear_substitutions = if apply_linear { + let linear_substitutions = if remove_unused { let now = SystemTime::now(); let (subs, mut cons) = linear_simplification( &mut substitution_log, @@ -563,15 +563,17 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { crate::state_utils::empty_encoding_constraints(&mut smp.dag_encoding); let _dur = now.elapsed().unwrap().as_millis(); // println!("Storages built in {} ms", dur); - no_rounds -= 1; + if remove_unused { + no_rounds -= 1; + } (with_linear, storage) }; let mut round_id = 0; let _ = round_id; let mut linear = with_linear; - let mut apply_round = apply_linear && no_rounds > 0 && !linear.is_empty(); - let mut non_linear_map = if apply_round || remove_unused{ + let mut apply_round = remove_unused && no_rounds > 0 && !linear.is_empty(); + let mut non_linear_map = if apply_round || remove_unused { // println!("Building non-linear map"); let now = SystemTime::now(); let non_linear_map = build_non_linear_signal_map(&constraint_storage); @@ -615,7 +617,7 @@ pub fn simplification(smp: &mut Simplifier) -> (ConstraintStorage, SignalMap) { } for constraint in linear { - if remove_unused{ + if remove_unused { let signals = C::take_cloned_signals(&constraint); let c_id = constraint_storage.add_constraint(constraint); for signal in signals { diff --git a/constraint_list/src/lib.rs b/constraint_list/src/lib.rs index ca16397..0b5f11b 100644 --- a/constraint_list/src/lib.rs +++ b/constraint_list/src/lib.rs @@ -26,8 +26,12 @@ pub struct SignalInfo { } pub struct EncodingNode { pub id: usize, + pub name: String, + pub parameters: Vec, pub signals: Vec, + pub ordered_signals: Vec, pub non_linear: LinkedList, + pub is_custom_gate: bool, } pub struct EncodingEdge { diff --git a/constraint_list/src/r1cs_porting.rs b/constraint_list/src/r1cs_porting.rs index ac83e12..45e6a86 100644 --- a/constraint_list/src/r1cs_porting.rs +++ b/constraint_list/src/r1cs_porting.rs @@ -1,5 +1,5 @@ -use super::{ConstraintList, C}; -use constraint_writers::r1cs_writer::{ConstraintSection, HeaderData, R1CSWriter, SignalSection}; +use super::{ConstraintList, C, EncodingIterator, SignalMap}; +use constraint_writers::r1cs_writer::{ConstraintSection, CustomGatesAppliedData, HeaderData, R1CSWriter, SignalSection}; pub fn port_r1cs(list: &ConstraintList, output: &str) -> Result<(), ()> { use constraint_writers::log_writer::Log; @@ -45,7 +45,72 @@ pub fn port_r1cs(list: &ConstraintList, output: &str) -> Result<(), ()> { for id in list.get_witness_as_vec() { SignalSection::write_signal_usize(&mut signal_section, id)?; } - let _r1cs = signal_section.end_section()?; + let r1cs = signal_section.end_section()?; + + let mut custom_gates_used_section = R1CSWriter::start_custom_gates_used_section(r1cs)?; + let (usage_data, occurring_order) = { + let mut usage_data = vec![]; + let mut occurring_order = vec![]; + for node in &list.dag_encoding.nodes { + if node.is_custom_gate { + let mut name = node.name.clone(); + occurring_order.push(name.clone()); + while name.pop() != Some('(') {}; + usage_data.push((name, node.parameters.clone())); + } + } + (usage_data, occurring_order) + }; + custom_gates_used_section.write_custom_gates_usages(usage_data)?; + let r1cs = custom_gates_used_section.end_section()?; + + let mut custom_gates_applied_section = R1CSWriter::start_custom_gates_applied_section(r1cs)?; + let application_data = { + fn find_indexes( + occurring_order: Vec, + application_data: Vec<(String, Vec)> + ) -> CustomGatesAppliedData { + let mut new_application_data = vec![]; + for (custom_gate_name, signals) in application_data { + let mut index = 0; + while occurring_order[index] != custom_gate_name { + index += 1; + } + new_application_data.push((index, signals)); + } + new_application_data + } + + fn iterate( + iterator: EncodingIterator, + map: &SignalMap, + application_data: &mut Vec<(String, Vec)> + ) { + let node = &iterator.encoding.nodes[iterator.node_id]; + if node.is_custom_gate { + let mut signals = vec![]; + for signal in &node.ordered_signals { + let new_signal = signal + iterator.offset; + let signal_numbering = map.get(&new_signal).unwrap(); + signals.push(*signal_numbering); + } + application_data.push((node.name.clone(), signals)); + } else { + for edge in EncodingIterator::edges(&iterator) { + let next = EncodingIterator::next(&iterator, edge); + iterate(next, map, application_data); + } + } + } + + let mut application_data = vec![]; + let iterator = EncodingIterator::new(&list.dag_encoding); + iterate(iterator, &list.signal_map, &mut application_data); + find_indexes(occurring_order, application_data) + }; + custom_gates_applied_section.write_custom_gates_applications(application_data)?; + let _r1cs = custom_gates_applied_section.end_section()?; + Log::print(&log); Ok(()) } diff --git a/constraint_writers/src/r1cs_writer.rs b/constraint_writers/src/r1cs_writer.rs index c3c2c5e..765a0fe 100644 --- a/constraint_writers/src/r1cs_writer.rs +++ b/constraint_writers/src/r1cs_writer.rs @@ -3,12 +3,15 @@ use std::collections::HashMap; use std::fs::File; use std::io::{BufWriter, Seek, SeekFrom, Write}; +const SECTIONS: u8 = 5; const MAGIC: &[u8] = b"r1cs"; const VERSION: &[u8] = &[1, 0, 0, 0]; -const NUMBER_OF_SECTIONS: &[u8] = &[3, 0, 0, 0]; +const NUMBER_OF_SECTIONS: &[u8] = &[SECTIONS, 0, 0, 0]; const HEADER_TYPE: &[u8] = &[1, 0, 0, 0]; const CONSTRAINT_TYPE: &[u8] = &[2, 0, 0, 0]; const WIRE2LABEL_TYPE: &[u8] = &[3, 0, 0, 0]; +const CUSTOM_GATES_USED_TYPE: &[u8] = &[4, 0, 0, 0]; +const CUSTOM_GATES_APPLIED_TYPE: &[u8] = &[5, 0, 0, 0]; const PLACE_HOLDER: &[u8] = &[3, 3, 3, 3, 3, 3, 3, 3]; fn into_format(number: &[u8], with_bytes: usize) -> (Vec, usize) { @@ -33,6 +36,7 @@ fn initialize_section(writer: &mut BufWriter, header: &[u8]) -> Result, go_back: u64, size: usize) -> Result<(), ()> { let go_back_1 = writer.seek(SeekFrom::Current(0)).map_err(|_err| {})?; writer.seek(SeekFrom::Start(go_back)).map_err(|_err| {})?; @@ -45,10 +49,7 @@ fn end_section(writer: &mut BufWriter, go_back: u64, size: usize) -> Resul fn obtain_linear_combination_block( linear_combination: &HashMap, field_size: usize, -) -> (Vec, usize) -where - T: AsRef<[u8]> + std::cmp::Ord + std::hash::Hash, -{ +) -> (Vec, usize) where T: AsRef<[u8]> + std::cmp::Ord + std::hash::Hash { let mut block = Vec::new(); let non_zero_factors = BigInt::from(linear_combination.len()); let mut size = 0; @@ -76,10 +77,7 @@ fn write_constraint( b: &HashMap, c: &HashMap, field_size: usize, -) -> Result -where - T: AsRef<[u8]> + std::cmp::Ord + std::hash::Hash, -{ +) -> Result where T: AsRef<[u8]> + std::cmp::Ord + std::hash::Hash { let (block_a, size_a) = obtain_linear_combination_block(a, field_size); let (block_b, size_b) = obtain_linear_combination_block(b, field_size); let (block_c, size_c) = obtain_linear_combination_block(c, field_size); @@ -105,16 +103,18 @@ fn initialize_file(writer: &mut BufWriter) -> Result<(), ()> { pub struct R1CSWriter { field_size: usize, writer: BufWriter, - sections: [bool; 3], + sections: [bool; SECTIONS as usize] } + pub struct HeaderSection { writer: BufWriter, go_back: u64, size: usize, index: usize, field_size: usize, - sections: [bool; 3], + sections: [bool; SECTIONS as usize] } + pub struct ConstraintSection { writer: BufWriter, number_of_constraints: usize, @@ -122,25 +122,45 @@ pub struct ConstraintSection { size: usize, index: usize, field_size: usize, - sections: [bool; 3], + sections: [bool; SECTIONS as usize] } + pub struct SignalSection { writer: BufWriter, go_back: u64, size: usize, index: usize, field_size: usize, - sections: [bool; 3], + sections: [bool; SECTIONS as usize] +} + +pub struct CustomGatesUsedSection { + writer: BufWriter, + go_back: u64, + size: usize, + index: usize, + field_size: usize, + sections: [bool; SECTIONS as usize] +} + +pub struct CustomGatesAppliedSection { + writer: BufWriter, + go_back: u64, + size: usize, + index: usize, + field_size: usize, + sections: [bool; SECTIONS as usize] } impl R1CSWriter { pub fn new(output_file: String, field_size: usize) -> Result { - let sections = [false; 3]; + let sections = [false; SECTIONS as usize]; let mut writer = File::create(&output_file).map_err(|_err| {}).map(|f| BufWriter::new(f))?; initialize_file(&mut writer)?; Result::Ok(R1CSWriter { writer, sections, field_size }) } + pub fn start_header_section(mut r1cs: R1CSWriter) -> Result { let start = initialize_section(&mut r1cs.writer, HEADER_TYPE)?; Result::Ok(HeaderSection { @@ -152,6 +172,7 @@ impl R1CSWriter { sections: r1cs.sections, }) } + pub fn start_constraints_section(mut r1cs: R1CSWriter) -> Result { let start = initialize_section(&mut r1cs.writer, CONSTRAINT_TYPE)?; Result::Ok(ConstraintSection { @@ -164,6 +185,7 @@ impl R1CSWriter { sections: r1cs.sections, }) } + pub fn start_signal_section(mut r1cs: R1CSWriter) -> Result { let start = initialize_section(&mut r1cs.writer, WIRE2LABEL_TYPE)?; Result::Ok(SignalSection { @@ -175,6 +197,30 @@ impl R1CSWriter { sections: r1cs.sections, }) } + + pub fn start_custom_gates_used_section(mut r1cs: R1CSWriter) -> Result { + let start = initialize_section(&mut r1cs.writer, CUSTOM_GATES_USED_TYPE)?; + Result::Ok(CustomGatesUsedSection { + writer: r1cs.writer, + go_back: start, + size: 0, + index: 3, + field_size: r1cs.field_size, + sections: r1cs.sections + }) + } + + pub fn start_custom_gates_applied_section(mut r1cs: R1CSWriter) -> Result { + let start = initialize_section(&mut r1cs.writer, CUSTOM_GATES_APPLIED_TYPE)?; + Result::Ok(CustomGatesAppliedSection { + writer: r1cs.writer, + go_back: start, + size: 0, + index: 4, + field_size: r1cs.field_size, + sections: r1cs.sections + }) + } } pub struct HeaderData { @@ -186,6 +232,7 @@ pub struct HeaderData { pub number_of_labels: usize, pub number_of_constraints: usize, } + impl HeaderSection { pub fn write_section(&mut self, data: HeaderData) -> Result<(), ()> { let (field_stream, bytes_field) = bigint_as_bytes(&data.field, self.field_size); @@ -250,12 +297,17 @@ impl ConstraintSection { self.number_of_constraints += 1; Result::Ok(()) } + pub fn end_section(mut self) -> Result { end_section(&mut self.writer, self.go_back, self.size)?; let mut sections = self.sections; let index = self.index; sections[index] = true; - Result::Ok(R1CSWriter { writer: self.writer, field_size: self.field_size, sections }) + Result::Ok(R1CSWriter { + writer: self.writer, + field_size: self.field_size, + sections + }) } pub fn constraints_written(&self) -> usize { @@ -264,24 +316,130 @@ impl ConstraintSection { } impl SignalSection { - pub fn write_signal(&mut self, bytes: &T) -> Result<(), ()> - where - T: AsRef<[u8]>, - { + pub fn write_signal( + &mut self, + bytes: &T + ) -> Result<(), ()> where T: AsRef<[u8]> { let (bytes, size) = into_format(bytes.as_ref(), 8); self.size += size; self.writer.write_all(&bytes).map_err(|_err| {})?; self.writer.flush().map_err(|_err| {}) } + pub fn write_signal_usize(&mut self, signal: usize) -> Result<(), ()> { let (_, as_bytes) = BigInt::from(signal).to_bytes_le(); SignalSection::write_signal(self, &as_bytes) } + + pub fn end_section(mut self) -> Result { + end_section(&mut self.writer, self.go_back, self.size)?; + let mut sections = self.sections; + let index = self.index; + sections[index] = true; + Result::Ok(R1CSWriter { + writer: self.writer, + field_size: self.field_size, + sections + }) + } +} + +pub type CustomGatesUsedData = Vec<(String, Vec)>; +impl CustomGatesUsedSection { + pub fn write_custom_gates_usages(&mut self, data: CustomGatesUsedData) -> Result<(), ()> { + let no_custom_gates = data.len(); + let (no_custom_gates_stream, no_custom_gates_size) = + bigint_as_bytes(&BigInt::from(no_custom_gates), 4); + self.size += no_custom_gates_size; + self.writer.write_all(&no_custom_gates_stream).map_err(|_err| {})?; + self.writer.flush().map_err(|_err| {})?; + + for custom_gate in data { + let custom_gate_name = custom_gate.0; + let custom_gate_name_stream = custom_gate_name.as_bytes(); + self.size += custom_gate_name_stream.len() + 1; + self.writer.write_all(custom_gate_name_stream).map_err(|_err| {})?; + self.writer.write_all(&[0]).map_err(|_err| {})?; + self.writer.flush().map_err(|_err| {})?; + + let custom_gate_parameters = custom_gate.1; + let no_custom_gate_parameters = custom_gate_parameters.len(); + let (no_custom_gate_parameters_stream, no_custom_gate_parameters_size) = + bigint_as_bytes(&BigInt::from(no_custom_gate_parameters), 4); + self.size += no_custom_gate_parameters_size; + self.writer.write_all(&no_custom_gate_parameters_stream).map_err(|_err| {})?; + self.writer.flush().map_err(|_err| {})?; + + for parameter in custom_gate_parameters { + let (parameter_stream, parameter_size) = bigint_as_bytes(¶meter, self.field_size); + self.size += parameter_size; + self.writer.write(¶meter_stream).map_err(|_err| {})?; + self.writer.flush().map_err(|_err| {})?; + } + } + + Result::Ok(()) + } + pub fn end_section(mut self) -> Result { end_section(&mut self.writer, self.go_back, self.size)?; let mut sections = self.sections; let index = self.index; sections[index] = true; - Result::Ok(R1CSWriter { writer: self.writer, field_size: self.field_size, sections }) + Result::Ok(R1CSWriter { + writer: self.writer, + field_size: self.field_size, + sections + }) + } +} + +pub type CustomGatesAppliedData = Vec<(usize, Vec)>; +impl CustomGatesAppliedSection { + pub fn write_custom_gates_applications(&mut self, data: CustomGatesAppliedData) -> Result<(), ()> { + let no_custom_gate_applications = data.len(); + let (no_custom_gate_applications_stream, no_custom_gate_applications_size) = + bigint_as_bytes(&BigInt::from(no_custom_gate_applications), 4); + self.size += no_custom_gate_applications_size; + self.writer.write_all(&no_custom_gate_applications_stream).map_err(|_err| {})?; + self.writer.flush().map_err(|_err| {})?; + + for custom_gate_application in data { + let custom_gate_index = custom_gate_application.0; + let (custom_gate_index_stream, custom_gate_index_size) = + bigint_as_bytes(&BigInt::from(custom_gate_index), 4); + self.size += custom_gate_index_size; + self.writer.write_all(&custom_gate_index_stream).map_err(|_err| {})?; + self.writer.flush().map_err(|_err| {})?; + + let custom_gate_signals = custom_gate_application.1; + let no_custom_gate_signals = custom_gate_signals.len(); + let (no_custom_gate_signals_stream, no_custom_gate_signals_size) = + bigint_as_bytes(&BigInt::from(no_custom_gate_signals), 4); + self.size += no_custom_gate_signals_size; + self.writer.write_all(&no_custom_gate_signals_stream).map_err(|_err| {})?; + self.writer.flush().map_err(|_err| {})?; + + for signal in custom_gate_signals { + let (signal_stream, signal_size) = bigint_as_bytes(&BigInt::from(signal), 4); + self.size += signal_size; + self.writer.write(&signal_stream).map_err(|_err| {})?; + self.writer.flush().map_err(|_err| {})?; + } + } + + Result::Ok(()) + } + + pub fn end_section(mut self) -> Result { + end_section(&mut self.writer, self.go_back, self.size)?; + let mut sections = self.sections; + let index = self.index; + sections[index] = true; + Result::Ok(R1CSWriter { + writer: self.writer, + field_size: self.field_size, + sections + }) } } diff --git a/dag/src/lib.rs b/dag/src/lib.rs index a04becd..fabdb73 100644 --- a/dag/src/lib.rs +++ b/dag/src/lib.rs @@ -137,6 +137,7 @@ impl Edge { pub struct Node { entry: Edge, template_name: String, + parameters: Vec, number_of_signals: usize, number_of_components: usize, intermediates_length: usize, @@ -144,24 +145,37 @@ pub struct Node { inputs_length: usize, outputs_length: usize, signal_correspondence: HashMap, + ordered_signals: Vec, locals: HashSet, forbidden_if_main: HashSet, io_signals: Vec, constraints: Vec, is_parallel: bool, has_parallel_sub_cmp: bool, + is_custom_gate: bool, number_of_subcomponents_indexes: usize, } impl Node { - fn new(id: usize, template_name: String, is_parallel:bool) -> Node { - Node { + fn new( + id: usize, + template_name: String, + parameters: Vec, + ordered_signals: Vec, + is_parallel: bool, + is_custom_gate: bool + ) -> Node { + Node { template_name, entry: Edge::new_entry(id), - number_of_components: 1, - is_parallel, - has_parallel_sub_cmp: false, + parameters, + number_of_components: 1, + ordered_signals, + is_parallel, + has_parallel_sub_cmp: false, + is_custom_gate, forbidden_if_main: vec![0].into_iter().collect(), - ..Node::default() } + ..Node::default() + } } fn add_input(&mut self, name: String, is_public: bool) { @@ -206,6 +220,10 @@ impl Node { self.number_of_subcomponents_indexes = number_scmp } + pub fn parameters(&self) -> &Vec { + &self.parameters + } + fn is_local_signal(&self, s: usize) -> bool { self.locals.contains(&s) } @@ -254,6 +272,10 @@ impl Node { self.has_parallel_sub_cmp } + pub fn is_custom_gate(&self) -> bool { + self.is_custom_gate + } + pub fn number_of_subcomponents_indexes(&self) -> usize { self.number_of_subcomponents_indexes } @@ -324,9 +346,18 @@ impl DAG { } } - pub fn add_node(&mut self, template_name: String, is_parallel:bool) -> usize { + pub fn add_node( + &mut self, + template_name: String, + parameters: Vec, + ordered_signals: Vec, + is_parallel: bool, + is_custom_gate: bool + ) -> usize { let id = self.nodes.len(); - self.nodes.push(Node::new(id, template_name, is_parallel)); + self.nodes.push( + Node::new(id, template_name, parameters, ordered_signals, is_parallel, is_custom_gate) + ); self.adjacency.push(vec![]); id } diff --git a/dag/src/map_to_constraint_list.rs b/dag/src/map_to_constraint_list.rs index 10140b1..2f14e00 100644 --- a/dag/src/map_to_constraint_list.rs +++ b/dag/src/map_to_constraint_list.rs @@ -9,11 +9,19 @@ struct CHolder { constant_equalities: LinkedList, } -fn map_tree(tree: &Tree, witness: &mut Vec, c_holder: &mut CHolder) -> usize { +fn map_tree( + tree: &Tree, + witness: &mut Vec, + c_holder: &mut CHolder, + forbidden: &mut HashSet +) -> usize { let mut no_constraints = 0; for signal in &tree.signals { Vec::push(witness, *signal); + if tree.dag.nodes[tree.node_id].is_custom_gate { + forbidden.insert(*signal); + } } for constraint in &tree.constraints { @@ -30,7 +38,7 @@ fn map_tree(tree: &Tree, witness: &mut Vec, c_holder: &mut CHolder) -> us for edge in Tree::get_edges(tree) { let subtree = Tree::go_to_subtree(tree, edge); - no_constraints += map_tree(&subtree, witness, c_holder); + no_constraints += map_tree(&subtree, witness, c_holder, forbidden); } no_constraints } @@ -62,6 +70,7 @@ fn produce_encoding( fn map_node_to_encoding(id: usize, node: Node) -> EncodingNode { let mut signals = Vec::new(); + let mut ordered_signals = Vec::new(); let locals = node.locals; let mut non_linear = LinkedList::new(); for c in node.constraints { @@ -69,6 +78,12 @@ fn map_node_to_encoding(id: usize, node: Node) -> EncodingNode { LinkedList::push_back(&mut non_linear, c); } } + + for signal in node.ordered_signals { + let signal_numbering = node.signal_correspondence.get(&signal).unwrap(); + ordered_signals.push(*signal_numbering); + } + for (name, id) in node.signal_correspondence { if HashSet::contains(&locals, &id) { let new_signal = SignalInfo { name, id }; @@ -77,7 +92,15 @@ fn map_node_to_encoding(id: usize, node: Node) -> EncodingNode { } signals.sort_by(|a, b| a.id.cmp(&b.id)); - EncodingNode { id, signals, non_linear } + EncodingNode { + id, + name: node.template_name, + parameters: node.parameters, + signals, + ordered_signals, + non_linear, + is_custom_gate: node.is_custom_gate, + } } fn map_edge_to_encoding(edge: Edge) -> EncodingEdge { @@ -94,10 +117,10 @@ pub fn map(dag: DAG, flags: SimplificationFlags) -> ConstraintList { let no_public_inputs = dag.public_inputs(); let no_public_outputs = dag.public_outputs(); let no_private_inputs = dag.private_inputs(); - let forbidden = dag.get_main().unwrap().forbidden_if_main.clone(); + let mut forbidden = dag.get_main().unwrap().forbidden_if_main.clone(); let mut c_holder = CHolder::default(); let mut signal_map = vec![0]; - let no_constraints = map_tree(&Tree::new(&dag), &mut signal_map, &mut c_holder); + let no_constraints = map_tree(&Tree::new(&dag), &mut signal_map, &mut c_holder, &mut forbidden); let max_signal = Vec::len(&signal_map); let name_encoding = produce_encoding(no_constraints, init_id, dag.nodes, dag.adjacency); let _dur = now.elapsed().unwrap().as_millis(); diff --git a/dag/src/r1cs_porting.rs b/dag/src/r1cs_porting.rs index 4becca1..56cbebd 100644 --- a/dag/src/r1cs_porting.rs +++ b/dag/src/r1cs_porting.rs @@ -1,6 +1,6 @@ use super::{Constraint, Tree, DAG}; use constraint_writers::log_writer::Log; -use constraint_writers::r1cs_writer::{ConstraintSection, HeaderData, R1CSWriter}; +use constraint_writers::r1cs_writer::{ConstraintSection, CustomGatesAppliedData, HeaderData, R1CSWriter}; pub fn write(dag: &DAG, output: &str) -> Result<(), ()> { let tree = Tree::new(dag); @@ -33,11 +33,71 @@ pub fn write(dag: &DAG, output: &str) -> Result<(), ()> { let mut header_section = R1CSWriter::start_header_section(r1cs)?; header_section.write_section(header_data)?; let r1cs = header_section.end_section()?; + let mut signal_section = R1CSWriter::start_signal_section(r1cs)?; for signal in 0..labels { signal_section.write_signal_usize(signal)?; } - let _r1cs = signal_section.end_section()?; + let r1cs = signal_section.end_section()?; + + let mut custom_gates_used_section = R1CSWriter::start_custom_gates_used_section(r1cs)?; + let (usage_data, occurring_order) = { + let mut usage_data = vec![]; + let mut occurring_order = vec![]; + for node in &dag.nodes { + if node.is_custom_gate() { + let mut name = node.template_name.clone(); + occurring_order.push(name.clone()); + while name.pop() != Some('(') {}; + usage_data.push((name, node.parameters().clone())); + } + } + (usage_data, occurring_order) + }; + custom_gates_used_section.write_custom_gates_usages(usage_data)?; + let r1cs = custom_gates_used_section.end_section()?; + + let mut custom_gates_applied_section = R1CSWriter::start_custom_gates_applied_section(r1cs)?; + let application_data = { + fn find_indexes( + occurring_order: Vec, + application_data: Vec<(String, Vec)> + ) -> CustomGatesAppliedData { + let mut new_application_data = vec![]; + for (custom_gate_name, signals) in application_data { + let mut index = 0; + while occurring_order[index] != custom_gate_name { + index += 1; + } + new_application_data.push((index, signals)); + } + new_application_data + } + + fn traverse_tree(tree: &Tree, application_data: &mut Vec<(String, Vec)>) { + let node = &tree.dag.nodes[tree.node_id]; + if node.is_custom_gate() { + let mut signals = vec![]; + for signal in &node.ordered_signals { + let signal_numbering = node.signal_correspondence.get(signal).unwrap(); + signals.push(*signal_numbering + tree.offset); + } + application_data.push((node.template_name.clone(), signals)); + } else { + for edge in Tree::get_edges(tree) { + let subtree = Tree::go_to_subtree(tree, edge); + traverse_tree(&subtree, application_data); + } + } + } + + let mut application_data = vec![]; + traverse_tree(&tree, &mut application_data); + find_indexes(occurring_order, application_data) + }; + custom_gates_applied_section.write_custom_gates_applications(application_data)?; + let _r1cs = custom_gates_applied_section.end_section()?; + Log::print(&log); Result::Ok(()) } diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index b679483..396f601 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -27,6 +27,10 @@ ParsePragma : Version = { // maybe change to usize instead of BigInt => version, }; +// Pragma to indicate that we are using PLONK with custom gates. +ParseCustomGates : () = { + "pragma" "ultraPlonk" ";" => () +} // Includes are added at the start of the file. // Their structure is the following: #include "path to the file" @@ -36,22 +40,14 @@ ParseInclude : String = { }; // Parsing a program requires: -// Parsing the "pragma" instruction, if there is one -// Parsing "includes" instructions, if there is anyone, -// Parsing function and template definitions, +// Parsing the version pragma, if there is one +// Parsing the custom gates pragma, if there is one +// Parsing "includes" instructions, if there is anyone +// Parsing function and template definitions // Parsing the declaration of the main component pub ParseAst : AST = { - - => AST::new(Meta::new(s,e), Option::Some(pragma), includes,definitions,Option::Some(main)), - - - => AST::new(Meta::new(s,e),Option::None, includes,definitions,Option::Some(main)), - - - => AST::new(Meta::new(s,e), Option::Some(pragma), includes,definitions,Option::None), - - - => AST::new(Meta::new(s,e), Option::None,includes,definitions,Option::None), + + => AST::new(Meta::new(s,e), version, custom_gates.is_some(), includes, definitions, main), }; // ==================================================================== @@ -84,9 +80,16 @@ pub ParseDefinition : Definition = { "template" "(" ")" => match arg_names { None - => build_template(Meta::new(s,e), name, Vec::new(), args..arge, body, parallel.is_some()), + => build_template(Meta::new(s,e), name, Vec::new(), args..arge, body, parallel.is_some(), false), Some(a) - => build_template(Meta::new(s,e), name, a, args..arge, body, parallel.is_some()), + => build_template(Meta::new(s,e), name, a, args..arge, body, parallel.is_some(), false), + }, + "custom_gate" "(" ")" + => match arg_names { + None + => build_template(Meta::new(s,e), name, Vec::new(), args..arge, body, parallel.is_some(), true), + Some(a) + => build_template(Meta::new(s,e), name, a, args..arge, body, parallel.is_some(), true), }, }; @@ -193,6 +196,13 @@ ParseDeclaration : Statement = { symbols.push(symbol); ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignVar) }, + "custom_component" ",")*> => { + let mut symbols = symbols; + let meta = Meta::new(s,e); + let xtype = VariableType::Component; + symbols.push(symbol); + ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignVar) + }, ",")*> => { let mut symbols = symbols; diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 7671e51..ebb807c 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -52,8 +52,7 @@ pub fn run_parser(file: String, version: &str) -> Result<(ProgramArchive, Report } else if main_components.len() > 1 { let report = errors::MultipleMainError::produce_report(); Err((file_library, vec![report])) - } - else{ + } else { let (main_id, main_component) = main_components.pop().unwrap(); let result_program_archive = ProgramArchive::new(file_library, main_id, main_component, definitions); diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 3b95907..8dc67c4 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -80,6 +80,7 @@ impl Meta { pub struct AST { pub meta: Meta, pub compiler_version: Option, + pub custom_gates: bool, pub includes: Vec, pub definitions: Vec, pub main_component: Option, @@ -88,11 +89,12 @@ impl AST { pub fn new( meta: Meta, compiler_version: Option, + custom_gates: bool, includes: Vec, definitions: Vec, main_component: Option, ) -> AST { - AST { meta, compiler_version, includes, definitions, main_component } + AST { meta, compiler_version, custom_gates, includes, definitions, main_component } } } @@ -105,6 +107,7 @@ pub enum Definition { arg_location: FileLocation, body: Statement, parallel: bool, + is_custom_gate: bool, }, Function { meta: Meta, @@ -121,6 +124,7 @@ pub fn build_template( arg_location: FileLocation, body: Statement, parallel: bool, + is_custom_gate: bool, ) -> Definition { Definition::Template { meta, @@ -129,6 +133,7 @@ pub fn build_template( arg_location, body, parallel, + is_custom_gate, } } diff --git a/program_structure/src/program_library/program_merger.rs b/program_structure/src/program_library/program_merger.rs index dfa3810..7e44cac 100644 --- a/program_structure/src/program_library/program_merger.rs +++ b/program_structure/src/program_library/program_merger.rs @@ -29,7 +29,7 @@ impl Merger { let mut reports = vec![]; for definition in definitions { let (name, meta) = match definition { - Definition::Template { name, args, arg_location, body, meta, parallel } => { + Definition::Template { name, args, arg_location, body, meta, parallel, is_custom_gate } => { if self.contains_function(&name) || self.contains_template(&name) { (Option::Some(name), meta) } else { @@ -42,6 +42,7 @@ impl Merger { arg_location, &mut self.fresh_id, parallel, + is_custom_gate, ); self.get_mut_template_info().insert(name.clone(), new_data); (Option::None, meta) diff --git a/program_structure/src/program_library/template_data.rs b/program_structure/src/program_library/template_data.rs index bac3057..5ffd52c 100644 --- a/program_structure/src/program_library/template_data.rs +++ b/program_structure/src/program_library/template_data.rs @@ -18,6 +18,7 @@ pub struct TemplateData { input_signals: SignalInfo, output_signals: SignalInfo, is_parallel: bool, + is_custom_gate: bool, } impl TemplateData { @@ -30,6 +31,7 @@ impl TemplateData { param_location: FileLocation, elem_id: &mut usize, is_parallel: bool, + is_custom_gate: bool, ) -> TemplateData { body.fill(file_id, elem_id); let mut input_signals = SignalInfo::new(); @@ -45,6 +47,7 @@ impl TemplateData { input_signals, output_signals, is_parallel, + is_custom_gate, } } pub fn get_file_id(&self) -> FileID { @@ -95,6 +98,9 @@ impl TemplateData { pub fn is_parallel(&self) -> bool { self.is_parallel } + pub fn is_custom_gate(&self) -> bool { + self.is_custom_gate + } } fn fill_inputs_and_outputs(