Skip to content

Commit 9bd5894

Browse files
MerricxStefanosChaliasos
authored andcommitted
Implement Circom runner
1 parent 53e7dd7 commit 9bd5894

File tree

8 files changed

+477
-133
lines changed

8 files changed

+477
-133
lines changed

src/zkregex_fuzzer/cli.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
"""
44

55
import argparse
6+
from pathlib import Path
67
from zkregex_fuzzer.fuzzer import fuzz_with_grammar
78
from zkregex_fuzzer.grammar import REGEX_GRAMMAR
89
from zkregex_fuzzer.configs import TARGETS, VALID_INPUT_GENERATORS
910
from zkregex_fuzzer.logger import logger
11+
from zkregex_fuzzer.runner.circom import CircomSubprocess, SnarkjsSubprocess, ZkRegexSubprocess
1012

1113
def main():
1214
parser = argparse.ArgumentParser(
@@ -24,6 +26,12 @@ def main():
2426
default=10,
2527
help="Number of inputs to generate for each regex (default: 10)."
2628
)
29+
parser.add_argument(
30+
"--max-input-size",
31+
type=int,
32+
default=200,
33+
help="Maximum size of the circuit input (default: 200)."
34+
)
2735
parser.add_argument(
2836
"--oracle",
2937
choices=["valid", "invalid"],
@@ -52,10 +60,56 @@ def main():
5260
help="Maximum depth of recursion in the grammar (default: 5)."
5361
)
5462

63+
parser.add_argument(
64+
"--circom-library",
65+
nargs="*",
66+
type=str,
67+
help="Path to the circom library to be included"
68+
)
69+
70+
parser.add_argument(
71+
"--circom-prove",
72+
action="store_true",
73+
help="Run the proving and verification step with SnarkJS."
74+
)
75+
76+
parser.add_argument(
77+
"--circom-ptau",
78+
type=str,
79+
help="Path to the ptau (powers-of-tau) file for the proving step"
80+
)
81+
5582
args = parser.parse_args()
5683

5784
if args.oracle == "valid" and not args.valid_input_generator:
5885
raise ValueError("Valid input generator is required for valid oracle.")
86+
87+
if args.target == "circom":
88+
circom_version = CircomSubprocess.get_installed_version()
89+
snarkjs_version = SnarkjsSubprocess.get_installed_version()
90+
zk_regex_version = ZkRegexSubprocess.get_installed_version()
91+
print("-" * 80)
92+
print(f"Circom: {circom_version}")
93+
print(f"SnarkJS: {snarkjs_version}")
94+
print(f"zk-regex: {zk_regex_version}")
95+
96+
# check if path to circom library passed as -l arg exists
97+
normalized_path = []
98+
for path in args.circom_library:
99+
abs_path = Path(path).resolve()
100+
if not abs_path.exists():
101+
raise ValueError(f"Path to circom library {abs_path} does not exist.")
102+
normalized_path.append(str(abs_path))
103+
args.circom_library = normalized_path
104+
105+
# check if path to ptau used in proving step exists
106+
if args.circom_prove:
107+
if not args.circom_ptau:
108+
raise ValueError("Path to ptau file is required for proving.")
109+
110+
ptau_path = Path(args.circom_ptau).resolve()
111+
if not ptau_path.exists():
112+
raise ValueError(f"Path to ptau file {ptau_path} does not exist.")
59113

60114
print("-" * 80)
61115
print(f"Fuzzing with {args.fuzzer} fuzzer.")
@@ -76,6 +130,7 @@ def main():
76130
regex_num=args.regex_num,
77131
inputs_num=args.inputs_num,
78132
max_depth=args.max_depth,
133+
kwargs=vars(args)
79134
)
80135

81136

src/zkregex_fuzzer/fuzzer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def fuzz_with_grammar(
1818
regex_num: int,
1919
inputs_num: int,
2020
max_depth: int,
21+
kwargs
2122
):
2223
"""
2324
Fuzz test with grammar.
@@ -42,7 +43,7 @@ def fuzz_with_grammar(
4243
primary_runner = PythonReRunner
4344
for regex, inputs in zip(regexes, regexes_inputs):
4445
print(f"Testing regex: {regex} -------- ({len(inputs)} inputs)")
45-
result = harness(regex, primary_runner, target_runner, inputs, oracle)
46+
result = harness(regex, primary_runner, target_runner, inputs, oracle, kwargs)
4647
if result.status == HarnessStatus.FAILED:
4748
print("-" * 80)
4849
print(f"Found a bug with regex: {regex}")

src/zkregex_fuzzer/harness.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Type, List
1010
from enum import Enum
1111
from dataclasses import dataclass
12+
from zkregex_fuzzer.logger import logger
1213
from zkregex_fuzzer.runner import Runner, RegexCompileError, RegexRunError
1314

1415
class HarnessStatus(Enum):
@@ -28,7 +29,7 @@ class HarnessResult:
2829
# Status (an enum for the result of the test)
2930
status: HarnessStatus
3031

31-
def harness(regex: str, primary_runner_cls: Type[Runner], secondary_runner_cls: Type[Runner], inputs: List[str], oracle: bool) -> HarnessResult:
32+
def harness(regex: str, primary_runner_cls: Type[Runner], secondary_runner_cls: Type[Runner], inputs: List[str], oracle: bool, kwargs) -> HarnessResult:
3233
"""
3334
Harness for running regexes.
3435
@@ -37,6 +38,7 @@ def harness(regex: str, primary_runner_cls: Type[Runner], secondary_runner_cls:
3738
secondary_runner_cls: The class of the secondary runner (either circom or noir runners).
3839
inputs: The inputs to use to test the regex.
3940
oracle: The oracle to use to test the regex. True if the inputs are valid regexes. False if the inputs are invalid regexes.
41+
kwargs: The arguments to pass to the secondary runner.
4042
4143
Returns:
4244
A HarnessResult object.
@@ -49,7 +51,7 @@ def harness(regex: str, primary_runner_cls: Type[Runner], secondary_runner_cls:
4951
return HarnessResult(regex, inp_num, oracle, [], HarnessStatus.INVALID_SEED)
5052

5153
try:
52-
secondary_runner = secondary_runner_cls(regex)
54+
secondary_runner = secondary_runner_cls(regex, kwargs)
5355
except RegexCompileError as e:
5456
return HarnessResult(regex, inp_num, oracle, [], HarnessStatus.COMPILE_ERROR)
5557

@@ -63,7 +65,7 @@ def harness(regex: str, primary_runner_cls: Type[Runner], secondary_runner_cls:
6365
try:
6466
if secondary_runner.match(input) != oracle:
6567
failed_inputs.append(input)
66-
except RegexRunError:
68+
except RegexRunError as e:
6769
return HarnessResult(regex, inp_num, oracle, [input], HarnessStatus.RUN_ERROR)
6870

6971
if len(failed_inputs) > 0:

src/zkregex_fuzzer/runner.py

Lines changed: 0 additions & 129 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .base_runner import Runner, RegexCompileError, RegexRunError
2+
from .python import PythonReRunner
3+
from .circom import CircomRunner
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""
2+
Runners for regex implementations.
3+
4+
Supported runners:
5+
- Python re module
6+
7+
TODO:
8+
- Circom runner
9+
- Noir runner
10+
"""
11+
12+
from abc import ABC, abstractmethod
13+
14+
15+
class RegexCompileError(Exception):
16+
"""
17+
Exception raised when a regex cannot be compiled.
18+
"""
19+
pass
20+
21+
22+
class RegexRunError(Exception):
23+
"""
24+
Exception raised when a regex cannot be run.
25+
"""
26+
pass
27+
28+
29+
class Runner(ABC):
30+
"""
31+
Abstract base class for regex runners.
32+
"""
33+
34+
def __init__(self, regex: str):
35+
self._regex = regex
36+
self._runner = "Abstract runner"
37+
self._regex_object = self.compile(regex)
38+
39+
@abstractmethod
40+
def compile(self, regex: str) -> None:
41+
"""
42+
Compile a regex.
43+
"""
44+
pass
45+
46+
@abstractmethod
47+
def match(self, input: str) -> bool:
48+
"""
49+
Match the regex on an input.
50+
"""
51+
pass
52+
53+
def clean(self) -> None:
54+
"""
55+
Clean any produced temporary files.
56+
"""
57+
pass
58+
59+
def save(self, path: str) -> None:
60+
"""
61+
Save any produced temporary files.
62+
"""
63+
pass
64+

0 commit comments

Comments
 (0)