secure-fl 26.8.85__tar.gz

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 (64) hide show
  1. secure_fl-26.8.85/.gitignore +216 -0
  2. secure_fl-26.8.85/LICENSE +21 -0
  3. secure_fl-26.8.85/PKG-INFO +344 -0
  4. secure_fl-26.8.85/README.md +291 -0
  5. secure_fl-26.8.85/docs/paper/README.md +13 -0
  6. secure_fl-26.8.85/experiments/README.md +64 -0
  7. secure_fl-26.8.85/pyproject.toml +252 -0
  8. secure_fl-26.8.85/src/secure_fl/__init__.py +452 -0
  9. secure_fl-26.8.85/src/secure_fl/__main__.py +11 -0
  10. secure_fl-26.8.85/src/secure_fl/benchmarks/__init__.py +42 -0
  11. secure_fl-26.8.85/src/secure_fl/benchmarks/attacks.py +444 -0
  12. secure_fl-26.8.85/src/secure_fl/benchmarks/defaults.py +15 -0
  13. secure_fl-26.8.85/src/secure_fl/benchmarks/execution.py +573 -0
  14. secure_fl-26.8.85/src/secure_fl/benchmarks/pipeline.py +104 -0
  15. secure_fl-26.8.85/src/secure_fl/benchmarks/reporting.py +185 -0
  16. secure_fl-26.8.85/src/secure_fl/cli/__init__.py +70 -0
  17. secure_fl-26.8.85/src/secure_fl/cli/__main__.py +6 -0
  18. secure_fl-26.8.85/src/secure_fl/cli/handlers.py +240 -0
  19. secure_fl-26.8.85/src/secure_fl/cli/main.py +253 -0
  20. secure_fl-26.8.85/src/secure_fl/cli/setup.py +143 -0
  21. secure_fl-26.8.85/src/secure_fl/cli/zkp_check.py +93 -0
  22. secure_fl-26.8.85/src/secure_fl/core/__init__.py +137 -0
  23. secure_fl-26.8.85/src/secure_fl/core/_version.py +44 -0
  24. secure_fl-26.8.85/src/secure_fl/core/config.py +366 -0
  25. secure_fl-26.8.85/src/secure_fl/core/exceptions.py +509 -0
  26. secure_fl-26.8.85/src/secure_fl/core/types.py +634 -0
  27. secure_fl-26.8.85/src/secure_fl/data/__init__.py +23 -0
  28. secure_fl-26.8.85/src/secure_fl/data/benchmark_data.py +300 -0
  29. secure_fl-26.8.85/src/secure_fl/federation/__init__.py +33 -0
  30. secure_fl-26.8.85/src/secure_fl/federation/aggregation.py +466 -0
  31. secure_fl-26.8.85/src/secure_fl/federation/client.py +638 -0
  32. secure_fl-26.8.85/src/secure_fl/federation/client_runtime.py +113 -0
  33. secure_fl-26.8.85/src/secure_fl/federation/client_utils.py +138 -0
  34. secure_fl-26.8.85/src/secure_fl/federation/server.py +470 -0
  35. secure_fl-26.8.85/src/secure_fl/federation/server_runtime.py +103 -0
  36. secure_fl-26.8.85/src/secure_fl/federation/stability_monitor.py +484 -0
  37. secure_fl-26.8.85/src/secure_fl/models/__init__.py +85 -0
  38. secure_fl-26.8.85/src/secure_fl/models/factory.py +55 -0
  39. secure_fl-26.8.85/src/secure_fl/models/mnist_cnn.py +30 -0
  40. secure_fl-26.8.85/src/secure_fl/models/simple.py +94 -0
  41. secure_fl-26.8.85/src/secure_fl/proofs/__init__.py +0 -0
  42. secure_fl-26.8.85/src/secure_fl/proofs/client/__init__.py +0 -0
  43. secure_fl-26.8.85/src/secure_fl/proofs/client/delta_bound.circom +56 -0
  44. secure_fl-26.8.85/src/secure_fl/proofs/client_circuits/__init__.py +0 -0
  45. secure_fl-26.8.85/src/secure_fl/proofs/client_circuits/delta_bound.py +36 -0
  46. secure_fl-26.8.85/src/secure_fl/proofs/server/aggregation.circom +55 -0
  47. secure_fl-26.8.85/src/secure_fl/proofs/server/pot12_final.ptau +0 -0
  48. secure_fl-26.8.85/src/secure_fl/proofs/server/pot13_final.ptau +0 -0
  49. secure_fl-26.8.85/src/secure_fl/proofs/server/test.txt +3 -0
  50. secure_fl-26.8.85/src/secure_fl/py.typed +0 -0
  51. secure_fl-26.8.85/src/secure_fl/utils/__init__.py +59 -0
  52. secure_fl-26.8.85/src/secure_fl/utils/helpers.py +286 -0
  53. secure_fl-26.8.85/src/secure_fl/utils/logging_config.py +433 -0
  54. secure_fl-26.8.85/src/secure_fl/zkp/__init__.py +39 -0
  55. secure_fl-26.8.85/src/secure_fl/zkp/base.py +33 -0
  56. secure_fl-26.8.85/src/secure_fl/zkp/client_proof_manager.py +504 -0
  57. secure_fl-26.8.85/src/secure_fl/zkp/client_setup.py +205 -0
  58. secure_fl-26.8.85/src/secure_fl/zkp/client_snark_io.py +164 -0
  59. secure_fl-26.8.85/src/secure_fl/zkp/proof_manager.py +11 -0
  60. secure_fl-26.8.85/src/secure_fl/zkp/proof_schema.py +69 -0
  61. secure_fl-26.8.85/src/secure_fl/zkp/quantization.py +703 -0
  62. secure_fl-26.8.85/src/secure_fl/zkp/server_proof_manager.py +627 -0
  63. secure_fl-26.8.85/src/secure_fl/zkp/server_setup.py +127 -0
  64. secure_fl-26.8.85/src/secure_fl/zkp/server_snark_io.py +142 -0
