diff --git a/Cargo.toml b/Cargo.toml index 195b305..d00bdab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ serde = { version = "1.0.196", features = ["derive"] } thiserror = "1.0.59" strum_macros = "0.26.4" strum = "0.26.2" +sim-circuit = { git = "https://github.com/brech1/sim-circuit" } # DSL circom-circom_algebra = { git = "https://github.com/iden3/circom", package = "circom_algebra" } @@ -29,8 +30,3 @@ circom-dag = { git = "https://github.com/iden3/circom", package = "dag" } circom-parser = { git = "https://github.com/iden3/circom", package = "parser" } circom-program_structure = { git = "https://github.com/iden3/circom", package = "program_structure" } circom-type_analysis = { git = "https://github.com/iden3/circom", package = "type_analysis" } - -# MPZ -mpz-circuits = { git = "https://github.com/privacy-scaling-explorations/mpz", package = "mpz-circuits" } -bmr16-mpz = { git = "https://github.com/tkmct/mpz", package = "mpz-circuits" } -sim-circuit = { git = "https://github.com/brech1/sim-circuit" } diff --git a/src/arithmetic_circuit.rs b/src/arithmetic_circuit.rs index 7b74861..a51efe8 100644 --- a/src/arithmetic_circuit.rs +++ b/src/arithmetic_circuit.rs @@ -1,12 +1,64 @@ use crate::compiler::{ArithmeticGate, CircuitError}; +use circom_program_structure::ast::ExpressionInfixOpcode; use serde::{Deserialize, Serialize}; -use serde_json::{from_str, to_string}; -use sim_circuit::arithmetic_circuit::ArithmeticCircuit as SimArithmeticCircuit; use std::{ collections::HashMap, io::{BufRead, BufReader, BufWriter, Write}, str::FromStr, }; +use strum_macros::{Display as StrumDisplay, EnumString}; + +/// The supported Arithmetic gate types. +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, EnumString, StrumDisplay)] +pub enum AGateType { + AAdd, + ADiv, + AEq, + AGEq, + AGt, + ALEq, + ALt, + AMul, + ANeq, + ASub, + AXor, + APow, + AIntDiv, + AMod, + AShiftL, + AShiftR, + ABoolOr, + ABoolAnd, + ABitOr, + ABitAnd, +} + +impl From<&ExpressionInfixOpcode> for AGateType { + fn from(opcode: &ExpressionInfixOpcode) -> Self { + match opcode { + ExpressionInfixOpcode::Mul => AGateType::AMul, + ExpressionInfixOpcode::Div => AGateType::ADiv, + ExpressionInfixOpcode::Add => AGateType::AAdd, + ExpressionInfixOpcode::Sub => AGateType::ASub, + ExpressionInfixOpcode::Pow => AGateType::APow, + ExpressionInfixOpcode::IntDiv => AGateType::AIntDiv, + ExpressionInfixOpcode::Mod => AGateType::AMod, + ExpressionInfixOpcode::ShiftL => AGateType::AShiftL, + ExpressionInfixOpcode::ShiftR => AGateType::AShiftR, + ExpressionInfixOpcode::LesserEq => AGateType::ALEq, + ExpressionInfixOpcode::GreaterEq => AGateType::AGEq, + ExpressionInfixOpcode::Lesser => AGateType::ALt, + ExpressionInfixOpcode::Greater => AGateType::AGt, + ExpressionInfixOpcode::Eq => AGateType::AEq, + ExpressionInfixOpcode::NotEq => AGateType::ANeq, + ExpressionInfixOpcode::BoolOr => AGateType::ABoolOr, + ExpressionInfixOpcode::BoolAnd => AGateType::ABoolAnd, + ExpressionInfixOpcode::BitOr => AGateType::ABitOr, + ExpressionInfixOpcode::BitAnd => AGateType::ABitAnd, + ExpressionInfixOpcode::BitXor => AGateType::AXor, + } + } +} #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ArithmeticCircuit { @@ -29,10 +81,6 @@ pub struct ConstantInfo { } impl ArithmeticCircuit { - pub fn to_sim(&self) -> SimArithmeticCircuit { - from_str(&to_string(self).unwrap()).unwrap() - } - pub fn get_bristol_string(&self) -> Result { let mut output = Vec::new(); let mut writer = BufWriter::new(&mut output); @@ -216,7 +264,6 @@ impl BristolLine { #[cfg(test)] mod tests { use super::*; - use crate::compiler::AGateType; use std::io::{BufReader, Cursor}; // Helper function to create a sample ArithmeticCircuit diff --git a/src/compiler.rs b/src/compiler.rs index 35c205d..dce2b66 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -3,78 +3,15 @@ //! This module defines the data structures used to represent the arithmetic circuit. use crate::{ - arithmetic_circuit::{ArithmeticCircuit, CircuitInfo, ConstantInfo}, + arithmetic_circuit::{AGateType, ArithmeticCircuit, CircuitInfo, ConstantInfo}, program::ProgramError, topological_sort::topological_sort, }; -use bmr16_mpz::{ - arithmetic::{ - circuit::ArithmeticCircuit as MpzCircuit, - ops::{add, cmul, mul, sub}, - types::CrtRepr, - ArithCircuitError as MpzCircuitError, - }, - ArithmeticCircuitBuilder, -}; -use circom_program_structure::ast::ExpressionInfixOpcode; use log::debug; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; -use strum_macros::{Display as StrumDisplay, EnumString}; use thiserror::Error; -/// Types of gates that can be used in an arithmetic circuit. -#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, EnumString, StrumDisplay)] -pub enum AGateType { - AAdd, - ADiv, - AEq, - AGEq, - AGt, - ALEq, - ALt, - AMul, - ANeq, - ASub, - AXor, - APow, - AIntDiv, - AMod, - AShiftL, - AShiftR, - ABoolOr, - ABoolAnd, - ABitOr, - ABitAnd, -} - -impl From<&ExpressionInfixOpcode> for AGateType { - fn from(opcode: &ExpressionInfixOpcode) -> Self { - match opcode { - ExpressionInfixOpcode::Mul => AGateType::AMul, - ExpressionInfixOpcode::Div => AGateType::ADiv, - ExpressionInfixOpcode::Add => AGateType::AAdd, - ExpressionInfixOpcode::Sub => AGateType::ASub, - ExpressionInfixOpcode::Pow => AGateType::APow, - ExpressionInfixOpcode::IntDiv => AGateType::AIntDiv, - ExpressionInfixOpcode::Mod => AGateType::AMod, - ExpressionInfixOpcode::ShiftL => AGateType::AShiftL, - ExpressionInfixOpcode::ShiftR => AGateType::AShiftR, - ExpressionInfixOpcode::LesserEq => AGateType::ALEq, - ExpressionInfixOpcode::GreaterEq => AGateType::AGEq, - ExpressionInfixOpcode::Lesser => AGateType::ALt, - ExpressionInfixOpcode::Greater => AGateType::AGt, - ExpressionInfixOpcode::Eq => AGateType::AEq, - ExpressionInfixOpcode::NotEq => AGateType::ANeq, - ExpressionInfixOpcode::BoolOr => AGateType::ABoolOr, - ExpressionInfixOpcode::BoolAnd => AGateType::ABoolAnd, - ExpressionInfixOpcode::BitOr => AGateType::ABitOr, - ExpressionInfixOpcode::BitAnd => AGateType::ABitAnd, - ExpressionInfixOpcode::BitXor => AGateType::AXor, - } - } -} - /// Represents a signal in the circuit, with a name and an optional value. #[derive(Debug, Serialize, Deserialize)] pub struct Signal { @@ -541,94 +478,6 @@ impl Compiler { }) } - /// Builds an arithmetic circuit using the mpz circuit builder. - pub fn build_mpz_circuit(&self, report: &CircuitReport) -> Result { - let builder = ArithmeticCircuitBuilder::new(); - - // Initialize CRT signals map with the circuit inputs - let mut crt_signals: HashMap = - report - .inputs - .iter() - .try_fold(HashMap::new(), |mut acc, signal| { - let input = builder - .add_input::(signal.names[0].to_string()) - .map_err(CircuitError::MPZCircuitError)?; - acc.insert(signal.id, input.repr); - Ok::<_, CircuitError>(acc) - })?; - - // Initialize a vec for indices of gates that need processing - let mut to_process = std::collections::VecDeque::new(); - to_process.extend(0..self.gates.len()); - - while let Some(index) = to_process.pop_front() { - let gate = &self.gates[index]; - - if let (Some(lh_in_repr), Some(rh_in_repr)) = - (crt_signals.get(&gate.lh_in), crt_signals.get(&gate.rh_in)) - { - let result_repr = match gate.op { - AGateType::AAdd => { - add(&mut builder.state().borrow_mut(), lh_in_repr, rh_in_repr) - .map_err(|e| e.into()) - } - AGateType::AMul => { - // Get the constant value from one of the signals if available - let constant_value = self - .signals - .get(&gate.lh_in) - .and_then(|signal| signal.value.map(|v| v as u64)) - .or_else(|| { - self.signals - .get(&gate.rh_in) - .and_then(|signal| signal.value.map(|v| v as u64)) - }); - - // Perform multiplication depending on whether one input is a constant - if let Some(value) = constant_value { - Ok::<_, CircuitError>(cmul( - &mut builder.state().borrow_mut(), - lh_in_repr, - value, - )) - } else { - mul(&mut builder.state().borrow_mut(), lh_in_repr, rh_in_repr) - .map_err(|e| e.into()) - } - } - AGateType::ASub => { - sub(&mut builder.state().borrow_mut(), lh_in_repr, rh_in_repr) - .map_err(|e| e.into()) - } - _ => { - return Err(CircuitError::UnsupportedGateType(format!( - "{:?} not supported by MPZ", - gate.op - ))) - } - }?; - - crt_signals.insert(gate.out, result_repr); - } else { - // Not ready to process, push back for later attempt. - to_process.push_back(index); - } - } - - // Add output signals - for signal in &report.outputs { - let output_repr = crt_signals - .get(&signal.id) - .ok_or_else(|| CircuitError::UnprocessedNode)?; - builder.add_output(output_repr); - } - - builder - .build() - .map_err(|_| CircuitError::MPZCircuitBuilderError) - } - /// Returns a node id and increments the count. fn get_node_id(&mut self) -> u32 { self.node_count += 1; @@ -694,10 +543,6 @@ pub enum CircuitError { DisconnectedSignal, #[error(transparent)] IOError(#[from] std::io::Error), - #[error("MPZ arithmetic circuit error: {0}")] - MPZCircuitError(MpzCircuitError), - #[error("MPZ arithmetic circuit builder error")] - MPZCircuitBuilderError, #[error(transparent)] ParseIntError(#[from] std::num::ParseIntError), #[error("Signal already declared")] @@ -720,12 +565,6 @@ impl From for ProgramError { } } -impl From for CircuitError { - fn from(e: MpzCircuitError) -> Self { - CircuitError::MPZCircuitError(e) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/process.rs b/src/process.rs index d76a9b7..f3efd8c 100644 --- a/src/process.rs +++ b/src/process.rs @@ -2,7 +2,8 @@ //! //! Handles execution of statements and expressions for arithmetic circuit generation within a `Runtime` environment. -use crate::compiler::{AGateType, Compiler}; +use crate::arithmetic_circuit::AGateType; +use crate::compiler::Compiler; use crate::program::ProgramError; use crate::runtime::{ generate_u32, increment_indices, u32_to_access, Context, DataAccess, DataType, NestedValue, @@ -759,6 +760,7 @@ fn to_equivalent_infix(op: &ExpressionPrefixOpcode) -> (u32, ExpressionInfixOpco ExpressionPrefixOpcode::Complement => (u32::MAX, ExpressionInfixOpcode::BitXor), } } + #[cfg(test)] mod tests { use super::*; diff --git a/tests/integration.rs b/tests/integration.rs index f76a2f3..e371524 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,53 +1,286 @@ -use circom_2_arithc::{cli::Args, program::compile}; -use sim_circuit::{simulate, NumberU32}; +use circom_2_arithc::arithmetic_circuit::{ + AGateType, ArithmeticCircuit as CompilerArithmeticCircuit, +}; +use sim_circuit::{ + circuit::{CircuitBuilder, CircuitMemory, GenericCircuit, GenericCircuitExecutor}, + model::{Component, Executable, Memory}, +}; use std::collections::HashMap; -pub fn simulation_test< - Input: IntoIterator, - Output: IntoIterator, ->( - test_file_path: &str, - input: Input, - expected_output: Output, -) { - let compiler_input = Args::new(test_file_path.into(), "./".into()); - let circuit = compile(&compiler_input).unwrap().build_circuit().unwrap(); - - let input = input - .into_iter() - .map(|(name, value)| (name.to_string(), NumberU32(value))) - .collect::>(); - - let expected_output = expected_output - .into_iter() - .map(|(name, value)| (name.to_string(), NumberU32(value))) - .collect::>(); - - let output = simulate(&circuit.to_sim(), &input).unwrap(); - - assert_eq!(output, expected_output); +#[derive(Debug, PartialEq, Eq, Clone)] +enum ArithmeticOperation { + ADD, + DIV, + EQ, + GEQ, + GT, + LEQ, + LT, + MUL, + NEQ, + SUB, + XOR, + POW, + INTDIV, + MOD, + SHIFTL, + SHIFTR, + BOOLOR, + BOOLAND, + BITOR, + BITAND, +} + +impl From for ArithmeticOperation { + fn from(gate_type: AGateType) -> Self { + match gate_type { + AGateType::AAdd => ArithmeticOperation::ADD, + AGateType::ADiv => ArithmeticOperation::DIV, + AGateType::AEq => ArithmeticOperation::EQ, + AGateType::AGEq => ArithmeticOperation::GEQ, + AGateType::AGt => ArithmeticOperation::GT, + AGateType::ALEq => ArithmeticOperation::LEQ, + AGateType::ALt => ArithmeticOperation::LT, + AGateType::AMul => ArithmeticOperation::MUL, + AGateType::ANeq => ArithmeticOperation::NEQ, + AGateType::ASub => ArithmeticOperation::SUB, + AGateType::AXor => ArithmeticOperation::XOR, + AGateType::APow => ArithmeticOperation::POW, + AGateType::AIntDiv => ArithmeticOperation::INTDIV, + AGateType::AMod => ArithmeticOperation::MOD, + AGateType::AShiftL => ArithmeticOperation::SHIFTL, + AGateType::AShiftR => ArithmeticOperation::SHIFTR, + AGateType::ABoolOr => ArithmeticOperation::BOOLOR, + AGateType::ABoolAnd => ArithmeticOperation::BOOLAND, + AGateType::ABitOr => ArithmeticOperation::BITOR, + AGateType::ABitAnd => ArithmeticOperation::BITAND, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct ArithmeticGate { + operation: ArithmeticOperation, + inputs: Vec, + outputs: Vec, +} + +impl Component for ArithmeticGate { + fn inputs(&self) -> &[usize] { + &self.inputs + } + + fn outputs(&self) -> &[usize] { + &self.outputs + } + + fn set_inputs(&mut self, inputs: Vec) { + self.inputs = inputs; + } + + fn set_outputs(&mut self, outputs: Vec) { + self.outputs = outputs; + } +} + +impl Executable> for ArithmeticGate { + type Error = (); + + fn execute(&self, memory: &mut CircuitMemory) -> Result<(), Self::Error> { + let a = memory.read(self.inputs[0]).unwrap(); + let b = memory.read(self.inputs[1]).unwrap(); + + let result = match self.operation { + ArithmeticOperation::ADD => a + b, + ArithmeticOperation::DIV => a / b, + ArithmeticOperation::EQ => (a == b) as u32, + ArithmeticOperation::GEQ => (a >= b) as u32, + ArithmeticOperation::GT => (a > b) as u32, + ArithmeticOperation::LEQ => (a <= b) as u32, + ArithmeticOperation::LT => (a < b) as u32, + ArithmeticOperation::MUL => a * b, + ArithmeticOperation::NEQ => (a != b) as u32, + ArithmeticOperation::SUB => a - b, + ArithmeticOperation::XOR => a ^ b, + ArithmeticOperation::POW => a.pow(b), + ArithmeticOperation::INTDIV => a / b, + ArithmeticOperation::MOD => a % b, + ArithmeticOperation::SHIFTL => a << b, + ArithmeticOperation::SHIFTR => a >> b, + ArithmeticOperation::BOOLOR => (a != 0 || b != 0) as u32, + ArithmeticOperation::BOOLAND => (a != 0 && b != 0) as u32, + ArithmeticOperation::BITOR => a | b, + ArithmeticOperation::BITAND => a & b, + }; + + memory.write(self.outputs[0], result).unwrap(); + Ok(()) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ArithmeticCircuit { + gates: Vec, + constants: HashMap, + label_to_index: HashMap, + input_indices: Vec, + outputs: Vec, +} + +impl ArithmeticCircuit { + /// Create a new `ArithmeticCircuit` from a compiled circuit + pub fn new_from_compiled_circuit( + circuit: CompilerArithmeticCircuit, + ) -> Result { + let mut label_to_index: HashMap = HashMap::new(); + let mut outputs: Vec = Vec::new(); + let mut input_indices: Vec = Vec::new(); + let mut gates: Vec = Vec::new(); + + // Get circuit inputs + let inputs = circuit.info.input_name_to_wire_index; + for (label, index) in inputs { + label_to_index.insert(label, index as usize); + input_indices.push(index as usize); + } + + // Get circuit constants + let mut constants: HashMap = HashMap::new(); + for (_, constant_info) in circuit.info.constants { + input_indices.push(constant_info.wire_index as usize); + constants.insert( + constant_info.wire_index as usize, + constant_info.value.parse().unwrap(), + ); + } + + // Get circuit outputs + let output_map = circuit.info.output_name_to_wire_index; + let mut output_indices = vec![]; + for (label, index) in output_map { + let index = index as usize; + label_to_index.insert(label.clone(), index); + outputs.push(label); + output_indices.push(index); + } + + // Transform and add gates + for gate in circuit.gates { + let operation = ArithmeticOperation::from(gate.op); + let inputs = vec![gate.lh_in as usize, gate.rh_in as usize]; + let outputs = vec![gate.out as usize]; + + let arithmetic_gate = ArithmeticGate { + operation, + inputs, + outputs, + }; + gates.push(arithmetic_gate); + } + + Ok(Self { + gates, + constants, + label_to_index, + input_indices, + outputs, + }) + } + + /// Run the circuit + pub fn run(&self, inputs: HashMap) -> Result, &'static str> { + // Build circuit + let circuit = self.build_circuit(); + // Instantiate a circuit executor + let mut executor: GenericCircuitExecutor = + GenericCircuitExecutor::new(circuit); + + // The executor receives a map of WireIndex -> Value + let input_map: HashMap = inputs + .iter() + .map(|(label, value)| { + let index = self + .label_to_index + .get(label) + .ok_or("Input label not found") + .unwrap(); + (*index, *value) + }) + .collect(); + + // Load constants into the input map + let input_map = self + .constants + .iter() + .fold(input_map, |mut acc, (index, value)| { + acc.insert(*index, *value); + acc + }); + + let output = executor.run(&input_map).unwrap(); + + // The executor returns a map of WireIndex -> Value + let output_map: HashMap = self + .outputs + .iter() + .map(|label| { + let index = self + .label_to_index + .get(label) + .ok_or("Output label not found") + .unwrap(); + (label.clone(), output.get(index).unwrap().clone()) + }) + .collect(); + + Ok(output_map) + } + + fn build_circuit(&self) -> GenericCircuit { + let mut builder = CircuitBuilder::::new(); + builder.add_inputs(&self.input_indices); + + for gate in &self.gates { + builder.add_component(gate.clone()).unwrap(); + } + + builder.build().unwrap() + } } #[cfg(test)] mod integration_tests { use super::*; - use circom_2_arithc::{cli::Args, program::compile}; + use circom_2_arithc::{arithmetic_circuit::ConstantInfo, cli::Args, program::compile}; - #[test] - fn test_add_zero() { - simulation_test( - "tests/circuits/integration/addZero.circom", - [("0.in", 42)], - [("0.out", 42)], - ); + fn simulation_test( + circuit_path: &str, + inputs: &[(&str, u32)], + expected_outputs: &[(&str, u32)], + ) { + let compiler_input = Args::new(circuit_path.into(), "./".into()); + let circuit = compile(&compiler_input).unwrap().build_circuit().unwrap(); + let arithmetic_circuit = ArithmeticCircuit::new_from_compiled_circuit(circuit).unwrap(); + + let mut input_map: HashMap = HashMap::new(); + for (label, value) in inputs { + input_map.insert(label.to_string(), *value); + } + + let outputs: HashMap = arithmetic_circuit.run(input_map).unwrap(); + + for (label, expected_value) in expected_outputs { + let value = outputs.get(&label.to_string()).unwrap(); + assert_eq!(value, expected_value); + } } #[test] - fn test_constant_sum() { + fn test_add_zero() { simulation_test( - "tests/circuits/integration/constantSum.circom", - [], - [("0.out", 8)], + "tests/circuits/integration/addZero.circom", + &[("0.in", 42)], + &[("0.out", 42)], ); } @@ -55,7 +288,7 @@ mod integration_tests { fn test_infix_ops() { simulation_test( "tests/circuits/integration/infixOps.circom", - [ + &[ ("0.x0", 0), ("0.x1", 1), ("0.x2", 2), @@ -63,7 +296,7 @@ mod integration_tests { ("0.x4", 4), ("0.x5", 5), ], - [ + &[ ("0.mul_2_3", 6), // ("0.div_4_3", 1), // unsupported for NumberU32 ("0.idiv_4_3", 1), @@ -102,7 +335,7 @@ mod integration_tests { fn test_matrix_element_multiplication() { simulation_test( "tests/circuits/integration/matElemMul.circom", - [ + &[ ("0.a[0][0]", 2), ("0.a[0][1]", 2), ("0.a[1][0]", 2), @@ -112,7 +345,7 @@ mod integration_tests { ("0.b[1][0]", 2), ("0.b[1][1]", 2), ], - [ + &[ ("0.out[0][0]", 4), ("0.out[0][1]", 4), ("0.out[1][0]", 4), @@ -121,45 +354,12 @@ mod integration_tests { ); } - #[test] - #[should_panic] // FIXME: Should NOT panic (see comment below) - fn test_prefix_ops() { - // FIXME: The compiler sees several of the outputs as inputs, leading to the error below - // CircuitError(Inconsistency { - // message: "Node 10 used for both input 0.complementC and output 0.complementC" - // }) - simulation_test( - "tests/circuits/integration/prefixOps.circom", - [("0.a", 0), ("0.b", 1), ("0.c", 2)], - [ - ("0.negateA", 0), // -0 - ("0.notA", 1), // !0 - ("0.notB", 0), // !1 - ("0.notC", 0), // !2 - ("0.complementA", 0b_11111111_11111111_11111111_11111111), // ~0 - ("0.complementB", 0b_11111111_11111111_11111111_11111110), // ~1 - ("0.complementC", 0b_11111111_11111111_11111111_11111101), // ~2 - ], - ); - } - #[test] fn test_sum() { simulation_test( "tests/circuits/integration/sum.circom", - [("0.a", 3), ("0.b", 5)], - [("0.out", 8)], - ); - } - - #[test] - fn test_under_constrained() { - // FIXME: There should be an error instead (zero comes from default initialization, not from - // running the circuit) - simulation_test( - "tests/circuits/integration/underConstrained.circom", - [], - [("0.x", 0)], + &[("0.a", 3), ("0.b", 5)], + &[("0.out", 8)], ); } @@ -167,17 +367,8 @@ mod integration_tests { fn test_x_eq_x() { simulation_test( "tests/circuits/integration/xEqX.circom", - [("0.x", 37)], - [("0.out", 1)], - ); - } - - #[test] - fn test_direct_output() { - simulation_test( - "tests/circuits/integration/directOutput.circom", - [], - [("0.out", 42)], + &[("0.x", 37)], + &[("0.out", 1)], ); } @@ -195,4 +386,84 @@ mod integration_tests { "Runtime error: Index out of bounds" ); } + + #[test] + fn test_constant_sum() { + let compiler_input = Args::new( + "tests/circuits/integration/constantSum.circom".into(), + "./".into(), + ); + let circuit_res = compile(&compiler_input); + + assert_eq!(circuit_res.is_ok(), true); + + let circuit = circuit_res.unwrap().build_circuit().unwrap(); + + assert_eq!(circuit.info.constants.len(), 1); + assert_eq!( + circuit.info.constants.get("0.const_signal_8_1"), + Some(&ConstantInfo { + value: "8".to_string(), // 5 + 3 + wire_index: 0 + }) + ); + } + + #[test] + fn test_direct_output() { + let compiler_input = Args::new( + "tests/circuits/integration/directOutput.circom".into(), + "./".into(), + ); + let circuit_res = compile(&compiler_input); + + assert_eq!(circuit_res.is_ok(), true); + + let circuit = circuit_res.unwrap().build_circuit().unwrap(); + + let expected_output = HashMap::from([("0.out".to_string(), 0)]); + assert_eq!(circuit.info.output_name_to_wire_index, expected_output); + assert_eq!(circuit.info.constants.len(), 1); + assert_eq!( + circuit.info.constants.get("0.const_signal_42_1"), + Some(&ConstantInfo { + value: "42".to_string(), + wire_index: 0 + }) + ); + } + + #[ignore] + #[test] + fn test_under_constrained() { + // FIXME: There should be an error instead (zero comes from default initialization, not from + // running the circuit) + simulation_test( + "tests/circuits/integration/underConstrained.circom", + &[], + &[("0.x", 0)], + ); + } + + #[ignore] + #[test] + fn test_prefix_ops() { + // FIXME: The compiler sees several of the outputs as inputs, leading to the error below + // CircuitError(Inconsistency { + // message: "Node 10 used for both input 0.complementC and output 0.complementC" + // }) + simulation_test( + "tests/circuits/integration/prefixOps.circom", + &[("0.a", 0), ("0.b", 1), ("0.c", 2)], + &[ + ("0.negateA", 0), // -0 + ("0.notA", 1), // !0 + ("0.notB", 0), // !1 + ("0.notC", 0), // !2 + ("0.complementA", 0b_11111111_11111111_11111111_11111111), // ~0 + ("0.complementB", 0b_11111111_11111111_11111111_11111110), // ~1 + ("0.complementC", 0b_11111111_11111111_11111111_11111101), // ~2 + ], + ); + } }