JSTprove 1.0.0__py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. jstprove-1.0.0.dist-info/METADATA +397 -0
  2. jstprove-1.0.0.dist-info/RECORD +81 -0
  3. jstprove-1.0.0.dist-info/WHEEL +6 -0
  4. jstprove-1.0.0.dist-info/entry_points.txt +2 -0
  5. jstprove-1.0.0.dist-info/licenses/LICENSE +26 -0
  6. jstprove-1.0.0.dist-info/top_level.txt +1 -0
  7. python/__init__.py +0 -0
  8. python/core/__init__.py +3 -0
  9. python/core/binaries/__init__.py +0 -0
  10. python/core/binaries/expander-exec +0 -0
  11. python/core/binaries/onnx_generic_circuit_1-0-0 +0 -0
  12. python/core/circuit_models/__init__.py +0 -0
  13. python/core/circuit_models/generic_onnx.py +231 -0
  14. python/core/circuit_models/simple_circuit.py +133 -0
  15. python/core/circuits/__init__.py +0 -0
  16. python/core/circuits/base.py +1000 -0
  17. python/core/circuits/errors.py +188 -0
  18. python/core/circuits/zk_model_base.py +25 -0
  19. python/core/model_processing/__init__.py +0 -0
  20. python/core/model_processing/converters/__init__.py +0 -0
  21. python/core/model_processing/converters/base.py +143 -0
  22. python/core/model_processing/converters/onnx_converter.py +1181 -0
  23. python/core/model_processing/errors.py +147 -0
  24. python/core/model_processing/onnx_custom_ops/__init__.py +16 -0
  25. python/core/model_processing/onnx_custom_ops/conv.py +111 -0
  26. python/core/model_processing/onnx_custom_ops/custom_helpers.py +56 -0
  27. python/core/model_processing/onnx_custom_ops/gemm.py +91 -0
  28. python/core/model_processing/onnx_custom_ops/maxpool.py +79 -0
  29. python/core/model_processing/onnx_custom_ops/onnx_helpers.py +173 -0
  30. python/core/model_processing/onnx_custom_ops/relu.py +43 -0
  31. python/core/model_processing/onnx_quantizer/__init__.py +0 -0
  32. python/core/model_processing/onnx_quantizer/exceptions.py +168 -0
  33. python/core/model_processing/onnx_quantizer/layers/__init__.py +0 -0
  34. python/core/model_processing/onnx_quantizer/layers/base.py +396 -0
  35. python/core/model_processing/onnx_quantizer/layers/constant.py +118 -0
  36. python/core/model_processing/onnx_quantizer/layers/conv.py +180 -0
  37. python/core/model_processing/onnx_quantizer/layers/gemm.py +171 -0
  38. python/core/model_processing/onnx_quantizer/layers/maxpool.py +140 -0
  39. python/core/model_processing/onnx_quantizer/layers/relu.py +76 -0
  40. python/core/model_processing/onnx_quantizer/onnx_op_quantizer.py +200 -0
  41. python/core/model_templates/__init__.py +0 -0
  42. python/core/model_templates/circuit_template.py +57 -0
  43. python/core/utils/__init__.py +0 -0
  44. python/core/utils/benchmarking_helpers.py +163 -0
  45. python/core/utils/constants.py +4 -0
  46. python/core/utils/errors.py +117 -0
  47. python/core/utils/general_layer_functions.py +268 -0
  48. python/core/utils/helper_functions.py +1138 -0
  49. python/core/utils/model_registry.py +166 -0
  50. python/core/utils/scratch_tests.py +66 -0
  51. python/core/utils/witness_utils.py +291 -0
  52. python/frontend/__init__.py +0 -0
  53. python/frontend/cli.py +115 -0
  54. python/frontend/commands/__init__.py +17 -0
  55. python/frontend/commands/args.py +100 -0
  56. python/frontend/commands/base.py +199 -0
  57. python/frontend/commands/bench/__init__.py +54 -0
  58. python/frontend/commands/bench/list.py +42 -0
  59. python/frontend/commands/bench/model.py +172 -0
  60. python/frontend/commands/bench/sweep.py +212 -0
  61. python/frontend/commands/compile.py +58 -0
  62. python/frontend/commands/constants.py +5 -0
  63. python/frontend/commands/model_check.py +53 -0
  64. python/frontend/commands/prove.py +50 -0
  65. python/frontend/commands/verify.py +73 -0
  66. python/frontend/commands/witness.py +64 -0
  67. python/scripts/__init__.py +0 -0
  68. python/scripts/benchmark_runner.py +833 -0
  69. python/scripts/gen_and_bench.py +482 -0
  70. python/tests/__init__.py +0 -0
  71. python/tests/circuit_e2e_tests/__init__.py +0 -0
  72. python/tests/circuit_e2e_tests/circuit_model_developer_test.py +1158 -0
  73. python/tests/circuit_e2e_tests/helper_fns_for_tests.py +190 -0
  74. python/tests/circuit_e2e_tests/other_e2e_test.py +217 -0
  75. python/tests/circuit_parent_classes/__init__.py +0 -0
  76. python/tests/circuit_parent_classes/test_circuit.py +969 -0
  77. python/tests/circuit_parent_classes/test_onnx_converter.py +201 -0
  78. python/tests/circuit_parent_classes/test_ort_custom_layers.py +116 -0
  79. python/tests/test_cli.py +1021 -0
  80. python/tests/utils_testing/__init__.py +0 -0
  81. python/tests/utils_testing/test_helper_functions.py +891 -0
