Skip to content

Commit 0bc8c43

Browse files
committed
Initial Noir runner implementation
1 parent 5925f63 commit 0bc8c43

File tree

7 files changed

+557
-237
lines changed

7 files changed

+557
-237
lines changed

src/zkregex_fuzzer/cli.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from zkregex_fuzzer.harness import HarnessStatus
1515
from zkregex_fuzzer.logger import logger
1616
from zkregex_fuzzer.runner.circom import CircomSubprocess, SnarkjsSubprocess, ZkRegexSubprocess
17+
from zkregex_fuzzer.runner.subprocess import BarretenbergSubprocess, NoirSubprocess
1718

1819
def fuzz_parser():
1920
parser = argparse.ArgumentParser(
@@ -71,7 +72,7 @@ def fuzz_parser():
7172
help="Maximum depth of recursion in the grammar (default: 5)."
7273
)
7374
parser.add_argument(
74-
"--circom-max-input-size",
75+
"--max-input-size",
7576
type=int,
7677
default=600,
7778
help="Maximum size of the circuit input (default: 600)."
@@ -94,6 +95,19 @@ def fuzz_parser():
9495
type=str,
9596
help="Path to the ptau (powers-of-tau) file for the proving step"
9697
)
98+
99+
parser.add_argument(
100+
"--noir-prove",
101+
action="store_true",
102+
help="Run the proving and verification step with Barretenberg."
103+
)
104+
105+
parser.add_argument(
106+
"--logger-level",
107+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
108+
default="INFO",
109+
help="Set the logger level (default: INFO)."
110+
)
97111

98112

99113
return parser
@@ -110,6 +124,12 @@ def reproduce_parser():
110124
help="Path to the target directory output that want to be reproduced (support wildcard pattern).",
111125
required=True
112126
)
127+
parser.add_argument(
128+
"--logger-level",
129+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
130+
default="INFO",
131+
help="Set the logger level (default: INFO)."
132+
)
113133

114134
return parser
115135

@@ -120,13 +140,15 @@ def do_fuzz(args):
120140

121141
if args.target == "circom":
122142
try:
123-
circom_version = CircomSubprocess.get_installed_version()
124-
snarkjs_version = SnarkjsSubprocess.get_installed_version()
125143
zk_regex_version = ZkRegexSubprocess.get_installed_version()
144+
circom_version = CircomSubprocess.get_installed_version()
126145
print("-" * 80)
127-
print(f"Circom: {circom_version}")
128-
print(f"SnarkJS: {snarkjs_version}")
129146
print(f"zk-regex: {zk_regex_version}")
147+
print(f"Circom: {circom_version}")
148+
if args.circom_prove:
149+
snarkjs_version = SnarkjsSubprocess.get_installed_version()
150+
print(f"SnarkJS: {snarkjs_version}")
151+
130152
except ValueError as e:
131153
print(e)
132154
exit(1)
@@ -156,6 +178,20 @@ def do_fuzz(args):
156178
print(f"Path to ptau file {ptau_path} does not exist.")
157179
exit(1)
158180

181+
elif args.target == "noir":
182+
try:
183+
zk_regex_version = ZkRegexSubprocess.get_installed_version()
184+
noir_version = NoirSubprocess.get_installed_version()
185+
print("-" * 80)
186+
print(f"zk-regex: {zk_regex_version}")
187+
print(f"Noir: {noir_version}")
188+
if args.noir_prove:
189+
bb_version = BarretenbergSubprocess.get_installed_version()
190+
print(f"Barretenberg: {bb_version}")
191+
except ValueError as e:
192+
print(e)
193+
exit(1)
194+
159195
print("-" * 80)
160196
print(f"Fuzzing with {args.fuzzer} fuzzer.")
161197
print("=" * 80)
@@ -199,12 +235,6 @@ def do_reproduce(args):
199235
def main():
200236

201237
parser = argparse.ArgumentParser()
202-
parser.add_argument(
203-
"--logger-level",
204-
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
205-
default="INFO",
206-
help="Set the logger level (default: INFO)."
207-
)
208238

