@zama-fhe/relayer-sdk 0.1.0 → 0.1.1
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/README.md +6 -1
- package/bundle/relayer-sdk-js.js +10811 -10645
- package/bundle/relayer-sdk-js.umd.cjs +14 -14
- package/lib/node.cjs +399 -214
- package/lib/node.d.ts +19 -5
- package/lib/node.js +397 -214
- package/lib/web.d.ts +20 -6
- package/lib/web.js +397 -214
- package/package.json +1 -5
package/lib/node.cjs
CHANGED
|
@@ -44,7 +44,9 @@ const fromHexString = (hexString) => {
|
|
|
44
44
|
return new Uint8Array();
|
|
45
45
|
return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
|
|
46
46
|
};
|
|
47
|
-
|
|
47
|
+
function toHexString(bytes, with0x = false) {
|
|
48
|
+
return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
|
|
49
|
+
}
|
|
48
50
|
const bytesToBigInt = function (byteArray) {
|
|
49
51
|
if (!byteArray || byteArray?.length === 0) {
|
|
50
52
|
return BigInt(0);
|
|
@@ -55,95 +57,370 @@ const bytesToBigInt = function (byteArray) {
|
|
|
55
57
|
return BigInt(`0x${hex}`);
|
|
56
58
|
};
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return keyurlCache[url];
|
|
60
|
+
function getErrorCause(e) {
|
|
61
|
+
if (e instanceof Error && typeof e.cause === 'object' && e.cause !== null) {
|
|
62
|
+
return e.cause;
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
else {
|
|
97
|
-
publicKey = new Uint8Array(await publicKeyResponse.arrayBuffer());
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
function getErrorCauseCode(e) {
|
|
67
|
+
const cause = getErrorCause(e);
|
|
68
|
+
if (!cause || !('code' in cause) || !cause.code) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
if (typeof cause.code !== 'string') {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return cause.code;
|
|
75
|
+
}
|
|
76
|
+
function getErrorCauseStatus(e) {
|
|
77
|
+
const cause = getErrorCause(e);
|
|
78
|
+
if (!cause || !('status' in cause) || cause.status === undefined) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
if (typeof cause.status !== 'number') {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
return cause.status;
|
|
85
|
+
}
|
|
86
|
+
async function throwRelayerResponseError(operation, response) {
|
|
87
|
+
let message;
|
|
88
|
+
// Special case for 429
|
|
89
|
+
if (response.status === 429) {
|
|
90
|
+
message = `Relayer rate limit exceeded: Please wait and try again later.`;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
switch (operation) {
|
|
94
|
+
case 'PUBLIC_DECRYPT': {
|
|
95
|
+
message = `Public decrypt failed: relayer respond with HTTP code ${response.status}`;
|
|
96
|
+
break;
|
|
98
97
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (!publicParams2048Response.ok) {
|
|
103
|
-
throw new Error(`HTTP error! status: ${publicParams2048Response.status} on ${publicParams2048Response.url}`);
|
|
98
|
+
case 'USER_DECRYPT': {
|
|
99
|
+
message = `User decrypt failed: relayer respond with HTTP code ${response.status}`;
|
|
100
|
+
break;
|
|
104
101
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
publicParams2048 = await publicParams2048Response.bytes();
|
|
102
|
+
case 'KEY_URL': {
|
|
103
|
+
message = `HTTP error! status: ${response.status}`;
|
|
104
|
+
break;
|
|
109
105
|
}
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
default: {
|
|
107
|
+
const responseText = await response.text();
|
|
108
|
+
message = `Relayer didn't response correctly. Bad status ${response.statusText}. Content: ${responseText}`;
|
|
109
|
+
break;
|
|
112
110
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const cause = {
|
|
114
|
+
code: 'RELAYER_FETCH_ERROR',
|
|
115
|
+
operation,
|
|
116
|
+
status: response.status,
|
|
117
|
+
statusText: response.statusText,
|
|
118
|
+
url: response.url,
|
|
119
|
+
};
|
|
120
|
+
throw new Error(message, {
|
|
121
|
+
cause,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function throwRelayerJSONError(operation, error) {
|
|
125
|
+
let message;
|
|
126
|
+
switch (operation) {
|
|
127
|
+
case 'PUBLIC_DECRYPT': {
|
|
128
|
+
message = "Public decrypt failed: Relayer didn't return a JSON";
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case 'USER_DECRYPT': {
|
|
132
|
+
message = "User decrypt failed: Relayer didn't return a JSON";
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
default: {
|
|
136
|
+
message = "Relayer didn't return a JSON";
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const cause = {
|
|
141
|
+
code: 'RELAYER_NO_JSON_ERROR',
|
|
142
|
+
operation,
|
|
143
|
+
error,
|
|
144
|
+
};
|
|
145
|
+
throw new Error(message, {
|
|
146
|
+
cause,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function throwRelayerUnexpectedJSONError(operation, error) {
|
|
150
|
+
let message;
|
|
151
|
+
switch (operation) {
|
|
152
|
+
case 'PUBLIC_DECRYPT': {
|
|
153
|
+
message =
|
|
154
|
+
'Public decrypt failed: Relayer returned an unexpected JSON response';
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
case 'USER_DECRYPT': {
|
|
158
|
+
message =
|
|
159
|
+
'User decrypt failed: Relayer returned an unexpected JSON response';
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
default: {
|
|
163
|
+
message = 'Relayer returned an unexpected JSON response';
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const cause = {
|
|
168
|
+
code: 'RELAYER_UNEXPECTED_JSON_ERROR',
|
|
169
|
+
operation,
|
|
170
|
+
error,
|
|
171
|
+
};
|
|
172
|
+
throw new Error(message, {
|
|
173
|
+
cause,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function throwRelayerInternalError(operation, json) {
|
|
177
|
+
let message;
|
|
178
|
+
switch (operation) {
|
|
179
|
+
case 'PUBLIC_DECRYPT': {
|
|
180
|
+
message =
|
|
181
|
+
"Pulbic decrypt failed: the public decryption didn't succeed for an unknown reason";
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case 'USER_DECRYPT': {
|
|
185
|
+
message =
|
|
186
|
+
"User decrypt failed: the user decryption didn't succeed for an unknown reason";
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
default: {
|
|
190
|
+
message = "Relayer didn't response correctly.";
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const cause = {
|
|
195
|
+
code: 'RELAYER_INTERNAL_ERROR',
|
|
196
|
+
operation,
|
|
197
|
+
error: json,
|
|
198
|
+
};
|
|
199
|
+
throw new Error(message, {
|
|
200
|
+
cause,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function throwRelayerUnknownError(operation, error, message) {
|
|
204
|
+
if (!message) {
|
|
205
|
+
switch (operation) {
|
|
206
|
+
case 'PUBLIC_DECRYPT': {
|
|
207
|
+
message = "Public decrypt failed: Relayer didn't respond";
|
|
208
|
+
break;
|
|
116
209
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
});
|
|
210
|
+
case 'USER_DECRYPT': {
|
|
211
|
+
message = "User decrypt failed: Relayer didn't respond";
|
|
212
|
+
break;
|
|
121
213
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
214
|
+
default: {
|
|
215
|
+
message = "Relayer didn't response correctly. Bad JSON.";
|
|
216
|
+
break;
|
|
125
217
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const cause = {
|
|
221
|
+
code: 'RELAYER_UNKNOWN_ERROR',
|
|
222
|
+
operation,
|
|
223
|
+
error,
|
|
224
|
+
};
|
|
225
|
+
throw new Error(message ?? "Relayer didn't response correctly.", {
|
|
226
|
+
cause,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function assertIsRelayerFetchResponseJson(json) {
|
|
231
|
+
if (!json || typeof json !== 'object') {
|
|
232
|
+
throw new Error('Unexpected response JSON.');
|
|
233
|
+
}
|
|
234
|
+
if (!('response' in json &&
|
|
235
|
+
json.response !== null &&
|
|
236
|
+
json.response !== undefined)) {
|
|
237
|
+
throw new Error("Unexpected response JSON format: missing 'response' property.");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async function fetchRelayerJsonRpcPost(relayerOperation, url, payload, options) {
|
|
241
|
+
const init = {
|
|
242
|
+
method: 'POST',
|
|
243
|
+
headers: {
|
|
244
|
+
'Content-Type': 'application/json',
|
|
245
|
+
...(options?.apiKey && { 'x-api-key': options.apiKey }),
|
|
246
|
+
},
|
|
247
|
+
body: JSON.stringify(payload),
|
|
248
|
+
};
|
|
249
|
+
let response;
|
|
250
|
+
let json;
|
|
251
|
+
try {
|
|
252
|
+
response = await fetch(url, init);
|
|
253
|
+
}
|
|
254
|
+
catch (e) {
|
|
255
|
+
throwRelayerUnknownError(relayerOperation, e);
|
|
256
|
+
}
|
|
257
|
+
if (!response.ok) {
|
|
258
|
+
await throwRelayerResponseError(relayerOperation, response);
|
|
259
|
+
}
|
|
260
|
+
let parsed;
|
|
261
|
+
try {
|
|
262
|
+
parsed = await response.json();
|
|
263
|
+
}
|
|
264
|
+
catch (e) {
|
|
265
|
+
throwRelayerJSONError(relayerOperation, e);
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
assertIsRelayerFetchResponseJson(parsed);
|
|
269
|
+
json = parsed;
|
|
270
|
+
}
|
|
271
|
+
catch (e) {
|
|
272
|
+
throwRelayerUnexpectedJSONError(relayerOperation, e);
|
|
273
|
+
}
|
|
274
|
+
return json;
|
|
275
|
+
}
|
|
276
|
+
async function fetchRelayerGet(relayerOperation, url) {
|
|
277
|
+
let response;
|
|
278
|
+
let json;
|
|
279
|
+
try {
|
|
280
|
+
response = await fetch(url);
|
|
281
|
+
}
|
|
282
|
+
catch (e) {
|
|
283
|
+
throwRelayerUnknownError(relayerOperation, e);
|
|
284
|
+
}
|
|
285
|
+
if (!response.ok) {
|
|
286
|
+
await throwRelayerResponseError(relayerOperation, response);
|
|
287
|
+
}
|
|
288
|
+
let parsed;
|
|
289
|
+
try {
|
|
290
|
+
parsed = await response.json();
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
throwRelayerJSONError(relayerOperation, e);
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
assertIsRelayerFetchResponseJson(parsed);
|
|
297
|
+
json = parsed;
|
|
298
|
+
}
|
|
299
|
+
catch (e) {
|
|
300
|
+
throwRelayerUnexpectedJSONError(relayerOperation, e);
|
|
301
|
+
}
|
|
302
|
+
return json;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// export type RelayerKeysItem = {
|
|
306
|
+
// data_id: string;
|
|
307
|
+
// param_choice: number;
|
|
308
|
+
// urls: string[];
|
|
309
|
+
// signatures: string[];
|
|
310
|
+
// };
|
|
311
|
+
// export type RelayerKey = {
|
|
312
|
+
// data_id: string;
|
|
313
|
+
// param_choice: number;
|
|
314
|
+
// signatures: string[];
|
|
315
|
+
// urls: string[];
|
|
316
|
+
// };
|
|
317
|
+
// export type RelayerKeys = {
|
|
318
|
+
// response: {
|
|
319
|
+
// fhe_key_info: {
|
|
320
|
+
// fhe_public_key: RelayerKey;
|
|
321
|
+
// fhe_server_key: RelayerKey;
|
|
322
|
+
// }[];
|
|
323
|
+
// verf_public_key: {
|
|
324
|
+
// key_id: string;
|
|
325
|
+
// server_id: number;
|
|
326
|
+
// verf_public_key_address: string;
|
|
327
|
+
// verf_public_key_url: string;
|
|
328
|
+
// }[];
|
|
329
|
+
// crs: {
|
|
330
|
+
// [key: string]: RelayerKeysItem;
|
|
331
|
+
// };
|
|
332
|
+
// };
|
|
333
|
+
// status: string;
|
|
334
|
+
// };
|
|
335
|
+
const keyurlCache = {};
|
|
336
|
+
const getKeysFromRelayer = async (url, publicKeyId) => {
|
|
337
|
+
if (keyurlCache[url]) {
|
|
338
|
+
return keyurlCache[url];
|
|
339
|
+
}
|
|
340
|
+
const data = await fetchRelayerGet('KEY_URL', `${url}/v1/keyurl`);
|
|
341
|
+
try {
|
|
342
|
+
// const response = await fetch(`${url}/v1/keyurl`);
|
|
343
|
+
// if (!response.ok) {
|
|
344
|
+
// await throwRelayerResponseError("KEY_URL", response);
|
|
345
|
+
// }
|
|
346
|
+
//const data: RelayerKeys = await response.json();
|
|
347
|
+
//if (data) {
|
|
348
|
+
let pubKeyUrl;
|
|
349
|
+
// If no publicKeyId is provided, use the first one
|
|
350
|
+
// Warning: if there are multiple keys available, the first one will most likely never be the
|
|
351
|
+
// same between several calls (fetching the infos is non-deterministic)
|
|
352
|
+
if (!publicKeyId) {
|
|
353
|
+
pubKeyUrl = data.response.fhe_key_info[0].fhe_public_key.urls[0];
|
|
354
|
+
publicKeyId = data.response.fhe_key_info[0].fhe_public_key.data_id;
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
// If a publicKeyId is provided, get the corresponding info
|
|
358
|
+
const keyInfo = data.response.fhe_key_info.find((info) => info.fhe_public_key.data_id === publicKeyId);
|
|
359
|
+
if (!keyInfo) {
|
|
360
|
+
throw new Error(`Could not find FHE key info with data_id ${publicKeyId}`);
|
|
130
361
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return result;
|
|
362
|
+
// TODO: Get a given party's public key url instead of the first one
|
|
363
|
+
pubKeyUrl = keyInfo.fhe_public_key.urls[0];
|
|
364
|
+
}
|
|
365
|
+
const publicKeyResponse = await fetch(pubKeyUrl);
|
|
366
|
+
if (!publicKeyResponse.ok) {
|
|
367
|
+
throw new Error(`HTTP error! status: ${publicKeyResponse.status} on ${publicKeyResponse.url}`);
|
|
368
|
+
}
|
|
369
|
+
let publicKey;
|
|
370
|
+
if (typeof publicKeyResponse.bytes === 'function') {
|
|
371
|
+
// bytes is not widely supported yet
|
|
372
|
+
publicKey = await publicKeyResponse.bytes();
|
|
143
373
|
}
|
|
144
374
|
else {
|
|
145
|
-
|
|
375
|
+
publicKey = new Uint8Array(await publicKeyResponse.arrayBuffer());
|
|
376
|
+
}
|
|
377
|
+
const publicParamsUrl = data.response.crs['2048'].urls[0];
|
|
378
|
+
const publicParamsId = data.response.crs['2048'].data_id;
|
|
379
|
+
const publicParams2048Response = await fetch(publicParamsUrl);
|
|
380
|
+
if (!publicParams2048Response.ok) {
|
|
381
|
+
throw new Error(`HTTP error! status: ${publicParams2048Response.status} on ${publicParams2048Response.url}`);
|
|
382
|
+
}
|
|
383
|
+
let publicParams2048;
|
|
384
|
+
if (typeof publicParams2048Response.bytes === 'function') {
|
|
385
|
+
// bytes is not widely supported yet
|
|
386
|
+
publicParams2048 = await publicParams2048Response.bytes();
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
publicParams2048 = new Uint8Array(await publicParams2048Response.arrayBuffer());
|
|
390
|
+
}
|
|
391
|
+
let pub_key;
|
|
392
|
+
try {
|
|
393
|
+
pub_key = TFHE.TfheCompactPublicKey.safe_deserialize(publicKey, SERIALIZED_SIZE_LIMIT_PK);
|
|
394
|
+
}
|
|
395
|
+
catch (e) {
|
|
396
|
+
throw new Error('Invalid public key (deserialization failed)', {
|
|
397
|
+
cause: e,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
let crs;
|
|
401
|
+
try {
|
|
402
|
+
crs = TFHE.CompactPkeCrs.safe_deserialize(new Uint8Array(publicParams2048), SERIALIZED_SIZE_LIMIT_CRS);
|
|
146
403
|
}
|
|
404
|
+
catch (e) {
|
|
405
|
+
throw new Error('Invalid crs (deserialization failed)', {
|
|
406
|
+
cause: e,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
const result = {
|
|
410
|
+
publicKey: pub_key,
|
|
411
|
+
publicKeyId,
|
|
412
|
+
publicParams: {
|
|
413
|
+
2048: {
|
|
414
|
+
publicParams: crs,
|
|
415
|
+
publicParamsId,
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
keyurlCache[url] = result;
|
|
420
|
+
return result;
|
|
421
|
+
// } else {
|
|
422
|
+
// throw new Error('No public key available');
|
|
423
|
+
// }
|
|
147
424
|
}
|
|
148
425
|
catch (e) {
|
|
149
426
|
throw new Error('Impossible to fetch public key: wrong relayer url.', {
|
|
@@ -259,9 +536,6 @@ const NumEncryptedBits = {
|
|
|
259
536
|
6: 128, // euint128
|
|
260
537
|
7: 160, // eaddress
|
|
261
538
|
8: 256, // euint256
|
|
262
|
-
9: 512, // ebytes64
|
|
263
|
-
10: 1024, // ebytes128
|
|
264
|
-
11: 2048, // ebytes256
|
|
265
539
|
};
|
|
266
540
|
function checkEncryptedBits(handles) {
|
|
267
541
|
let total = 0;
|
|
@@ -284,6 +558,8 @@ function checkEncryptedBits(handles) {
|
|
|
284
558
|
return total;
|
|
285
559
|
}
|
|
286
560
|
|
|
561
|
+
// Add type checking
|
|
562
|
+
const getAddress$1 = (value) => ethers.getAddress(value);
|
|
287
563
|
const aclABI$1 = [
|
|
288
564
|
'function persistAllowed(bytes32 handle, address account) view returns (bool)',
|
|
289
565
|
];
|
|
@@ -296,7 +572,7 @@ function formatAccordingToType(decryptedBigInt, type) {
|
|
|
296
572
|
}
|
|
297
573
|
else if (type === 7) {
|
|
298
574
|
// eaddress
|
|
299
|
-
return
|
|
575
|
+
return getAddress$1('0x' + decryptedBigInt.toString(16).padStart(40, '0'));
|
|
300
576
|
}
|
|
301
577
|
else if (type === 9) {
|
|
302
578
|
// ebytes64
|
|
@@ -339,7 +615,16 @@ function checkDeadlineValidity(startTimestamp, durationDays) {
|
|
|
339
615
|
throw Error('User decrypt request has expired');
|
|
340
616
|
}
|
|
341
617
|
}
|
|
342
|
-
const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider) => async (_handles, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays) => {
|
|
618
|
+
const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider, options) => async (_handles, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays) => {
|
|
619
|
+
let pubKey;
|
|
620
|
+
let privKey;
|
|
621
|
+
try {
|
|
622
|
+
pubKey = TKMS.u8vec_to_ml_kem_pke_pk(fromHexString(publicKey));
|
|
623
|
+
privKey = TKMS.u8vec_to_ml_kem_pke_sk(fromHexString(privateKey));
|
|
624
|
+
}
|
|
625
|
+
catch (e) {
|
|
626
|
+
throw new Error('Invalid public or private key', { cause: e });
|
|
627
|
+
}
|
|
343
628
|
// Casting handles if string
|
|
344
629
|
const signatureSanitized = signature.replace(/^(0x)/, '');
|
|
345
630
|
const publicKeySanitized = publicKey.replace(/^(0x)/, '');
|
|
@@ -347,7 +632,7 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
|
|
|
347
632
|
handle: typeof h.handle === 'string'
|
|
348
633
|
? toHexString(fromHexString(h.handle), true)
|
|
349
634
|
: toHexString(h.handle, true),
|
|
350
|
-
contractAddress: h.contractAddress,
|
|
635
|
+
contractAddress: getAddress$1(h.contractAddress),
|
|
351
636
|
}));
|
|
352
637
|
checkEncryptedBits(handles.map((h) => h.handle));
|
|
353
638
|
checkDeadlineValidity(BigInt(startTimestamp), BigInt(durationDays));
|
|
@@ -382,51 +667,12 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
|
|
|
382
667
|
durationDays: durationDays.toString(), // Convert to string
|
|
383
668
|
},
|
|
384
669
|
contractsChainId: chainId.toString(), // Convert to string
|
|
385
|
-
contractAddresses: contractAddresses.map((c) =>
|
|
386
|
-
userAddress:
|
|
670
|
+
contractAddresses: contractAddresses.map((c) => getAddress$1(c)),
|
|
671
|
+
userAddress: getAddress$1(userAddress),
|
|
387
672
|
signature: signatureSanitized,
|
|
388
673
|
publicKey: publicKeySanitized,
|
|
389
674
|
};
|
|
390
|
-
const
|
|
391
|
-
method: 'POST',
|
|
392
|
-
headers: {
|
|
393
|
-
'Content-Type': 'application/json',
|
|
394
|
-
},
|
|
395
|
-
body: JSON.stringify(payloadForRequest),
|
|
396
|
-
};
|
|
397
|
-
let pubKey;
|
|
398
|
-
let privKey;
|
|
399
|
-
try {
|
|
400
|
-
pubKey = TKMS.u8vec_to_ml_kem_pke_pk(fromHexString(publicKey));
|
|
401
|
-
privKey = TKMS.u8vec_to_ml_kem_pke_sk(fromHexString(privateKey));
|
|
402
|
-
}
|
|
403
|
-
catch (e) {
|
|
404
|
-
throw new Error('Invalid public or private key', { cause: e });
|
|
405
|
-
}
|
|
406
|
-
let response;
|
|
407
|
-
let json;
|
|
408
|
-
try {
|
|
409
|
-
response = await fetch(`${relayerUrl}/v1/user-decrypt`, options);
|
|
410
|
-
if (!response.ok) {
|
|
411
|
-
throw new Error(`User decrypt failed: relayer respond with HTTP code ${response.status}`);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
catch (e) {
|
|
415
|
-
throw new Error("User decrypt failed: Relayer didn't respond", {
|
|
416
|
-
cause: e,
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
try {
|
|
420
|
-
json = await response.json();
|
|
421
|
-
}
|
|
422
|
-
catch (e) {
|
|
423
|
-
throw new Error("User decrypt failed: Relayer didn't return a JSON", {
|
|
424
|
-
cause: e,
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
if (json.status === 'failure') {
|
|
428
|
-
throw new Error("User decrypt failed: the user decryption didn't succeed for an unknown reason", { cause: json });
|
|
429
|
-
}
|
|
675
|
+
const json = await fetchRelayerJsonRpcPost('USER_DECRYPT', `${relayerUrl}/v1/user-decrypt`, payloadForRequest, options);
|
|
430
676
|
// assume the KMS Signers have the correct order
|
|
431
677
|
let indexedKmsSigners = kmsSigners.map((signer, index) => {
|
|
432
678
|
return TKMS.new_server_id_addr(index + 1, signer);
|
|
@@ -673,6 +919,8 @@ const computeHandles = (ciphertextWithZKProof, bitwidths, aclContractAddress, ch
|
|
|
673
919
|
return handles;
|
|
674
920
|
};
|
|
675
921
|
|
|
922
|
+
// Add type checking
|
|
923
|
+
const getAddress = (value) => ethers.getAddress(value);
|
|
676
924
|
const currentCiphertextVersion = () => {
|
|
677
925
|
return 0;
|
|
678
926
|
};
|
|
@@ -692,6 +940,20 @@ function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold)
|
|
|
692
940
|
}
|
|
693
941
|
return recoveredAddresses.length >= threshold;
|
|
694
942
|
}
|
|
943
|
+
function isFhevmRelayerInputProofResponse(json) {
|
|
944
|
+
const response = json.response;
|
|
945
|
+
if (typeof response !== 'object' || response === null) {
|
|
946
|
+
return false;
|
|
947
|
+
}
|
|
948
|
+
if (!('handles' in response && Array.isArray(response.handles))) {
|
|
949
|
+
return false;
|
|
950
|
+
}
|
|
951
|
+
if (!('signatures' in response && Array.isArray(response.signatures))) {
|
|
952
|
+
return false;
|
|
953
|
+
}
|
|
954
|
+
return (response.signatures.every((s) => typeof s === 'string') &&
|
|
955
|
+
response.handles.every((h) => typeof h === 'string'));
|
|
956
|
+
}
|
|
695
957
|
const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerUrl, tfheCompactPublicKey, publicParams, coprocessorSigners, thresholdCoprocessorSigners) => (contractAddress, userAddress) => {
|
|
696
958
|
if (!ethers.isAddress(contractAddress)) {
|
|
697
959
|
throw new Error('Contract address is not a valid address.');
|
|
@@ -737,18 +999,6 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
|
|
|
737
999
|
input.add256(value);
|
|
738
1000
|
return this;
|
|
739
1001
|
},
|
|
740
|
-
addBytes64(value) {
|
|
741
|
-
input.addBytes64(value);
|
|
742
|
-
return this;
|
|
743
|
-
},
|
|
744
|
-
addBytes128(value) {
|
|
745
|
-
input.addBytes128(value);
|
|
746
|
-
return this;
|
|
747
|
-
},
|
|
748
|
-
addBytes256(value) {
|
|
749
|
-
input.addBytes256(value);
|
|
750
|
-
return this;
|
|
751
|
-
},
|
|
752
1002
|
addAddress(value) {
|
|
753
1003
|
input.addAddress(value);
|
|
754
1004
|
return this;
|
|
@@ -756,43 +1006,18 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
|
|
|
756
1006
|
getBits() {
|
|
757
1007
|
return input.getBits();
|
|
758
1008
|
},
|
|
759
|
-
encrypt: async () => {
|
|
1009
|
+
encrypt: async (options) => {
|
|
760
1010
|
const bits = input.getBits();
|
|
761
1011
|
const ciphertext = input.encrypt();
|
|
762
|
-
// https://github.com/zama-ai/fhevm-relayer/blob/978b08f62de060a9b50d2c6cc19fd71b5fb8d873/src/input_http_listener.rs#L13C1-L22C1
|
|
763
1012
|
const payload = {
|
|
764
|
-
contractAddress:
|
|
765
|
-
userAddress:
|
|
1013
|
+
contractAddress: getAddress(contractAddress),
|
|
1014
|
+
userAddress: getAddress(userAddress),
|
|
766
1015
|
ciphertextWithInputVerification: toHexString(ciphertext),
|
|
767
|
-
contractChainId: '0x' + chainId.toString(16),
|
|
1016
|
+
contractChainId: ('0x' + chainId.toString(16)),
|
|
768
1017
|
};
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
'Content-Type': 'application/json',
|
|
773
|
-
},
|
|
774
|
-
body: JSON.stringify(payload),
|
|
775
|
-
};
|
|
776
|
-
const url = `${relayerUrl}/v1/input-proof`;
|
|
777
|
-
let json;
|
|
778
|
-
try {
|
|
779
|
-
const response = await fetch(url, options);
|
|
780
|
-
if (!response.ok) {
|
|
781
|
-
throw new Error(`Relayer didn't response correctly. Bad status ${response.statusText}. Content: ${await response.text()}`);
|
|
782
|
-
}
|
|
783
|
-
try {
|
|
784
|
-
json = await response.json();
|
|
785
|
-
}
|
|
786
|
-
catch (e) {
|
|
787
|
-
throw new Error("Relayer didn't response correctly. Bad JSON.", {
|
|
788
|
-
cause: e,
|
|
789
|
-
});
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
catch (e) {
|
|
793
|
-
throw new Error("Relayer didn't response correctly.", {
|
|
794
|
-
cause: e,
|
|
795
|
-
});
|
|
1018
|
+
const json = await fetchRelayerJsonRpcPost('INPUT_PROOF', `${relayerUrl}/v1/input-proof`, payload, options);
|
|
1019
|
+
if (!isFhevmRelayerInputProofResponse(json)) {
|
|
1020
|
+
throwRelayerInternalError('INPUT_PROOF', json);
|
|
796
1021
|
}
|
|
797
1022
|
const handles = computeHandles(ciphertext, bits, aclContractAddress, chainId, currentCiphertextVersion());
|
|
798
1023
|
// Note that the hex strings returned by the relayer do have have the 0x prefix
|
|
@@ -884,9 +1109,6 @@ const CiphertextType = {
|
|
|
884
1109
|
6: 'uint256',
|
|
885
1110
|
7: 'address',
|
|
886
1111
|
8: 'uint256',
|
|
887
|
-
9: 'bytes',
|
|
888
|
-
10: 'bytes',
|
|
889
|
-
11: 'bytes',
|
|
890
1112
|
};
|
|
891
1113
|
function deserializeDecryptedResult(handles, decryptedResult) {
|
|
892
1114
|
let typesList = [];
|
|
@@ -911,7 +1133,7 @@ function deserializeDecryptedResult(handles, decryptedResult) {
|
|
|
911
1133
|
handles.forEach((handle, idx) => (results[handle] = rawValues[idx]));
|
|
912
1134
|
return results;
|
|
913
1135
|
}
|
|
914
|
-
const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider) => async (_handles) => {
|
|
1136
|
+
const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider, options) => async (_handles) => {
|
|
915
1137
|
const acl = new ethers.ethers.Contract(aclContractAddress, aclABI, provider);
|
|
916
1138
|
let handles;
|
|
917
1139
|
try {
|
|
@@ -929,51 +1151,12 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
|
|
|
929
1151
|
catch (e) {
|
|
930
1152
|
throw e;
|
|
931
1153
|
}
|
|
932
|
-
const verifications = handles.map(async (ctHandle) => {
|
|
933
|
-
const isAllowedForDecryption = await acl.isAllowedForDecryption(ctHandle);
|
|
934
|
-
if (!isAllowedForDecryption) {
|
|
935
|
-
throw new Error(`Handle ${ctHandle} is not allowed for public decryption!`);
|
|
936
|
-
}
|
|
937
|
-
});
|
|
938
|
-
await Promise.all(verifications).catch((e) => {
|
|
939
|
-
throw e;
|
|
940
|
-
});
|
|
941
1154
|
// check 2048 bits limit
|
|
942
1155
|
checkEncryptedBits(handles);
|
|
943
1156
|
const payloadForRequest = {
|
|
944
1157
|
ciphertextHandles: handles,
|
|
945
1158
|
};
|
|
946
|
-
const
|
|
947
|
-
method: 'POST',
|
|
948
|
-
headers: {
|
|
949
|
-
'Content-Type': 'application/json',
|
|
950
|
-
},
|
|
951
|
-
body: JSON.stringify(payloadForRequest),
|
|
952
|
-
};
|
|
953
|
-
let response;
|
|
954
|
-
let json;
|
|
955
|
-
try {
|
|
956
|
-
response = await fetch(`${relayerUrl}/v1/public-decrypt`, options);
|
|
957
|
-
if (!response.ok) {
|
|
958
|
-
throw new Error(`Public decrypt failed: relayer respond with HTTP code ${response.status}`);
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
catch (e) {
|
|
962
|
-
throw new Error("Public decrypt failed: Relayer didn't respond", {
|
|
963
|
-
cause: e,
|
|
964
|
-
});
|
|
965
|
-
}
|
|
966
|
-
try {
|
|
967
|
-
json = await response.json();
|
|
968
|
-
}
|
|
969
|
-
catch (e) {
|
|
970
|
-
throw new Error("Public decrypt failed: Relayer didn't return a JSON", {
|
|
971
|
-
cause: e,
|
|
972
|
-
});
|
|
973
|
-
}
|
|
974
|
-
if (json.status === 'failure') {
|
|
975
|
-
throw new Error("Public decrypt failed: the public decrypt didn't succeed for an unknown reason", { cause: json });
|
|
976
|
-
}
|
|
1159
|
+
const json = await fetchRelayerJsonRpcPost('PUBLIC_DECRYPT', `${relayerUrl}/v1/public-decrypt`, payloadForRequest, options);
|
|
977
1160
|
// verify signatures on decryption:
|
|
978
1161
|
const domain = {
|
|
979
1162
|
name: 'Decryption',
|
|
@@ -1208,3 +1391,5 @@ exports.createInstance = createInstance;
|
|
|
1208
1391
|
exports.createTfheKeypair = createTfheKeypair;
|
|
1209
1392
|
exports.createTfhePublicKey = createTfhePublicKey;
|
|
1210
1393
|
exports.generateKeypair = generateKeypair;
|
|
1394
|
+
exports.getErrorCauseCode = getErrorCauseCode;
|
|
1395
|
+
exports.getErrorCauseStatus = getErrorCauseStatus;
|