@@ -0,0 +1,891 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ from unittest.mock import MagicMock, mock_open, patch
6
+
7
+ import pytest
8
+
9
+ from python.core.utils.errors import ProofBackendError, ProofSystemNotImplementedError
10
+ from python.core.utils.helper_functions import (
11
+ CircuitExecutionConfig,
12
+ ExpanderMode,
13
+ RunType,
14
+ ZKProofSystems,
15
+ compile_circuit,
16
+ compute_and_store_output,
17
+ generate_proof,
18
+ generate_verification,
19
+ generate_witness,
20
+ get_expander_file_paths,
21
+ get_files,
22
+ prepare_io_files,
23
+ read_from_json,
24
+ run_cargo_command,
25
+ run_end_to_end,
26
+ to_json,
27
+ )
28
+
29
+
30
+ # ---------- compute_and_store_output ----------
31
+ @pytest.mark.unit
32
+ @patch("python.core.utils.helper_functions.Path.mkdir")
33
+ @patch("python.core.utils.helper_functions.Path.exists", return_value=False)
34
+ @patch("python.core.utils.helper_functions.json.dump")
35
+ @patch("python.core.utils.helper_functions.Path.open")
36
+ def test_compute_and_store_output_saves(
37
+ mock_file: MagicMock,
38
+ mock_dump: MagicMock,
39
+ mock_exists: MagicMock,
40
+ mock_mkdir: MagicMock,
41
+ ) -> None:
42
+ mock_file.return_value.__enter__.return_value = MagicMock()
43
+ mock_file.return_value.__exit__.return_value = None
44
+
45
+ class Dummy:
46
+ name = "test"
47
+ temp_folder = "temp_test"
48
+
49
+ @compute_and_store_output
50
+ def get_outputs(self: Dummy) -> dict[str, int]:
51
+ return {"out": 123}
52
+
53
+ d = Dummy()
54
+ result = d.get_outputs()
55
+
56
+ mock_mkdir.assert_called_once()
57
+ mock_dump.assert_called_once()
58
+ assert result == {"out": 123}
59
+
60
+
61
+ @pytest.mark.unit
62
+ @patch("python.core.utils.helper_functions.Path")
63
+ @patch("python.core.utils.helper_functions.json.load", return_value={"out": 456})
64
+ def test_compute_and_store_output_loads_from_cache(
65
+ mock_load: MagicMock,
66
+ mock_path_class: MagicMock,
67
+ ) -> None:
68
+ # Arrange the Path mock
69
+ mock_path_instance = MagicMock()
70
+ mock_path_instance.exists.return_value = True
71
+ mock_path_instance.open.return_value = mock_open(
72
+ read_data='{"out": 456}',
73
+ ).return_value
74
+ mock_path_class.return_value = mock_path_instance
75
+
76
+ class Dummy:
77
+ name = "test"
78
+ temp_folder = "temp_test"
79
+
80
+ @compute_and_store_output
81
+ def get_outputs(self: Dummy) -> dict[str, int]:
82
+ return {"out": 999} # should not run
83
+
84
+ # Act
85
+ d = Dummy()
86
+ output = d.get_outputs()
87
+
88
+ # Assert
89
+ assert output == {"out": 456}
90
+
91
+
92
+ @pytest.mark.unit
93
+ @patch("python.core.utils.helper_functions.os.path.exists", return_value=True)
94
+ @patch("python.core.utils.helper_functions.open", new_callable=mock_open)
95
+ @patch(
96
+ "python.core.utils.helper_functions.json.load",
97
+ side_effect=json.JSONDecodeError("msg", "doc", 0),
98
+ )
99
+ @patch("python.core.utils.helper_functions.json.dump")
100
+ def test_compute_and_store_output_on_json_error(
101
+ mock_dump: MagicMock,
102
+ mock_load: MagicMock,
103
+ mock_file: MagicMock,
104
+ mock_exists: MagicMock,
105
+ ) -> None:
106
+
107
+ class Dummy:
108
+ name = "bad"
109
+ temp_folder = "temp_test"
110
+
111
+ @compute_and_store_output
112
+ def get_outputs(self: Dummy) -> dict[str, bool]:
113
+ return {"fallback": True}
114
+
115
+ d = Dummy()
116
+ output = d.get_outputs()
117
+ assert output == {"fallback": True}
118
+
119
+
120
+ # ---------- prepare_io_files ----------
121
+ @pytest.mark.unit
122
+ @patch(
123
+ "python.core.utils.helper_functions.get_files",
124
+ return_value={
125
+ "witness_file": "witness.wtns",
126
+ "input_file": "input.json",
127
+ "proof_path": "proof.json",
128
+ "public_path": "public.json",
129
+ "verification_key": "vk.key",
130
+ "circuit_name": "test_circuit",
131
+ "metadata_path": "metadata.json",
132
+ "architecture_path": "architecture.json",
133
+ "w_and_b_path": "w_and_b.json",
134
+ "output_file": "out.json",
135
+ },
136
+ )
137
+ @patch("python.core.utils.helper_functions.to_json")
138
+ @patch(
139
+ "python.core.utils.helper_functions.os.path.splitext",
140
+ return_value=("model", ".onnx"),
141
+ )
142
+ @patch("python.core.utils.helper_functions.open", new_callable=mock_open)
143
+ def test_prepare_io_files_runs_func(
144
+ mock_file: MagicMock,
145
+ mock_splitext: MagicMock,
146
+ mock_json: MagicMock,
147
+ mock_get_files: MagicMock,
148
+ ) -> None:
149
+
150
+ class Dummy:
151
+ name = "model"
152
+ input_shape = (1, 4)
153
+ scale_base = 10
154
+ scale_exponent = 1
155
+
156
+ def __init__(self: Dummy) -> None:
157
+ self.get_inputs = lambda: 1
158
+ self.get_outputs = lambda _x=None: 2
159
+ self.get_inputs_from_file = lambda _file_name, _is_scaled=True: 3
160
+ self.format_inputs = lambda x: {"input": x}
161
+ self.format_outputs = lambda x: {"output": x}
162
+ self.get_weights = lambda: {"weights": [1, 2]}
163
+ self.save_quantized_model = MagicMock()
164
+ self.get_model_and_quantize = MagicMock()
165
+
166
+ @prepare_io_files
167
+ def base_testing(
168
+ self: Dummy,
169
+ exec_config: CircuitExecutionConfig,
170
+ ) -> dict[str, bool]:
171
+ assert exec_config.run_type == RunType.GEN_WITNESS
172
+ return {"test": True}
173
+
174
+ d = Dummy()
175
+ print(CircuitExecutionConfig(run_type=RunType.GEN_WITNESS, write_json=True))
176
+ result = d.base_testing(
177
+ CircuitExecutionConfig(run_type=RunType.GEN_WITNESS, write_json=True),
178
+ )
179
+ assert result == {"test": True}
180
+ assert d._file_info["output_file"] == "out.json"
181
+ assert d._file_info["w_and_b_path"] == "w_and_b.json"
182
+
183
+
184
+ # ---------- to_json ----------
185
+ @pytest.mark.unit
186
+ @patch("python.core.utils.helper_functions.Path")
187
+ @patch("python.core.utils.helper_functions.json.dump")
188
+ def test_to_json_saves_json(mock_dump: MagicMock, mock_path: MagicMock) -> None:
189
+ mock_path.return_value.open.return_value.__enter__.return_value = MagicMock()
190
+
191
+ data = {"a": 1}
192
+ to_json(data, "output.json")
193
+
194
+ mock_path.assert_called_with("output.json") # verify the filename was used
195
+ mock_path.return_value.open.assert_called_once_with(
196
+ "w",
197
+ ) # verify open was called with write mode
198
+ mock_dump.assert_called_once_with(
199
+ data,
200
+ mock_path.return_value.open.return_value.__enter__.return_value,
201
+ )
202
+
203
+
204
+ # ---------- read_from_json ----------
205
+ @pytest.mark.unit
206
+ @patch("python.core.utils.helper_functions.Path")
207
+ @patch("python.core.utils.helper_functions.json.load", return_value={"x": 42})
208
+ def test_read_from_json_loads_json(mock_load: MagicMock, mock_path: MagicMock) -> None:
209
+ mock_path.return_value.open.return_value.__enter__.return_value = MagicMock()
210
+ result = read_from_json("input.json")
211
+ mock_path.assert_called_once_with("input.json") # check filename used
212
+ mock_path.return_value.open.assert_called_once_with() # open called with no args
213
+ mock_load.assert_called_once_with(
214
+ mock_path.return_value.open.return_value.__enter__.return_value,
215
+ )
216
+ assert result == {"x": 42}
217
+
218
+
219
+ # ---------- run_cargo_command ----------
220
+ @pytest.mark.unit
221
+ @patch("python.core.utils.helper_functions.Path")
222
+ @patch("python.core.utils.helper_functions.subprocess.run")
223
+ def test_run_cargo_command_normal(
224
+ mock_run: MagicMock,
225
+ mock_path_class: MagicMock,
226
+ ) -> None:
227
+ mock_path_instance = MagicMock()
228
+ mock_path_instance.exists.return_value = True
229
+ mock_path_class.return_value = mock_path_instance
230
+
231
+ mock_run.return_value = MagicMock(returncode=0, stdout="ok")
232
+ code = run_cargo_command("zkbinary", "run", {"i": "input.json"}, dev_mode=False)
233
+
234
+ mock_run.assert_called_once()
235
+ args = mock_run.call_args[0][0]
236
+ assert "./target/release/zkbinary" in args[0]
237
+ assert "run" in args
238
+ assert "-i" in args
239
+ assert "input.json" in args
240
+ assert code.returncode == 0
241
+
242
+
243
+ @pytest.mark.unit
244
+ @patch("python.core.utils.helper_functions.subprocess.run")
245
+ @patch("python.core.utils.helper_functions.Path.read_text")
246
+ def test_run_cargo_command_dev_mode(
247
+ mock_read_text: MagicMock,
248
+ mock_run: MagicMock,
249
+ ) -> None:
250
+ # Mock pyproject.toml contents so version parsing succeeds
251
+ mock_read_text.return_value = """
252
+ [project]
253
+ version = "1.2.3"
254
+ """
255
+
256
+ # Mock subprocess.run success
257
+ mock_run.return_value = MagicMock(returncode=0)
258
+
259
+ # Run the function under test
260
+
261
+ run_cargo_command("testbin", "compile", dev_mode=True)
262
+
263
+ # Extract the actual cargo command used
264
+ args = mock_run.call_args[0][0]
265
+
266
+ # Build the expected binary name based on pyproject.toml version
267
+ expected_bin = "testbin_None"
268
+
269
+ # Assertions
270
+ assert args[:5] == ["cargo", "run", "--bin", expected_bin, "--release"]
271
+ assert "compile" in args
272
+
273
+
274
+ @pytest.mark.unit
275
+ @patch("python.core.utils.helper_functions.Path")
276
+ @patch("python.core.utils.helper_functions.subprocess.run")
277
+ def test_run_cargo_command_fallback_to_cargo_run(
278
+ mock_run: MagicMock,
279
+ mock_path_class: MagicMock,
280
+ ) -> None:
281
+ """Test that when binary doesn't exist
282
+ and dev_mode=False, it falls back to cargo run."""
283
+ mock_path_instance = MagicMock()
284
+ mock_path_instance.exists.return_value = False
285
+ mock_path_class.return_value = mock_path_instance
286
+
287
+ mock_run.return_value = MagicMock(returncode=0)
288
+ run_cargo_command("missingbin", "compile", dev_mode=False)
289
+
290
+ args = mock_run.call_args[0][0]
291
+ assert args[:3] == ["cargo", "run", "--bin"]
292
+ assert "missingbin" in args[3]
293
+ assert args[4] == "--release"
294
+ assert "compile" in args
295
+
296
+
297
+ @pytest.mark.unit
298
+ @patch("python.core.utils.helper_functions.subprocess.run")
299
+ def test_run_cargo_command_bool_args(mock_run: MagicMock) -> None:
300
+ mock_run.return_value = MagicMock(returncode=0)
301
+ run_cargo_command("zkproof", "verify", {"v": True, "json": False, "i": "in.json"})
302
+
303
+ args = mock_run.call_args[0][0]
304
+ assert "-v" in args
305
+ assert "-i" in args
306
+ assert "in.json" in args
307
+ assert "-json" in args # Even though False, it's added
308
+
309
+
310
+ @pytest.mark.unit
311
+ @patch(
312
+ "python.core.utils.helper_functions.subprocess.run",
313
+ side_effect=Exception("subprocess failed"),
314
+ )
315
+ def test_run_cargo_command_raises_on_failure(mock_run: MagicMock) -> None:
316
+ with pytest.raises(Exception, match="subprocess failed"):
317
+ run_cargo_command("failbin", "fail_cmd", {"x": 1})
318
+
319
+
320
+ @pytest.mark.unit
321
+ @patch("python.core.utils.helper_functions.Path")
322
+ @patch("python.core.utils.helper_functions.subprocess.run")
323
+ def test_run_command_failure(mock_run: MagicMock, mock_path_class: MagicMock) -> None:
324
+ mock_path_instance = MagicMock()
325
+ mock_path_instance.exists.return_value = True
326
+ mock_path_class.return_value = mock_path_instance
327
+
328
+ mock_run.side_effect = subprocess.CalledProcessError(
329
+ returncode=1,
330
+ cmd=["fakecmd"],
331
+ stderr="boom!",
332
+ )
333
+
334
+ with pytest.raises(ProofBackendError) as excinfo:
335
+ run_cargo_command("fakecmd", "type")
336
+
337
+ assert "Rust backend error" in str(excinfo.value)
338
+ assert "fakecmd" in str(excinfo.value)
339
+ assert "type" in str(excinfo.value)
340
+
341
+
342
+ # ---------- get_expander_file_paths ----------
343
+ @pytest.mark.unit
344
+ def test_get_expander_file_paths() -> None:
345
+ name = "model"
346
+ paths = get_expander_file_paths(name)
347
+ assert paths["circuit_file"] == "model_circuit.txt"
348
+ assert paths["witness_file"] == "model_witness.txt"
349
+ assert paths["proof_file"] == "model_proof.txt"
350
+
351
+
352
+ # ---------- compile_circuit ----------
353
+ @pytest.mark.integration
354
+ @patch("python.core.utils.helper_functions.run_cargo_command")
355
+ def test_compile_circuit_expander(mock_run: MagicMock) -> None:
356
+ compile_circuit(
357
+ "model",
358
+ "path/to/circuit",
359
+ "path/to/metadata.json",
360
+ "path/to/architecture.json",
361
+ "path/to/w_and_b.json",
362
+ ZKProofSystems.Expander,
363
+ )
364
+ _, kwargs = mock_run.call_args
365
+ assert kwargs["dev_mode"]
366
+ assert kwargs["args"]["n"] == "model"
367
+ assert kwargs["args"]["c"] == "path/to/circuit"
368
+ assert kwargs["args"]["m"] == "path/to/metadata.json"
369
+ assert kwargs["args"]["a"] == "path/to/architecture.json"
370
+ assert kwargs["args"]["b"] == "path/to/w_and_b.json"
371
+ mock_run.assert_called_once()
372
+
373
+
374
+ @pytest.mark.integration
375
+ @patch("python.core.utils.helper_functions.run_cargo_command")
376
+ def test_compile_circuit_expander_dev_mode_true(mock_run: MagicMock) -> None:
377
+ compile_circuit(
378
+ "model2",
379
+ "path/to/circuit2",
380
+ "path/to/metadata2.json",
381
+ "path/to/architecture2.json",
382
+ "path/to/w_and_b2.json",
383
+ ZKProofSystems.Expander,
384
+ dev_mode=True,
385
+ )
386
+ _, kwargs = mock_run.call_args
387
+ assert kwargs["dev_mode"]
388
+ assert kwargs["args"]["n"] == "model2"
389
+ assert kwargs["args"]["c"] == "path/to/circuit2"
390
+ assert kwargs["args"]["m"] == "path/to/metadata2.json"
391
+ assert kwargs["args"]["a"] == "path/to/architecture2.json"
392
+ assert kwargs["args"]["b"] == "path/to/w_and_b2.json"
393
+ mock_run.assert_called_once()
394
+
395
+
396
+ @pytest.mark.integration
397
+ @patch(
398
+ "python.core.utils.helper_functions.run_cargo_command",
399
+ side_effect=ProofBackendError("TEST"),
400
+ )
401
+ def test_compile_circuit_expander_rust_error(
402
+ mock_run: MagicMock,
403
+ caplog: pytest.LogCaptureFixture,
404
+ ) -> None:
405
+ with pytest.raises(Exception, match="TEST"):
406
+ compile_circuit(
407
+ "model2",
408
+ "path/to/circuit2",
409
+ "path/to/metadata2.json",
410
+ "path/to/architecture2.json",
411
+ "path/to/w_and_b2.json",
412
+ ZKProofSystems.Expander,
413
+ dev_mode=True,
414
+ )
415
+ assert "Warning: Compile operation failed: TEST" in caplog.text
416
+ assert "Using binary: model2" in caplog.text
417
+
418
+
419
+ @pytest.mark.integration
420
+ def test_compile_circuit_unknown_raises() -> None:
421
+ with pytest.raises(ProofSystemNotImplementedError):
422
+ compile_circuit("m", "p", "meta.json", "arch.json", "w.json", "unsupported")
423
+
424
+
425
+ # # ---------- generate_witness ----------
426
+ @pytest.mark.integration
427
+ @patch("python.core.utils.helper_functions.run_cargo_command")
428
+ def test_generate_witness_expander(mock_run: MagicMock) -> None:
429
+ generate_witness(
430
+ "model",
431
+ "path/to/circuit",
432
+ "witness",
433
+ "input",
434
+ "output",
435
+ "metadata.json",
436
+ ZKProofSystems.Expander,
437
+ )
438
+ _, kwargs = mock_run.call_args
439
+ assert not kwargs["dev_mode"]
440
+ assert kwargs["args"]["n"] == "model"
441
+ assert kwargs["args"]["c"] == "path/to/circuit"
442
+ assert kwargs["args"]["w"] == "witness"
443
+ assert kwargs["args"]["i"] == "input"
444
+ assert kwargs["args"]["o"] == "output"
445
+ assert kwargs["args"]["m"] == "metadata.json"
446
+ mock_run.assert_called_once()
447
+
448
+
449
+ @pytest.mark.integration
450
+ @patch("python.core.utils.helper_functions.run_cargo_command")
451
+ def test_generate_witness_expander_dev_mode_true(mock_run: MagicMock) -> None:
452
+ generate_witness(
453
+ "model",
454
+ "path/to/circuit",
455
+ "witness",
456
+ "input",
457
+ "output",
458
+ "metadata.json",
459
+ ZKProofSystems.Expander,
460
+ dev_mode=True,
461
+ )
462
+ _, kwargs = mock_run.call_args
463
+ assert kwargs["dev_mode"]
464
+ assert kwargs["args"]["n"] == "model"
465
+ assert kwargs["args"]["c"] == "path/to/circuit"
466
+ assert kwargs["args"]["w"] == "witness"
467
+ assert kwargs["args"]["i"] == "input"
468
+ assert kwargs["args"]["o"] == "output"
469
+ assert kwargs["args"]["m"] == "metadata.json"
470
+ mock_run.assert_called_once()
471
+
472
+
473
+ @pytest.mark.integration
474
+ @patch(
475
+ "python.core.utils.helper_functions.run_cargo_command",
476
+ side_effect=ProofBackendError("TEST"),
477
+ )
478
+ def test_generate_witness_expander_rust_error(
479
+ mock_run: MagicMock,
480
+ caplog: pytest.LogCaptureFixture,
481
+ ) -> None:
482
+ with pytest.raises(Exception, match="TEST"):
483
+ generate_witness(
484
+ "model2",
485
+ "path/to/circuit2",
486
+ "witness",
487
+ "input",
488
+ "output",
489
+ "metadata.json",
490
+ ZKProofSystems.Expander,
491
+ dev_mode=True,
492
+ )
493
+ assert "Warning: Witness generation failed: TEST" in caplog.text
494
+
495
+
496
+ @pytest.mark.unit
497
+ def test_generate_witness_unknown_raises() -> None:
498
+ with pytest.raises(ProofSystemNotImplementedError):
499
+ generate_witness(
500
+ "m",
501
+ "p",
502
+ "witness",
503
+ "input",
504
+ "output",
505
+ "metadata.json",
506
+ "unsupported",
507
+ )
508
+
509
+
510
+ # ---------- generate_proof ----------
511
+
512
+
513
+ @pytest.mark.integration
514
+ @patch("python.core.utils.helper_functions.run_expander_raw")
515
+ @patch(
516
+ "python.core.utils.helper_functions.get_expander_file_paths",
517
+ return_value={"circuit_file": "c", "witness_file": "w", "proof_file": "p"},
518
+ )
519
+ def test_generate_proof_expander_no_ecc(
520
+ mock_paths: MagicMock,
521
+ mock_exec: MagicMock,
522
+ ) -> None:
523
+ generate_proof("model", "c", "w", "p", ZKProofSystems.Expander, ecc=False)
524
+ assert mock_exec.call_args[1]["mode"] == ExpanderMode.PROVE
525
+ assert mock_exec.call_args[1]["circuit_file"] == "c"
526
+ assert mock_exec.call_args[1]["witness_file"] == "w"
527
+ assert mock_exec.call_args[1]["proof_file"] == "p"
528
+
529
+ assert mock_exec.call_count == 1
530
+
531
+
532
+ @pytest.mark.integration
533
+ @patch("python.core.utils.helper_functions.run_cargo_command")
534
+ def test_generate_proof_expander_with_ecc(mock_run: MagicMock) -> None:
535
+ generate_proof(
536
+ "model",
537
+ "c",
538
+ "w",
539
+ "p",
540
+ "metadata.json",
541
+ ZKProofSystems.Expander,
542
+ ecc=True,
543
+ )
544
+ mock_run.assert_called_once()
545
+ _, kwargs = mock_run.call_args
546
+ assert kwargs["binary_name"] == "model"
547
+ assert kwargs["command_type"] == "run_prove_witness"
548
+ assert not kwargs["dev_mode"]
549
+ assert kwargs["args"]["n"] == "model"
550
+ assert kwargs["args"]["c"] == "c"
551
+ assert kwargs["args"]["w"] == "w"
552
+ assert kwargs["args"]["p"] == "p"
553
+ assert kwargs["args"]["m"] == "metadata.json"
554
+
555
+
556
+ @pytest.mark.integration
557
+ @patch("python.core.utils.helper_functions.run_cargo_command")
558
+ def test_generate_proof_expander_with_ecc_dev_mode_true(mock_run: MagicMock) -> None:
559
+ generate_proof(
560
+ "model",
561
+ "c",
562
+ "w",
563
+ "p",
564
+ "metadata.json",
565
+ ZKProofSystems.Expander,
566
+ ecc=True,
567
+ dev_mode=True,
568
+ )
569
+ mock_run.assert_called_once()
570
+ _, kwargs = mock_run.call_args
571
+ assert kwargs["binary_name"] == "model"
572
+ assert kwargs["command_type"] == "run_prove_witness"
573
+ assert kwargs["dev_mode"]
574
+ assert kwargs["args"]["n"] == "model"
575
+ assert kwargs["args"]["c"] == "c"
576
+ assert kwargs["args"]["w"] == "w"
577
+ assert kwargs["args"]["p"] == "p"
578
+ assert kwargs["args"]["m"] == "metadata.json"
579
+
580
+
581
+ @pytest.mark.unit
582
+ def test_generate_proof_unknown_raises() -> None:
583
+ with pytest.raises(ProofSystemNotImplementedError):
584
+ generate_proof("m", "p", "w", "proof", "metadata.json", "unsupported")
585
+
586
+
587
+ @pytest.mark.unit
588
+ @patch(
589
+ "python.core.utils.helper_functions.run_cargo_command",
590
+ side_effect=ProofBackendError("TEST"),
591
+ )
592
+ def test_generate_proof_expander_rust_error(
593
+ mock_run: MagicMock,
594
+ caplog: pytest.LogCaptureFixture,
595
+ ) -> None:
596
+ with pytest.raises(Exception, match="TEST"):
597
+ generate_proof(
598
+ "model2",
599
+ "path/to/circuit2",
600
+ "w",
601
+ "p",
602
+ "metadata.json",
603
+ ZKProofSystems.Expander,
604
+ dev_mode=True,
605
+ )
606
+ assert "Warning: Proof generation failed: TEST" in caplog.text
607
+
608
+
609
+ # # ---------- generate_verification ----------
610
+ @pytest.mark.integration
611
+ @patch("python.core.utils.helper_functions.run_expander_raw")
612
+ @patch(
613
+ "python.core.utils.helper_functions.get_expander_file_paths",
614
+ return_value={"circuit_file": "c", "witness_file": "w", "proof_file": "p"},
615
+ )
616
+ def test_generate_verify_expander_no_ecc(
617
+ mock_paths: MagicMock,
618
+ mock_exec: MagicMock,
619
+ ) -> None:
620
+ generate_verification(
621
+ "model",
622
+ "c",
623
+ "i",
624
+ "o",
625
+ "w",
626
+ "p",
627
+ ZKProofSystems.Expander,
628
+ ecc=False,
629
+ )
630
+ assert mock_exec.call_args[1]["mode"] == ExpanderMode.VERIFY
631
+ assert mock_exec.call_args[1]["circuit_file"] == "c"
632
+ assert mock_exec.call_args[1]["witness_file"] == "w"
633
+ assert mock_exec.call_args[1]["proof_file"] == "p"
634
+
635
+ assert mock_exec.call_count == 1
636
+
637
+
638
+ @pytest.mark.integration
639
+ @patch("python.core.utils.helper_functions.run_cargo_command")
640
+ def test_generate_verify_expander_with_ecc(mock_run: MagicMock) -> None:
641
+ generate_verification(
642
+ "model",
643
+ "c",
644
+ "i",
645
+ "o",
646
+ "w",
647
+ "p",
648
+ "metadata.json",
649
+ ZKProofSystems.Expander,
650
+ ecc=True,
651
+ )
652
+ mock_run.assert_called_once()
653
+
654
+ _, kwargs = mock_run.call_args
655
+ assert kwargs["binary_name"] == "model"
656
+ assert kwargs["command_type"] == "run_gen_verify"
657
+ assert not kwargs["dev_mode"]
658
+ assert kwargs["args"]["n"] == "model"
659
+ assert kwargs["args"]["c"] == "c"
660
+ assert kwargs["args"]["w"] == "w"
661
+ assert kwargs["args"]["p"] == "p"
662
+ assert kwargs["args"]["i"] == "i"
663
+ assert kwargs["args"]["o"] == "o"
664
+ assert kwargs["args"]["m"] == "metadata.json"
665
+ mock_run.assert_called_once()
666
+
667
+
668
+ @pytest.mark.integration
669
+ @patch("python.core.utils.helper_functions.run_cargo_command")
670
+ def test_generate_verify_expander_with_ecc_dev_mode_true(mock_run: MagicMock) -> None:
671
+ generate_verification(
672
+ "model",
673
+ "c",
674
+ "i",
675
+ "o",
676
+ "w",
677
+ "p",
678
+ "metadata.json",
679
+ ZKProofSystems.Expander,
680
+ ecc=True,
681
+ dev_mode=True,
682
+ )
683
+ _, kwargs = mock_run.call_args
684
+ assert kwargs["binary_name"] == "model"
685
+ assert kwargs["command_type"] == "run_gen_verify"
686
+ assert kwargs["dev_mode"]
687
+ assert kwargs["args"]["n"] == "model"
688
+ assert kwargs["args"]["c"] == "c"
689
+ assert kwargs["args"]["w"] == "w"
690
+ assert kwargs["args"]["p"] == "p"
691
+ assert kwargs["args"]["i"] == "i"
692
+ assert kwargs["args"]["o"] == "o"
693
+ assert kwargs["args"]["m"] == "metadata.json"
694
+ mock_run.assert_called_once()
695
+
696
+
697
+ @pytest.mark.unit
698
+ def test_generate_verify_unknown_raises() -> None:
699
+ with pytest.raises(ProofSystemNotImplementedError):
700
+ generate_verification(
701
+ "model",
702
+ "c",
703
+ "i",
704
+ "o",
705
+ "w",
706
+ "p",
707
+ "metadata.json",
708
+ "unsupported",
709
+ )
710
+
711
+
712
+ @pytest.mark.unit
713
+ def test_proof_system_not_implemented_full_process() -> None:
714
+ with pytest.raises(
715
+ ProofSystemNotImplementedError,
716
+ match="Proof system UnknownProofSystem not implemented",
717
+ ):
718
+ generate_verification(
719
+ "model",
720
+ "c",
721
+ "i",
722
+ "o",
723
+ "w",
724
+ "p",
725
+ "metadata.json",
726
+ "UnknownProofSystem",
727
+ )
728
+ with pytest.raises(
729
+ ProofSystemNotImplementedError,
730
+ match="Proof system UnknownProofSystem not implemented",
731
+ ):
732
+ generate_proof("m", "p", "w", "proof", "metadata.json", "UnknownProofSystem")
733
+ with pytest.raises(
734
+ ProofSystemNotImplementedError,
735
+ match="Proof system UnknownProofSystem not implemented",
736
+ ):
737
+ generate_witness(
738
+ "m",
739
+ "p",
740
+ "witness",
741
+ "input",
742
+ "output",
743
+ "metadata.json",
744
+ "UnknownProofSystem",
745
+ )
746
+ with pytest.raises(
747
+ ProofSystemNotImplementedError,
748
+ match="Proof system UnknownProofSystem not implemented",
749
+ ):
750
+ compile_circuit(
751
+ "model",
752
+ "path/to/circuit",
753
+ "metadata.json",
754
+ "architecture.json",
755
+ "w_and_b.json",
756
+ "UnknownProofSystem",
757
+ )
758
+
759
+
760
+ @pytest.mark.unit
761
+ @patch(
762
+ "python.core.utils.helper_functions.run_cargo_command",
763
+ side_effect=ProofBackendError("TEST"),
764
+ )
765
+ def test_generate_verify_expander_rust_error(
766
+ mock_run: MagicMock,
767
+ caplog: pytest.LogCaptureFixture,
768
+ ) -> None:
769
+ with pytest.raises(Exception, match="TEST"):
770
+ generate_verification(
771
+ "model",
772
+ "c",
773
+ "i",
774
+ "o",
775
+ "w",
776
+ "p",
777
+ "metadata.json",
778
+ ZKProofSystems.Expander,
779
+ dev_mode=True,
780
+ )
781
+ assert "Warning: Verification generation failed: TEST" in caplog.text
782
+
783
+
784
+ # # ---------- run_end_to_end ----------
785
+ @pytest.mark.unit
786
+ @patch("python.core.utils.helper_functions.generate_verification")
787
+ @patch("python.core.utils.helper_functions.generate_proof")
788
+ @patch("python.core.utils.helper_functions.generate_witness")
789
+ @patch("python.core.utils.helper_functions.compile_circuit")
790
+ def test_run_end_to_end_calls_all(
791
+ mock_compile: MagicMock,
792
+ mock_witness: MagicMock,
793
+ mock_proof: MagicMock,
794
+ mock_verify: MagicMock,
795
+ ) -> None:
796
+ run_end_to_end("m", "m_circuit.txt", "i.json", "o.json")
797
+ mock_compile.assert_called_once_with(
798
+ "m",
799
+ "m_circuit.txt",
800
+ "m_circuit_metadata.json",
801
+ "m_circuit_architecture.json",
802
+ "m_circuit_wandb.json",
803
+ ZKProofSystems.Expander,
804
+ False, # noqa: FBT003
805
+ )
806
+ mock_witness.assert_called_once_with(
807
+ "m",
808
+ "m_circuit.txt",
809
+ "m_circuit_witness.txt",
810
+ "i.json",
811
+ "o.json",
812
+ "m_circuit_metadata.json",
813
+ ZKProofSystems.Expander,
814
+ False, # noqa: FBT003
815
+ )
816
+ mock_proof.assert_called_once_with(
817
+ "m",
818
+ "m_circuit.txt",
819
+ "m_circuit_witness.txt",
820
+ "m_circuit_proof.bin",
821
+ "m_circuit_metadata.json",
822
+ ZKProofSystems.Expander,
823
+ False, # noqa: FBT003
824
+ True, # noqa: FBT003
825
+ )
826
+ mock_verify.assert_called_once_with(
827
+ "m",
828
+ "m_circuit.txt",
829
+ "i.json",
830
+ "o.json",
831
+ "m_circuit_witness.txt",
832
+ "m_circuit_proof.bin",
833
+ "m_circuit_metadata.json",
834
+ ZKProofSystems.Expander,
835
+ False, # noqa: FBT003
836
+ True, # noqa: FBT003
837
+ )
838
+
839
+
840
+ @pytest.mark.unit
841
+ @patch("python.core.utils.helper_functions.generate_verification")
842
+ @patch("python.core.utils.helper_functions.generate_proof")
843
+ @patch("python.core.utils.helper_functions.generate_witness")
844
+ @patch("python.core.utils.helper_functions.compile_circuit")
845
+ def test_unknown_proof_system_errors_end_to_end(
846
+ mock_compile: MagicMock,
847
+ mock_witness: MagicMock,
848
+ mock_proof: MagicMock,
849
+ mock_verify: MagicMock,
850
+ ) -> None:
851
+ with pytest.raises(
852
+ ProofSystemNotImplementedError,
853
+ match="Proof system UnknownProofSystem not implemented",
854
+ ):
855
+ run_end_to_end("m", "m_circuit.txt", "i.json", "o.json", "UnknownProofSystem")
856
+
857
+
858
+ # # ---------- get_files ----------
859
+ @pytest.mark.unit
860
+ def test_get_files() -> None:
861
+ folders = {
862
+ "input": "inputs",
863
+ "proof": "proofs",
864
+ "temp": "tmp",
865
+ "circuit": "circuits",
866
+ "weights": "weights",
867
+ "output": "out",
868
+ "quantized_model": "quantized_models",
869
+ }
870
+ paths = get_files("model", ZKProofSystems.Expander, folders)
871
+ assert paths["input_file"].endswith("model_input.json")
872
+
873
+
874
+ @pytest.mark.unit
875
+ def test_get_files_non_proof_system() -> None:
876
+
877
+ folders = {
878
+ "input": "inputs",
879
+ "proof": "proofs",
880
+ "temp": "tmp",
881
+ "circuit": "circuits",
882
+ "weights": "weights",
883
+ "output": "out",
884
+ "quantized_model": "quantized_models",
885
+ }
886
+ fake_proof_system = "unknown"
887
+ with pytest.raises(
888
+ ProofSystemNotImplementedError,
889
+ match=f"Proof system {fake_proof_system} not implemented",
890
+ ):
891
+ get_files("model", fake_proof_system, folders)