Merge pull request #86 from zkemail/dimidumo/zk-1138-zk-regex-dfa-check

Dimidumo/zk 1138 zk regex dfa check
This commit is contained in:
Shreyas Londhe
2025-02-26 12:33:54 +05:30
committed by GitHub
8 changed files with 3538 additions and 24 deletions

View File

@@ -27,3 +27,4 @@ wasm-bindgen = "0.2"
serde-wasm-bindgen = "0.6.5"
js-sys = "0.3.69"
wasm-bindgen-test = "0.3.42"
console_error_panic_hook = "0.1.7"

View File

@@ -1,9 +1,16 @@
use crate::extract_substrs::*;
use crate::*;
use console_error_panic_hook;
use js_sys::Array;
use serde_json::Value;
use std::panic;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn init_panic_hook() {
panic::set_hook(Box::new(console_error_panic_hook::hook));
}
#[wasm_bindgen]
#[allow(non_snake_case)]
pub fn padString(str: &str, paddedBytesSize: usize) -> Array {

3474
packages/circom/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -36,3 +36,4 @@ regex = "=1.10.6"
getrandom = { version = "0.2", features = ["js"] }
wasm-bindgen = "0.2"
serde-wasm-bindgen = "0.6.5"
console_error_panic_hook = "0.1.7"

View File

@@ -22,17 +22,17 @@ use std::{
///
/// # Returns
///
/// A tuple containing:
/// A Result tuple containing:
/// * The reverse graph as a `BTreeMap<usize, BTreeMap<usize, Vec<u8>>>`.
/// * A `BTreeSet<usize>` of accepting state IDs.
///
/// # Errors
///
/// Returns a `CompilerError::NoAcceptedState` if no accepting states are found.
/// Returns a `CompilerError::AcceptNodesError` if no accepting states are found.
fn build_reverse_graph(
state_len: usize,
dfa_graph: &DFAGraph,
) -> (BTreeMap<usize, BTreeMap<usize, Vec<u8>>>, BTreeSet<usize>) {
) -> Result<(BTreeMap<usize, BTreeMap<usize, Vec<u8>>>, BTreeSet<usize>), CompilerError> {
let mut rev_graph = BTreeMap::<usize, BTreeMap<usize, Vec<u8>>>::new();
let mut accept_nodes = BTreeSet::<usize>::new();
@@ -52,10 +52,12 @@ fn build_reverse_graph(
}
if accept_nodes.is_empty() {
panic!("Accept node must exist");
return Err(CompilerError::AcceptNodesError(
"Accept node must exist".to_string(),
));
}
(rev_graph, accept_nodes)
Ok((rev_graph, accept_nodes))
}
/// Optimizes character ranges by grouping consecutive characters and identifying individual characters.
@@ -659,20 +661,25 @@ fn generate_init_code(state_len: usize) -> Vec<String> {
///
/// # Returns
///
/// A Vec of Strings containing the generated acceptance logic code.
/// A Result Vec of Strings containing the generated acceptance logic code.
///
/// # Panics
///
/// Panics if there are no accept nodes or if there is more than one accept node.
fn generate_accept_logic(accept_nodes: BTreeSet<usize>, end_anchor: bool) -> Vec<String> {
/// Returns error if there are no accept nodes or if there is more than one accept node.
fn generate_accept_logic(
accept_nodes: BTreeSet<usize>,
end_anchor: bool,
) -> Result<Vec<String>, CompilerError> {
let mut accept_lines = vec![];
if accept_nodes.is_empty() {
panic!("Accept node must exist");
return Err(CompilerError::AcceptNodesError(
"Accept node must exist".to_string(),
));
}
if accept_nodes.len() != 1 {
panic!("The size of accept nodes must be one");
return Err(CompilerError::AcceptNodesError(
"The size of accept nodes must be one".to_string(),
));
}
let accept_node = *accept_nodes.iter().next().unwrap();
@@ -704,7 +711,7 @@ fn generate_accept_logic(accept_nodes: BTreeSet<usize>, end_anchor: bool) -> Vec
accept_lines.push("\tout <== is_accepted.out;".to_string());
}
accept_lines
Ok(accept_lines)
}
/// Generates the complete Circom circuit as a string.
@@ -727,10 +734,10 @@ fn gen_circom_allstr(
template_name: &str,
regex_str: &str,
end_anchor: bool,
) -> String {
) -> Result<String, CompilerError> {
let state_len = dfa_graph.states.len();
let (rev_graph, accept_nodes) = build_reverse_graph(state_len, dfa_graph);
let (rev_graph, accept_nodes) = build_reverse_graph(state_len, dfa_graph)?;
let (eq_i, lt_i, and_i, multi_or_i, lines) =
generate_state_transition_logic(&rev_graph, state_len, end_anchor);
@@ -748,11 +755,11 @@ fn gen_circom_allstr(
let init_code = generate_init_code(state_len);
let accept_lines = generate_accept_logic(accept_nodes, end_anchor);
let accept_lines = generate_accept_logic(accept_nodes, end_anchor)?;
let final_code = [declarations, init_code, lines, accept_lines].concat();
final_code.join("\n")
Ok(final_code.join("\n"))
}
/// Writes the consecutive logic for the Circom circuit.
@@ -965,7 +972,7 @@ pub(crate) fn gen_circom_template(
template_name,
&regex_and_dfa.regex_pattern,
regex_and_dfa.has_end_anchor,
);
)?;
let mut file = File::create(circom_path)?;
file.write_all(circom.as_bytes())?;
@@ -1000,7 +1007,7 @@ pub(crate) fn gen_circom_string(
template_name,
&regex_and_dfa.regex_pattern,
regex_and_dfa.has_end_anchor,
);
)?;
let substrs = add_substrs_constraints(regex_and_dfa)?;
let result = circom + &substrs;
Ok(result)

View File

@@ -24,4 +24,6 @@ pub enum CompilerError {
GraphError(String),
#[error("No accepted state found in DFA")]
NoAcceptedState,
#[error("Accept Nodes Error: {0}")]
AcceptNodesError(String),
}

View File

@@ -215,3 +215,6 @@ pub fn gen_circom_from_decomposed_regex(
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub use crate::wasm::*;

View File

@@ -1,17 +1,36 @@
use crate::*;
use console_error_panic_hook;
use serde_wasm_bindgen::from_value;
use std::panic;
use wasm_bindgen::prelude::*;
use self::circom::gen_circom_string;
#[wasm_bindgen(start)]
pub fn init_panic_hook() {
panic::set_hook(Box::new(console_error_panic_hook::hook));
}
#[wasm_bindgen]
#[allow(non_snake_case)]
pub fn genFromDecomposed(decomposedRegexJson: &str, circomTemplateName: &str) -> String {
pub fn genFromDecomposed(
decomposedRegexJson: &str,
circomTemplateName: &str,
) -> Result<String, JsValue> {
let mut decomposed_regex_config: DecomposedRegexConfig =
serde_json::from_str(decomposedRegexJson).expect("failed to parse decomposed_regex json");
let regex_and_dfa = get_regex_and_dfa(&mut decomposed_regex_config)
.expect("failed to convert the decomposed regex to dfa");
gen_circom_string(&regex_and_dfa, circomTemplateName).expect("failed to generate circom")
serde_json::from_str(decomposedRegexJson).map_err(|e| {
JsValue::from_str(&format!("failed to parse decomposed_regex json: {}", e))
})?;
let regex_and_dfa = get_regex_and_dfa(&mut decomposed_regex_config).map_err(|e| {
JsValue::from_str(&format!(
"failed to convert the decomposed regex to dfa: {}",
e
))
})?;
gen_circom_string(&regex_and_dfa, circomTemplateName)
.map_err(|e| JsValue::from_str(&format!("Failed to generate Circom string: {}", e)))
}
#[wasm_bindgen]