chainlesschain 0.47.8 → 0.49.0
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.
- package/bin/chainlesschain.js +0 -0
- package/package.json +10 -8
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
- package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
- package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/activitypub.js +533 -0
- package/src/commands/codegen.js +303 -0
- package/src/commands/collab.js +482 -0
- package/src/commands/compliance.js +597 -6
- package/src/commands/crosschain.js +382 -0
- package/src/commands/dbevo.js +388 -0
- package/src/commands/dev.js +411 -0
- package/src/commands/federation.js +427 -0
- package/src/commands/fusion.js +332 -0
- package/src/commands/governance.js +505 -0
- package/src/commands/hardening.js +110 -0
- package/src/commands/incentive.js +373 -0
- package/src/commands/inference.js +304 -0
- package/src/commands/infra.js +361 -0
- package/src/commands/kg.js +371 -0
- package/src/commands/marketplace.js +326 -0
- package/src/commands/matrix.js +283 -0
- package/src/commands/mcp.js +441 -18
- package/src/commands/nlprog.js +329 -0
- package/src/commands/nostr.js +196 -7
- package/src/commands/ops.js +408 -0
- package/src/commands/perception.js +385 -0
- package/src/commands/pqc.js +34 -0
- package/src/commands/privacy.js +345 -0
- package/src/commands/quantization.js +280 -0
- package/src/commands/recommend.js +336 -0
- package/src/commands/reputation.js +349 -0
- package/src/commands/runtime.js +500 -0
- package/src/commands/sla.js +352 -0
- package/src/commands/social.js +265 -0
- package/src/commands/stress.js +252 -0
- package/src/commands/tech.js +268 -0
- package/src/commands/tenant.js +576 -0
- package/src/commands/trust.js +366 -0
- package/src/harness/mcp-client.js +330 -54
- package/src/index.js +114 -0
- package/src/lib/activitypub-bridge.js +623 -0
- package/src/lib/aiops.js +523 -0
- package/src/lib/autonomous-developer.js +524 -0
- package/src/lib/code-agent.js +442 -0
- package/src/lib/collaboration-governance.js +556 -0
- package/src/lib/community-governance.js +649 -0
- package/src/lib/compliance-framework-reporter.js +600 -0
- package/src/lib/content-recommendation.js +600 -0
- package/src/lib/cross-chain.js +669 -0
- package/src/lib/dbevo.js +669 -0
- package/src/lib/decentral-infra.js +445 -0
- package/src/lib/federation-hardening.js +587 -0
- package/src/lib/hardening-manager.js +409 -0
- package/src/lib/inference-network.js +407 -0
- package/src/lib/knowledge-graph.js +530 -0
- package/src/lib/matrix-bridge.js +252 -0
- package/src/lib/mcp-client.js +3 -0
- package/src/lib/mcp-registry.js +347 -0
- package/src/lib/mcp-scaffold.js +385 -0
- package/src/lib/multimodal.js +698 -0
- package/src/lib/nl-programming.js +595 -0
- package/src/lib/nostr-bridge.js +214 -38
- package/src/lib/perception.js +500 -0
- package/src/lib/pqc-manager.js +141 -9
- package/src/lib/privacy-computing.js +575 -0
- package/src/lib/protocol-fusion.js +535 -0
- package/src/lib/quantization.js +362 -0
- package/src/lib/reputation-optimizer.js +509 -0
- package/src/lib/skill-marketplace.js +397 -0
- package/src/lib/sla-manager.js +484 -0
- package/src/lib/social-graph.js +408 -0
- package/src/lib/stix-parser.js +167 -0
- package/src/lib/stress-tester.js +383 -0
- package/src/lib/tech-learning-engine.js +651 -0
- package/src/lib/tenant-saas.js +831 -0
- package/src/lib/threat-intel.js +268 -0
- package/src/lib/token-incentive.js +513 -0
- package/src/lib/topic-classifier.js +400 -0
- package/src/lib/trust-security.js +473 -0
- package/src/lib/ueba.js +403 -0
- package/src/lib/universal-runtime.js +771 -0
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privacy Computing — CLI port of Phase 91 隐私计算框架
|
|
3
|
+
* (docs/design/modules/56_隐私计算框架.md).
|
|
4
|
+
*
|
|
5
|
+
* Desktop uses real FedAvg, Shamir MPC, Laplace DP, and Paillier HE
|
|
6
|
+
* with Pinia store + PrivacyComputingPage.vue. CLI port ships:
|
|
7
|
+
*
|
|
8
|
+
* - Federated learning model lifecycle (init → train → aggregate → complete)
|
|
9
|
+
* - MPC computation tracking (Shamir/Beaver/GMW protocols)
|
|
10
|
+
* - Differential privacy publish with Laplace/Gaussian noise
|
|
11
|
+
* - Homomorphic encryption query simulation
|
|
12
|
+
* - Privacy budget tracking + report
|
|
13
|
+
*
|
|
14
|
+
* What does NOT port: real cryptographic primitives (Shamir polynomials,
|
|
15
|
+
* Paillier key gen), PrivacyComputingPage.vue, Pinia store.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import crypto from "crypto";
|
|
19
|
+
|
|
20
|
+
/* ── Constants ─────────────────────────────────────────────── */
|
|
21
|
+
|
|
22
|
+
export const FL_STATUS = Object.freeze({
|
|
23
|
+
INITIALIZING: "initializing",
|
|
24
|
+
TRAINING: "training",
|
|
25
|
+
AGGREGATING: "aggregating",
|
|
26
|
+
COMPLETED: "completed",
|
|
27
|
+
FAILED: "failed",
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const MPC_PROTOCOL = Object.freeze({
|
|
31
|
+
SHAMIR: Object.freeze({
|
|
32
|
+
id: "shamir",
|
|
33
|
+
name: "Shamir Secret Sharing",
|
|
34
|
+
description: "Shamir (t,n) 秘密共享",
|
|
35
|
+
}),
|
|
36
|
+
BEAVER: Object.freeze({
|
|
37
|
+
id: "beaver",
|
|
38
|
+
name: "Beaver Triples",
|
|
39
|
+
description: "Beaver 三元组乘法",
|
|
40
|
+
}),
|
|
41
|
+
GMW: Object.freeze({
|
|
42
|
+
id: "gmw",
|
|
43
|
+
name: "GMW Protocol",
|
|
44
|
+
description: "Goldreich-Micali-Wigderson",
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const DP_MECHANISM = Object.freeze({
|
|
49
|
+
LAPLACE: Object.freeze({
|
|
50
|
+
id: "laplace",
|
|
51
|
+
name: "Laplace",
|
|
52
|
+
description: "Laplace 噪声",
|
|
53
|
+
}),
|
|
54
|
+
GAUSSIAN: Object.freeze({
|
|
55
|
+
id: "gaussian",
|
|
56
|
+
name: "Gaussian",
|
|
57
|
+
description: "高斯噪声",
|
|
58
|
+
}),
|
|
59
|
+
EXPONENTIAL: Object.freeze({
|
|
60
|
+
id: "exponential",
|
|
61
|
+
name: "Exponential",
|
|
62
|
+
description: "指数机制",
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const HE_SCHEME = Object.freeze({
|
|
67
|
+
PAILLIER: Object.freeze({
|
|
68
|
+
id: "paillier",
|
|
69
|
+
name: "Paillier",
|
|
70
|
+
description: "加法同态",
|
|
71
|
+
}),
|
|
72
|
+
BFV: Object.freeze({ id: "bfv", name: "BFV", description: "全同态 (整数)" }),
|
|
73
|
+
CKKS: Object.freeze({
|
|
74
|
+
id: "ckks",
|
|
75
|
+
name: "CKKS",
|
|
76
|
+
description: "全同态 (浮点)",
|
|
77
|
+
}),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
export const DEFAULT_CONFIG = Object.freeze({
|
|
81
|
+
maxRounds: 100,
|
|
82
|
+
minParticipants: 3,
|
|
83
|
+
aggregationStrategy: "fedavg",
|
|
84
|
+
defaultEpsilon: 1.0,
|
|
85
|
+
defaultDelta: 1e-5,
|
|
86
|
+
maxBudget: 10.0,
|
|
87
|
+
mpcTimeoutMs: 30000,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/* ── State ─────────────────────────────────────────────── */
|
|
91
|
+
|
|
92
|
+
let _models = new Map();
|
|
93
|
+
let _computations = new Map();
|
|
94
|
+
let _privacyBudget = { spent: 0, limit: DEFAULT_CONFIG.maxBudget };
|
|
95
|
+
|
|
96
|
+
function _id() {
|
|
97
|
+
return crypto.randomUUID();
|
|
98
|
+
}
|
|
99
|
+
function _now() {
|
|
100
|
+
return Date.now();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function _strip(row) {
|
|
104
|
+
if (!row) return null;
|
|
105
|
+
const out = {};
|
|
106
|
+
for (const [k, v] of Object.entries(row)) {
|
|
107
|
+
if (k !== "_rowid_" && k !== "rowid") out[k] = v;
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* ── Schema ────────────────────────────────────────────── */
|
|
113
|
+
|
|
114
|
+
export function ensurePrivacyTables(db) {
|
|
115
|
+
db.exec(`CREATE TABLE IF NOT EXISTS fl_models (
|
|
116
|
+
id TEXT PRIMARY KEY,
|
|
117
|
+
name TEXT NOT NULL,
|
|
118
|
+
model_type TEXT,
|
|
119
|
+
architecture TEXT,
|
|
120
|
+
status TEXT DEFAULT 'initializing',
|
|
121
|
+
current_round INTEGER DEFAULT 0,
|
|
122
|
+
total_rounds INTEGER,
|
|
123
|
+
participant_count INTEGER DEFAULT 0,
|
|
124
|
+
accuracy REAL DEFAULT 0.0,
|
|
125
|
+
loss REAL,
|
|
126
|
+
learning_rate REAL DEFAULT 0.01,
|
|
127
|
+
aggregation_strategy TEXT DEFAULT 'fedavg',
|
|
128
|
+
privacy_budget_spent REAL DEFAULT 0.0,
|
|
129
|
+
created_at INTEGER,
|
|
130
|
+
updated_at INTEGER
|
|
131
|
+
)`);
|
|
132
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_flm_status ON fl_models(status)");
|
|
133
|
+
|
|
134
|
+
db.exec(`CREATE TABLE IF NOT EXISTS mpc_computations (
|
|
135
|
+
id TEXT PRIMARY KEY,
|
|
136
|
+
computation_type TEXT NOT NULL,
|
|
137
|
+
protocol TEXT DEFAULT 'shamir',
|
|
138
|
+
participant_count INTEGER,
|
|
139
|
+
participant_ids TEXT,
|
|
140
|
+
result_hash TEXT,
|
|
141
|
+
status TEXT DEFAULT 'pending',
|
|
142
|
+
shares_received INTEGER DEFAULT 0,
|
|
143
|
+
shares_required INTEGER,
|
|
144
|
+
computation_time_ms REAL,
|
|
145
|
+
error_message TEXT,
|
|
146
|
+
created_at INTEGER,
|
|
147
|
+
completed_at INTEGER
|
|
148
|
+
)`);
|
|
149
|
+
db.exec(
|
|
150
|
+
"CREATE INDEX IF NOT EXISTS idx_mpc_status ON mpc_computations(status)",
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
_loadAll(db);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function _loadAll(db) {
|
|
157
|
+
_models.clear();
|
|
158
|
+
_computations.clear();
|
|
159
|
+
_privacyBudget = { spent: 0, limit: DEFAULT_CONFIG.maxBudget };
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
for (const row of db.prepare("SELECT * FROM fl_models").all()) {
|
|
163
|
+
const m = _strip(row);
|
|
164
|
+
_models.set(m.id, m);
|
|
165
|
+
_privacyBudget.spent += m.privacy_budget_spent || 0;
|
|
166
|
+
}
|
|
167
|
+
} catch (_e) {
|
|
168
|
+
/* table may not exist */
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
for (const row of db.prepare("SELECT * FROM mpc_computations").all()) {
|
|
172
|
+
const c = _strip(row);
|
|
173
|
+
c.participant_ids = _parseJson(c.participant_ids, []);
|
|
174
|
+
_computations.set(c.id, c);
|
|
175
|
+
}
|
|
176
|
+
} catch (_e) {
|
|
177
|
+
/* table may not exist */
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function _parseJson(str, fallback) {
|
|
182
|
+
if (!str) return fallback;
|
|
183
|
+
try {
|
|
184
|
+
return JSON.parse(str);
|
|
185
|
+
} catch (_e) {
|
|
186
|
+
return fallback;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* ── Federated Learning ────────────────────────────────── */
|
|
191
|
+
|
|
192
|
+
export function createModel(
|
|
193
|
+
db,
|
|
194
|
+
name,
|
|
195
|
+
{ modelType, architecture, totalRounds, learningRate, participants } = {},
|
|
196
|
+
) {
|
|
197
|
+
const id = _id();
|
|
198
|
+
const now = _now();
|
|
199
|
+
const rounds = totalRounds || 10;
|
|
200
|
+
const lr = learningRate || 0.01;
|
|
201
|
+
const participantCount = participants || 0;
|
|
202
|
+
|
|
203
|
+
const model = {
|
|
204
|
+
id,
|
|
205
|
+
name,
|
|
206
|
+
model_type: modelType || "neural_network",
|
|
207
|
+
architecture: architecture || "mlp",
|
|
208
|
+
status: "initializing",
|
|
209
|
+
current_round: 0,
|
|
210
|
+
total_rounds: rounds,
|
|
211
|
+
participant_count: participantCount,
|
|
212
|
+
accuracy: 0,
|
|
213
|
+
loss: null,
|
|
214
|
+
learning_rate: lr,
|
|
215
|
+
aggregation_strategy: DEFAULT_CONFIG.aggregationStrategy,
|
|
216
|
+
privacy_budget_spent: 0,
|
|
217
|
+
created_at: now,
|
|
218
|
+
updated_at: now,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
db.prepare(
|
|
222
|
+
`INSERT INTO fl_models (id, name, model_type, architecture, status, current_round, total_rounds,
|
|
223
|
+
participant_count, accuracy, loss, learning_rate, aggregation_strategy, privacy_budget_spent, created_at, updated_at)
|
|
224
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
225
|
+
).run(
|
|
226
|
+
id,
|
|
227
|
+
name,
|
|
228
|
+
model.model_type,
|
|
229
|
+
model.architecture,
|
|
230
|
+
"initializing",
|
|
231
|
+
0,
|
|
232
|
+
rounds,
|
|
233
|
+
participantCount,
|
|
234
|
+
0,
|
|
235
|
+
null,
|
|
236
|
+
lr,
|
|
237
|
+
model.aggregation_strategy,
|
|
238
|
+
0,
|
|
239
|
+
now,
|
|
240
|
+
now,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
_models.set(id, model);
|
|
244
|
+
return { modelId: id };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function trainRound(db, modelId) {
|
|
248
|
+
const m = _models.get(modelId);
|
|
249
|
+
if (!m) return { trained: false, reason: "not_found" };
|
|
250
|
+
if (m.status === "completed")
|
|
251
|
+
return { trained: false, reason: "already_completed" };
|
|
252
|
+
if (m.status === "failed") return { trained: false, reason: "model_failed" };
|
|
253
|
+
|
|
254
|
+
if (m.status === "initializing") m.status = "training";
|
|
255
|
+
|
|
256
|
+
m.current_round += 1;
|
|
257
|
+
// Simulated accuracy improvement
|
|
258
|
+
const baseAcc = 0.5;
|
|
259
|
+
const progress = m.current_round / m.total_rounds;
|
|
260
|
+
m.accuracy = Math.round((baseAcc + progress * 0.45) * 1000) / 1000;
|
|
261
|
+
m.loss = Math.round((1 - m.accuracy) * 0.5 * 1000) / 1000;
|
|
262
|
+
m.privacy_budget_spent += DEFAULT_CONFIG.defaultEpsilon * 0.1;
|
|
263
|
+
m.updated_at = _now();
|
|
264
|
+
|
|
265
|
+
if (m.current_round >= m.total_rounds) {
|
|
266
|
+
m.status = "completed";
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
db.prepare(
|
|
270
|
+
`UPDATE fl_models SET status = ?, current_round = ?, accuracy = ?, loss = ?,
|
|
271
|
+
privacy_budget_spent = ?, updated_at = ? WHERE id = ?`,
|
|
272
|
+
).run(
|
|
273
|
+
m.status,
|
|
274
|
+
m.current_round,
|
|
275
|
+
m.accuracy,
|
|
276
|
+
m.loss,
|
|
277
|
+
m.privacy_budget_spent,
|
|
278
|
+
m.updated_at,
|
|
279
|
+
modelId,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
trained: true,
|
|
284
|
+
round: m.current_round,
|
|
285
|
+
accuracy: m.accuracy,
|
|
286
|
+
status: m.status,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function failModel(db, modelId, reason) {
|
|
291
|
+
const m = _models.get(modelId);
|
|
292
|
+
if (!m) return { failed: false, reason: "not_found" };
|
|
293
|
+
m.status = "failed";
|
|
294
|
+
m.updated_at = _now();
|
|
295
|
+
db.prepare(
|
|
296
|
+
"UPDATE fl_models SET status = ?, updated_at = ? WHERE id = ?",
|
|
297
|
+
).run("failed", m.updated_at, modelId);
|
|
298
|
+
return { failed: true };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function getModel(db, modelId) {
|
|
302
|
+
const m = _models.get(modelId);
|
|
303
|
+
return m ? { ...m } : null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function listModels(db, { status, limit = 50 } = {}) {
|
|
307
|
+
let models = [..._models.values()];
|
|
308
|
+
if (status) models = models.filter((m) => m.status === status);
|
|
309
|
+
return models
|
|
310
|
+
.sort((a, b) => b.updated_at - a.updated_at)
|
|
311
|
+
.slice(0, limit)
|
|
312
|
+
.map((m) => ({ ...m }));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* ── MPC Computation ───────────────────────────────────── */
|
|
316
|
+
|
|
317
|
+
const VALID_PROTOCOLS = new Set(Object.values(MPC_PROTOCOL).map((p) => p.id));
|
|
318
|
+
|
|
319
|
+
export function createComputation(
|
|
320
|
+
db,
|
|
321
|
+
computationType,
|
|
322
|
+
{ protocol, participantIds, sharesRequired } = {},
|
|
323
|
+
) {
|
|
324
|
+
const proto = protocol || "shamir";
|
|
325
|
+
if (!VALID_PROTOCOLS.has(proto))
|
|
326
|
+
return { computationId: null, reason: "invalid_protocol" };
|
|
327
|
+
|
|
328
|
+
const ids = participantIds || [];
|
|
329
|
+
const required = sharesRequired || Math.ceil(ids.length / 2) || 2;
|
|
330
|
+
|
|
331
|
+
const id = _id();
|
|
332
|
+
const now = _now();
|
|
333
|
+
const comp = {
|
|
334
|
+
id,
|
|
335
|
+
computation_type: computationType,
|
|
336
|
+
protocol: proto,
|
|
337
|
+
participant_count: ids.length,
|
|
338
|
+
participant_ids: ids,
|
|
339
|
+
result_hash: null,
|
|
340
|
+
status: "pending",
|
|
341
|
+
shares_received: 0,
|
|
342
|
+
shares_required: required,
|
|
343
|
+
computation_time_ms: null,
|
|
344
|
+
error_message: null,
|
|
345
|
+
created_at: now,
|
|
346
|
+
completed_at: null,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
db.prepare(
|
|
350
|
+
`INSERT INTO mpc_computations (id, computation_type, protocol, participant_count, participant_ids,
|
|
351
|
+
result_hash, status, shares_received, shares_required, computation_time_ms, error_message, created_at, completed_at)
|
|
352
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
353
|
+
).run(
|
|
354
|
+
id,
|
|
355
|
+
computationType,
|
|
356
|
+
proto,
|
|
357
|
+
ids.length,
|
|
358
|
+
JSON.stringify(ids),
|
|
359
|
+
null,
|
|
360
|
+
"pending",
|
|
361
|
+
0,
|
|
362
|
+
required,
|
|
363
|
+
null,
|
|
364
|
+
null,
|
|
365
|
+
now,
|
|
366
|
+
null,
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
_computations.set(id, comp);
|
|
370
|
+
return { computationId: id };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function submitShare(db, computationId) {
|
|
374
|
+
const c = _computations.get(computationId);
|
|
375
|
+
if (!c) return { submitted: false, reason: "not_found" };
|
|
376
|
+
if (c.status === "completed")
|
|
377
|
+
return { submitted: false, reason: "already_completed" };
|
|
378
|
+
|
|
379
|
+
c.shares_received += 1;
|
|
380
|
+
if (c.status === "pending") c.status = "computing";
|
|
381
|
+
|
|
382
|
+
if (c.shares_received >= c.shares_required) {
|
|
383
|
+
c.status = "completed";
|
|
384
|
+
c.completed_at = _now();
|
|
385
|
+
c.computation_time_ms = c.completed_at - c.created_at;
|
|
386
|
+
c.result_hash = crypto
|
|
387
|
+
.createHash("sha256")
|
|
388
|
+
.update(`result-${c.id}`)
|
|
389
|
+
.digest("hex")
|
|
390
|
+
.slice(0, 16);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
db.prepare(
|
|
394
|
+
`UPDATE mpc_computations SET shares_received = ?, status = ?, completed_at = ?,
|
|
395
|
+
computation_time_ms = ?, result_hash = ? WHERE id = ?`,
|
|
396
|
+
).run(
|
|
397
|
+
c.shares_received,
|
|
398
|
+
c.status,
|
|
399
|
+
c.completed_at,
|
|
400
|
+
c.computation_time_ms,
|
|
401
|
+
c.result_hash,
|
|
402
|
+
computationId,
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
submitted: true,
|
|
407
|
+
sharesReceived: c.shares_received,
|
|
408
|
+
status: c.status,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export function getComputation(db, computationId) {
|
|
413
|
+
const c = _computations.get(computationId);
|
|
414
|
+
return c ? { ...c } : null;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function listComputations(db, { protocol, status, limit = 50 } = {}) {
|
|
418
|
+
let comps = [..._computations.values()];
|
|
419
|
+
if (protocol) comps = comps.filter((c) => c.protocol === protocol);
|
|
420
|
+
if (status) comps = comps.filter((c) => c.status === status);
|
|
421
|
+
return comps
|
|
422
|
+
.sort((a, b) => b.created_at - a.created_at)
|
|
423
|
+
.slice(0, limit)
|
|
424
|
+
.map((c) => ({ ...c }));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/* ── Differential Privacy ──────────────────────────────── */
|
|
428
|
+
|
|
429
|
+
export function dpPublish(
|
|
430
|
+
db,
|
|
431
|
+
{ data, epsilon, delta, mechanism, sensitivity } = {},
|
|
432
|
+
) {
|
|
433
|
+
const eps = epsilon || DEFAULT_CONFIG.defaultEpsilon;
|
|
434
|
+
const del = delta || DEFAULT_CONFIG.defaultDelta;
|
|
435
|
+
const mech = mechanism || "laplace";
|
|
436
|
+
const sens = sensitivity || 1.0;
|
|
437
|
+
|
|
438
|
+
// Check budget
|
|
439
|
+
if (_privacyBudget.spent + eps > _privacyBudget.limit) {
|
|
440
|
+
return {
|
|
441
|
+
published: false,
|
|
442
|
+
reason: "budget_exceeded",
|
|
443
|
+
remaining: _privacyBudget.limit - _privacyBudget.spent,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Simulate noise addition
|
|
448
|
+
let noisyData;
|
|
449
|
+
if (Array.isArray(data)) {
|
|
450
|
+
noisyData = data.map((v) => {
|
|
451
|
+
const noise = _generateNoise(mech, sens / eps);
|
|
452
|
+
return typeof v === "number" ? Math.round((v + noise) * 1000) / 1000 : v;
|
|
453
|
+
});
|
|
454
|
+
} else if (typeof data === "number") {
|
|
455
|
+
const noise = _generateNoise(mech, sens / eps);
|
|
456
|
+
noisyData = Math.round((data + noise) * 1000) / 1000;
|
|
457
|
+
} else {
|
|
458
|
+
noisyData = data;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
_privacyBudget.spent += eps;
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
published: true,
|
|
465
|
+
data: noisyData,
|
|
466
|
+
epsilon: eps,
|
|
467
|
+
delta: del,
|
|
468
|
+
mechanism: mech,
|
|
469
|
+
budgetSpent: Math.round(_privacyBudget.spent * 1000) / 1000,
|
|
470
|
+
budgetRemaining:
|
|
471
|
+
Math.round((_privacyBudget.limit - _privacyBudget.spent) * 1000) / 1000,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function _generateNoise(mechanism, scale) {
|
|
476
|
+
// Simplified noise generation for CLI simulation
|
|
477
|
+
if (mechanism === "laplace") {
|
|
478
|
+
// Laplace noise: double exponential
|
|
479
|
+
const u = Math.random() - 0.5;
|
|
480
|
+
return -scale * Math.sign(u) * Math.log(1 - 2 * Math.abs(u));
|
|
481
|
+
}
|
|
482
|
+
if (mechanism === "gaussian") {
|
|
483
|
+
// Box-Muller transform
|
|
484
|
+
const u1 = Math.random();
|
|
485
|
+
const u2 = Math.random();
|
|
486
|
+
return scale * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
487
|
+
}
|
|
488
|
+
// Exponential fallback
|
|
489
|
+
return scale * -Math.log(Math.random());
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/* ── Homomorphic Encryption ────────────────────────────── */
|
|
493
|
+
|
|
494
|
+
const VALID_SCHEMES = new Set(Object.values(HE_SCHEME).map((s) => s.id));
|
|
495
|
+
|
|
496
|
+
export function heQuery({ data, operation, scheme } = {}) {
|
|
497
|
+
const sch = scheme || "paillier";
|
|
498
|
+
if (!VALID_SCHEMES.has(sch))
|
|
499
|
+
return { result: null, reason: "unsupported_scheme" };
|
|
500
|
+
|
|
501
|
+
const validOps = ["sum", "product", "mean", "count"];
|
|
502
|
+
if (!validOps.includes(operation))
|
|
503
|
+
return { result: null, reason: "unsupported_operation" };
|
|
504
|
+
|
|
505
|
+
if (!Array.isArray(data) || data.length === 0)
|
|
506
|
+
return { result: null, reason: "invalid_data" };
|
|
507
|
+
|
|
508
|
+
// Simulate encrypted computation
|
|
509
|
+
let result;
|
|
510
|
+
if (operation === "sum") result = data.reduce((a, b) => a + b, 0);
|
|
511
|
+
else if (operation === "product") result = data.reduce((a, b) => a * b, 1);
|
|
512
|
+
else if (operation === "mean")
|
|
513
|
+
result = data.reduce((a, b) => a + b, 0) / data.length;
|
|
514
|
+
else if (operation === "count") result = data.length;
|
|
515
|
+
|
|
516
|
+
return {
|
|
517
|
+
result: Math.round(result * 1000) / 1000,
|
|
518
|
+
operation,
|
|
519
|
+
scheme: sch,
|
|
520
|
+
inputCount: data.length,
|
|
521
|
+
encrypted: true, // Simulated — in real impl this would be ciphertext
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/* ── Privacy Report ────────────────────────────────────── */
|
|
526
|
+
|
|
527
|
+
export function getPrivacyReport(db) {
|
|
528
|
+
const models = [..._models.values()];
|
|
529
|
+
const comps = [..._computations.values()];
|
|
530
|
+
|
|
531
|
+
const flStats = {
|
|
532
|
+
totalModels: models.length,
|
|
533
|
+
completed: models.filter((m) => m.status === "completed").length,
|
|
534
|
+
training: models.filter((m) => m.status === "training").length,
|
|
535
|
+
avgAccuracy:
|
|
536
|
+
models.length > 0
|
|
537
|
+
? Math.round(
|
|
538
|
+
(models.reduce((s, m) => s + m.accuracy, 0) / models.length) * 1000,
|
|
539
|
+
) / 1000
|
|
540
|
+
: 0,
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
const mpcStats = {
|
|
544
|
+
totalComputations: comps.length,
|
|
545
|
+
completed: comps.filter((c) => c.status === "completed").length,
|
|
546
|
+
pending: comps.filter(
|
|
547
|
+
(c) => c.status === "pending" || c.status === "computing",
|
|
548
|
+
).length,
|
|
549
|
+
byProtocol: {},
|
|
550
|
+
};
|
|
551
|
+
for (const c of comps) {
|
|
552
|
+
mpcStats.byProtocol[c.protocol] =
|
|
553
|
+
(mpcStats.byProtocol[c.protocol] || 0) + 1;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return {
|
|
557
|
+
privacyBudget: {
|
|
558
|
+
spent: Math.round(_privacyBudget.spent * 1000) / 1000,
|
|
559
|
+
limit: _privacyBudget.limit,
|
|
560
|
+
remaining:
|
|
561
|
+
Math.round((_privacyBudget.limit - _privacyBudget.spent) * 1000) / 1000,
|
|
562
|
+
exhausted: _privacyBudget.spent >= _privacyBudget.limit,
|
|
563
|
+
},
|
|
564
|
+
federatedLearning: flStats,
|
|
565
|
+
mpc: mpcStats,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/* ── Reset (tests) ─────────────────────────────────────── */
|
|
570
|
+
|
|
571
|
+
export function _resetState() {
|
|
572
|
+
_models.clear();
|
|
573
|
+
_computations.clear();
|
|
574
|
+
_privacyBudget = { spent: 0, limit: DEFAULT_CONFIG.maxBudget };
|
|
575
|
+
}
|