Spaces:
Running
Running
compatible with server
Browse files- wasm-demo.js +134 -81
wasm-demo.js
CHANGED
@@ -2,7 +2,7 @@ import initWasm, {
|
|
2 |
decrypt_serialized_u64_radix_flat_wasm
|
3 |
} from './concrete-ml-extensions-wasm/concrete_ml_extensions_wasm.js';
|
4 |
|
5 |
-
const SERVER = 'https://
|
6 |
|
7 |
let clientKey, serverKey;
|
8 |
let encTokens;
|
@@ -10,6 +10,7 @@ let encServerResult;
|
|
10 |
let keygenWorker;
|
11 |
let encryptWorker;
|
12 |
let sessionUid;
|
|
|
13 |
|
14 |
// Memory-efficient base64 encoding for large Uint8Array
|
15 |
function uint8ToBase64(uint8) {
|
@@ -78,37 +79,43 @@ show('tokenizerSpin', false);
|
|
78 |
// Initialize the worker with the client key
|
79 |
encryptWorker.postMessage({ type: 'init', clientKey });
|
80 |
|
81 |
-
console.log('[Main]
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
|
|
|
|
|
|
|
|
90 |
});
|
91 |
-
|
92 |
-
if (!
|
93 |
-
const errorText = await
|
94 |
-
throw new Error(`Server
|
95 |
}
|
96 |
-
|
97 |
-
const { uid } = await
|
98 |
sessionUid = uid;
|
99 |
-
console.log('[Main] Server
|
100 |
-
$('keygenStatus').textContent = '
|
101 |
enable('btnEncrypt');
|
102 |
} catch (error) {
|
103 |
-
console.error('[Main] Server
|
104 |
-
$('keygenStatus').textContent = `Server
|
105 |
enable('btnEncrypt', false);
|
|
|
|
|
106 |
}
|
107 |
} else {
|
108 |
console.error('[Main] Key generation error:', e.data.error);
|
109 |
$('keygenStatus').textContent = `Error generating keys: ${e.data.error}`;
|
|
|
110 |
}
|
111 |
-
show('keygenSpin', false);
|
112 |
};
|
113 |
} catch (e) {
|
114 |
console.error('[Main] Failed to initialize WASM module:', e);
|
@@ -164,79 +171,125 @@ $('btnEncrypt').onclick = async () => {
|
|
164 |
}
|
165 |
};
|
166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
$('btnSend').onclick = async () => {
|
168 |
if ($('spin').hidden === false) {
|
169 |
-
console.log('[Main]
|
|
|
|
|
|
|
|
|
170 |
return;
|
171 |
}
|
|
|
172 |
show('encIcon', false);
|
173 |
show('spin', true);
|
174 |
-
$('srvStatus').textContent = '
|
175 |
$('srvComputing').hidden = true;
|
176 |
-
|
177 |
-
|
178 |
try {
|
179 |
-
const
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
const
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
}).catch(error => {
|
191 |
-
throw new Error(`Server unreachable: ${error.message}`);
|
192 |
});
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
throw new Error(`Server error: ${response.status} ${errorText}`);
|
198 |
}
|
199 |
|
200 |
-
|
|
|
|
|
|
|
201 |
$('srvComputing').hidden = false;
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
const endTime = startTime + (estimatedTime * 1000);
|
207 |
-
|
208 |
-
const progressInterval = setInterval(() => {
|
209 |
-
const now = performance.now();
|
210 |
-
const elapsed = (now - startTime) / 1000;
|
211 |
-
const remaining = Math.max(0, estimatedTime - elapsed);
|
212 |
-
$('srvProgress').textContent = `Estimated time remaining: ${Math.ceil(remaining)}s`;
|
213 |
-
|
214 |
-
if (remaining <= 0) {
|
215 |
-
clearInterval(progressInterval);
|
216 |
}
|
217 |
-
}
|
218 |
-
|
219 |
-
const {result_b64} = await response.json();
|
220 |
-
clearInterval(progressInterval);
|
221 |
-
const actualEndTime = performance.now();
|
222 |
-
const duration = ((actualEndTime - startTime) / 1000).toFixed(2);
|
223 |
-
console.log(`[Main] Server request completed in ${duration}s`);
|
224 |
-
console.log('[Main] Processing server response...');
|
225 |
-
encServerResult = Uint8Array.from(atob(result_b64), c=>c.charCodeAt(0));
|
226 |
-
console.log(`[Main] Received encrypted result: ${encServerResult.length} bytes`);
|
227 |
-
$('encResult').value = `(${encServerResult.length} B)`;
|
228 |
-
$('srvComputing').hidden = true;
|
229 |
-
$('srvProgress').hidden = true;
|
230 |
-
$('srvStatus').textContent = `✓ received (${duration}s)`;
|
231 |
-
enable('btnDecrypt');
|
232 |
} catch (e) {
|
233 |
-
const
|
234 |
-
|
235 |
-
|
236 |
-
$('srvComputing').hidden = true;
|
237 |
-
$('srvProgress').hidden = true;
|
238 |
-
$('srvStatus').textContent = `Server error: ${e.message} (${duration}s)`;
|
239 |
show('spin', false);
|
|
|
240 |
}
|
241 |
};
|
242 |
|
@@ -248,9 +301,9 @@ $('btnDecrypt').onclick = () => {
|
|
248 |
const score = (Number(score_scaled) / 1e6).toFixed(6);
|
249 |
console.log('[Main] Decryption successful');
|
250 |
console.log(`[Main] Result - flag: ${flag}, score: ${score}, total_g: ${total_g}`);
|
251 |
-
$('decResult').textContent = `
|
252 |
} catch (e) {
|
253 |
console.error('[Main] Decryption error:', e);
|
254 |
-
$('decResult').textContent = `
|
255 |
}
|
256 |
};
|
|
|
2 |
decrypt_serialized_u64_radix_flat_wasm
|
3 |
} from './concrete-ml-extensions-wasm/concrete_ml_extensions_wasm.js';
|
4 |
|
5 |
+
const SERVER = 'https://api.zama.ai';
|
6 |
|
7 |
let clientKey, serverKey;
|
8 |
let encTokens;
|
|
|
10 |
let keygenWorker;
|
11 |
let encryptWorker;
|
12 |
let sessionUid;
|
13 |
+
let taskId;
|
14 |
|
15 |
// Memory-efficient base64 encoding for large Uint8Array
|
16 |
function uint8ToBase64(uint8) {
|
|
|
79 |
// Initialize the worker with the client key
|
80 |
encryptWorker.postMessage({ type: 'init', clientKey });
|
81 |
|
82 |
+
console.log('[Main] Sending server key to server...');
|
83 |
+
$('keygenStatus').textContent = 'Keys generated, sending server key...';
|
84 |
+
show('keygenSpin', true);
|
85 |
+
|
86 |
+
const formData = new FormData();
|
87 |
+
const serverKeyBlob = new Blob([serverKey], { type: 'application/octet-stream' });
|
88 |
+
const serverKeyFile = new File([serverKeyBlob], "server.key");
|
89 |
+
formData.append('key', serverKeyFile);
|
90 |
+
formData.append('task_name', 'synthid');
|
91 |
+
|
92 |
+
const addKeyResponse = await fetch(`${SERVER}/add_key`, {
|
93 |
+
method: 'POST',
|
94 |
+
body: formData
|
95 |
});
|
96 |
+
|
97 |
+
if (!addKeyResponse.ok) {
|
98 |
+
const errorText = await addKeyResponse.text();
|
99 |
+
throw new Error(`Server /add_key failed: ${addKeyResponse.status} ${errorText}`);
|
100 |
}
|
101 |
+
|
102 |
+
const { uid } = await addKeyResponse.json();
|
103 |
sessionUid = uid;
|
104 |
+
console.log('[Main] Server key sent and UID received:', sessionUid);
|
105 |
+
$('keygenStatus').textContent = 'Keys generated & UID received ✓';
|
106 |
enable('btnEncrypt');
|
107 |
} catch (error) {
|
108 |
+
console.error('[Main] Server key submission error:', error);
|
109 |
+
$('keygenStatus').textContent = `Server key submission failed: ${error.message}`;
|
110 |
enable('btnEncrypt', false);
|
111 |
+
} finally {
|
112 |
+
show('keygenSpin', false);
|
113 |
}
|
114 |
} else {
|
115 |
console.error('[Main] Key generation error:', e.data.error);
|
116 |
$('keygenStatus').textContent = `Error generating keys: ${e.data.error}`;
|
117 |
+
show('keygenSpin', false);
|
118 |
}
|
|
|
119 |
};
|
120 |
} catch (e) {
|
121 |
console.error('[Main] Failed to initialize WASM module:', e);
|
|
|
171 |
}
|
172 |
};
|
173 |
|
174 |
+
async function pollTaskStatus(currentTaskId, currentUid) {
|
175 |
+
try {
|
176 |
+
const statusResponse = await fetch(`${SERVER}/get_task_status?task_id=${currentTaskId}&uid=${currentUid}`);
|
177 |
+
if (!statusResponse.ok) {
|
178 |
+
const errorText = await statusResponse.text();
|
179 |
+
console.error(`[Poll] Error fetching status: ${statusResponse.status} ${errorText}`);
|
180 |
+
$('srvStatus').textContent = `Status check error: ${statusResponse.status}`;
|
181 |
+
show('spin', false);
|
182 |
+
return null;
|
183 |
+
}
|
184 |
+
|
185 |
+
const statusData = await statusResponse.json();
|
186 |
+
console.log('[Poll] Task status:', statusData);
|
187 |
+
$('srvStatus').textContent = `Status: ${statusData.status} - ${statusData.details}`;
|
188 |
+
|
189 |
+
if (statusData.status === 'success' || statusData.status === 'completed') {
|
190 |
+
return statusData;
|
191 |
+
} else if (['failure', 'revoked', 'unknown', 'error'].includes(statusData.status.toLowerCase())) {
|
192 |
+
console.error('[Poll] Task failed or unrecoverable:', statusData);
|
193 |
+
$('srvStatus').textContent = `Task failed: ${statusData.status}`;
|
194 |
+
show('spin', false);
|
195 |
+
return null;
|
196 |
+
} else {
|
197 |
+
setTimeout(() => pollTaskStatus(currentTaskId, currentUid).then(finalStatus => {
|
198 |
+
if (finalStatus && (finalStatus.status === 'success' || finalStatus.status === 'completed')) {
|
199 |
+
getTaskResult(currentTaskId, currentUid, 'synthid');
|
200 |
+
}
|
201 |
+
}), 5000);
|
202 |
+
return null;
|
203 |
+
}
|
204 |
+
} catch (e) {
|
205 |
+
console.error('[Poll] Polling exception:', e);
|
206 |
+
$('srvStatus').textContent = `Polling error: ${e.message}`;
|
207 |
+
show('spin', false);
|
208 |
+
return null;
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
async function getTaskResult(currentTaskId, currentUid, taskName) {
|
213 |
+
$('srvStatus').textContent = 'Fetching result...';
|
214 |
+
try {
|
215 |
+
const resultResponse = await fetch(`${SERVER}/get_task_result?task_name=${taskName}&task_id=${currentTaskId}&uid=${currentUid}`);
|
216 |
+
|
217 |
+
if (!resultResponse.ok) {
|
218 |
+
const errorText = await resultResponse.text();
|
219 |
+
throw new Error(`Server /get_task_result error: ${resultResponse.status} ${errorText}`);
|
220 |
+
}
|
221 |
+
|
222 |
+
const resultArrayBuffer = await resultResponse.arrayBuffer();
|
223 |
+
encServerResult = new Uint8Array(resultArrayBuffer);
|
224 |
+
|
225 |
+
console.log(`[Main] Received encrypted result: ${encServerResult.length} bytes`);
|
226 |
+
$('encResult').value = `(${encServerResult.length} B)`;
|
227 |
+
$('srvStatus').textContent = `✓ result received (${((performance.now() - window.taskStartTime) / 1000).toFixed(2)}s total)`;
|
228 |
+
enable('btnDecrypt');
|
229 |
+
|
230 |
+
} catch (e) {
|
231 |
+
const duration = window.taskStartTime ? ((performance.now() - window.taskStartTime) / 1000).toFixed(2) : 'N/A';
|
232 |
+
console.error(`[Main] /get_task_result failed after ${duration}s:`, e);
|
233 |
+
$('srvStatus').textContent = `Result fetch error: ${e.message} (${duration}s)`;
|
234 |
+
} finally {
|
235 |
+
show('spin', false);
|
236 |
+
$('srvComputing').hidden = true;
|
237 |
+
}
|
238 |
+
}
|
239 |
+
|
240 |
$('btnSend').onclick = async () => {
|
241 |
if ($('spin').hidden === false) {
|
242 |
+
console.log('[Main] Task submission/polling already in progress, ignoring click');
|
243 |
+
return;
|
244 |
+
}
|
245 |
+
if (!sessionUid || !encTokens) {
|
246 |
+
alert('Please generate keys and encrypt text first.');
|
247 |
return;
|
248 |
}
|
249 |
+
|
250 |
show('encIcon', false);
|
251 |
show('spin', true);
|
252 |
+
$('srvStatus').textContent = 'Submitting task…';
|
253 |
$('srvComputing').hidden = true;
|
254 |
+
window.taskStartTime = performance.now();
|
255 |
+
|
256 |
try {
|
257 |
+
const formData = new FormData();
|
258 |
+
formData.append('uid', sessionUid);
|
259 |
+
formData.append('task_name', 'synthid');
|
260 |
+
|
261 |
+
const encryptedInputBlob = new Blob([encTokens], { type: 'application/octet-stream' });
|
262 |
+
const encryptedInputFile = new File([encryptedInputBlob], "input.fheencrypted");
|
263 |
+
formData.append('encrypted_input', encryptedInputFile);
|
264 |
+
|
265 |
+
const startTaskResponse = await fetch(`${SERVER}/start_task`, {
|
266 |
+
method: 'POST',
|
267 |
+
body: formData
|
|
|
|
|
268 |
});
|
269 |
+
|
270 |
+
if (!startTaskResponse.ok) {
|
271 |
+
const errorText = await startTaskResponse.text();
|
272 |
+
throw new Error(`Server /start_task error: ${startTaskResponse.status} ${errorText}`);
|
|
|
273 |
}
|
274 |
|
275 |
+
const { task_id: newTaskId } = await startTaskResponse.json();
|
276 |
+
taskId = newTaskId;
|
277 |
+
console.log('[Main] Task submitted to server. Task ID:', taskId);
|
278 |
+
$('srvStatus').textContent = `Task submitted (ID: ${taskId.substring(0,8)}...). Polling status...`;
|
279 |
$('srvComputing').hidden = false;
|
280 |
+
|
281 |
+
pollTaskStatus(taskId, sessionUid).then(finalStatus => {
|
282 |
+
if (finalStatus && (finalStatus.status === 'success' || finalStatus.status === 'completed')) {
|
283 |
+
getTaskResult(taskId, sessionUid, 'synthid');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
284 |
}
|
285 |
+
});
|
286 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
287 |
} catch (e) {
|
288 |
+
const duration = ((performance.now() - window.taskStartTime) / 1000).toFixed(2);
|
289 |
+
console.error(`[Main] Task submission failed after ${duration}s:`, e);
|
290 |
+
$('srvStatus').textContent = `Task submission error: ${e.message} (${duration}s)`;
|
|
|
|
|
|
|
291 |
show('spin', false);
|
292 |
+
$('srvComputing').hidden = true;
|
293 |
}
|
294 |
};
|
295 |
|
|
|
301 |
const score = (Number(score_scaled) / 1e6).toFixed(6);
|
302 |
console.log('[Main] Decryption successful');
|
303 |
console.log(`[Main] Result - flag: ${flag}, score: ${score}, total_g: ${total_g}`);
|
304 |
+
$('decResult').textContent = `Flag: ${flag}, Score: ${score}, Total G: ${total_g}`;
|
305 |
} catch (e) {
|
306 |
console.error('[Main] Decryption error:', e);
|
307 |
+
$('decResult').textContent = `Decryption failed: ${e.message}`;
|
308 |
}
|
309 |
};
|