@@ -0,0 +1,216 @@
1
+ # ===========================
2
+ # Python
3
+ # ===========================
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+ *.so
8
+ .Python
9
+ build/
10
+ develop-eggs/
11
+ dist/
12
+ downloads/
13
+ eggs/
14
+ .eggs/
15
+ lib/
16
+ lib64/
17
+ parts/
18
+ sdist/
19
+ var/
20
+ wheels/
21
+ pip-wheel-metadata/
22
+ share/python-wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+ MANIFEST
27
+
28
+ # PyInstaller
29
+ *.manifest
30
+ *.spec
31
+
32
+ # Unit test / coverage reports
33
+ htmlcov/
34
+ .tox/
35
+ .nox/
36
+ .coverage
37
+ .coverage.*
38
+ .cache
39
+ nosetests.xml
40
+ coverage.xml
41
+ *.cover
42
+ *.py,cover
43
+ .hypothesis/
44
+ .pytest_cache/
45
+
46
+ # Virtual environments
47
+ .env
48
+ .venv
49
+ env/
50
+ venv/
51
+ ENV/
52
+ env.bak/
53
+ venv.bak/
54
+
55
+ # IDEs and editors
56
+ .vscode/
57
+ .idea/
58
+ *.swp
59
+ *.swo
60
+ *.swn
61
+ *.vim
62
+ .spyderproject
63
+ .spyproject
64
+ .ropeproject
65
+
66
+ # Jupyter Notebook
67
+ .ipynb_checkpoints
68
+
69
+ # pyenv
70
+ .python-version
71
+
72
+ # pipenv
73
+ Pipfile.lock
74
+
75
+ # PEP 582
76
+ __pypackages__/
77
+
78
+ # Celery stuff
79
+ celerybeat-schedule
80
+ celerybeat.pid
81
+
82
+ # SageMath parsed files
83
+ *.sage.py
84
+
85
+ # Spyder project settings
86
+ .spyderproject
87
+ .spyproject
88
+
89
+ # Rope project settings
90
+ .ropeproject
91
+
92
+ # mypy
93
+ .mypy_cache/
94
+ .dmypy.json
95
+ dmypy.json
96
+
97
+ # Pyre type checker
98
+ .pyre/
99
+
100
+ # ===========================
101
+ # Secure FL Specific
102
+ # ===========================
103
+ # Runtime logs and results
104
+ logs/
105
+ results/*
106
+ !results/full_benchmark/
107
+ !results/full_benchmark_50/
108
+ !results/full_benchmark_v2/
109
+ results_bak/
110
+ *.log
111
+
112
+ # Stray local artifacts (empty dirs from mock leaks / tool runs, not code-created)
113
+ /MagicMock/
114
+ /.benchmarks/
115
+
116
+ # Data directories
117
+ datasets/
118
+
119
+ # ZKP artifacts
120
+ *.r1cs
121
+ *.wtns
122
+ # *.circom
123
+ output/
124
+ graphify-out/
125
+ witness/
126
+ circuit.wasm
127
+ circuit_js/
128
+ proofs/compiled/
129
+ *.proof
130
+ *.vkey
131
+ *.zkey
132
+
133
+ # pysnark debug/trace output files
134
+ pysnark_eqs
135
+ pysnark_eqs_main
136
+ pysnark_schedule
137
+ pysnark_values
138
+ pysnark_wires
139
+
140
+ # Temporary files
141
+ temp/
142
+ tmp/
143
+ .temp/
144
+ .tmp/
145
+
146
+ # Build artifacts
147
+ *.compiled.json
148
+
149
+ # Package management
150
+ .pdm-python
151
+ .pdm-build/
152
+ #uv.lock
153
+
154
+ # ===========================
155
+ # System Files
156
+ # ===========================
157
+ .DS_Store
158
+ .DS_Store?
159
+ ._*
160
+ .Spotlight-V100
161
+ .Trashes
162
+ ehthumbs.db
163
+ Thumbs.db
164
+
165
+ # ===========================
166
+ # LaTeX (for documentation)
167
+ # ===========================
168
+ *.aux
169
+ *.bbl
170
+ *.blg
171
+ *.fls
172
+ *.fdb_latexmk
173
+ *.synctex.gz
174
+ *.out
175
+ *.toc
176
+ *.lof
177
+ *.lot
178
+ *.log
179
+ *.pdf
180
+ *.dvi
181
+ *.ps
182
+ # Allow committed figure PDFs in the report
183
+ !docs/final-defense/report/images/figures/*.pdf
184
+ !docs/final-defense/report/diagrams/*.pdf
185
+ !docs/research\ paper/figures/*.pdf
186
+ !docs/paper/figures/*.pdf
187
+
188
+ # ===========================
189
+ # Development Tools
190
+ # ===========================
191
+ # Ruff cache
192
+ .ruff_cache/
193
+
194
+
195
+ # Docker
196
+ .dockerignore
197
+
198
+ # GitHub Actions local testing
199
+ .secrets
200
+ /tmp/artifacts/
201
+ *-event.json
202
+ .act/
203
+
204
+ # Node.js (for ZKP tools)
205
+ node_modules/
206
+ npm-debug.log*
207
+ yarn-debug.log*
208
+ yarn-error.log*
209
+
210
+ # Environment variables
211
+ .env*
212
+ !.env.example
213
+
214
+
215
+ experiments/data/
216
+ *.pptx
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Krishant Timilsina, Bindu Paudel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,344 @@
1
+ Metadata-Version: 2.5
2
+ Name: secure-fl
3
+ Version: 26.8.85
4
+ Summary: Dual-Verifiable Framework for Federated Learning using Zero-Knowledge Proofs
5
+ Project-URL: Homepage, https://github.com/krishantt/secure-fl
6
+ Project-URL: Bug Reports, https://github.com/krishantt/secure-fl/issues
7
+ Project-URL: Source, https://github.com/krishantt/secure-fl
8
+ Project-URL: Documentation, https://github.com/krishantt/secure-fl/blob/main/README.md
9
+ Author-email: Krishant Timilsina <krishtimil@gmail.com>, Bindu Paudel <binduupaudel565@gmail.com>
10
+ Maintainer-email: Krishant Timilsina <krishtimil@gmail.com>, Bindu Paudel <binduupaudel565@gmail.com>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: cryptography,federated-learning,machine-learning,privacy,zero-knowledge-proofs,zk-snarks,zk-starks
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: Security :: Cryptography
23
+ Requires-Python: >=3.12
24
+ Requires-Dist: click>=8.0.0
25
+ Requires-Dist: flwr>=1.5.0
26
+ Requires-Dist: numpy>=1.24.0
27
+ Requires-Dist: pysnark
28
+ Requires-Dist: pyyaml>=6.0
29
+ Requires-Dist: rich>=13.0.0
30
+ Requires-Dist: textual>=6.2.1
31
+ Requires-Dist: torch>=2.8.0
32
+ Requires-Dist: torchvision>=0.23.0
33
+ Provides-Extra: benchmark
34
+ Requires-Dist: memory-profiler>=0.61.0; extra == 'benchmark'
35
+ Requires-Dist: pytest-benchmark>=5.2.3; extra == 'benchmark'
36
+ Provides-Extra: dev
37
+ Requires-Dist: mypy>=1.19.0; extra == 'dev'
38
+ Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
39
+ Requires-Dist: psutil>=5.9.0; extra == 'dev'
40
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
41
+ Requires-Dist: pytest-xdist>=3.3.0; extra == 'dev'
42
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
43
+ Requires-Dist: ruff>=0.14.8; extra == 'dev'
44
+ Requires-Dist: types-psutil; extra == 'dev'
45
+ Requires-Dist: types-pyyaml; extra == 'dev'
46
+ Provides-Extra: medical
47
+ Requires-Dist: medmnist>=2.2.0; extra == 'medical'
48
+ Provides-Extra: viz
49
+ Requires-Dist: matplotlib>=3.7.0; extra == 'viz'
50
+ Requires-Dist: plotly>=5.0.0; extra == 'viz'
51
+ Requires-Dist: seaborn>=0.12.0; extra == 'viz'
52
+ Description-Content-Type: text/markdown
53
+
54
+ # Secure FL: Zero-Knowledge Federated Learning
55
+
56
+ A dual-verifiable framework for federated learning using zero-knowledge proofs to ensure training integrity and aggregation correctness.
57
+
58
+ ## Core Features
59
+
60
+ - **Dual ZKP Verification**: Client-side Groth16 zk-SNARK (Circom, transferable) + PySNARK local audit + Server-side Groth16 zk-SNARK (Circom/SnarkJS/rapidsnark) for aggregation
61
+ - **FedJSCM Aggregation**: Momentum-based federated optimization with auto-reinit on architecture changes
62
+ - **Dynamic Proof Rigor**: `StabilityMonitor` adapts proof complexity (`low`/`medium`/`high`/`adaptive`) based on training stability
63
+ - **Deterministic Hashing**: SHA-256 over the full parameter state dict — safe across Python/numpy versions
64
+ - **Attack Detection**: Norm-bound enforcement, hash verification, and ZKP circuit checks block poisoned updates
65
+
66
+ ## Architecture
67
+
68
+ ```
69
+ Client: Local Training → Circom Groth16 delta-bound proof (transferable) + PySNARK (local audit)
70
+ → Send full state_dict + proof
71
+ Server: Receive updates → verify (hash + norm + Groth16) → FedJSCM aggregation
72
+ → Groth16 SNARK proof → Distribute model
73
+ ```
74
+
75
+ **Dual verification:**
76
+ 1. **Clients** generate a transferable Groth16 proof of correct local training (delta norm bound), independently server-verifiable, plus a SHA-256 hash commitment over the full parameter state
77
+ 2. **Server** generates a Groth16 proof of correct FedJSCM aggregation
78
+
79
+ ## Quick Start
80
+
81
+ ### Installation
82
+
83
+ ```bash
84
+ # From source with uv (recommended)
85
+ git clone https://github.com/krishantt/secure-fl
86
+ cd secure-fl
87
+ uv sync --all-extras
88
+ ```
89
+
90
+ ### ZKP Prerequisites
91
+
92
+ ```bash
93
+ # Automated setup (installs Rust, Circom, SnarkJS)
94
+ make setup-zkp
95
+
96
+ # Verify
97
+ uv run secure-fl setup --action check
98
+ ```
99
+
100
+ ### Run the attack defense demo
101
+
102
+ ```bash
103
+ # Synthetic data — fastest, no download needed
104
+ uv run python demo/simulate_attack.py --dataset synthetic_small --num-rounds 6
105
+
106
+ # MNIST
107
+ uv run python demo/simulate_attack.py --dataset mnist
108
+
109
+ # CIFAR-10 with more malicious clients
110
+ uv run python demo/simulate_attack.py --dataset cifar10 --malicious 2 --proof-rigor low
111
+
112
+ # MedMNIST
113
+ uv run python demo/simulate_attack.py --dataset medmnist --malicious 2 --proof-rigor low
114
+ ```
115
+
116
+ The TUI shows real-time ZKP verification, attack detection, and accuracy comparison (with ZKP vs projected without). Attackers appear in ~65% of rounds; the rest are all-honest.
117
+
118
+ ### Start a federated learning session
119
+
120
+ ```bash
121
+ # Server
122
+ uv run secure-fl-server --config experiments/config.yaml
123
+
124
+ # Client (in separate terminals)
125
+ uv run secure-fl-client --server localhost:8080 --dataset mnist --client-id client_1
126
+ uv run secure-fl-client --server localhost:8080 --dataset mnist --client-id client_2
127
+
128
+ # Or use Docker
129
+ docker compose up -d
130
+ ```
131
+
132
+ ## Python API
133
+
134
+ ```python
135
+ from secure_fl.federation.client_runtime import create_client
136
+ from secure_fl.models.factory import model_fn
137
+
138
+ # Create a secure FL client
139
+ factory = model_fn("mnist") # returns MNISTCNN factory
140
+ client = create_client(
141
+ client_id="client_1",
142
+ model_fn=factory,
143
+ train_data=train_dataset,
144
+ enable_zkp=True,
145
+ proof_rigor="low", # "low" | "medium" | "high"
146
+ local_epochs=1,
147
+ learning_rate=0.01,
148
+ )
149
+
150
+ # client.fit(parameters, config) → (updated_params, num_examples, metrics)
151
+ # metrics["zkp_proof"] contains the JSON proof string when enable_zkp=True
152
+ ```
153
+
154
+ ```python
155
+ from secure_fl.federation.aggregation import FedJSCMAggregator
156
+ from secure_fl.zkp.proof_manager import ServerProofManager
157
+
158
+ aggregator = FedJSCMAggregator(momentum=0.9, learning_rate=1.0)
159
+ server_pm = ServerProofManager()
160
+
161
+ # Verify a client proof
162
+ ok = server_pm.verify_client_proof(proof_json, updated_params, global_params)
163
+
164
+ # Aggregate verified updates
165
+ global_params = aggregator.aggregate(
166
+ client_updates=verified_updates,
167
+ client_weights=normalized_weights,
168
+ server_round=rnd,
169
+ global_params=global_params,
170
+ )
171
+ ```
172
+
173
+ ## Configuration
174
+
175
+ ```yaml
176
+ # experiments/config.yaml
177
+ server:
178
+ host: "localhost"
179
+ port: 8080
180
+ num_rounds: 10
181
+
182
+ strategy:
183
+ min_fit_clients: 2
184
+ fraction_fit: 1.0
185
+ momentum: 0.9
186
+
187
+ zkp:
188
+ enable_zkp: true
189
+ proof_rigor: "high" # "low" | "medium" | "high" | "adaptive"
190
+ quantize_weights: true
191
+ quantization_bits: 8
192
+ ```
193
+
194
+ ## Models and Datasets
195
+
196
+ | Dataset | Model | Notes |
197
+ |---|---|---|
198
+ | `mnist` | `MNISTCNN` | 2-layer CNN, fast |
199
+ | `cifar10` | `ResNet18Model` | Full ResNet18 |
200
+ | `medmnist` | `ResNet18Model` | Medical imaging |
201
+ | `synthetic_small` | `SimpleModel` | MLP, no download needed |
202
+
203
+ ```python
204
+ from secure_fl.models.factory import model_fn
205
+ factory = model_fn("cifar10") # returns callable → ResNet18Model instance
206
+ model = factory()
207
+ ```
208
+
209
+ ```python
210
+ from secure_fl.data.benchmark_data import load_dataset, partition_dataset, subsample_dataset
211
+
212
+ train, test = load_dataset("mnist", seed=42)
213
+ train = subsample_dataset(train, max_samples=2000, seed=42)
214
+ client_subsets = partition_dataset(train, n=6, iid=False, seed=42) # Dirichlet non-IID
215
+ ```
216
+
217
+ ## Technical Details
218
+
219
+ ### FedJSCM Aggregation
220
+
221
+ ```
222
+ m^{t+1} = γ·m^t + (1−γ)·Δ
223
+ w^{t+1} = w^t + η·m^{t+1}
224
+ ```
225
+ where `Δ = weighted_avg(client_updates) − global_params`. Momentum is reinitialized automatically if the model architecture changes between rounds. Integer-dtype buffers (e.g. BatchNorm `num_batches_tracked`) are preserved through aggregation.
226
+
227
+ ### ZKP Proof Schema
228
+
229
+ Each client proof is a JSON object containing:
230
+ - `initial_hash`, `updated_hash`, `delta_hash` — SHA-256 commitments over the **full parameter state dict** (weights, biases, and BatchNorm running stats) — catches any silent parameter substitution, including in buffers not covered by the norm check
231
+ - `delta_norm_l2`, `max_delta_norm_l2` — L2 norm bound enforcement over **learnable parameters only** (BN buffers excluded — their early-round deltas are large and not adversarially controlled)
232
+ - `pysnark` — PySNARK circuit result (`delta_bound_proof`), a secondary in-process local audit
233
+ - `circom_proof` — transferable Groth16 proof (`{"proof": ..., "public": ...}`) when Circom/SnarkJS tools are available client-side; independently verifiable by the server via the same mechanism, without re-running any client code
234
+
235
+ Server verification order: proof present → valid JSON → hash matches (full state dict) → delta hash matches (full state dict) → norm within bound (learnable-only) → PySNARK circuit ok → `circom_proof` verified (if present).
236
+
237
+ ### Parameter Representations
238
+
239
+ Two lists exist and must not be mixed:
240
+
241
+ | | Source | Contents | Used for |
242
+ |---|---|---|---|
243
+ | Full state dict | `model.state_dict().values()` | weights + biases + BN buffers | hash commitment, aggregation, eval |
244
+ | Learnable only | `model.parameters()` | weights + biases only | norm-bound enforcement, ZKP circuits |
245
+
246
+ `fit()` returns full state dict. Mixing the two representations with `zip` causes shape-mismatch errors on BN models (ResNet18, MNISTCNN) — see `CLAUDE.md`'s "Parameter list conventions" section for the full detail.
247
+
248
+ ### Dynamic Proof Rigor
249
+
250
+ `StabilityMonitor` tracks gradient variance and convergence across rounds and adapts `ProofRigor`:
251
+ - High variance / early rounds → `high` (more proof coverage)
252
+ - Stable training → `low` (faster proofs)
253
+ - `adaptive` mode enables automatic adjustment each round
254
+
255
+ ## Experiments & Benchmarks
256
+
257
+ `experiments/full_benchmark.py` is the single source of truth for paper-quality
258
+ results — k-fold accuracy, overhead, and attack resilience in one run.
259
+
260
+ ```bash
261
+ # Fast smoke run
262
+ uv run python experiments/full_benchmark.py --quick
263
+
264
+ # Paper-quality run (real ZKP proofs enforced, resumable — skips
265
+ # already-completed config JSONs unless --force)
266
+ uv run python experiments/full_benchmark.py \
267
+ --datasets mnist --k-folds 5 --num-clients 20 --clients-per-round 5 \
268
+ --num-rounds 100 --require-real-proofs --force \
269
+ --output-dir results/full_benchmark
270
+ ```
271
+
272
+ Outputs per-dataset, per-config k-fold JSONs under `--output-dir`
273
+ (`aggregate` summary stats + `folds[i].rounds[j]` per-round detail). See
274
+ `CLAUDE.md`'s "Benchmark results status" section for which result set is
275
+ currently canonical and why.
276
+
277
+ ## Repository Structure
278
+
279
+ ```
280
+ secure-fl/
281
+ ├── src/secure_fl/
282
+ │ ├── core/ # Types, config (thread-safe singleton), exceptions
283
+ │ ├── federation/ # SecureFlowerClient, SecureFlowerServer, FedJSCMAggregator,
284
+ │ │ # StabilityMonitor, client_utils, helpers
285
+ │ ├── zkp/ # ClientProofManager, ServerProofManager, quantization
286
+ │ ├── models/ # SimpleModel, MNISTCNN, ResNet18Model, factory
287
+ │ ├── data/ # benchmark_data: load/partition/subsample datasets
288
+ │ ├── cli/ # Click CLI: secure-fl, secure-fl-server, secure-fl-client, secure-fl-setup
289
+ │ ├── proofs/
290
+ │ │ ├── client_circuits/ # PySNARK delta_bound_proof circuit
291
+ │ │ └── server/ # Circom aggregation SNARK circuits
292
+ │ └── utils/ # helpers (compute_hash, compute_parameter_norm), logging
293
+ ├── demo/
294
+ │ └── simulate_attack.py # Textual TUI: real ZKP attack detection demo
295
+ ├── experiments/
296
+ │ ├── full_benchmark.py # Canonical paper benchmark (accuracy + overhead + attacks)
297
+ │ ├── train.py # Single training run
298
+ │ ├── demo.py # CLI demo entry point
299
+ │ └── config.yaml
300
+ └── tests/
301
+ ├── unit/ # No external dependencies (fast)
302
+ └── integration/ # Requires Circom + SnarkJS
303
+ ```
304
+
305
+ ## Development
306
+
307
+ ```bash
308
+ make dev # Full setup (uv sync + ZKP tools)
309
+ make lint # Ruff lint
310
+ make format # Ruff format ← run before every commit
311
+ make type-check # mypy (strict)
312
+ make test # Full suite
313
+ make test-quick # Fast, --exitfirst
314
+ make test-cov # With coverage (threshold: 30%)
315
+ ```
316
+
317
+ ## Known Limitations
318
+
319
+ - Server ZKP (Circom/SnarkJS) requires `make setup-zkp`; subprocess calls time out after 300 s
320
+ - Client-side transferable Groth16 proof falls back gracefully to PySNARK-only when Circom/SnarkJS tools are unavailable client-side
321
+ - Coverage threshold is 30% — being raised incrementally
322
+ - Norm-bound enforcement does not defend against within-bound poisoning (adversarial updates crafted to satisfy the norm constraint) — see `docs/paper/main.tex` §Limitations
323
+
324
+ ## License
325
+
326
+ MIT License — see [LICENSE](LICENSE) for details.
327
+
328
+ ## Citation
329
+
330
+ ```bibtex
331
+ @misc{timilsina2026securefl,
332
+ title={Secure-FL: Zero-Knowledge Proofs for Dual-Verifiable Federated Learning},
333
+ author={Timilsina, Krishant and Paudel, Bindu and Timilsina, Arun Kumar},
334
+ year={2026},
335
+ url={https://github.com/krishantt/secure-fl},
336
+ note={Preprint}
337
+ }
338
+ ```
339
+
340
+ ## Acknowledgments
341
+
342
+ - [Flower](https://flower.dev) — federated learning infrastructure
343
+ - [Circom](https://github.com/iden3/circom) / [SnarkJS](https://github.com/iden3/snarkjs) — zk-SNARK toolchain
344
+ - [PySNARK](https://github.com/meilof/pysnark) — Python zk-SNARK (Groth16) library