@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.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as TFHEPkg from 'node-tfhe';
|
|
2
2
|
import { ShortintParameters, ShortintParametersName, ShortintCompactPublicKeyEncryptionParameters, ShortintCompactPublicKeyEncryptionParametersName, TfheConfigBuilder, TfheClientKey, TfheCompactPublicKey, CompactPkeCrs } from 'node-tfhe';
|
|
3
3
|
import * as TKMSPkg from 'node-tkms';
|
|
4
|
-
import { JsonRpcProvider, BrowserProvider, Contract, ethers, getAddress, isAddress, AbiCoder } from 'ethers';
|
|
4
|
+
import { JsonRpcProvider, BrowserProvider, Contract, ethers, getAddress as getAddress$2, isAddress, AbiCoder } from 'ethers';
|
|
5
5
|
import createHash from 'keccak';
|
|
6
6
|
import fetchRetry from 'fetch-retry';
|
|
7
7
|
|
|
@@ -23,7 +23,9 @@ const fromHexString = (hexString) => {
|
|
|
23
23
|
return new Uint8Array();
|
|
24
24
|
return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
|
|
25
25
|
};
|
|
26
|
-
|
|
26
|
+
function toHexString(bytes, with0x = false) {
|
|
27
|
+
return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
|
|
28
|
+
}
|
|
27
29
|
const bytesToBigInt = function (byteArray) {
|
|
28
30
|
if (!byteArray || byteArray?.length === 0) {
|
|
29
31
|
return BigInt(0);
|
|
@@ -34,95 +36,370 @@ const bytesToBigInt = function (byteArray) {
|
|
|
34
36
|
return BigInt(`0x${hex}`);
|
|
35
37
|
};
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return keyurlCache[url];
|
|
39
|
+
function getErrorCause(e) {
|
|
40
|
+
if (e instanceof Error && typeof e.cause === 'object' && e.cause !== null) {
|
|
41
|
+
return e.cause;
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
else {
|
|
76
|
-
publicKey = new Uint8Array(await publicKeyResponse.arrayBuffer());
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
function getErrorCauseCode(e) {
|
|
46
|
+
const cause = getErrorCause(e);
|
|
47
|
+
if (!cause || !('code' in cause) || !cause.code) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
if (typeof cause.code !== 'string') {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return cause.code;
|
|
54
|
+
}
|
|
55
|
+
function getErrorCauseStatus(e) {
|
|
56
|
+
const cause = getErrorCause(e);
|
|
57
|
+
if (!cause || !('status' in cause) || cause.status === undefined) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
if (typeof cause.status !== 'number') {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return cause.status;
|
|
64
|
+
}
|
|
65
|
+
async function throwRelayerResponseError(operation, response) {
|
|
66
|
+
let message;
|
|
67
|
+
// Special case for 429
|
|
68
|
+
if (response.status === 429) {
|
|
69
|
+
message = `Relayer rate limit exceeded: Please wait and try again later.`;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
switch (operation) {
|
|
73
|
+
case 'PUBLIC_DECRYPT': {
|
|
74
|
+
message = `Public decrypt failed: relayer respond with HTTP code ${response.status}`;
|
|
75
|
+
break;
|
|
77
76
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!publicParams2048Response.ok) {
|
|
82
|
-
throw new Error(`HTTP error! status: ${publicParams2048Response.status} on ${publicParams2048Response.url}`);
|
|
77
|
+
case 'USER_DECRYPT': {
|
|
78
|
+
message = `User decrypt failed: relayer respond with HTTP code ${response.status}`;
|
|
79
|
+
break;
|
|
83
80
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
publicParams2048 = await publicParams2048Response.bytes();
|
|
81
|
+
case 'KEY_URL': {
|
|
82
|
+
message = `HTTP error! status: ${response.status}`;
|
|
83
|
+
break;
|
|
88
84
|
}
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
default: {
|
|
86
|
+
const responseText = await response.text();
|
|
87
|
+
message = `Relayer didn't response correctly. Bad status ${response.statusText}. Content: ${responseText}`;
|
|
88
|
+
break;
|
|
91
89
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const cause = {
|
|
93
|
+
code: 'RELAYER_FETCH_ERROR',
|
|
94
|
+
operation,
|
|
95
|
+
status: response.status,
|
|
96
|
+
statusText: response.statusText,
|
|
97
|
+
url: response.url,
|
|
98
|
+
};
|
|
99
|
+
throw new Error(message, {
|
|
100
|
+
cause,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function throwRelayerJSONError(operation, error) {
|
|
104
|
+
let message;
|
|
105
|
+
switch (operation) {
|
|
106
|
+
case 'PUBLIC_DECRYPT': {
|
|
107
|
+
message = "Public decrypt failed: Relayer didn't return a JSON";
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'USER_DECRYPT': {
|
|
111
|
+
message = "User decrypt failed: Relayer didn't return a JSON";
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
default: {
|
|
115
|
+
message = "Relayer didn't return a JSON";
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const cause = {
|
|
120
|
+
code: 'RELAYER_NO_JSON_ERROR',
|
|
121
|
+
operation,
|
|
122
|
+
error,
|
|
123
|
+
};
|
|
124
|
+
throw new Error(message, {
|
|
125
|
+
cause,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function throwRelayerUnexpectedJSONError(operation, error) {
|
|
129
|
+
let message;
|
|
130
|
+
switch (operation) {
|
|
131
|
+
case 'PUBLIC_DECRYPT': {
|
|
132
|
+
message =
|
|
133
|
+
'Public decrypt failed: Relayer returned an unexpected JSON response';
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case 'USER_DECRYPT': {
|
|
137
|
+
message =
|
|
138
|
+
'User decrypt failed: Relayer returned an unexpected JSON response';
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
default: {
|
|
142
|
+
message = 'Relayer returned an unexpected JSON response';
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const cause = {
|
|
147
|
+
code: 'RELAYER_UNEXPECTED_JSON_ERROR',
|
|
148
|
+
operation,
|
|
149
|
+
error,
|
|
150
|
+
};
|
|
151
|
+
throw new Error(message, {
|
|
152
|
+
cause,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function throwRelayerInternalError(operation, json) {
|
|
156
|
+
let message;
|
|
157
|
+
switch (operation) {
|
|
158
|
+
case 'PUBLIC_DECRYPT': {
|
|
159
|
+
message =
|
|
160
|
+
"Pulbic decrypt failed: the public decryption didn't succeed for an unknown reason";
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
case 'USER_DECRYPT': {
|
|
164
|
+
message =
|
|
165
|
+
"User decrypt failed: the user decryption didn't succeed for an unknown reason";
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
default: {
|
|
169
|
+
message = "Relayer didn't response correctly.";
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const cause = {
|
|
174
|
+
code: 'RELAYER_INTERNAL_ERROR',
|
|
175
|
+
operation,
|
|
176
|
+
error: json,
|
|
177
|
+
};
|
|
178
|
+
throw new Error(message, {
|
|
179
|
+
cause,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function throwRelayerUnknownError(operation, error, message) {
|
|
183
|
+
if (!message) {
|
|
184
|
+
switch (operation) {
|
|
185
|
+
case 'PUBLIC_DECRYPT': {
|
|
186
|
+
message = "Public decrypt failed: Relayer didn't respond";
|
|
187
|
+
break;
|
|
95
188
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
189
|
+
case 'USER_DECRYPT': {
|
|
190
|
+
message = "User decrypt failed: Relayer didn't respond";
|
|
191
|
+
break;
|
|
100
192
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
193
|
+
default: {
|
|
194
|
+
message = "Relayer didn't response correctly. Bad JSON.";
|
|
195
|
+
break;
|
|
104
196
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const cause = {
|
|
200
|
+
code: 'RELAYER_UNKNOWN_ERROR',
|
|
201
|
+
operation,
|
|
202
|
+
error,
|
|
203
|
+
};
|
|
204
|
+
throw new Error(message ?? "Relayer didn't response correctly.", {
|
|
205
|
+
cause,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function assertIsRelayerFetchResponseJson(json) {
|
|
210
|
+
if (!json || typeof json !== 'object') {
|
|
211
|
+
throw new Error('Unexpected response JSON.');
|
|
212
|
+
}
|
|
213
|
+
if (!('response' in json &&
|
|
214
|
+
json.response !== null &&
|
|
215
|
+
json.response !== undefined)) {
|
|
216
|
+
throw new Error("Unexpected response JSON format: missing 'response' property.");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function fetchRelayerJsonRpcPost(relayerOperation, url, payload, options) {
|
|
220
|
+
const init = {
|
|
221
|
+
method: 'POST',
|
|
222
|
+
headers: {
|
|
223
|
+
'Content-Type': 'application/json',
|
|
224
|
+
...(options?.apiKey && { 'x-api-key': options.apiKey }),
|
|
225
|
+
},
|
|
226
|
+
body: JSON.stringify(payload),
|
|
227
|
+
};
|
|
228
|
+
let response;
|
|
229
|
+
let json;
|
|
230
|
+
try {
|
|
231
|
+
response = await fetch(url, init);
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
throwRelayerUnknownError(relayerOperation, e);
|
|
235
|
+
}
|
|
236
|
+
if (!response.ok) {
|
|
237
|
+
await throwRelayerResponseError(relayerOperation, response);
|
|
238
|
+
}
|
|
239
|
+
let parsed;
|
|
240
|
+
try {
|
|
241
|
+
parsed = await response.json();
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
throwRelayerJSONError(relayerOperation, e);
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
assertIsRelayerFetchResponseJson(parsed);
|
|
248
|
+
json = parsed;
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
throwRelayerUnexpectedJSONError(relayerOperation, e);
|
|
252
|
+
}
|
|
253
|
+
return json;
|
|
254
|
+
}
|
|
255
|
+
async function fetchRelayerGet(relayerOperation, url) {
|
|
256
|
+
let response;
|
|
257
|
+
let json;
|
|
258
|
+
try {
|
|
259
|
+
response = await fetch(url);
|
|
260
|
+
}
|
|
261
|
+
catch (e) {
|
|
262
|
+
throwRelayerUnknownError(relayerOperation, e);
|
|
263
|
+
}
|
|
264
|
+
if (!response.ok) {
|
|
265
|
+
await throwRelayerResponseError(relayerOperation, response);
|
|
266
|
+
}
|
|
267
|
+
let parsed;
|
|
268
|
+
try {
|
|
269
|
+
parsed = await response.json();
|
|
270
|
+
}
|
|
271
|
+
catch (e) {
|
|
272
|
+
throwRelayerJSONError(relayerOperation, e);
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
assertIsRelayerFetchResponseJson(parsed);
|
|
276
|
+
json = parsed;
|
|
277
|
+
}
|
|
278
|
+
catch (e) {
|
|
279
|
+
throwRelayerUnexpectedJSONError(relayerOperation, e);
|
|
280
|
+
}
|
|
281
|
+
return json;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// export type RelayerKeysItem = {
|
|
285
|
+
// data_id: string;
|
|
286
|
+
// param_choice: number;
|
|
287
|
+
// urls: string[];
|
|
288
|
+
// signatures: string[];
|
|
289
|
+
// };
|
|
290
|
+
// export type RelayerKey = {
|
|
291
|
+
// data_id: string;
|
|
292
|
+
// param_choice: number;
|
|
293
|
+
// signatures: string[];
|
|
294
|
+
// urls: string[];
|
|
295
|
+
// };
|
|
296
|
+
// export type RelayerKeys = {
|
|
297
|
+
// response: {
|
|
298
|
+
// fhe_key_info: {
|
|
299
|
+
// fhe_public_key: RelayerKey;
|
|
300
|
+
// fhe_server_key: RelayerKey;
|
|
301
|
+
// }[];
|
|
302
|
+
// verf_public_key: {
|
|
303
|
+
// key_id: string;
|
|
304
|
+
// server_id: number;
|
|
305
|
+
// verf_public_key_address: string;
|
|
306
|
+
// verf_public_key_url: string;
|
|
307
|
+
// }[];
|
|
308
|
+
// crs: {
|
|
309
|
+
// [key: string]: RelayerKeysItem;
|
|
310
|
+
// };
|
|
311
|
+
// };
|
|
312
|
+
// status: string;
|
|
313
|
+
// };
|
|
314
|
+
const keyurlCache = {};
|
|
315
|
+
const getKeysFromRelayer = async (url, publicKeyId) => {
|
|
316
|
+
if (keyurlCache[url]) {
|
|
317
|
+
return keyurlCache[url];
|
|
318
|
+
}
|
|
319
|
+
const data = await fetchRelayerGet('KEY_URL', `${url}/v1/keyurl`);
|
|
320
|
+
try {
|
|
321
|
+
// const response = await fetch(`${url}/v1/keyurl`);
|
|
322
|
+
// if (!response.ok) {
|
|
323
|
+
// await throwRelayerResponseError("KEY_URL", response);
|
|
324
|
+
// }
|
|
325
|
+
//const data: RelayerKeys = await response.json();
|
|
326
|
+
//if (data) {
|
|
327
|
+
let pubKeyUrl;
|
|
328
|
+
// If no publicKeyId is provided, use the first one
|
|
329
|
+
// Warning: if there are multiple keys available, the first one will most likely never be the
|
|
330
|
+
// same between several calls (fetching the infos is non-deterministic)
|
|
331
|
+
if (!publicKeyId) {
|
|
332
|
+
pubKeyUrl = data.response.fhe_key_info[0].fhe_public_key.urls[0];
|
|
333
|
+
publicKeyId = data.response.fhe_key_info[0].fhe_public_key.data_id;
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
// If a publicKeyId is provided, get the corresponding info
|
|
337
|
+
const keyInfo = data.response.fhe_key_info.find((info) => info.fhe_public_key.data_id === publicKeyId);
|
|
338
|
+
if (!keyInfo) {
|
|
339
|
+
throw new Error(`Could not find FHE key info with data_id ${publicKeyId}`);
|
|
109
340
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return result;
|
|
341
|
+
// TODO: Get a given party's public key url instead of the first one
|
|
342
|
+
pubKeyUrl = keyInfo.fhe_public_key.urls[0];
|
|
343
|
+
}
|
|
344
|
+
const publicKeyResponse = await fetch(pubKeyUrl);
|
|
345
|
+
if (!publicKeyResponse.ok) {
|
|
346
|
+
throw new Error(`HTTP error! status: ${publicKeyResponse.status} on ${publicKeyResponse.url}`);
|
|
347
|
+
}
|
|
348
|
+
let publicKey;
|
|
349
|
+
if (typeof publicKeyResponse.bytes === 'function') {
|
|
350
|
+
// bytes is not widely supported yet
|
|
351
|
+
publicKey = await publicKeyResponse.bytes();
|
|
122
352
|
}
|
|
123
353
|
else {
|
|
124
|
-
|
|
354
|
+
publicKey = new Uint8Array(await publicKeyResponse.arrayBuffer());
|
|
355
|
+
}
|
|
356
|
+
const publicParamsUrl = data.response.crs['2048'].urls[0];
|
|
357
|
+
const publicParamsId = data.response.crs['2048'].data_id;
|
|
358
|
+
const publicParams2048Response = await fetch(publicParamsUrl);
|
|
359
|
+
if (!publicParams2048Response.ok) {
|
|
360
|
+
throw new Error(`HTTP error! status: ${publicParams2048Response.status} on ${publicParams2048Response.url}`);
|
|
361
|
+
}
|
|
362
|
+
let publicParams2048;
|
|
363
|
+
if (typeof publicParams2048Response.bytes === 'function') {
|
|
364
|
+
// bytes is not widely supported yet
|
|
365
|
+
publicParams2048 = await publicParams2048Response.bytes();
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
publicParams2048 = new Uint8Array(await publicParams2048Response.arrayBuffer());
|
|
369
|
+
}
|
|
370
|
+
let pub_key;
|
|
371
|
+
try {
|
|
372
|
+
pub_key = TFHE.TfheCompactPublicKey.safe_deserialize(publicKey, SERIALIZED_SIZE_LIMIT_PK);
|
|
373
|
+
}
|
|
374
|
+
catch (e) {
|
|
375
|
+
throw new Error('Invalid public key (deserialization failed)', {
|
|
376
|
+
cause: e,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
let crs;
|
|
380
|
+
try {
|
|
381
|
+
crs = TFHE.CompactPkeCrs.safe_deserialize(new Uint8Array(publicParams2048), SERIALIZED_SIZE_LIMIT_CRS);
|
|
125
382
|
}
|
|
383
|
+
catch (e) {
|
|
384
|
+
throw new Error('Invalid crs (deserialization failed)', {
|
|
385
|
+
cause: e,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
const result = {
|
|
389
|
+
publicKey: pub_key,
|
|
390
|
+
publicKeyId,
|
|
391
|
+
publicParams: {
|
|
392
|
+
2048: {
|
|
393
|
+
publicParams: crs,
|
|
394
|
+
publicParamsId,
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
keyurlCache[url] = result;
|
|
399
|
+
return result;
|
|
400
|
+
// } else {
|
|
401
|
+
// throw new Error('No public key available');
|
|
402
|
+
// }
|
|
126
403
|
}
|
|
127
404
|
catch (e) {
|
|
128
405
|
throw new Error('Impossible to fetch public key: wrong relayer url.', {
|
|
@@ -238,9 +515,6 @@ const NumEncryptedBits = {
|
|
|
238
515
|
6: 128, // euint128
|
|
239
516
|
7: 160, // eaddress
|
|
240
517
|
8: 256, // euint256
|
|
241
|
-
9: 512, // ebytes64
|
|
242
|
-
10: 1024, // ebytes128
|
|
243
|
-
11: 2048, // ebytes256
|
|
244
518
|
};
|
|
245
519
|
function checkEncryptedBits(handles) {
|
|
246
520
|
let total = 0;
|
|
@@ -263,6 +537,8 @@ function checkEncryptedBits(handles) {
|
|
|
263
537
|
return total;
|
|
264
538
|
}
|
|
265
539
|
|
|
540
|
+
// Add type checking
|
|
541
|
+
const getAddress$1 = (value) => getAddress$2(value);
|
|
266
542
|
const aclABI$1 = [
|
|
267
543
|
'function persistAllowed(bytes32 handle, address account) view returns (bool)',
|
|
268
544
|
];
|
|
@@ -275,7 +551,7 @@ function formatAccordingToType(decryptedBigInt, type) {
|
|
|
275
551
|
}
|
|
276
552
|
else if (type === 7) {
|
|
277
553
|
// eaddress
|
|
278
|
-
return getAddress('0x' + decryptedBigInt.toString(16).padStart(40, '0'));
|
|
554
|
+
return getAddress$1('0x' + decryptedBigInt.toString(16).padStart(40, '0'));
|
|
279
555
|
}
|
|
280
556
|
else if (type === 9) {
|
|
281
557
|
// ebytes64
|
|
@@ -318,7 +594,16 @@ function checkDeadlineValidity(startTimestamp, durationDays) {
|
|
|
318
594
|
throw Error('User decrypt request has expired');
|
|
319
595
|
}
|
|
320
596
|
}
|
|
321
|
-
const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider) => async (_handles, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays) => {
|
|
597
|
+
const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider, options) => async (_handles, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays) => {
|
|
598
|
+
let pubKey;
|
|
599
|
+
let privKey;
|
|
600
|
+
try {
|
|
601
|
+
pubKey = TKMS.u8vec_to_ml_kem_pke_pk(fromHexString(publicKey));
|
|
602
|
+
privKey = TKMS.u8vec_to_ml_kem_pke_sk(fromHexString(privateKey));
|
|
603
|
+
}
|
|
604
|
+
catch (e) {
|
|
605
|
+
throw new Error('Invalid public or private key', { cause: e });
|
|
606
|
+
}
|
|
322
607
|
// Casting handles if string
|
|
323
608
|
const signatureSanitized = signature.replace(/^(0x)/, '');
|
|
324
609
|
const publicKeySanitized = publicKey.replace(/^(0x)/, '');
|
|
@@ -326,7 +611,7 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
|
|
|
326
611
|
handle: typeof h.handle === 'string'
|
|
327
612
|
? toHexString(fromHexString(h.handle), true)
|
|
328
613
|
: toHexString(h.handle, true),
|
|
329
|
-
contractAddress: h.contractAddress,
|
|
614
|
+
contractAddress: getAddress$1(h.contractAddress),
|
|
330
615
|
}));
|
|
331
616
|
checkEncryptedBits(handles.map((h) => h.handle));
|
|
332
617
|
checkDeadlineValidity(BigInt(startTimestamp), BigInt(durationDays));
|
|
@@ -361,51 +646,12 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
|
|
|
361
646
|
durationDays: durationDays.toString(), // Convert to string
|
|
362
647
|
},
|
|
363
648
|
contractsChainId: chainId.toString(), // Convert to string
|
|
364
|
-
contractAddresses: contractAddresses.map((c) => getAddress(c)),
|
|
365
|
-
userAddress: getAddress(userAddress),
|
|
649
|
+
contractAddresses: contractAddresses.map((c) => getAddress$1(c)),
|
|
650
|
+
userAddress: getAddress$1(userAddress),
|
|
366
651
|
signature: signatureSanitized,
|
|
367
652
|
publicKey: publicKeySanitized,
|
|
368
653
|
};
|
|
369
|
-
const
|
|
370
|
-
method: 'POST',
|
|
371
|
-
headers: {
|
|
372
|
-
'Content-Type': 'application/json',
|
|
373
|
-
},
|
|
374
|
-
body: JSON.stringify(payloadForRequest),
|
|
375
|
-
};
|
|
376
|
-
let pubKey;
|
|
377
|
-
let privKey;
|
|
378
|
-
try {
|
|
379
|
-
pubKey = TKMS.u8vec_to_ml_kem_pke_pk(fromHexString(publicKey));
|
|
380
|
-
privKey = TKMS.u8vec_to_ml_kem_pke_sk(fromHexString(privateKey));
|
|
381
|
-
}
|
|
382
|
-
catch (e) {
|
|
383
|
-
throw new Error('Invalid public or private key', { cause: e });
|
|
384
|
-
}
|
|
385
|
-
let response;
|
|
386
|
-
let json;
|
|
387
|
-
try {
|
|
388
|
-
response = await fetch(`${relayerUrl}/v1/user-decrypt`, options);
|
|
389
|
-
if (!response.ok) {
|
|
390
|
-
throw new Error(`User decrypt failed: relayer respond with HTTP code ${response.status}`);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
catch (e) {
|
|
394
|
-
throw new Error("User decrypt failed: Relayer didn't respond", {
|
|
395
|
-
cause: e,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
try {
|
|
399
|
-
json = await response.json();
|
|
400
|
-
}
|
|
401
|
-
catch (e) {
|
|
402
|
-
throw new Error("User decrypt failed: Relayer didn't return a JSON", {
|
|
403
|
-
cause: e,
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
if (json.status === 'failure') {
|
|
407
|
-
throw new Error("User decrypt failed: the user decryption didn't succeed for an unknown reason", { cause: json });
|
|
408
|
-
}
|
|
654
|
+
const json = await fetchRelayerJsonRpcPost('USER_DECRYPT', `${relayerUrl}/v1/user-decrypt`, payloadForRequest, options);
|
|
409
655
|
// assume the KMS Signers have the correct order
|
|
410
656
|
let indexedKmsSigners = kmsSigners.map((signer, index) => {
|
|
411
657
|
return TKMS.new_server_id_addr(index + 1, signer);
|
|
@@ -652,6 +898,8 @@ const computeHandles = (ciphertextWithZKProof, bitwidths, aclContractAddress, ch
|
|
|
652
898
|
return handles;
|
|
653
899
|
};
|
|
654
900
|
|
|
901
|
+
// Add type checking
|
|
902
|
+
const getAddress = (value) => getAddress$2(value);
|
|
655
903
|
const currentCiphertextVersion = () => {
|
|
656
904
|
return 0;
|
|
657
905
|
};
|
|
@@ -671,6 +919,20 @@ function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold)
|
|
|
671
919
|
}
|
|
672
920
|
return recoveredAddresses.length >= threshold;
|
|
673
921
|
}
|
|
922
|
+
function isFhevmRelayerInputProofResponse(json) {
|
|
923
|
+
const response = json.response;
|
|
924
|
+
if (typeof response !== 'object' || response === null) {
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
if (!('handles' in response && Array.isArray(response.handles))) {
|
|
928
|
+
return false;
|
|
929
|
+
}
|
|
930
|
+
if (!('signatures' in response && Array.isArray(response.signatures))) {
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
return (response.signatures.every((s) => typeof s === 'string') &&
|
|
934
|
+
response.handles.every((h) => typeof h === 'string'));
|
|
935
|
+
}
|
|
674
936
|
const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerUrl, tfheCompactPublicKey, publicParams, coprocessorSigners, thresholdCoprocessorSigners) => (contractAddress, userAddress) => {
|
|
675
937
|
if (!isAddress(contractAddress)) {
|
|
676
938
|
throw new Error('Contract address is not a valid address.');
|
|
@@ -716,18 +978,6 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
|
|
|
716
978
|
input.add256(value);
|
|
717
979
|
return this;
|
|
718
980
|
},
|
|
719
|
-
addBytes64(value) {
|
|
720
|
-
input.addBytes64(value);
|
|
721
|
-
return this;
|
|
722
|
-
},
|
|
723
|
-
addBytes128(value) {
|
|
724
|
-
input.addBytes128(value);
|
|
725
|
-
return this;
|
|
726
|
-
},
|
|
727
|
-
addBytes256(value) {
|
|
728
|
-
input.addBytes256(value);
|
|
729
|
-
return this;
|
|
730
|
-
},
|
|
731
981
|
addAddress(value) {
|
|
732
982
|
input.addAddress(value);
|
|
733
983
|
return this;
|
|
@@ -735,43 +985,18 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
|
|
|
735
985
|
getBits() {
|
|
736
986
|
return input.getBits();
|
|
737
987
|
},
|
|
738
|
-
encrypt: async () => {
|
|
988
|
+
encrypt: async (options) => {
|
|
739
989
|
const bits = input.getBits();
|
|
740
990
|
const ciphertext = input.encrypt();
|
|
741
|
-
// https://github.com/zama-ai/fhevm-relayer/blob/978b08f62de060a9b50d2c6cc19fd71b5fb8d873/src/input_http_listener.rs#L13C1-L22C1
|
|
742
991
|
const payload = {
|
|
743
992
|
contractAddress: getAddress(contractAddress),
|
|
744
993
|
userAddress: getAddress(userAddress),
|
|
745
994
|
ciphertextWithInputVerification: toHexString(ciphertext),
|
|
746
|
-
contractChainId: '0x' + chainId.toString(16),
|
|
995
|
+
contractChainId: ('0x' + chainId.toString(16)),
|
|
747
996
|
};
|
|
748
|
-
const
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
'Content-Type': 'application/json',
|
|
752
|
-
},
|
|
753
|
-
body: JSON.stringify(payload),
|
|
754
|
-
};
|
|
755
|
-
const url = `${relayerUrl}/v1/input-proof`;
|
|
756
|
-
let json;
|
|
757
|
-
try {
|
|
758
|
-
const response = await fetch(url, options);
|
|
759
|
-
if (!response.ok) {
|
|
760
|
-
throw new Error(`Relayer didn't response correctly. Bad status ${response.statusText}. Content: ${await response.text()}`);
|
|
761
|
-
}
|
|
762
|
-
try {
|
|
763
|
-
json = await response.json();
|
|
764
|
-
}
|
|
765
|
-
catch (e) {
|
|
766
|
-
throw new Error("Relayer didn't response correctly. Bad JSON.", {
|
|
767
|
-
cause: e,
|
|
768
|
-
});
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
catch (e) {
|
|
772
|
-
throw new Error("Relayer didn't response correctly.", {
|
|
773
|
-
cause: e,
|
|
774
|
-
});
|
|
997
|
+
const json = await fetchRelayerJsonRpcPost('INPUT_PROOF', `${relayerUrl}/v1/input-proof`, payload, options);
|
|
998
|
+
if (!isFhevmRelayerInputProofResponse(json)) {
|
|
999
|
+
throwRelayerInternalError('INPUT_PROOF', json);
|
|
775
1000
|
}
|
|
776
1001
|
const handles = computeHandles(ciphertext, bits, aclContractAddress, chainId, currentCiphertextVersion());
|
|
777
1002
|
// Note that the hex strings returned by the relayer do have have the 0x prefix
|
|
@@ -863,9 +1088,6 @@ const CiphertextType = {
|
|
|
863
1088
|
6: 'uint256',
|
|
864
1089
|
7: 'address',
|
|
865
1090
|
8: 'uint256',
|
|
866
|
-
9: 'bytes',
|
|
867
|
-
10: 'bytes',
|
|
868
|
-
11: 'bytes',
|
|
869
1091
|
};
|
|
870
1092
|
function deserializeDecryptedResult(handles, decryptedResult) {
|
|
871
1093
|
let typesList = [];
|
|
@@ -890,7 +1112,7 @@ function deserializeDecryptedResult(handles, decryptedResult) {
|
|
|
890
1112
|
handles.forEach((handle, idx) => (results[handle] = rawValues[idx]));
|
|
891
1113
|
return results;
|
|
892
1114
|
}
|
|
893
|
-
const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider) => async (_handles) => {
|
|
1115
|
+
const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider, options) => async (_handles) => {
|
|
894
1116
|
const acl = new ethers.Contract(aclContractAddress, aclABI, provider);
|
|
895
1117
|
let handles;
|
|
896
1118
|
try {
|
|
@@ -908,51 +1130,12 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
|
|
|
908
1130
|
catch (e) {
|
|
909
1131
|
throw e;
|
|
910
1132
|
}
|
|
911
|
-
const verifications = handles.map(async (ctHandle) => {
|
|
912
|
-
const isAllowedForDecryption = await acl.isAllowedForDecryption(ctHandle);
|
|
913
|
-
if (!isAllowedForDecryption) {
|
|
914
|
-
throw new Error(`Handle ${ctHandle} is not allowed for public decryption!`);
|
|
915
|
-
}
|
|
916
|
-
});
|
|
917
|
-
await Promise.all(verifications).catch((e) => {
|
|
918
|
-
throw e;
|
|
919
|
-
});
|
|
920
1133
|
// check 2048 bits limit
|
|
921
1134
|
checkEncryptedBits(handles);
|
|
922
1135
|
const payloadForRequest = {
|
|
923
1136
|
ciphertextHandles: handles,
|
|
924
1137
|
};
|
|
925
|
-
const
|
|
926
|
-
method: 'POST',
|
|
927
|
-
headers: {
|
|
928
|
-
'Content-Type': 'application/json',
|
|
929
|
-
},
|
|
930
|
-
body: JSON.stringify(payloadForRequest),
|
|
931
|
-
};
|
|
932
|
-
let response;
|
|
933
|
-
let json;
|
|
934
|
-
try {
|
|
935
|
-
response = await fetch(`${relayerUrl}/v1/public-decrypt`, options);
|
|
936
|
-
if (!response.ok) {
|
|
937
|
-
throw new Error(`Public decrypt failed: relayer respond with HTTP code ${response.status}`);
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
catch (e) {
|
|
941
|
-
throw new Error("Public decrypt failed: Relayer didn't respond", {
|
|
942
|
-
cause: e,
|
|
943
|
-
});
|
|
944
|
-
}
|
|
945
|
-
try {
|
|
946
|
-
json = await response.json();
|
|
947
|
-
}
|
|
948
|
-
catch (e) {
|
|
949
|
-
throw new Error("Public decrypt failed: Relayer didn't return a JSON", {
|
|
950
|
-
cause: e,
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
if (json.status === 'failure') {
|
|
954
|
-
throw new Error("Public decrypt failed: the public decrypt didn't succeed for an unknown reason", { cause: json });
|
|
955
|
-
}
|
|
1138
|
+
const json = await fetchRelayerJsonRpcPost('PUBLIC_DECRYPT', `${relayerUrl}/v1/public-decrypt`, payloadForRequest, options);
|
|
956
1139
|
// verify signatures on decryption:
|
|
957
1140
|
const domain = {
|
|
958
1141
|
name: 'Decryption',
|
|
@@ -1180,4 +1363,4 @@ const createTfhePublicKey = () => {
|
|
|
1180
1363
|
global.TFHE = TFHEPkg;
|
|
1181
1364
|
global.TKMS = TKMSPkg;
|
|
1182
1365
|
|
|
1183
|
-
export { ENCRYPTION_TYPES, SepoliaConfig, createEIP712, createInstance, createTfheKeypair, createTfhePublicKey, generateKeypair };
|
|
1366
|
+
export { ENCRYPTION_TYPES, SepoliaConfig, createEIP712, createInstance, createTfheKeypair, createTfhePublicKey, generateKeypair, getErrorCauseCode, getErrorCauseStatus };
|