209239
subparser = parser.add_subparsers(dest="subcommand")
210240
subparser.add_parser(

src/zkregex_fuzzer/configs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
from zkregex_fuzzer.runner import CircomRunner, PythonReRunner
1+
from zkregex_fuzzer.runner import CircomRunner, PythonReRunner, NoirRunner
22
from zkregex_fuzzer.grammar import REGEX_GRAMMAR
33
from zkregex_fuzzer.regexgen import GrammarRegexGenerator, DatabaseRegexGenerator
44
from zkregex_fuzzer.vinpgen import GrammarBasedGenerator, RstrGenerator, ExrexGenerator
55

66

77
TARGETS = {
88
"circom": CircomRunner,
9+
"noir": NoirRunner,
910
"python_re": PythonReRunner,
1011
}
1112

src/zkregex_fuzzer/fuzzer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def fuzz_with_regexes(
6161
"""
6262
Fuzz test with pre-seeded regexes.
6363
"""
64-
max_input_size = kwargs.get("circom_max_input_size", None)
64+
max_input_size = kwargs.get("max_input_size", None)
6565
oracle, oracle_generator = oracle_params
6666
if oracle:
6767
generator = VALID_INPUT_GENERATORS[oracle_generator]
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .base_runner import Runner, RegexCompileError, RegexRunError
22
from .python import PythonReRunner
3-
from .circom import CircomRunner
3+
from .circom import CircomRunner
4+
from .noir import NoirRunner

src/zkregex_fuzzer/runner/circom.py

Lines changed: 3 additions & 223 deletions
Original file line numberDiff line numberDiff line change
@@ -6,227 +6,7 @@
66
from pathlib import Path
77
from zkregex_fuzzer.logger import logger
88
from zkregex_fuzzer.runner.base_runner import Runner, RegexCompileError, RegexRunError
9-
10-
class ZkRegexSubprocess:
11-
12-
@classmethod
13-
def get_installed_version(cls) -> str:
14-
"""
15-
Get the installed version of zk-regex.
16-
"""
17-
if shutil.which("zk-regex"):
18-
cmd = ["zk-regex", "--version"]
19-
result = subprocess.run(cmd, capture_output=True, text=True)
20-
return result.stdout.strip()
21-
else:
22-
raise ValueError("zk-regex is not installed")
23-
24-
@classmethod
25-
def compile(cls, json_file_path: str, output_file_path: str, template_name: str = "TestRegex", substr=True):
26-
"""
27-
Compile a regex using zk-regex.
28-
"""
29-
cmd = [
30-
"zk-regex", "decomposed",
31-
"-d", json_file_path,
32-
"-c", output_file_path,
33-
"-t", template_name,
34-
"-g", "true" if substr else "false"
35-
]
36-
result = subprocess.run(cmd, capture_output=True, text=True)
37-
38-
if result.returncode != 0:
39-
raise RegexCompileError(f"Error compiling with zk-regex: {result.stderr}")
40-
41-
class CircomSubprocess:
42-
43-
@classmethod
44-
def get_installed_version(cls) -> str:
45-
"""
46-
Get the installed version of Circom.
47-
"""
48-
if shutil.which("circom"):
49-
cmd = ["circom", "--version"]
50-
result = subprocess.run(cmd, capture_output=True, text=True)
51-
return result.stdout.strip()
52-
else:
53-
raise ValueError("Circom is not installed")
54-
55-
@classmethod
56-
def compile(cls, circom_file_path: str, link_path: list[str]) -> tuple[str, str]:
57-
"""
58-
Compile a circom file to r1cs and wasm.
59-
"""
60-
61-
base_name = Path(circom_file_path).stem
62-
base_dir = Path(circom_file_path).parent
63-
64-
cmd = [
65-
"circom", circom_file_path, "--wasm", "--r1cs", "-o", str(base_dir)
66-
]
67-
for path in link_path:
68-
cmd.append("-l")
69-
cmd.append(path)
70-
71-
logger.debug(" ".join(cmd))
72-
result = subprocess.run(cmd, capture_output=True, text=True)
73-
74-
if result.returncode != 0:
75-
raise RegexCompileError(f"Error compiling with Circom: {result.stderr}")
76-
77-
r1cs_file_path = base_dir / f"{base_name}.r1cs"
78-
wasm_file_path = base_dir / f"{base_name}_js/{base_name}.wasm"
79-
return str(wasm_file_path), str(r1cs_file_path)
80-
81-
class SnarkjsSubprocess:
82-
83-
@classmethod
84-
def get_installed_version(cls) -> str:
85-
"""
86-
Get the installed version of SnarkJS.
87-
"""
88-
if shutil.which("snarkjs"):
89-
cmd = ["snarkjs", "--help"]
90-
result = subprocess.run(cmd, capture_output=True, text=True)
91-
return result.stdout.split('\n')[0]
92-
else:
93-
raise ValueError("SnarkJS is not installed")
94-
95-
@classmethod
96-
def setup_zkey(cls, circuit_path: str, ptau_path: str) -> str:
97-
"""
98-
Setup the circuit with the powers of tau.
99-
"""
100-
101-
base_name = Path(circuit_path).stem
102-
output_path = str(Path(circuit_path).parent / f"{base_name}.zkey")
103-
104-
cmd = [
105-
"snarkjs", "groth16", "setup",
106-
circuit_path,
107-
ptau_path,
108-
output_path
109-
]
110-
result = subprocess.run(cmd, capture_output=True, text=True)
111-
112-
if result.returncode != 0:
113-
raise RegexRunError(f"Error running with SnarkJS: {result.stderr}")
114-
115-
return str(output_path)
116-
117-
@classmethod
118-
def export_verification_key(cls, zkey_path: str) -> str:
119-
"""
120-
Export the verification key from the zkey.
121-
"""
122-
123-
base_name = Path(zkey_path).stem
124-
output_path = str(Path(zkey_path).parent / f"{base_name}.vkey.json")
125-
126-
cmd = [
127-
"snarkjs", "zkey", "export", "verificationkey",
128-
zkey_path,
129-
output_path
130-
]
131-
result = subprocess.run(cmd, capture_output=True, text=True)
132-
133-
if result.returncode != 0:
134-
raise RegexRunError(f"Error running with SnarkJS: {result.stdout}")
135-
136-
return str(output_path)
137-
138-
@classmethod
139-
def witness_gen(cls, wasm_file_path: str, input_path: str) -> str:
140-
"""
141-
Generate a witness for the wasm file.
142-
"""
143-
144-
base_name = Path(wasm_file_path).stem
145-
output_path = str(Path(input_path).parent / f"{base_name}.wtns")
146-
147-
cmd = [
148-
"snarkjs", "wtns", "calculate",
149-
wasm_file_path,
150-
input_path,
151-
output_path
152-
]
153-
result = subprocess.run(cmd, capture_output=True, text=True)
154-
155-
logger.debug(" ".join(cmd))
156-
if result.returncode != 0:
157-
raise RegexRunError(f"Error running with SnarkJS: {result.stdout}")
158-
159-
return str(output_path)
160-
161-
@classmethod
162-
def prove(cls, zkey_path: str, witness_path: str) -> tuple[str, str]:
163-
"""
164-
geenrate proof from the witness with the zkey.
165-
"""
166-
167-
base_name = Path(zkey_path).stem
168-
proof_path = str(Path(witness_path).parent / f"{base_name}.proof.json")
169-
public_input_path = str(Path(witness_path).parent / f"{base_name}.public.json")
170-
171-
cmd = [
172-
"snarkjs", "groth16", "prove",
173-
zkey_path,
174-
witness_path,
175-
proof_path,
176-
public_input_path
177-
]
178-
result = subprocess.run(cmd, capture_output=True, text=True)
179-
180-
if result.returncode != 0:
181-
raise RegexRunError(f"Error running with SnarkJS: {result.stdout}")
182-
183-
return proof_path, public_input_path
184-
185-
@classmethod
186-
def verify(cls, vkey_path: str, proof_path: str, public_input_path: str) -> bool:
187-
"""
188-
Verify the proof with the verification key.
189-
"""
190-
191-
cmd = [
192-
"snarkjs", "groth16", "verify",
193-
vkey_path,
194-
public_input_path,
195-
proof_path,
196-
]
197-
result = subprocess.run(cmd, capture_output=True, text=True)
198-
199-
if result.returncode != 0:
200-
raise RegexRunError(f"Error running with SnarkJS: {result.stdout}")
201-
202-
return "OK" in result.stdout
203-
204-
@classmethod
205-
def extract_witness(cls, witness_path: str) -> dict:
206-
"""
207-
Extract the witness from the witness file.
208-
"""
209-
210-
base_name = Path(witness_path).stem
211-
output_path = str(Path(witness_path).parent / f"{base_name}.json")
212-
213-
cmd = [
214-
"snarkjs", "wtns", "export", "json",
215-
witness_path,
216-
output_path,
217-
]
218-
result = subprocess.run(cmd, capture_output=True, text=True)
219-
220-
logger.debug(" ".join(cmd))
221-
if result.returncode != 0:
222-
logger.debug(result.stdout)
223-
raise RegexRunError(f"Error running with SnarkJS: {result.stdout}")
224-
225-
json_result = json.loads(open(output_path, 'r').read())
226-
Path(output_path).unlink()
227-
228-
return json_result
229-
9+
from zkregex_fuzzer.runner.subprocess import ZkRegexSubprocess, CircomSubprocess, SnarkjsSubprocess
23010

23111
class CircomRunner(Runner):
23212
"""
@@ -244,7 +24,7 @@ def __init__(self, regex: str, kwargs: dict):
24424
self._run_the_prover = kwargs.get("circom_prove", False)
24525
self._ptau_path = kwargs.get("circom_ptau", None)
24626
self._link_path = kwargs.get("circom_library", [])
247-
self._circom_max_input_size = kwargs.get("circom_max_input_size", 200)
27+
self._circom_max_input_size = kwargs.get("max_input_size", 200)
24828
self._template_name = "TestRegex"
24929
super().__init__(regex, kwargs)
25030
self._runner = "Circom"
@@ -274,7 +54,7 @@ def compile(self, regex: str) -> None:
27454

27555
# Call zk-regex to generate the circom code
27656
logger.debug(f"Generating circom code starts")
277-
ZkRegexSubprocess.compile(json_file_path, circom_file_path, self._template_name)
57+
ZkRegexSubprocess.compile_to_circom(json_file_path, circom_file_path, self._template_name)
27858
logger.debug(f"Generating circom code ends")
27959

28060
# Append the circom file to include the main function

0 commit comments

Comments
 (0)