secure-fl 2026.2.20.dev4__tar.gz → 2026.2.22.dev1__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.
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/PKG-INFO +17 -4
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/README.md +16 -3
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/pyproject.toml +0 -1
- secure_fl-2026.2.22.dev1/src/secure_fl/.build_number +1 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/cli/__init__.py +2 -2
- secure_fl-2026.2.22.dev1/src/secure_fl/cli/handlers.py +270 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/cli/main.py +258 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/cli/zkp_check.py +78 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/federation/__init__.py +2 -2
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/federation/client.py +36 -172
- secure_fl-2026.2.22.dev1/src/secure_fl/federation/client_runtime.py +91 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/federation/client_utils.py +117 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/federation/server.py +9 -102
- secure_fl-2026.2.22.dev1/src/secure_fl/federation/server_runtime.py +104 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/proofs/server/pot12_final.ptau +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/zkp/__init__.py +2 -2
- secure_fl-2026.2.22.dev1/src/secure_fl/zkp/base.py +33 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/zkp/client_proof_manager.py +242 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/zkp/proof_manager.py +11 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/zkp/proof_schema.py +69 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/zkp/server_proof_manager.py +459 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/zkp/server_setup.py +127 -0
- secure_fl-2026.2.22.dev1/src/secure_fl/zkp/server_snark_io.py +115 -0
- secure_fl-2026.2.20.dev4/examples/README.md +0 -275
- secure_fl-2026.2.20.dev4/proofs/.gitkeep +0 -0
- secure_fl-2026.2.20.dev4/proofs/server/pot12_final.ptau +0 -0
- secure_fl-2026.2.20.dev4/scripts/README.md +0 -322
- secure_fl-2026.2.20.dev4/src/secure_fl/.build_number +0 -1
- secure_fl-2026.2.20.dev4/src/secure_fl/cli/main.py +0 -611
- secure_fl-2026.2.20.dev4/src/secure_fl/zkp/proof_manager.py +0 -951
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/.gitignore +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/LICENSE +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/experiments/README.md +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/__init__.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/__main__.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/cli/setup.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/core/__init__.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/core/_version.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/core/config.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/core/exceptions.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/core/types.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/federation/aggregation.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/federation/stability_monitor.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/models/__init__.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/models/cifar10.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/models/flexible_mlp.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/models/mnist.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/models/resnet.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/models/simple.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1/src/secure_fl}/proofs/__init__.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1/src/secure_fl}/proofs/client_circuits/__init__.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1/src/secure_fl}/proofs/client_circuits/delta_bound.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1/src/secure_fl}/proofs/client_circuits/sgd_full_trace.cairo +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1/src/secure_fl}/proofs/server/test.txt +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/py.typed +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/utils/__init__.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/utils/helpers.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/utils/logging_config.py +0 -0
- {secure_fl-2026.2.20.dev4 → secure_fl-2026.2.22.dev1}/src/secure_fl/zkp/quantization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: secure-fl
|
|
3
|
-
Version: 2026.2.
|
|
3
|
+
Version: 2026.2.22.dev1
|
|
4
4
|
Summary: Dual-Verifiable Framework for Federated Learning using Zero-Knowledge Proofs
|
|
5
5
|
Project-URL: Homepage, https://github.com/krishantt/secure-fl
|
|
6
6
|
Project-URL: Bug Reports, https://github.com/krishantt/secure-fl/issues
|
|
@@ -268,6 +268,19 @@ make demo # Run demonstration
|
|
|
268
268
|
make clean # Clean artifacts
|
|
269
269
|
```
|
|
270
270
|
|
|
271
|
+
### Docker (Minimal)
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# Build image
|
|
275
|
+
docker build -t secure-fl:local .
|
|
276
|
+
|
|
277
|
+
# Start local FL stack
|
|
278
|
+
docker compose up -d
|
|
279
|
+
|
|
280
|
+
# Stop services
|
|
281
|
+
docker compose down
|
|
282
|
+
```
|
|
283
|
+
|
|
271
284
|
## 📈 Experiments
|
|
272
285
|
|
|
273
286
|
Run benchmarks and experiments:
|
|
@@ -298,9 +311,9 @@ secure-fl/
|
|
|
298
311
|
│ ├── core/ # Types, config, exceptions, versioning
|
|
299
312
|
│ ├── cli/ # CLI commands and setup helpers
|
|
300
313
|
│ └── utils/ # Logging and helper utilities
|
|
301
|
-
├── proofs/
|
|
302
|
-
│ ├── client_circuits/ # zk-STARK
|
|
303
|
-
│ └── server/
|
|
314
|
+
│ ├── proofs/ # ZKP circuits and proving assets
|
|
315
|
+
│ │ ├── client_circuits/ # zk-STARK/PySNARK client circuits
|
|
316
|
+
│ │ └── server/ # zk-SNARK (Circom) server circuits
|
|
304
317
|
├── experiments/ # Research experiments
|
|
305
318
|
├── tests/ # Test suite
|
|
306
319
|
└── docs/ # Documentation
|
|
@@ -212,6 +212,19 @@ make demo # Run demonstration
|
|
|
212
212
|
make clean # Clean artifacts
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
+
### Docker (Minimal)
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# Build image
|
|
219
|
+
docker build -t secure-fl:local .
|
|
220
|
+
|
|
221
|
+
# Start local FL stack
|
|
222
|
+
docker compose up -d
|
|
223
|
+
|
|
224
|
+
# Stop services
|
|
225
|
+
docker compose down
|
|
226
|
+
```
|
|
227
|
+
|
|
215
228
|
## 📈 Experiments
|
|
216
229
|
|
|
217
230
|
Run benchmarks and experiments:
|
|
@@ -242,9 +255,9 @@ secure-fl/
|
|
|
242
255
|
│ ├── core/ # Types, config, exceptions, versioning
|
|
243
256
|
│ ├── cli/ # CLI commands and setup helpers
|
|
244
257
|
│ └── utils/ # Logging and helper utilities
|
|
245
|
-
├── proofs/
|
|
246
|
-
│ ├── client_circuits/ # zk-STARK
|
|
247
|
-
│ └── server/
|
|
258
|
+
│ ├── proofs/ # ZKP circuits and proving assets
|
|
259
|
+
│ │ ├── client_circuits/ # zk-STARK/PySNARK client circuits
|
|
260
|
+
│ │ └── server/ # zk-SNARK (Circom) server circuits
|
|
248
261
|
├── experiments/ # Research experiments
|
|
249
262
|
├── tests/ # Test suite
|
|
250
263
|
└── docs/ # Documentation
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2026.02.22:1
|
|
@@ -6,12 +6,12 @@ with the Secure FL framework, including commands for running servers,
|
|
|
6
6
|
clients, configuration management, and system validation.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from .main import check_zkp
|
|
10
|
-
from .main import check_zkp_tools
|
|
11
9
|
from .main import client
|
|
12
10
|
from .main import create_config
|
|
13
11
|
from .main import main
|
|
14
12
|
from .main import server
|
|
13
|
+
from .zkp_check import check_zkp
|
|
14
|
+
from .zkp_check import check_zkp_tools
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def print_system_info() -> None:
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""Command handlers for the Secure FL CLI."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
from typing import Union
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
from rich import print as rprint
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.progress import Progress
|
|
14
|
+
from rich.progress import SpinnerColumn
|
|
15
|
+
from rich.progress import TextColumn
|
|
16
|
+
from rich.table import Table
|
|
17
|
+
|
|
18
|
+
from secure_fl.core.config import create_example_config
|
|
19
|
+
from secure_fl.models import CIFAR10Model
|
|
20
|
+
from secure_fl.models import MNISTModel
|
|
21
|
+
from secure_fl.models import SimpleModel
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from collections.abc import Callable
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def run_server(
|
|
30
|
+
*,
|
|
31
|
+
config: str | None,
|
|
32
|
+
host: str,
|
|
33
|
+
port: int,
|
|
34
|
+
rounds: int,
|
|
35
|
+
min_clients: int,
|
|
36
|
+
enable_zkp: bool,
|
|
37
|
+
proof_rigor: str,
|
|
38
|
+
momentum: float,
|
|
39
|
+
blockchain: bool,
|
|
40
|
+
model: str,
|
|
41
|
+
console: Console,
|
|
42
|
+
) -> None:
|
|
43
|
+
from secure_fl.core import get_default_config
|
|
44
|
+
from secure_fl.federation.server import SecureFlowerServer
|
|
45
|
+
from secure_fl.federation.server import create_server_strategy
|
|
46
|
+
|
|
47
|
+
if config:
|
|
48
|
+
import yaml
|
|
49
|
+
|
|
50
|
+
with open(config) as f:
|
|
51
|
+
cfg = yaml.safe_load(f)
|
|
52
|
+
else:
|
|
53
|
+
cfg = get_default_config()
|
|
54
|
+
|
|
55
|
+
cfg["server"].update({"host": host, "port": port, "num_rounds": rounds})
|
|
56
|
+
|
|
57
|
+
strategy_config = {**cfg["strategy"], **cfg["aggregation"], **cfg["zkp"]}
|
|
58
|
+
strategy_config.update(
|
|
59
|
+
{
|
|
60
|
+
"min_available_clients": min_clients,
|
|
61
|
+
"min_fit_clients": min_clients,
|
|
62
|
+
"min_evaluate_clients": min_clients,
|
|
63
|
+
"momentum": momentum,
|
|
64
|
+
"enable_zkp": enable_zkp,
|
|
65
|
+
"proof_rigor": proof_rigor,
|
|
66
|
+
"blockchain_verification": blockchain,
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
model_fn: Callable[[], Union[MNISTModel, CIFAR10Model, SimpleModel]]
|
|
71
|
+
if model == "mnist":
|
|
72
|
+
|
|
73
|
+
def model_fn() -> MNISTModel:
|
|
74
|
+
return MNISTModel()
|
|
75
|
+
elif model == "cifar10":
|
|
76
|
+
|
|
77
|
+
def model_fn() -> CIFAR10Model:
|
|
78
|
+
return CIFAR10Model()
|
|
79
|
+
elif model == "simple":
|
|
80
|
+
|
|
81
|
+
def model_fn() -> SimpleModel:
|
|
82
|
+
return SimpleModel(input_dim=784, output_dim=10)
|
|
83
|
+
else:
|
|
84
|
+
raise click.ClickException(
|
|
85
|
+
f"Unknown model '{model}'. Available: mnist, cifar10, simple"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
strategy = create_server_strategy(model_fn=model_fn, **strategy_config)
|
|
89
|
+
|
|
90
|
+
with Progress(
|
|
91
|
+
SpinnerColumn(),
|
|
92
|
+
TextColumn("[progress.description]{task.description}"),
|
|
93
|
+
console=console,
|
|
94
|
+
) as progress:
|
|
95
|
+
task = progress.add_task("Starting Secure FL Server...", total=None)
|
|
96
|
+
server = SecureFlowerServer(strategy=strategy, **cfg["server"])
|
|
97
|
+
progress.update(task, description="Server ready! Waiting for clients...")
|
|
98
|
+
|
|
99
|
+
table = Table(title="Server Configuration")
|
|
100
|
+
table.add_column("Parameter", style="cyan")
|
|
101
|
+
table.add_column("Value", style="green")
|
|
102
|
+
table.add_row("Host", f"{host}:{port}")
|
|
103
|
+
table.add_row("Rounds", str(rounds))
|
|
104
|
+
table.add_row("Min Clients", str(min_clients))
|
|
105
|
+
table.add_row("ZKP Enabled", "✓" if enable_zkp else "✗")
|
|
106
|
+
table.add_row("Proof Rigor", proof_rigor)
|
|
107
|
+
table.add_row("Momentum", str(momentum))
|
|
108
|
+
table.add_row("Blockchain", "✓" if blockchain else "✗")
|
|
109
|
+
console.print(table)
|
|
110
|
+
server.start()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def run_client(
|
|
114
|
+
*,
|
|
115
|
+
server_address: str,
|
|
116
|
+
client_id: str,
|
|
117
|
+
dataset: str,
|
|
118
|
+
data_path: str | None,
|
|
119
|
+
partition: int | None,
|
|
120
|
+
enable_zkp: bool,
|
|
121
|
+
epochs: int,
|
|
122
|
+
batch_size: int,
|
|
123
|
+
learning_rate: float,
|
|
124
|
+
proof_rigor: str,
|
|
125
|
+
console: Console,
|
|
126
|
+
) -> None:
|
|
127
|
+
from secure_fl.data import FederatedDataLoader
|
|
128
|
+
from secure_fl.federation.client import create_client
|
|
129
|
+
from secure_fl.federation.client import start_client
|
|
130
|
+
|
|
131
|
+
if data_path:
|
|
132
|
+
data_loader = FederatedDataLoader(
|
|
133
|
+
dataset_name="synthetic",
|
|
134
|
+
num_clients=1,
|
|
135
|
+
batch_size=batch_size,
|
|
136
|
+
)
|
|
137
|
+
loaders = data_loader.create_client_dataloaders(client_id=0)
|
|
138
|
+
train_loader, val_loader = (
|
|
139
|
+
loaders if not isinstance(loaders, list) else loaders[0]
|
|
140
|
+
)
|
|
141
|
+
train_data = train_loader.dataset
|
|
142
|
+
val_data = val_loader.dataset if val_loader else None
|
|
143
|
+
else:
|
|
144
|
+
data_loader = FederatedDataLoader(
|
|
145
|
+
dataset_name=dataset,
|
|
146
|
+
num_clients=max(1, partition + 1) if partition is not None else 5,
|
|
147
|
+
batch_size=batch_size,
|
|
148
|
+
)
|
|
149
|
+
client_partition = partition or 0
|
|
150
|
+
loaders = data_loader.create_client_dataloaders(client_id=client_partition)
|
|
151
|
+
train_loader, val_loader = (
|
|
152
|
+
loaders if not isinstance(loaders, list) else loaders[0]
|
|
153
|
+
)
|
|
154
|
+
train_data = train_loader.dataset
|
|
155
|
+
val_data = val_loader.dataset if val_loader else None
|
|
156
|
+
|
|
157
|
+
if dataset in ["mnist", "synthetic"] and dataset != "custom":
|
|
158
|
+
model_fn: Callable[[], Union[MNISTModel, CIFAR10Model, SimpleModel]]
|
|
159
|
+
if dataset == "mnist":
|
|
160
|
+
|
|
161
|
+
def model_fn() -> MNISTModel:
|
|
162
|
+
return MNISTModel()
|
|
163
|
+
elif dataset == "cifar10":
|
|
164
|
+
|
|
165
|
+
def model_fn() -> CIFAR10Model:
|
|
166
|
+
return CIFAR10Model()
|
|
167
|
+
else:
|
|
168
|
+
|
|
169
|
+
def model_fn() -> SimpleModel:
|
|
170
|
+
return SimpleModel()
|
|
171
|
+
else:
|
|
172
|
+
|
|
173
|
+
def model_fn() -> SimpleModel:
|
|
174
|
+
return SimpleModel()
|
|
175
|
+
|
|
176
|
+
client = create_client(
|
|
177
|
+
client_id=client_id,
|
|
178
|
+
model_fn=model_fn,
|
|
179
|
+
train_data=train_data,
|
|
180
|
+
val_data=val_data,
|
|
181
|
+
enable_zkp=enable_zkp,
|
|
182
|
+
local_epochs=epochs,
|
|
183
|
+
batch_size=batch_size,
|
|
184
|
+
learning_rate=learning_rate,
|
|
185
|
+
proof_rigor=proof_rigor,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
table = Table(title=f"Client {client_id} Configuration")
|
|
189
|
+
table.add_column("Parameter", style="cyan")
|
|
190
|
+
table.add_column("Value", style="green")
|
|
191
|
+
table.add_row("Server", server_address)
|
|
192
|
+
table.add_row("Dataset", dataset)
|
|
193
|
+
table.add_row("ZKP Enabled", "✓" if enable_zkp else "✗")
|
|
194
|
+
table.add_row("Epochs", str(epochs))
|
|
195
|
+
table.add_row("Batch Size", str(batch_size))
|
|
196
|
+
table.add_row("Learning Rate", str(learning_rate))
|
|
197
|
+
console.print(table)
|
|
198
|
+
|
|
199
|
+
with Progress(
|
|
200
|
+
SpinnerColumn(),
|
|
201
|
+
TextColumn("[progress.description]{task.description}"),
|
|
202
|
+
console=console,
|
|
203
|
+
) as progress:
|
|
204
|
+
progress.add_task("Connecting to server...", total=None)
|
|
205
|
+
start_client(client, server_address)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def run_setup(*, action: str, skip_zkp: bool) -> None:
|
|
209
|
+
from secure_fl.cli.setup import print_setup_status
|
|
210
|
+
from secure_fl.cli.setup import setup_environment
|
|
211
|
+
|
|
212
|
+
if action == "check":
|
|
213
|
+
rprint("[bold]🔍 Checking System Requirements[/bold]")
|
|
214
|
+
print_setup_status()
|
|
215
|
+
elif action == "install":
|
|
216
|
+
rprint("[bold]📦 Installing Python Dependencies[/bold]")
|
|
217
|
+
success = setup_environment()
|
|
218
|
+
if success:
|
|
219
|
+
rprint("[green]✓ Python dependencies installed successfully[/green]")
|
|
220
|
+
else:
|
|
221
|
+
rprint("[red]✗ Failed to install some dependencies[/red]")
|
|
222
|
+
elif action == "zkp":
|
|
223
|
+
if skip_zkp:
|
|
224
|
+
rprint("[yellow]⚠ Skipping ZKP tools installation[/yellow]")
|
|
225
|
+
return
|
|
226
|
+
rprint("[bold]🔐 Setting up ZKP Tools[/bold]")
|
|
227
|
+
from secure_fl.cli.setup import install_zkp_tools
|
|
228
|
+
|
|
229
|
+
success = install_zkp_tools()
|
|
230
|
+
if success:
|
|
231
|
+
rprint("[green]✓ ZKP tools setup completed[/green]")
|
|
232
|
+
else:
|
|
233
|
+
rprint("[yellow]⚠ ZKP tools setup completed with warnings[/yellow]")
|
|
234
|
+
elif action == "clean":
|
|
235
|
+
rprint("[bold]🧹 Cleaning temporary files[/bold]")
|
|
236
|
+
for cache_dir in [".ruff_cache", ".mypy_cache", "__pycache__"]:
|
|
237
|
+
if os.path.exists(cache_dir):
|
|
238
|
+
shutil.rmtree(cache_dir)
|
|
239
|
+
rprint("[green]✓ Cleanup completed[/green]")
|
|
240
|
+
elif action == "full":
|
|
241
|
+
rprint("[bold]🚀 Full Setup[/bold]")
|
|
242
|
+
rprint("1. Installing Python dependencies...")
|
|
243
|
+
success = setup_environment()
|
|
244
|
+
if not skip_zkp:
|
|
245
|
+
rprint("2. Setting up ZKP tools...")
|
|
246
|
+
from secure_fl.cli.setup import install_zkp_tools
|
|
247
|
+
|
|
248
|
+
install_zkp_tools()
|
|
249
|
+
else:
|
|
250
|
+
rprint("2. Skipping ZKP tools...")
|
|
251
|
+
|
|
252
|
+
rprint("3. Creating configuration...")
|
|
253
|
+
create_example_config("config.yaml")
|
|
254
|
+
rprint("4. Running tests...")
|
|
255
|
+
try:
|
|
256
|
+
subprocess.run(["uv", "run", "pytest", "--tb=short"], check=True)
|
|
257
|
+
test_success = True
|
|
258
|
+
except subprocess.CalledProcessError:
|
|
259
|
+
test_success = False
|
|
260
|
+
|
|
261
|
+
if success and test_success:
|
|
262
|
+
rprint("[green]✓ Full setup completed successfully![/green]")
|
|
263
|
+
rprint("\n[bold]Next steps:[/bold]")
|
|
264
|
+
rprint(
|
|
265
|
+
"1. Run a demo: [cyan]secure-fl experiment --dataset synthetic --rounds 3[/cyan]"
|
|
266
|
+
)
|
|
267
|
+
rprint("2. Start a server: [cyan]secure-fl server --rounds 5[/cyan]")
|
|
268
|
+
rprint("3. Connect clients: [cyan]secure-fl client -i client_1[/cyan]")
|
|
269
|
+
else:
|
|
270
|
+
rprint("[yellow]⚠ Setup completed with test failures[/yellow]")
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command Line Interface for Secure FL
|
|
3
|
+
|
|
4
|
+
This module provides command-line interfaces for running the Secure FL framework,
|
|
5
|
+
including server, client, experiment, and setup commands.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
from rich import print as rprint
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.logging import RichHandler
|
|
15
|
+
|
|
16
|
+
from secure_fl.cli.handlers import run_client
|
|
17
|
+
from secure_fl.cli.handlers import run_server
|
|
18
|
+
from secure_fl.cli.handlers import run_setup
|
|
19
|
+
from secure_fl.cli.zkp_check import check_zkp
|
|
20
|
+
from secure_fl.core.config import create_example_config
|
|
21
|
+
|
|
22
|
+
# Setup rich console and logging
|
|
23
|
+
console = Console()
|
|
24
|
+
logging.basicConfig(
|
|
25
|
+
level=logging.INFO,
|
|
26
|
+
format="%(message)s",
|
|
27
|
+
datefmt="[%X]",
|
|
28
|
+
handlers=[RichHandler(console=console, rich_tracebacks=True)],
|
|
29
|
+
)
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@click.group()
|
|
34
|
+
@click.version_option(version="0.1.0")
|
|
35
|
+
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose logging")
|
|
36
|
+
@click.option("--quiet", "-q", is_flag=True, help="Suppress output")
|
|
37
|
+
def main(verbose: bool, quiet: bool) -> None:
|
|
38
|
+
"""
|
|
39
|
+
🔐 Secure FL: Dual-Verifiable Federated Learning with Zero-Knowledge Proofs
|
|
40
|
+
|
|
41
|
+
A complete framework for federated learning with ZKP verification using
|
|
42
|
+
client-side zk-STARKs and server-side zk-SNARKs.
|
|
43
|
+
"""
|
|
44
|
+
if verbose:
|
|
45
|
+
logging.getLogger().setLevel(logging.DEBUG)
|
|
46
|
+
elif quiet:
|
|
47
|
+
logging.getLogger().setLevel(logging.ERROR)
|
|
48
|
+
|
|
49
|
+
# Display banner
|
|
50
|
+
if not quiet:
|
|
51
|
+
rprint(
|
|
52
|
+
"""
|
|
53
|
+
[bold blue]🔐 Secure FL Framework[/bold blue]
|
|
54
|
+
[dim]Dual-Verifiable Federated Learning with Zero-Knowledge Proofs[/dim]
|
|
55
|
+
"""
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@main.command()
|
|
60
|
+
@click.option(
|
|
61
|
+
"--config", "-c", type=click.Path(exists=True), help="Configuration file path"
|
|
62
|
+
)
|
|
63
|
+
@click.option("--host", default="localhost", help="Server host address")
|
|
64
|
+
@click.option("--port", default=8080, type=int, help="Server port")
|
|
65
|
+
@click.option("--rounds", "-r", default=10, type=int, help="Number of training rounds")
|
|
66
|
+
@click.option(
|
|
67
|
+
"--min-clients", default=2, type=int, help="Minimum number of clients required"
|
|
68
|
+
)
|
|
69
|
+
@click.option(
|
|
70
|
+
"--enable-zkp/--disable-zkp",
|
|
71
|
+
default=True,
|
|
72
|
+
help="Enable/disable zero-knowledge proofs",
|
|
73
|
+
)
|
|
74
|
+
@click.option(
|
|
75
|
+
"--proof-rigor",
|
|
76
|
+
type=click.Choice(["low", "medium", "high"]),
|
|
77
|
+
default="high",
|
|
78
|
+
help="ZKP rigor level",
|
|
79
|
+
)
|
|
80
|
+
@click.option(
|
|
81
|
+
"--momentum", default=0.9, type=float, help="FedJSCM momentum coefficient"
|
|
82
|
+
)
|
|
83
|
+
@click.option(
|
|
84
|
+
"--blockchain/--no-blockchain", default=False, help="Enable blockchain verification"
|
|
85
|
+
)
|
|
86
|
+
@click.option(
|
|
87
|
+
"--model",
|
|
88
|
+
type=click.Choice(["mnist", "cifar10", "simple", "custom"]),
|
|
89
|
+
default="mnist",
|
|
90
|
+
help="Model type to use",
|
|
91
|
+
)
|
|
92
|
+
def server(
|
|
93
|
+
config: str | None,
|
|
94
|
+
host: str,
|
|
95
|
+
port: int,
|
|
96
|
+
rounds: int,
|
|
97
|
+
min_clients: int,
|
|
98
|
+
enable_zkp: bool,
|
|
99
|
+
proof_rigor: str,
|
|
100
|
+
momentum: float,
|
|
101
|
+
blockchain: bool,
|
|
102
|
+
model: str,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Start the Secure FL server"""
|
|
105
|
+
try:
|
|
106
|
+
run_server(
|
|
107
|
+
config=config,
|
|
108
|
+
host=host,
|
|
109
|
+
port=port,
|
|
110
|
+
rounds=rounds,
|
|
111
|
+
min_clients=min_clients,
|
|
112
|
+
enable_zkp=enable_zkp,
|
|
113
|
+
proof_rigor=proof_rigor,
|
|
114
|
+
momentum=momentum,
|
|
115
|
+
blockchain=blockchain,
|
|
116
|
+
model=model,
|
|
117
|
+
console=console,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
except ImportError as e:
|
|
121
|
+
raise click.ClickException(f"Missing dependencies: {e}") from e
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error(f"Server failed to start: {e}")
|
|
124
|
+
raise click.ClickException(f"Server error: {e}") from e
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@main.command()
|
|
128
|
+
@click.option(
|
|
129
|
+
"--server-address",
|
|
130
|
+
"-s",
|
|
131
|
+
default="localhost:8080",
|
|
132
|
+
help="Server address (host:port)",
|
|
133
|
+
)
|
|
134
|
+
@click.option("--client-id", "-i", required=True, help="Unique client identifier")
|
|
135
|
+
@click.option(
|
|
136
|
+
"--dataset",
|
|
137
|
+
type=click.Choice(["mnist", "cifar10", "synthetic"]),
|
|
138
|
+
default="mnist",
|
|
139
|
+
help="Dataset to use for training",
|
|
140
|
+
)
|
|
141
|
+
@click.option("--data-path", type=click.Path(), help="Path to custom dataset")
|
|
142
|
+
@click.option("--partition", type=int, help="Data partition index")
|
|
143
|
+
@click.option(
|
|
144
|
+
"--enable-zkp/--disable-zkp",
|
|
145
|
+
default=True,
|
|
146
|
+
help="Enable/disable zero-knowledge proof generation",
|
|
147
|
+
)
|
|
148
|
+
@click.option(
|
|
149
|
+
"--epochs", "-e", default=5, type=int, help="Local training epochs per round"
|
|
150
|
+
)
|
|
151
|
+
@click.option("--batch-size", "-b", default=32, type=int, help="Training batch size")
|
|
152
|
+
@click.option(
|
|
153
|
+
"--learning-rate", "-lr", default=0.01, type=float, help="Local learning rate"
|
|
154
|
+
)
|
|
155
|
+
@click.option(
|
|
156
|
+
"--proof-rigor",
|
|
157
|
+
type=click.Choice(["high", "medium", "low"]),
|
|
158
|
+
default="high",
|
|
159
|
+
help="ZKP proof rigor level",
|
|
160
|
+
)
|
|
161
|
+
def client(
|
|
162
|
+
server_address: str,
|
|
163
|
+
client_id: str,
|
|
164
|
+
dataset: str,
|
|
165
|
+
data_path: str | None,
|
|
166
|
+
partition: int | None,
|
|
167
|
+
enable_zkp: bool,
|
|
168
|
+
epochs: int,
|
|
169
|
+
batch_size: int,
|
|
170
|
+
learning_rate: float,
|
|
171
|
+
proof_rigor: str,
|
|
172
|
+
) -> None:
|
|
173
|
+
"""Start a Secure FL client"""
|
|
174
|
+
try:
|
|
175
|
+
run_client(
|
|
176
|
+
server_address=server_address,
|
|
177
|
+
client_id=client_id,
|
|
178
|
+
dataset=dataset,
|
|
179
|
+
data_path=data_path,
|
|
180
|
+
partition=partition,
|
|
181
|
+
enable_zkp=enable_zkp,
|
|
182
|
+
epochs=epochs,
|
|
183
|
+
batch_size=batch_size,
|
|
184
|
+
learning_rate=learning_rate,
|
|
185
|
+
proof_rigor=proof_rigor,
|
|
186
|
+
console=console,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
except ImportError as e:
|
|
190
|
+
raise click.ClickException(f"Missing dependencies: {e}") from e
|
|
191
|
+
except Exception as e:
|
|
192
|
+
logger.error(f"Client failed to start: {e}")
|
|
193
|
+
raise click.ClickException(f"Client error: {e}") from e
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@main.command()
|
|
197
|
+
@click.option(
|
|
198
|
+
"--action",
|
|
199
|
+
type=click.Choice(["install", "zkp", "check", "clean", "full"]),
|
|
200
|
+
default="check",
|
|
201
|
+
help="Setup action to perform",
|
|
202
|
+
)
|
|
203
|
+
@click.option("--force", is_flag=True, help="Force reinstallation")
|
|
204
|
+
@click.option("--skip-zkp", is_flag=True, help="Skip ZKP tools installation")
|
|
205
|
+
def setup(action: str, force: bool, skip_zkp: bool) -> None:
|
|
206
|
+
"""Setup and configure Secure FL environment"""
|
|
207
|
+
try:
|
|
208
|
+
_ = force
|
|
209
|
+
run_setup(action=action, skip_zkp=skip_zkp)
|
|
210
|
+
|
|
211
|
+
except Exception as e:
|
|
212
|
+
logger.error(f"Setup failed: {e}")
|
|
213
|
+
raise click.ClickException(f"Setup error: {e}") from e
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@main.command()
|
|
217
|
+
def info() -> None:
|
|
218
|
+
"""Display system information and component status"""
|
|
219
|
+
|
|
220
|
+
from . import print_system_info
|
|
221
|
+
|
|
222
|
+
print_system_info()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def server_command() -> None:
|
|
226
|
+
"""Entry point for secure-fl-server command"""
|
|
227
|
+
server.main(args=sys.argv[1:], prog_name="secure-fl-server")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def client_command() -> None:
|
|
231
|
+
"""Entry point for secure-fl-client command"""
|
|
232
|
+
client.main(args=sys.argv[1:], prog_name="secure-fl-client")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def setup_command() -> None:
|
|
236
|
+
"""Entry point for secure-fl-setup command"""
|
|
237
|
+
setup.main(args=sys.argv[1:], prog_name="secure-fl-setup")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@main.command()
|
|
241
|
+
@click.option("--output", "-o", default="config.yaml", help="Output file path")
|
|
242
|
+
def create_config(output: str) -> None:
|
|
243
|
+
"""Create an example configuration file."""
|
|
244
|
+
try:
|
|
245
|
+
create_example_config(output)
|
|
246
|
+
console.print(f"✅ Example configuration created at [bold]{output}[/bold]")
|
|
247
|
+
console.print("Edit the file to customize your settings.")
|
|
248
|
+
except Exception as e:
|
|
249
|
+
console.print(f"❌ Error creating configuration: {e}", style="red")
|
|
250
|
+
raise click.Abort() from e
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# Add check-zkp command to main CLI
|
|
254
|
+
main.add_command(check_zkp, name="check-zkp")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
if __name__ == "__main__":
|
|
258
|
+
main()
|