@zama-fhe/relayer-sdk 0.3.0-7 → 0.4.0-alpha.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/bundle/relayer-sdk-js.js +13563 -16709
- package/bundle/relayer-sdk-js.umd.cjs +5 -23
- package/lib/node.cjs +3829 -258
- package/lib/node.d.ts +5 -1
- package/lib/node.js +3838 -267
- package/lib/web.d.ts +5 -1
- package/lib/web.js +3947 -376
- package/package.json +8 -3
package/lib/web.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { JsonRpcProvider, BrowserProvider, Contract, getAddress as getAddress$2, ethers, isAddress, AbiCoder } from 'ethers';
|
|
2
|
-
import createHash from 'keccak';
|
|
3
|
-
import fetchRetry from 'fetch-retry';
|
|
1
|
+
import { JsonRpcProvider, BrowserProvider, Contract, getAddress as getAddress$2, ethers, isAddress as isAddress$1, keccak256, AbiCoder } from 'ethers';
|
|
4
2
|
import { threads } from 'wasm-feature-detect';
|
|
5
3
|
|
|
6
4
|
var global$1 = (typeof global !== "undefined" ? global :
|
|
@@ -16255,36 +16253,6 @@ async function __wbg_init(module_or_path) {
|
|
|
16255
16253
|
const SERIALIZED_SIZE_LIMIT_CIPHERTEXT = BigInt(1024 * 1024 * 512);
|
|
16256
16254
|
const SERIALIZED_SIZE_LIMIT_PK = BigInt(1024 * 1024 * 512);
|
|
16257
16255
|
const SERIALIZED_SIZE_LIMIT_CRS = BigInt(1024 * 1024 * 512);
|
|
16258
|
-
const cleanURL = (url) => {
|
|
16259
|
-
if (!url)
|
|
16260
|
-
return '';
|
|
16261
|
-
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
16262
|
-
};
|
|
16263
|
-
const numberToHex = (num) => {
|
|
16264
|
-
let hex = num.toString(16);
|
|
16265
|
-
return hex.length % 2 ? '0' + hex : hex;
|
|
16266
|
-
};
|
|
16267
|
-
const fromHexString = (hexString) => {
|
|
16268
|
-
const arr = hexString.replace(/^(0x)/, '').match(/.{1,2}/g);
|
|
16269
|
-
if (!arr)
|
|
16270
|
-
return new Uint8Array();
|
|
16271
|
-
return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
|
|
16272
|
-
};
|
|
16273
|
-
function toHexString(bytes, with0x = false) {
|
|
16274
|
-
return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
|
|
16275
|
-
}
|
|
16276
|
-
const bytesToBigInt = function (byteArray) {
|
|
16277
|
-
if (!byteArray || byteArray?.length === 0) {
|
|
16278
|
-
return BigInt(0);
|
|
16279
|
-
}
|
|
16280
|
-
const hex = Array.from(byteArray)
|
|
16281
|
-
.map((b) => b.toString(16).padStart(2, '0')) // byte to hex
|
|
16282
|
-
.join('');
|
|
16283
|
-
return BigInt(`0x${hex}`);
|
|
16284
|
-
};
|
|
16285
|
-
function ensure0x(s) {
|
|
16286
|
-
return !s.startsWith('0x') ? `0x${s}` : s;
|
|
16287
|
-
}
|
|
16288
16256
|
|
|
16289
16257
|
function setAuth(init, auth) {
|
|
16290
16258
|
if (auth) {
|
|
@@ -16365,18 +16333,27 @@ async function throwRelayerResponseError(operation, response) {
|
|
|
16365
16333
|
}
|
|
16366
16334
|
}
|
|
16367
16335
|
}
|
|
16336
|
+
let responseJson;
|
|
16337
|
+
try {
|
|
16338
|
+
responseJson = await response.json();
|
|
16339
|
+
}
|
|
16340
|
+
catch {
|
|
16341
|
+
responseJson = '';
|
|
16342
|
+
}
|
|
16368
16343
|
const cause = {
|
|
16369
16344
|
code: 'RELAYER_FETCH_ERROR',
|
|
16370
16345
|
operation,
|
|
16371
16346
|
status: response.status,
|
|
16372
16347
|
statusText: response.statusText,
|
|
16373
16348
|
url: response.url,
|
|
16349
|
+
response,
|
|
16350
|
+
responseJson,
|
|
16374
16351
|
};
|
|
16375
16352
|
throw new Error(message, {
|
|
16376
16353
|
cause,
|
|
16377
16354
|
});
|
|
16378
16355
|
}
|
|
16379
|
-
function throwRelayerJSONError(operation, error) {
|
|
16356
|
+
function throwRelayerJSONError(operation, error, response) {
|
|
16380
16357
|
let message;
|
|
16381
16358
|
switch (operation) {
|
|
16382
16359
|
case 'PUBLIC_DECRYPT': {
|
|
@@ -16396,6 +16373,7 @@ function throwRelayerJSONError(operation, error) {
|
|
|
16396
16373
|
code: 'RELAYER_NO_JSON_ERROR',
|
|
16397
16374
|
operation,
|
|
16398
16375
|
error,
|
|
16376
|
+
response,
|
|
16399
16377
|
};
|
|
16400
16378
|
throw new Error(message, {
|
|
16401
16379
|
cause,
|
|
@@ -16482,7 +16460,8 @@ function throwRelayerUnknownError(operation, error, message) {
|
|
|
16482
16460
|
});
|
|
16483
16461
|
}
|
|
16484
16462
|
|
|
16485
|
-
|
|
16463
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
16464
|
+
function assertIsRelayerV1FetchResponseJson(json) {
|
|
16486
16465
|
if (!json || typeof json !== 'object') {
|
|
16487
16466
|
throw new Error('Unexpected response JSON.');
|
|
16488
16467
|
}
|
|
@@ -16516,10 +16495,10 @@ async function fetchRelayerJsonRpcPost(relayerOperation, url, payload, options)
|
|
|
16516
16495
|
parsed = await response.json();
|
|
16517
16496
|
}
|
|
16518
16497
|
catch (e) {
|
|
16519
|
-
throwRelayerJSONError(relayerOperation, e);
|
|
16498
|
+
throwRelayerJSONError(relayerOperation, e, response);
|
|
16520
16499
|
}
|
|
16521
16500
|
try {
|
|
16522
|
-
|
|
16501
|
+
assertIsRelayerV1FetchResponseJson(parsed);
|
|
16523
16502
|
json = parsed;
|
|
16524
16503
|
}
|
|
16525
16504
|
catch (e) {
|
|
@@ -16534,6 +16513,7 @@ async function fetchRelayerGet(relayerOperation, url) {
|
|
|
16534
16513
|
response = await fetch(url);
|
|
16535
16514
|
}
|
|
16536
16515
|
catch (e) {
|
|
16516
|
+
console.log(e);
|
|
16537
16517
|
throwRelayerUnknownError(relayerOperation, e);
|
|
16538
16518
|
}
|
|
16539
16519
|
if (!response.ok) {
|
|
@@ -16544,10 +16524,10 @@ async function fetchRelayerGet(relayerOperation, url) {
|
|
|
16544
16524
|
parsed = await response.json();
|
|
16545
16525
|
}
|
|
16546
16526
|
catch (e) {
|
|
16547
|
-
throwRelayerJSONError(relayerOperation, e);
|
|
16527
|
+
throwRelayerJSONError(relayerOperation, e, response);
|
|
16548
16528
|
}
|
|
16549
16529
|
try {
|
|
16550
|
-
|
|
16530
|
+
assertIsRelayerV1FetchResponseJson(parsed);
|
|
16551
16531
|
json = parsed;
|
|
16552
16532
|
}
|
|
16553
16533
|
catch (e) {
|
|
@@ -16556,49 +16536,13 @@ async function fetchRelayerGet(relayerOperation, url) {
|
|
|
16556
16536
|
return json;
|
|
16557
16537
|
}
|
|
16558
16538
|
|
|
16559
|
-
// export type RelayerKeysItem = {
|
|
16560
|
-
// data_id: string;
|
|
16561
|
-
// param_choice: number;
|
|
16562
|
-
// urls: string[];
|
|
16563
|
-
// signatures: string[];
|
|
16564
|
-
// };
|
|
16565
|
-
// export type RelayerKey = {
|
|
16566
|
-
// data_id: string;
|
|
16567
|
-
// param_choice: number;
|
|
16568
|
-
// signatures: string[];
|
|
16569
|
-
// urls: string[];
|
|
16570
|
-
// };
|
|
16571
|
-
// export type RelayerKeys = {
|
|
16572
|
-
// response: {
|
|
16573
|
-
// fhe_key_info: {
|
|
16574
|
-
// fhe_public_key: RelayerKey;
|
|
16575
|
-
// fhe_server_key: RelayerKey;
|
|
16576
|
-
// }[];
|
|
16577
|
-
// verf_public_key: {
|
|
16578
|
-
// key_id: string;
|
|
16579
|
-
// server_id: number;
|
|
16580
|
-
// verf_public_key_address: string;
|
|
16581
|
-
// verf_public_key_url: string;
|
|
16582
|
-
// }[];
|
|
16583
|
-
// crs: {
|
|
16584
|
-
// [key: string]: RelayerKeysItem;
|
|
16585
|
-
// };
|
|
16586
|
-
// };
|
|
16587
|
-
// status: string;
|
|
16588
|
-
// };
|
|
16589
16539
|
const keyurlCache = {};
|
|
16590
|
-
const getKeysFromRelayer = async (
|
|
16591
|
-
if (keyurlCache[
|
|
16592
|
-
return keyurlCache[
|
|
16540
|
+
const getKeysFromRelayer = async (versionUrl, publicKeyId) => {
|
|
16541
|
+
if (keyurlCache[versionUrl]) {
|
|
16542
|
+
return keyurlCache[versionUrl];
|
|
16593
16543
|
}
|
|
16594
|
-
const data = await fetchRelayerGet('KEY_URL', `${
|
|
16544
|
+
const data = await fetchRelayerGet('KEY_URL', `${versionUrl}/keyurl`);
|
|
16595
16545
|
try {
|
|
16596
|
-
// const response = await fetch(`${url}/v1/keyurl`);
|
|
16597
|
-
// if (!response.ok) {
|
|
16598
|
-
// await throwRelayerResponseError("KEY_URL", response);
|
|
16599
|
-
// }
|
|
16600
|
-
//const data: RelayerKeys = await response.json();
|
|
16601
|
-
//if (data) {
|
|
16602
16546
|
let pubKeyUrl;
|
|
16603
16547
|
// If no publicKeyId is provided, use the first one
|
|
16604
16548
|
// Warning: if there are multiple keys available, the first one will most likely never be the
|
|
@@ -16670,11 +16614,8 @@ const getKeysFromRelayer = async (url, publicKeyId) => {
|
|
|
16670
16614
|
},
|
|
16671
16615
|
},
|
|
16672
16616
|
};
|
|
16673
|
-
keyurlCache[
|
|
16617
|
+
keyurlCache[versionUrl] = result;
|
|
16674
16618
|
return result;
|
|
16675
|
-
// } else {
|
|
16676
|
-
// throw new Error('No public key available');
|
|
16677
|
-
// }
|
|
16678
16619
|
}
|
|
16679
16620
|
catch (e) {
|
|
16680
16621
|
throw new Error('Impossible to fetch public key: wrong relayer url.', {
|
|
@@ -16683,6 +16624,422 @@ const getKeysFromRelayer = async (url, publicKeyId) => {
|
|
|
16683
16624
|
}
|
|
16684
16625
|
};
|
|
16685
16626
|
|
|
16627
|
+
class RelayerErrorBase extends Error {
|
|
16628
|
+
name = 'RelayerErrorBase';
|
|
16629
|
+
_details;
|
|
16630
|
+
_docsPath;
|
|
16631
|
+
_docsUrl;
|
|
16632
|
+
_version;
|
|
16633
|
+
static VERSION = '0.3.0-6';
|
|
16634
|
+
static DEFAULT_DOCS_BASE_URL = 'https//docs.zama.org';
|
|
16635
|
+
static FULL_VERSION = `@zama-fhe/relayer-sdk@${RelayerErrorBase.VERSION}`;
|
|
16636
|
+
constructor(params) {
|
|
16637
|
+
let details;
|
|
16638
|
+
let docsPath;
|
|
16639
|
+
if (params.cause instanceof RelayerErrorBase) {
|
|
16640
|
+
docsPath = params.docsPath || params.cause.docsPath;
|
|
16641
|
+
details = params.details || params.cause.details;
|
|
16642
|
+
}
|
|
16643
|
+
else {
|
|
16644
|
+
docsPath = params.docsPath;
|
|
16645
|
+
details = params.details || params.cause?.message;
|
|
16646
|
+
}
|
|
16647
|
+
const docsUrl = docsPath
|
|
16648
|
+
? `${params.docsBaseUrl ?? RelayerErrorBase.DEFAULT_DOCS_BASE_URL}${docsPath}${params.docsSlug ? `#${params.docsSlug}` : ''}`
|
|
16649
|
+
: undefined;
|
|
16650
|
+
const message = [
|
|
16651
|
+
params.message || 'An error occurred.',
|
|
16652
|
+
'',
|
|
16653
|
+
...(params.metaMessages ? [...params.metaMessages, ''] : []),
|
|
16654
|
+
...(docsUrl ? [`Docs: ${docsUrl}`] : []),
|
|
16655
|
+
...(details ? [`Details: ${details}`] : []),
|
|
16656
|
+
`Version: ${RelayerErrorBase.FULL_VERSION}`,
|
|
16657
|
+
].join('\n');
|
|
16658
|
+
super(message, params.cause ? { cause: params.cause } : undefined);
|
|
16659
|
+
// This line is critical. If removed 'instanceof' will always fail
|
|
16660
|
+
// Restore prototype chain (required when extending Error in TypeScript)
|
|
16661
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
16662
|
+
this._details = details;
|
|
16663
|
+
this._docsPath = docsPath;
|
|
16664
|
+
this._docsUrl = docsUrl;
|
|
16665
|
+
this._version = RelayerErrorBase.VERSION;
|
|
16666
|
+
this.name = params.name ?? this.name;
|
|
16667
|
+
}
|
|
16668
|
+
get docsPath() {
|
|
16669
|
+
return this._docsPath;
|
|
16670
|
+
}
|
|
16671
|
+
get docsUrl() {
|
|
16672
|
+
return this._docsUrl;
|
|
16673
|
+
}
|
|
16674
|
+
get details() {
|
|
16675
|
+
return this._details;
|
|
16676
|
+
}
|
|
16677
|
+
get version() {
|
|
16678
|
+
return this._version;
|
|
16679
|
+
}
|
|
16680
|
+
}
|
|
16681
|
+
|
|
16682
|
+
class InvalidPropertyError extends RelayerErrorBase {
|
|
16683
|
+
_objName;
|
|
16684
|
+
_property;
|
|
16685
|
+
_expectedType;
|
|
16686
|
+
_index;
|
|
16687
|
+
_value;
|
|
16688
|
+
_type;
|
|
16689
|
+
_expectedValue;
|
|
16690
|
+
constructor({ objName, property, index, type, value, expectedValue, expectedType, }) {
|
|
16691
|
+
let missing = type === 'undefined' && expectedValue !== undefined;
|
|
16692
|
+
let varname;
|
|
16693
|
+
if (!property || property === '') {
|
|
16694
|
+
varname = index !== undefined ? `${objName}[${index}]` : `${objName}`;
|
|
16695
|
+
}
|
|
16696
|
+
else {
|
|
16697
|
+
varname =
|
|
16698
|
+
index !== undefined
|
|
16699
|
+
? `${objName}.${property}[${index}]`
|
|
16700
|
+
: `${objName}.${property}`;
|
|
16701
|
+
}
|
|
16702
|
+
let message = missing
|
|
16703
|
+
? `InvalidPropertyError: Missing '${varname}'`
|
|
16704
|
+
: `InvalidPropertyError: ${varname}`;
|
|
16705
|
+
if (type === expectedType) {
|
|
16706
|
+
if (value !== undefined) {
|
|
16707
|
+
message += ` unexpected value ${value}`;
|
|
16708
|
+
}
|
|
16709
|
+
}
|
|
16710
|
+
else {
|
|
16711
|
+
if (missing) {
|
|
16712
|
+
if (Array.isArray(expectedValue)) {
|
|
16713
|
+
expectedValue = expectedValue.join('|');
|
|
16714
|
+
}
|
|
16715
|
+
message += `, expected '${varname}: ${expectedValue}'.`;
|
|
16716
|
+
}
|
|
16717
|
+
else if (expectedType !== 'unknown' && type !== 'unknown') {
|
|
16718
|
+
message += ` not a ${expectedType}`;
|
|
16719
|
+
if (type) {
|
|
16720
|
+
message += `, type is ${type}`;
|
|
16721
|
+
}
|
|
16722
|
+
}
|
|
16723
|
+
}
|
|
16724
|
+
super({
|
|
16725
|
+
message,
|
|
16726
|
+
name: 'InvalidPropertyError',
|
|
16727
|
+
});
|
|
16728
|
+
this._objName = objName;
|
|
16729
|
+
this._property = property;
|
|
16730
|
+
this._value = value;
|
|
16731
|
+
this._type = type;
|
|
16732
|
+
this._expectedValue = expectedValue;
|
|
16733
|
+
this._expectedType = expectedType;
|
|
16734
|
+
this._index = index;
|
|
16735
|
+
}
|
|
16736
|
+
static missingProperty({ objName, property, expectedType, expectedValue, }) {
|
|
16737
|
+
return new InvalidPropertyError({
|
|
16738
|
+
objName,
|
|
16739
|
+
property,
|
|
16740
|
+
expectedType,
|
|
16741
|
+
expectedValue,
|
|
16742
|
+
type: 'undefined',
|
|
16743
|
+
});
|
|
16744
|
+
}
|
|
16745
|
+
static invalidFormat({ objName, property, }) {
|
|
16746
|
+
return new InvalidPropertyError({
|
|
16747
|
+
objName,
|
|
16748
|
+
property,
|
|
16749
|
+
expectedType: 'unknown',
|
|
16750
|
+
});
|
|
16751
|
+
}
|
|
16752
|
+
static invalidObject({ objName, expectedType, type, }) {
|
|
16753
|
+
return new InvalidPropertyError({
|
|
16754
|
+
objName,
|
|
16755
|
+
property: '',
|
|
16756
|
+
expectedType,
|
|
16757
|
+
type,
|
|
16758
|
+
});
|
|
16759
|
+
}
|
|
16760
|
+
}
|
|
16761
|
+
|
|
16762
|
+
/**
|
|
16763
|
+
* Type guard that checks if a property exists on an object and is non-null/non-undefined.
|
|
16764
|
+
*
|
|
16765
|
+
* @template K - The property key type (string literal)
|
|
16766
|
+
* @param o - The value to check (can be any type)
|
|
16767
|
+
* @param property - The property name to check for
|
|
16768
|
+
* @returns True if `o` is an object with the specified property that is not null or undefined
|
|
16769
|
+
*
|
|
16770
|
+
* @example
|
|
16771
|
+
* ```typescript
|
|
16772
|
+
* const data: unknown = { name: "Alice", age: 30 };
|
|
16773
|
+
* if (isNonNullableRecordProperty(data, 'name')) {
|
|
16774
|
+
* console.log(data.name); // OK
|
|
16775
|
+
* }
|
|
16776
|
+
* ```
|
|
16777
|
+
*/
|
|
16778
|
+
function isNonNullableRecordProperty(o, property) {
|
|
16779
|
+
if (!o ||
|
|
16780
|
+
typeof o !== 'object' ||
|
|
16781
|
+
!(property in o) ||
|
|
16782
|
+
o[property] === undefined ||
|
|
16783
|
+
o[property] === null) {
|
|
16784
|
+
return false;
|
|
16785
|
+
}
|
|
16786
|
+
return true;
|
|
16787
|
+
}
|
|
16788
|
+
/**
|
|
16789
|
+
* Assertion function that validates a property exists on an object and is non-null/non-undefined.
|
|
16790
|
+
* Throws an `InvalidPropertyError` if validation fails.
|
|
16791
|
+
*
|
|
16792
|
+
* @template K - The property key type (string literal)
|
|
16793
|
+
* @param o - The value to validate (can be any type)
|
|
16794
|
+
* @param property - The property name to check for
|
|
16795
|
+
* @param objName - The name of the object being validated (used in error messages)
|
|
16796
|
+
* @throws {InvalidPropertyError} When the property is missing, null, or undefined
|
|
16797
|
+
* @throws {never} No other errors are thrown
|
|
16798
|
+
*
|
|
16799
|
+
* @example
|
|
16800
|
+
* ```typescript
|
|
16801
|
+
* function processUser(data: unknown) {
|
|
16802
|
+
* assertNonNullableRecordProperty(data, 'userId', 'user');
|
|
16803
|
+
* console.log(data.userId);
|
|
16804
|
+
* }
|
|
16805
|
+
* ```
|
|
16806
|
+
*/
|
|
16807
|
+
function assertNonNullableRecordProperty(o, property, objName) {
|
|
16808
|
+
if (!isNonNullableRecordProperty(o, property)) {
|
|
16809
|
+
throw new InvalidPropertyError({
|
|
16810
|
+
objName,
|
|
16811
|
+
property,
|
|
16812
|
+
expectedType: 'non-nullable',
|
|
16813
|
+
type: typeofProperty(o, property),
|
|
16814
|
+
});
|
|
16815
|
+
}
|
|
16816
|
+
}
|
|
16817
|
+
/**
|
|
16818
|
+
* Type guard that checks if a property exists on an object and is an array.
|
|
16819
|
+
*
|
|
16820
|
+
* @template K - The property key type (string literal)
|
|
16821
|
+
* @param o - The value to check (can be any type)
|
|
16822
|
+
* @param property - The property name to check for
|
|
16823
|
+
* @returns True if `o` is an object with the specified property that is a non-null array
|
|
16824
|
+
*
|
|
16825
|
+
* @example
|
|
16826
|
+
* ```typescript
|
|
16827
|
+
* const data: unknown = { items: [1, 2, 3], count: 42 };
|
|
16828
|
+
* if (isRecordArrayProperty(data, 'items')) {
|
|
16829
|
+
* console.log(data.items.length); // OK
|
|
16830
|
+
* data.items.forEach(item => console.log(item)); // OK
|
|
16831
|
+
* }
|
|
16832
|
+
* ```
|
|
16833
|
+
*/
|
|
16834
|
+
function isRecordArrayProperty(o, property) {
|
|
16835
|
+
if (!isNonNullableRecordProperty(o, property)) {
|
|
16836
|
+
return false;
|
|
16837
|
+
}
|
|
16838
|
+
return Array.isArray(o[property]);
|
|
16839
|
+
}
|
|
16840
|
+
/**
|
|
16841
|
+
* Assertion function that validates a property exists on an object and is an array.
|
|
16842
|
+
* Throws an `InvalidPropertyError` if validation fails.
|
|
16843
|
+
*
|
|
16844
|
+
* @template K - The property key type (string literal)
|
|
16845
|
+
* @param o - The value to validate (can be any type)
|
|
16846
|
+
* @param property - The property name to check for
|
|
16847
|
+
* @param objName - The name of the object being validated (used in error messages)
|
|
16848
|
+
* @throws {InvalidPropertyError} When the property is missing, null, or not an array
|
|
16849
|
+
* @throws {never} No other errors are thrown
|
|
16850
|
+
*
|
|
16851
|
+
* @example
|
|
16852
|
+
* ```typescript
|
|
16853
|
+
* function processResults(data: unknown) {
|
|
16854
|
+
* assertRecordArrayProperty(data, 'results', 'response');
|
|
16855
|
+
* console.log(`Found ${data.results.length} results`);
|
|
16856
|
+
* data.results.forEach(result => processResult(result));
|
|
16857
|
+
* }
|
|
16858
|
+
* ```
|
|
16859
|
+
*/
|
|
16860
|
+
function assertRecordArrayProperty(o, property, objName) {
|
|
16861
|
+
if (!isRecordArrayProperty(o, property)) {
|
|
16862
|
+
throw new InvalidPropertyError({
|
|
16863
|
+
objName,
|
|
16864
|
+
property,
|
|
16865
|
+
expectedType: 'Array',
|
|
16866
|
+
type: typeofProperty(o, property),
|
|
16867
|
+
});
|
|
16868
|
+
}
|
|
16869
|
+
}
|
|
16870
|
+
function isRecordBooleanProperty(o, property) {
|
|
16871
|
+
if (!isNonNullableRecordProperty(o, property)) {
|
|
16872
|
+
return false;
|
|
16873
|
+
}
|
|
16874
|
+
return typeof o[property] === 'boolean';
|
|
16875
|
+
}
|
|
16876
|
+
function assertRecordBooleanProperty(o, property, objName, expectedValue) {
|
|
16877
|
+
if (!isRecordBooleanProperty(o, property))
|
|
16878
|
+
throw new InvalidPropertyError({
|
|
16879
|
+
objName,
|
|
16880
|
+
property,
|
|
16881
|
+
expectedType: 'boolean',
|
|
16882
|
+
type: typeofProperty(o, property),
|
|
16883
|
+
});
|
|
16884
|
+
if (expectedValue !== undefined) {
|
|
16885
|
+
if (o[property] !== expectedValue) {
|
|
16886
|
+
throw new InvalidPropertyError({
|
|
16887
|
+
objName,
|
|
16888
|
+
property,
|
|
16889
|
+
expectedType: 'boolean',
|
|
16890
|
+
expectedValue: String(expectedValue),
|
|
16891
|
+
type: typeof o[property],
|
|
16892
|
+
value: String(o[property]),
|
|
16893
|
+
});
|
|
16894
|
+
}
|
|
16895
|
+
}
|
|
16896
|
+
}
|
|
16897
|
+
function typeofProperty(o, property) {
|
|
16898
|
+
if (isNonNullableRecordProperty(o, property)) {
|
|
16899
|
+
return typeof o[property];
|
|
16900
|
+
}
|
|
16901
|
+
return 'undefined';
|
|
16902
|
+
}
|
|
16903
|
+
|
|
16904
|
+
class InternalError extends RelayerErrorBase {
|
|
16905
|
+
constructor(params) {
|
|
16906
|
+
super({
|
|
16907
|
+
...params,
|
|
16908
|
+
name: 'InternalError',
|
|
16909
|
+
message: params.message ?? 'internal error',
|
|
16910
|
+
});
|
|
16911
|
+
}
|
|
16912
|
+
}
|
|
16913
|
+
function assertRelayer(condition) {
|
|
16914
|
+
if (!condition) {
|
|
16915
|
+
throw new InternalError({ message: 'Assertion failed' });
|
|
16916
|
+
}
|
|
16917
|
+
}
|
|
16918
|
+
|
|
16919
|
+
function removeSuffix(s, suffix) {
|
|
16920
|
+
if (!s) {
|
|
16921
|
+
return '';
|
|
16922
|
+
}
|
|
16923
|
+
if (suffix.length === 0) {
|
|
16924
|
+
return s;
|
|
16925
|
+
}
|
|
16926
|
+
return s.endsWith(suffix) ? s.slice(0, -suffix.length) : s;
|
|
16927
|
+
}
|
|
16928
|
+
function is0x(s) {
|
|
16929
|
+
return typeof s === 'string' && s.startsWith('0x');
|
|
16930
|
+
}
|
|
16931
|
+
function isNo0x(s) {
|
|
16932
|
+
return typeof s === 'string' && !s.startsWith('0x');
|
|
16933
|
+
}
|
|
16934
|
+
function ensure0x(s) {
|
|
16935
|
+
return !s.startsWith('0x') ? `0x${s}` : s;
|
|
16936
|
+
}
|
|
16937
|
+
function remove0x(s) {
|
|
16938
|
+
return s.startsWith('0x') ? s.substring(2) : s;
|
|
16939
|
+
}
|
|
16940
|
+
/**
|
|
16941
|
+
* Type guard that checks if a property exists on an object and is a string.
|
|
16942
|
+
*
|
|
16943
|
+
* @template K - The property key type (string literal)
|
|
16944
|
+
* @param o - The value to check (can be any type)
|
|
16945
|
+
* @param property - The property name to check for
|
|
16946
|
+
* @returns True if `o` is an object with the specified property that is a non-null string
|
|
16947
|
+
*
|
|
16948
|
+
* @example
|
|
16949
|
+
* ```typescript
|
|
16950
|
+
* const data: unknown = { status: "active", count: 42 };
|
|
16951
|
+
* if (isRecordStringProperty(data, 'status')) {
|
|
16952
|
+
* console.log(data.status.toUpperCase()); // OK
|
|
16953
|
+
* }
|
|
16954
|
+
* ```
|
|
16955
|
+
*/
|
|
16956
|
+
function isRecordStringProperty(o, property) {
|
|
16957
|
+
if (!isNonNullableRecordProperty(o, property)) {
|
|
16958
|
+
return false;
|
|
16959
|
+
}
|
|
16960
|
+
return typeof o[property] === 'string';
|
|
16961
|
+
}
|
|
16962
|
+
/**
|
|
16963
|
+
* Assertion function that validates a property exists on an object, is a string,
|
|
16964
|
+
* and optionally matches specific expected value(s).
|
|
16965
|
+
* Throws an `InvalidPropertyError` if validation fails.
|
|
16966
|
+
*
|
|
16967
|
+
* @template K - The property key type (string literal)
|
|
16968
|
+
* @param o - The value to validate (can be any type)
|
|
16969
|
+
* @param property - The property name to check for
|
|
16970
|
+
* @param objName - The name of the object being validated (used in error messages)
|
|
16971
|
+
* @param expectedValue - Optional specific string value or array of allowed values to match against
|
|
16972
|
+
* @throws {InvalidPropertyError} When the property is missing, not a string, or doesn't match expectedValue
|
|
16973
|
+
* @throws {never} No other errors are thrown
|
|
16974
|
+
*
|
|
16975
|
+
* @example
|
|
16976
|
+
* ```typescript
|
|
16977
|
+
* // Check property is a string (any value)
|
|
16978
|
+
* assertRecordStringProperty(data, 'name', 'user');
|
|
16979
|
+
*
|
|
16980
|
+
* // Check property equals a specific value
|
|
16981
|
+
* assertRecordStringProperty(data, 'status', 'response', 'active');
|
|
16982
|
+
*
|
|
16983
|
+
* // Check property is one of multiple allowed values
|
|
16984
|
+
* assertRecordStringProperty(data, 'status', 'response', ['queued', 'processing', 'completed']);
|
|
16985
|
+
* ```
|
|
16986
|
+
*/
|
|
16987
|
+
function assertRecordStringProperty(o, property, objName, expectedValue) {
|
|
16988
|
+
if (!isRecordStringProperty(o, property)) {
|
|
16989
|
+
throw new InvalidPropertyError({
|
|
16990
|
+
objName,
|
|
16991
|
+
property,
|
|
16992
|
+
expectedType: 'string',
|
|
16993
|
+
expectedValue,
|
|
16994
|
+
type: typeofProperty(o, property),
|
|
16995
|
+
});
|
|
16996
|
+
}
|
|
16997
|
+
if (expectedValue !== undefined) {
|
|
16998
|
+
if (Array.isArray(expectedValue)) {
|
|
16999
|
+
// Check if value matches any of the allowed values
|
|
17000
|
+
for (let i = 0; i < expectedValue.length; ++i) {
|
|
17001
|
+
if (o[property] === expectedValue[i]) {
|
|
17002
|
+
return;
|
|
17003
|
+
}
|
|
17004
|
+
}
|
|
17005
|
+
throw new InvalidPropertyError({
|
|
17006
|
+
objName,
|
|
17007
|
+
property,
|
|
17008
|
+
expectedType: 'string',
|
|
17009
|
+
expectedValue,
|
|
17010
|
+
type: typeof o[property], // === "string"
|
|
17011
|
+
value: o[property],
|
|
17012
|
+
});
|
|
17013
|
+
}
|
|
17014
|
+
else {
|
|
17015
|
+
if (o[property] !== expectedValue) {
|
|
17016
|
+
throw new InvalidPropertyError({
|
|
17017
|
+
objName,
|
|
17018
|
+
property,
|
|
17019
|
+
expectedType: 'string',
|
|
17020
|
+
expectedValue,
|
|
17021
|
+
type: typeof o[property], // === "string"
|
|
17022
|
+
value: o[property],
|
|
17023
|
+
});
|
|
17024
|
+
}
|
|
17025
|
+
}
|
|
17026
|
+
}
|
|
17027
|
+
}
|
|
17028
|
+
function assertRecordStringArrayProperty(o, property, objName) {
|
|
17029
|
+
assertRecordArrayProperty(o, property, objName);
|
|
17030
|
+
const arr = o[property];
|
|
17031
|
+
for (let i = 0; i < arr.length; ++i) {
|
|
17032
|
+
if (typeof arr[i] !== 'string') {
|
|
17033
|
+
throw new InvalidPropertyError({
|
|
17034
|
+
objName,
|
|
17035
|
+
property: `${property}[${i}]`,
|
|
17036
|
+
expectedType: 'string',
|
|
17037
|
+
type: typeof arr[i],
|
|
17038
|
+
});
|
|
17039
|
+
}
|
|
17040
|
+
}
|
|
17041
|
+
}
|
|
17042
|
+
|
|
16686
17043
|
const abiKmsVerifier = [
|
|
16687
17044
|
'function getKmsSigners() view returns (address[])',
|
|
16688
17045
|
'function getThreshold() view returns (uint256)',
|
|
@@ -16691,12 +17048,12 @@ const abiInputVerifier = [
|
|
|
16691
17048
|
'function getCoprocessorSigners() view returns (address[])',
|
|
16692
17049
|
'function getThreshold() view returns (uint256)',
|
|
16693
17050
|
];
|
|
16694
|
-
const getProvider = (
|
|
16695
|
-
if (typeof
|
|
16696
|
-
return new JsonRpcProvider(
|
|
17051
|
+
const getProvider = (network) => {
|
|
17052
|
+
if (typeof network === 'string') {
|
|
17053
|
+
return new JsonRpcProvider(network);
|
|
16697
17054
|
}
|
|
16698
|
-
else if (
|
|
16699
|
-
return new BrowserProvider(
|
|
17055
|
+
else if (network) {
|
|
17056
|
+
return new BrowserProvider(network);
|
|
16700
17057
|
}
|
|
16701
17058
|
throw new Error('You must provide a network URL or a EIP1193 object (eg: window.ethereum)');
|
|
16702
17059
|
};
|
|
@@ -16713,8 +17070,8 @@ const getChainId = async (provider, config) => {
|
|
|
16713
17070
|
}
|
|
16714
17071
|
};
|
|
16715
17072
|
const getTfheCompactPublicKey = async (config) => {
|
|
16716
|
-
if (config.
|
|
16717
|
-
const inputs = await getKeysFromRelayer(
|
|
17073
|
+
if (config.relayerVersionUrl && !config.publicKey) {
|
|
17074
|
+
const inputs = await getKeysFromRelayer(removeSuffix(config.relayerVersionUrl, '/'));
|
|
16718
17075
|
return { publicKey: inputs.publicKey, publicKeyId: inputs.publicKeyId };
|
|
16719
17076
|
}
|
|
16720
17077
|
else if (config.publicKey && config.publicKey.data && config.publicKey.id) {
|
|
@@ -16736,8 +17093,8 @@ const getTfheCompactPublicKey = async (config) => {
|
|
|
16736
17093
|
}
|
|
16737
17094
|
};
|
|
16738
17095
|
const getPublicParams = async (config) => {
|
|
16739
|
-
if (config.
|
|
16740
|
-
const inputs = await getKeysFromRelayer(
|
|
17096
|
+
if (config.relayerVersionUrl && !config.publicParams) {
|
|
17097
|
+
const inputs = await getKeysFromRelayer(removeSuffix(config.relayerVersionUrl, '/'));
|
|
16741
17098
|
return inputs.publicParams;
|
|
16742
17099
|
}
|
|
16743
17100
|
else if (config.publicParams && config.publicParams['2048']) {
|
|
@@ -16760,23 +17117,23 @@ const getPublicParams = async (config) => {
|
|
|
16760
17117
|
throw new Error('You must provide a valid CRS with its CRS ID.');
|
|
16761
17118
|
}
|
|
16762
17119
|
};
|
|
16763
|
-
const getKMSSigners = async (provider,
|
|
16764
|
-
const kmsContract = new Contract(
|
|
17120
|
+
const getKMSSigners = async (provider, kmsContractAddress) => {
|
|
17121
|
+
const kmsContract = new Contract(kmsContractAddress, abiKmsVerifier, provider);
|
|
16765
17122
|
const signers = await kmsContract.getKmsSigners();
|
|
16766
17123
|
return signers;
|
|
16767
17124
|
};
|
|
16768
|
-
const getKMSSignersThreshold = async (provider,
|
|
16769
|
-
const kmsContract = new Contract(
|
|
17125
|
+
const getKMSSignersThreshold = async (provider, kmsContractAddress) => {
|
|
17126
|
+
const kmsContract = new Contract(kmsContractAddress, abiKmsVerifier, provider);
|
|
16770
17127
|
const threshold = await kmsContract.getThreshold();
|
|
16771
17128
|
return Number(threshold); // threshold is always supposed to fit in a number
|
|
16772
17129
|
};
|
|
16773
|
-
const getCoprocessorSigners = async (provider,
|
|
16774
|
-
const inputContract = new Contract(
|
|
17130
|
+
const getCoprocessorSigners = async (provider, inputVerifierContractAddress) => {
|
|
17131
|
+
const inputContract = new Contract(inputVerifierContractAddress, abiInputVerifier, provider);
|
|
16775
17132
|
const signers = await inputContract.getCoprocessorSigners();
|
|
16776
17133
|
return signers;
|
|
16777
17134
|
};
|
|
16778
|
-
const getCoprocessorSignersThreshold = async (provider,
|
|
16779
|
-
const inputContract = new Contract(
|
|
17135
|
+
const getCoprocessorSignersThreshold = async (provider, inputVerifierContractAddress) => {
|
|
17136
|
+
const inputContract = new Contract(inputVerifierContractAddress, abiInputVerifier, provider);
|
|
16780
17137
|
const threshold = await inputContract.getThreshold();
|
|
16781
17138
|
return Number(threshold); // threshold is always supposed to fit in a number
|
|
16782
17139
|
};
|
|
@@ -16823,119 +17180,465 @@ function checkEncryptedBits(handles) {
|
|
|
16823
17180
|
return total;
|
|
16824
17181
|
}
|
|
16825
17182
|
|
|
16826
|
-
|
|
16827
|
-
|
|
16828
|
-
|
|
16829
|
-
|
|
16830
|
-
|
|
16831
|
-
|
|
16832
|
-
|
|
16833
|
-
|
|
16834
|
-
|
|
16835
|
-
|
|
16836
|
-
|
|
17183
|
+
class InvalidTypeError extends RelayerErrorBase {
|
|
17184
|
+
_objName;
|
|
17185
|
+
_type;
|
|
17186
|
+
_expectedType;
|
|
17187
|
+
_expectedCustomType;
|
|
17188
|
+
constructor({ objName, type, expectedType, expectedCustomType, }) {
|
|
17189
|
+
super({
|
|
17190
|
+
message: `InvalidTypeError ${objName} ${expectedType} ${type}`,
|
|
17191
|
+
name: 'InvalidTypeError',
|
|
17192
|
+
});
|
|
17193
|
+
this._objName = objName;
|
|
17194
|
+
this._type = type;
|
|
17195
|
+
this._expectedType = expectedType;
|
|
17196
|
+
this._expectedCustomType = expectedCustomType;
|
|
16837
17197
|
}
|
|
16838
|
-
|
|
16839
|
-
|
|
16840
|
-
return getAddress$1('0x' + clearValueAsBigInt.toString(16).padStart(40, '0'));
|
|
17198
|
+
get objName() {
|
|
17199
|
+
return this._objName;
|
|
16841
17200
|
}
|
|
16842
|
-
|
|
16843
|
-
|
|
16844
|
-
|
|
17201
|
+
get type() {
|
|
17202
|
+
return this._type;
|
|
17203
|
+
}
|
|
17204
|
+
get expectedType() {
|
|
17205
|
+
return this._expectedType;
|
|
17206
|
+
}
|
|
17207
|
+
get expectedCustomType() {
|
|
17208
|
+
return this._expectedCustomType;
|
|
16845
17209
|
}
|
|
16846
|
-
// euintXXX
|
|
16847
|
-
return clearValueAsBigInt;
|
|
16848
17210
|
}
|
|
16849
|
-
|
|
16850
|
-
|
|
16851
|
-
|
|
16852
|
-
|
|
16853
|
-
const typeDiscriminant = parseInt(hexPair, 16);
|
|
16854
|
-
typesList.push(typeDiscriminant);
|
|
17211
|
+
|
|
17212
|
+
function isBytes(value, bytewidth) {
|
|
17213
|
+
if (!value) {
|
|
17214
|
+
return false;
|
|
16855
17215
|
}
|
|
16856
|
-
|
|
16857
|
-
|
|
16858
|
-
|
|
17216
|
+
if (!(value instanceof Uint8Array)) {
|
|
17217
|
+
return false;
|
|
17218
|
+
}
|
|
17219
|
+
return value.length === bytewidth ;
|
|
16859
17220
|
}
|
|
16860
|
-
function
|
|
16861
|
-
if (
|
|
16862
|
-
|
|
17221
|
+
function isBytesHex(value, bytewidth) {
|
|
17222
|
+
if (!is0x(value)) {
|
|
17223
|
+
return false;
|
|
16863
17224
|
}
|
|
16864
|
-
if (
|
|
16865
|
-
|
|
17225
|
+
if (bytewidth !== undefined && value.length !== 2 * bytewidth + 2) {
|
|
17226
|
+
return false;
|
|
16866
17227
|
}
|
|
16867
|
-
|
|
16868
|
-
|
|
16869
|
-
throw Error('startTimestamp is set in the future');
|
|
17228
|
+
if ((value.length - 2) % 2 !== 0) {
|
|
17229
|
+
return false;
|
|
16870
17230
|
}
|
|
16871
|
-
const
|
|
16872
|
-
if (
|
|
16873
|
-
|
|
17231
|
+
const hexRegex = /^0x[a-fA-F0-9]*$/;
|
|
17232
|
+
if (!hexRegex.test(value)) {
|
|
17233
|
+
return false;
|
|
16874
17234
|
}
|
|
17235
|
+
return true;
|
|
16875
17236
|
}
|
|
16876
|
-
|
|
16877
|
-
|
|
16878
|
-
|
|
16879
|
-
let privKey;
|
|
16880
|
-
try {
|
|
16881
|
-
pubKey = TKMS.u8vec_to_ml_kem_pke_pk(fromHexString(publicKey));
|
|
16882
|
-
privKey = TKMS.u8vec_to_ml_kem_pke_sk(fromHexString(privateKey));
|
|
17237
|
+
function isBytesHexNo0x(value, bytewidth) {
|
|
17238
|
+
if (!isNo0x(value)) {
|
|
17239
|
+
return false;
|
|
16883
17240
|
}
|
|
16884
|
-
|
|
16885
|
-
|
|
17241
|
+
if ((value.length - 2) % 2 !== 0) {
|
|
17242
|
+
return false;
|
|
16886
17243
|
}
|
|
16887
|
-
|
|
16888
|
-
|
|
16889
|
-
|
|
16890
|
-
const handles = _handles.map((h) => ({
|
|
16891
|
-
handle: typeof h.handle === 'string'
|
|
16892
|
-
? toHexString(fromHexString(h.handle), true)
|
|
16893
|
-
: toHexString(h.handle, true),
|
|
16894
|
-
contractAddress: getAddress$1(h.contractAddress),
|
|
16895
|
-
}));
|
|
16896
|
-
checkEncryptedBits(handles.map((h) => h.handle));
|
|
16897
|
-
checkDeadlineValidity(BigInt(startTimestamp), BigInt(durationDays));
|
|
16898
|
-
const acl = new ethers.Contract(aclContractAddress, aclABI$1, provider);
|
|
16899
|
-
const verifications = handles.map(async ({ handle, contractAddress }) => {
|
|
16900
|
-
const userAllowed = await acl.persistAllowed(handle, userAddress);
|
|
16901
|
-
const contractAllowed = await acl.persistAllowed(handle, contractAddress);
|
|
16902
|
-
if (!userAllowed) {
|
|
16903
|
-
throw new Error(`User ${userAddress} is not authorized to user decrypt handle ${handle}!`);
|
|
16904
|
-
}
|
|
16905
|
-
if (!contractAllowed) {
|
|
16906
|
-
throw new Error(`dapp contract ${contractAddress} is not authorized to user decrypt handle ${handle}!`);
|
|
16907
|
-
}
|
|
16908
|
-
if (userAddress === contractAddress) {
|
|
16909
|
-
throw new Error(`userAddress ${userAddress} should not be equal to contractAddress when requesting user decryption!`);
|
|
16910
|
-
}
|
|
16911
|
-
});
|
|
16912
|
-
const contractAddressesLength = contractAddresses.length;
|
|
16913
|
-
if (contractAddressesLength === 0) {
|
|
16914
|
-
throw Error('contractAddresses is empty');
|
|
17244
|
+
const hexRegex = /^[a-fA-F0-9]*$/;
|
|
17245
|
+
if (!hexRegex.test(value)) {
|
|
17246
|
+
return false;
|
|
16915
17247
|
}
|
|
16916
|
-
|
|
16917
|
-
|
|
17248
|
+
return true;
|
|
17249
|
+
}
|
|
17250
|
+
function isBytes32Hex(value) {
|
|
17251
|
+
return isBytesHex(value, 32);
|
|
17252
|
+
}
|
|
17253
|
+
function isBytes65Hex(value) {
|
|
17254
|
+
return isBytesHex(value, 65);
|
|
17255
|
+
}
|
|
17256
|
+
function isBytes32(value) {
|
|
17257
|
+
return isBytes(value, 32);
|
|
17258
|
+
}
|
|
17259
|
+
function assertIsBytes32(value) {
|
|
17260
|
+
if (!isBytes32(value)) {
|
|
17261
|
+
throw new InvalidTypeError({
|
|
17262
|
+
type: typeof value,
|
|
17263
|
+
expectedType: 'Bytes32',
|
|
17264
|
+
});
|
|
16918
17265
|
}
|
|
16919
|
-
|
|
16920
|
-
|
|
16921
|
-
|
|
16922
|
-
|
|
16923
|
-
|
|
16924
|
-
|
|
16925
|
-
|
|
16926
|
-
|
|
16927
|
-
|
|
16928
|
-
|
|
16929
|
-
|
|
16930
|
-
|
|
16931
|
-
|
|
16932
|
-
|
|
16933
|
-
|
|
16934
|
-
|
|
16935
|
-
|
|
16936
|
-
|
|
16937
|
-
|
|
16938
|
-
|
|
17266
|
+
}
|
|
17267
|
+
/**
|
|
17268
|
+
* Type guard that checks if a property exists on an object and is a valid hex bytes string.
|
|
17269
|
+
* A valid BytesHex string starts with "0x" followed by an even number of hexadecimal characters.
|
|
17270
|
+
*
|
|
17271
|
+
* @template K - The property key type (string literal)
|
|
17272
|
+
* @param o - The value to check (can be any type)
|
|
17273
|
+
* @param property - The property name to check for
|
|
17274
|
+
* @returns True if `o` is an object with the specified property that is a valid BytesHex string
|
|
17275
|
+
*
|
|
17276
|
+
* @example
|
|
17277
|
+
* ```typescript
|
|
17278
|
+
* const data: unknown = { hash: "0x1234abcd", value: 42 };
|
|
17279
|
+
* if (isRecordBytesHexProperty(data, 'hash')) {
|
|
17280
|
+
* console.log(data.hash); // "0x1234abcd"
|
|
17281
|
+
* }
|
|
17282
|
+
* ```
|
|
17283
|
+
*/
|
|
17284
|
+
function isRecordBytesHexProperty(o, property) {
|
|
17285
|
+
if (!isNonNullableRecordProperty(o, property)) {
|
|
17286
|
+
return false;
|
|
17287
|
+
}
|
|
17288
|
+
return isBytesHex(o[property]);
|
|
17289
|
+
}
|
|
17290
|
+
/**
|
|
17291
|
+
* Assertion function that validates a property exists on an object and is a valid hex bytes string.
|
|
17292
|
+
* A valid BytesHex string must start with "0x" followed by an even number of hexadecimal characters.
|
|
17293
|
+
* Throws an `InvalidPropertyError` if validation fails.
|
|
17294
|
+
*
|
|
17295
|
+
* @template K - The property key type (string literal)
|
|
17296
|
+
* @param o - The value to validate (can be any type)
|
|
17297
|
+
* @param property - The property name to check for
|
|
17298
|
+
* @param objName - The name of the object being validated (used in error messages)
|
|
17299
|
+
* @throws {InvalidPropertyError} When the property is missing, not a string, or not valid BytesHex format
|
|
17300
|
+
* @throws {never} No other errors are thrown
|
|
17301
|
+
*
|
|
17302
|
+
* @example
|
|
17303
|
+
* ```typescript
|
|
17304
|
+
* function processTransaction(data: unknown) {
|
|
17305
|
+
* assertRecordBytesHexProperty(data, 'txHash', 'transaction');
|
|
17306
|
+
* console.log(data.txHash); // e.g., "0x1234..."
|
|
17307
|
+
* }
|
|
17308
|
+
* ```
|
|
17309
|
+
*/
|
|
17310
|
+
function assertRecordBytesHexProperty(o, property, objName) {
|
|
17311
|
+
if (!isRecordBytesHexProperty(o, property)) {
|
|
17312
|
+
throw new InvalidPropertyError({
|
|
17313
|
+
objName,
|
|
17314
|
+
property,
|
|
17315
|
+
expectedType: 'BytesHex',
|
|
17316
|
+
type: typeofProperty(o, property),
|
|
17317
|
+
});
|
|
17318
|
+
}
|
|
17319
|
+
}
|
|
17320
|
+
function isRecordBytesHexNo0xProperty(o, property) {
|
|
17321
|
+
if (!isNonNullableRecordProperty(o, property)) {
|
|
17322
|
+
return false;
|
|
17323
|
+
}
|
|
17324
|
+
return isBytesHexNo0x(o[property]);
|
|
17325
|
+
}
|
|
17326
|
+
function assertRecordBytesHexNo0xProperty(o, property, objName) {
|
|
17327
|
+
if (!isRecordBytesHexNo0xProperty(o, property)) {
|
|
17328
|
+
throw new InvalidPropertyError({
|
|
17329
|
+
objName,
|
|
17330
|
+
property,
|
|
17331
|
+
expectedType: 'BytesHexNo0x',
|
|
17332
|
+
type: typeofProperty(o, property),
|
|
17333
|
+
});
|
|
17334
|
+
}
|
|
17335
|
+
}
|
|
17336
|
+
function assertRecordBytes32HexArrayProperty(o, property, objName) {
|
|
17337
|
+
assertRecordArrayProperty(o, property, objName);
|
|
17338
|
+
const arr = o[property];
|
|
17339
|
+
for (let i = 0; i < arr.length; ++i) {
|
|
17340
|
+
if (!isBytes32Hex(arr[i])) {
|
|
17341
|
+
throw new InvalidPropertyError({
|
|
17342
|
+
objName,
|
|
17343
|
+
property: `${property}[${i}]`,
|
|
17344
|
+
expectedType: 'Bytes32Hex',
|
|
17345
|
+
type: typeof arr[i],
|
|
17346
|
+
});
|
|
17347
|
+
}
|
|
17348
|
+
}
|
|
17349
|
+
}
|
|
17350
|
+
function assertRecordBytes65HexArrayProperty(o, property, objName) {
|
|
17351
|
+
assertRecordArrayProperty(o, property, objName);
|
|
17352
|
+
const arr = o[property];
|
|
17353
|
+
for (let i = 0; i < arr.length; ++i) {
|
|
17354
|
+
if (!isBytes65Hex(arr[i])) {
|
|
17355
|
+
throw new InvalidPropertyError({
|
|
17356
|
+
objName,
|
|
17357
|
+
property: `${property}[${i}]`,
|
|
17358
|
+
expectedType: 'Bytes65Hex',
|
|
17359
|
+
type: typeof arr[i],
|
|
17360
|
+
});
|
|
17361
|
+
}
|
|
17362
|
+
}
|
|
17363
|
+
}
|
|
17364
|
+
/**
|
|
17365
|
+
* Assertion function that validates a property exists on an object, is an array,
|
|
17366
|
+
* and every element is a valid hex bytes string (with "0x" prefix).
|
|
17367
|
+
* Throws an `InvalidPropertyError` if validation fails.
|
|
17368
|
+
*
|
|
17369
|
+
* @template K - The property key type (string literal)
|
|
17370
|
+
* @param o - The value to validate (can be any type)
|
|
17371
|
+
* @param property - The property name to check for
|
|
17372
|
+
* @param objName - The name of the object being validated (used in error messages)
|
|
17373
|
+
* @throws {InvalidPropertyError} When the property is missing, not an array, or any element is not valid BytesHex
|
|
17374
|
+
* @throws {never} No other errors are thrown
|
|
17375
|
+
*
|
|
17376
|
+
* @example
|
|
17377
|
+
* ```typescript
|
|
17378
|
+
* function processHashes(data: unknown) {
|
|
17379
|
+
* assertRecordBytesHexArrayProperty(data, 'txHashes', 'transaction');
|
|
17380
|
+
* data.txHashes.forEach(hash => {
|
|
17381
|
+
* console.log(hash); // e.g., "0x1234abcd..."
|
|
17382
|
+
* });
|
|
17383
|
+
* }
|
|
17384
|
+
* ```
|
|
17385
|
+
*/
|
|
17386
|
+
function assertRecordBytesHexArrayProperty(o, property, objName) {
|
|
17387
|
+
assertRecordArrayProperty(o, property, objName);
|
|
17388
|
+
const arr = o[property];
|
|
17389
|
+
for (let i = 0; i < arr.length; ++i) {
|
|
17390
|
+
if (!isBytesHex(arr[i])) {
|
|
17391
|
+
throw new InvalidPropertyError({
|
|
17392
|
+
objName,
|
|
17393
|
+
property: `${property}[${i}]`,
|
|
17394
|
+
expectedType: 'BytesHex',
|
|
17395
|
+
type: typeof arr[i],
|
|
17396
|
+
});
|
|
17397
|
+
}
|
|
17398
|
+
}
|
|
17399
|
+
}
|
|
17400
|
+
/**
|
|
17401
|
+
* Assertion function that validates a property exists on an object, is an array,
|
|
17402
|
+
* and every element is a valid hex bytes string (without "0x" prefix).
|
|
17403
|
+
* Throws an `InvalidPropertyError` if validation fails.
|
|
17404
|
+
*
|
|
17405
|
+
* @template K - The property key type (string literal)
|
|
17406
|
+
* @param o - The value to validate (can be any type)
|
|
17407
|
+
* @param property - The property name to check for
|
|
17408
|
+
* @param objName - The name of the object being validated (used in error messages)
|
|
17409
|
+
* @throws {InvalidPropertyError} When the property is missing, not an array, or any element is not valid BytesHexNo0x
|
|
17410
|
+
* @throws {never} No other errors are thrown
|
|
17411
|
+
*
|
|
17412
|
+
* @example
|
|
17413
|
+
* ```typescript
|
|
17414
|
+
* function processSignatures(data: unknown) {
|
|
17415
|
+
* assertRecordBytesHexNo0xArrayProperty(data, 'signatures', 'response');
|
|
17416
|
+
* data.signatures.forEach(sig => {
|
|
17417
|
+
* console.log(sig); // e.g., "1234abcd..." (no 0x prefix)
|
|
17418
|
+
* });
|
|
17419
|
+
* }
|
|
17420
|
+
* ```
|
|
17421
|
+
*/
|
|
17422
|
+
function assertRecordBytesHexNo0xArrayProperty(o, property, objName) {
|
|
17423
|
+
assertRecordArrayProperty(o, property, objName);
|
|
17424
|
+
const arr = o[property];
|
|
17425
|
+
for (let i = 0; i < arr.length; ++i) {
|
|
17426
|
+
if (!isBytesHexNo0x(arr[i])) {
|
|
17427
|
+
throw new InvalidPropertyError({
|
|
17428
|
+
objName,
|
|
17429
|
+
property: `${property}[${i}]`,
|
|
17430
|
+
expectedType: 'BytesHexNo0x',
|
|
17431
|
+
type: typeof arr[i],
|
|
17432
|
+
});
|
|
17433
|
+
}
|
|
17434
|
+
}
|
|
17435
|
+
}
|
|
17436
|
+
function isRecordUint8ArrayProperty(o, property) {
|
|
17437
|
+
if (!isNonNullableRecordProperty(o, property)) {
|
|
17438
|
+
return false;
|
|
17439
|
+
}
|
|
17440
|
+
return o[property] instanceof Uint8Array;
|
|
17441
|
+
}
|
|
17442
|
+
function assertUint8ArrayProperty(o, property, objName) {
|
|
17443
|
+
if (!isRecordUint8ArrayProperty(o, property)) {
|
|
17444
|
+
throw new InvalidPropertyError({
|
|
17445
|
+
objName,
|
|
17446
|
+
property,
|
|
17447
|
+
expectedType: 'Uint8Array',
|
|
17448
|
+
type: typeofProperty(o, property),
|
|
17449
|
+
});
|
|
17450
|
+
}
|
|
17451
|
+
}
|
|
17452
|
+
/**
|
|
17453
|
+
* Convert a Uint8Array to a hex string (without 0x prefix).
|
|
17454
|
+
*/
|
|
17455
|
+
function bytesToHexNo0x(bytes) {
|
|
17456
|
+
if (!bytes || bytes?.length === 0) {
|
|
17457
|
+
return '';
|
|
17458
|
+
}
|
|
17459
|
+
let hex = '';
|
|
17460
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
17461
|
+
hex += bytes[i].toString(16).padStart(2, '0');
|
|
17462
|
+
}
|
|
17463
|
+
return hex;
|
|
17464
|
+
}
|
|
17465
|
+
/**
|
|
17466
|
+
* Convert a Uint8Array to a 0x prefixed hex string
|
|
17467
|
+
*/
|
|
17468
|
+
function bytesToHex(bytes) {
|
|
17469
|
+
return `0x${bytesToHexNo0x(bytes)}`;
|
|
17470
|
+
}
|
|
17471
|
+
/**
|
|
17472
|
+
* Convert a hex string prefixed by 0x or not to a Uint8Array
|
|
17473
|
+
*/
|
|
17474
|
+
function hexToBytes(hexString) {
|
|
17475
|
+
const arr = hexString.replace(/^(0x)/, '').match(/.{1,2}/g);
|
|
17476
|
+
if (!arr)
|
|
17477
|
+
return new Uint8Array();
|
|
17478
|
+
return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
|
|
17479
|
+
}
|
|
17480
|
+
/**
|
|
17481
|
+
* Convert a Uint8Array to a bigint
|
|
17482
|
+
*/
|
|
17483
|
+
function bytesToBigInt(byteArray) {
|
|
17484
|
+
if (!byteArray || byteArray.length === 0) {
|
|
17485
|
+
return BigInt(0);
|
|
17486
|
+
}
|
|
17487
|
+
let result = BigInt(0);
|
|
17488
|
+
for (let i = 0; i < byteArray.length; i++) {
|
|
17489
|
+
result = (result << BigInt(8)) | BigInt(byteArray[i]);
|
|
17490
|
+
}
|
|
17491
|
+
return result;
|
|
17492
|
+
}
|
|
17493
|
+
function toHexString(bytes, with0x = false) {
|
|
17494
|
+
return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
|
|
17495
|
+
}
|
|
17496
|
+
async function fetchBytes(url) {
|
|
17497
|
+
const response = await fetch(url);
|
|
17498
|
+
if (!response.ok) {
|
|
17499
|
+
throw new Error(`HTTP error! status: ${response.status} on ${response.url}`);
|
|
17500
|
+
}
|
|
17501
|
+
// Warning : bytes is not widely supported yet!
|
|
17502
|
+
const bytes = typeof response.bytes === 'function'
|
|
17503
|
+
? await response.bytes()
|
|
17504
|
+
: new Uint8Array(await response.arrayBuffer());
|
|
17505
|
+
return bytes;
|
|
17506
|
+
}
|
|
17507
|
+
function concatBytes(...arrays) {
|
|
17508
|
+
let totalLength = 0;
|
|
17509
|
+
for (const arr of arrays) {
|
|
17510
|
+
totalLength += arr.length;
|
|
17511
|
+
}
|
|
17512
|
+
const result = new Uint8Array(totalLength);
|
|
17513
|
+
let offset = 0;
|
|
17514
|
+
for (const arr of arrays) {
|
|
17515
|
+
result.set(arr, offset);
|
|
17516
|
+
offset += arr.length;
|
|
17517
|
+
}
|
|
17518
|
+
return result;
|
|
17519
|
+
}
|
|
17520
|
+
|
|
17521
|
+
// Add type checking
|
|
17522
|
+
const getAddress$1 = (value) => getAddress$2(value);
|
|
17523
|
+
const aclABI$1 = [
|
|
17524
|
+
'function persistAllowed(bytes32 handle, address account) view returns (bool)',
|
|
17525
|
+
];
|
|
17526
|
+
const MAX_USER_DECRYPT_CONTRACT_ADDRESSES = 10;
|
|
17527
|
+
const MAX_USER_DECRYPT_DURATION_DAYS = BigInt(365);
|
|
17528
|
+
function formatAccordingToType(clearValueAsBigInt, type) {
|
|
17529
|
+
if (type === 0) {
|
|
17530
|
+
// ebool
|
|
17531
|
+
return clearValueAsBigInt === BigInt(1);
|
|
17532
|
+
}
|
|
17533
|
+
else if (type === 7) {
|
|
17534
|
+
// eaddress
|
|
17535
|
+
return getAddress$1('0x' + clearValueAsBigInt.toString(16).padStart(40, '0'));
|
|
17536
|
+
}
|
|
17537
|
+
else if (type > 8 || type == 1) {
|
|
17538
|
+
// type == 1 : euint4 (not supported)
|
|
17539
|
+
throw new Error(`Unsupported handle type ${type}`);
|
|
17540
|
+
}
|
|
17541
|
+
// euintXXX
|
|
17542
|
+
return clearValueAsBigInt;
|
|
17543
|
+
}
|
|
17544
|
+
function buildUserDecryptResults(handles, listBigIntDecryptions) {
|
|
17545
|
+
let typesList = [];
|
|
17546
|
+
for (const handle of handles) {
|
|
17547
|
+
const hexPair = handle.slice(-4, -2).toLowerCase();
|
|
17548
|
+
const typeDiscriminant = parseInt(hexPair, 16);
|
|
17549
|
+
typesList.push(typeDiscriminant);
|
|
17550
|
+
}
|
|
17551
|
+
const results = {};
|
|
17552
|
+
handles.forEach((handle, idx) => (results[handle] = formatAccordingToType(listBigIntDecryptions[idx], typesList[idx])));
|
|
17553
|
+
return results;
|
|
17554
|
+
}
|
|
17555
|
+
function checkDeadlineValidity(startTimestamp, durationDays) {
|
|
17556
|
+
if (durationDays === BigInt(0)) {
|
|
17557
|
+
throw Error('durationDays is null');
|
|
17558
|
+
}
|
|
17559
|
+
if (durationDays > MAX_USER_DECRYPT_DURATION_DAYS) {
|
|
17560
|
+
throw Error(`durationDays is above max duration of ${MAX_USER_DECRYPT_DURATION_DAYS}`);
|
|
17561
|
+
}
|
|
17562
|
+
const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
|
|
17563
|
+
if (startTimestamp > currentTimestamp) {
|
|
17564
|
+
throw Error('startTimestamp is set in the future');
|
|
17565
|
+
}
|
|
17566
|
+
const durationInSeconds = durationDays * BigInt(86400);
|
|
17567
|
+
if (startTimestamp + durationInSeconds < currentTimestamp) {
|
|
17568
|
+
throw Error('User decrypt request has expired');
|
|
17569
|
+
}
|
|
17570
|
+
}
|
|
17571
|
+
const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContractAddress, aclContractAddress,
|
|
17572
|
+
//relayerUrl: string,
|
|
17573
|
+
relayerProvider, provider, instanceOptions) => async (_handles, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays, options) => {
|
|
17574
|
+
const extraData = '0x00';
|
|
17575
|
+
let pubKey;
|
|
17576
|
+
let privKey;
|
|
17577
|
+
try {
|
|
17578
|
+
pubKey = TKMS.u8vec_to_ml_kem_pke_pk(hexToBytes(publicKey));
|
|
17579
|
+
privKey = TKMS.u8vec_to_ml_kem_pke_sk(hexToBytes(privateKey));
|
|
17580
|
+
}
|
|
17581
|
+
catch (e) {
|
|
17582
|
+
throw new Error('Invalid public or private key', { cause: e });
|
|
17583
|
+
}
|
|
17584
|
+
// Casting handles if string
|
|
17585
|
+
const signatureSanitized = signature.replace(/^(0x)/, '');
|
|
17586
|
+
const publicKeySanitized = publicKey.replace(/^(0x)/, '');
|
|
17587
|
+
const handles = _handles.map((h) => ({
|
|
17588
|
+
handle: typeof h.handle === 'string'
|
|
17589
|
+
? toHexString(hexToBytes(h.handle), true)
|
|
17590
|
+
: toHexString(h.handle, true),
|
|
17591
|
+
contractAddress: getAddress$1(h.contractAddress),
|
|
17592
|
+
}));
|
|
17593
|
+
checkEncryptedBits(handles.map((h) => h.handle));
|
|
17594
|
+
checkDeadlineValidity(BigInt(startTimestamp), BigInt(durationDays));
|
|
17595
|
+
const acl = new ethers.Contract(aclContractAddress, aclABI$1, provider);
|
|
17596
|
+
const verifications = handles.map(async ({ handle, contractAddress }) => {
|
|
17597
|
+
const userAllowed = await acl.persistAllowed(handle, userAddress);
|
|
17598
|
+
const contractAllowed = await acl.persistAllowed(handle, contractAddress);
|
|
17599
|
+
if (!userAllowed) {
|
|
17600
|
+
throw new Error(`User ${userAddress} is not authorized to user decrypt handle ${handle}!`);
|
|
17601
|
+
}
|
|
17602
|
+
if (!contractAllowed) {
|
|
17603
|
+
throw new Error(`dapp contract ${contractAddress} is not authorized to user decrypt handle ${handle}!`);
|
|
17604
|
+
}
|
|
17605
|
+
if (userAddress === contractAddress) {
|
|
17606
|
+
throw new Error(`userAddress ${userAddress} should not be equal to contractAddress when requesting user decryption!`);
|
|
17607
|
+
}
|
|
17608
|
+
});
|
|
17609
|
+
const contractAddressesLength = contractAddresses.length;
|
|
17610
|
+
if (contractAddressesLength === 0) {
|
|
17611
|
+
throw Error('contractAddresses is empty');
|
|
17612
|
+
}
|
|
17613
|
+
if (contractAddressesLength > MAX_USER_DECRYPT_CONTRACT_ADDRESSES) {
|
|
17614
|
+
throw Error(`contractAddresses max length of ${MAX_USER_DECRYPT_CONTRACT_ADDRESSES} exceeded`);
|
|
17615
|
+
}
|
|
17616
|
+
await Promise.all(verifications).catch((e) => {
|
|
17617
|
+
throw e;
|
|
17618
|
+
});
|
|
17619
|
+
const payloadForRequest = {
|
|
17620
|
+
handleContractPairs: handles,
|
|
17621
|
+
requestValidity: {
|
|
17622
|
+
startTimestamp: startTimestamp.toString(), // Convert to string
|
|
17623
|
+
durationDays: durationDays.toString(), // Convert to string
|
|
17624
|
+
},
|
|
17625
|
+
contractsChainId: chainId.toString(), // Convert to string
|
|
17626
|
+
contractAddresses: contractAddresses.map((c) => getAddress$1(c)),
|
|
17627
|
+
userAddress: getAddress$1(userAddress),
|
|
17628
|
+
signature: signatureSanitized,
|
|
17629
|
+
publicKey: publicKeySanitized,
|
|
17630
|
+
extraData,
|
|
17631
|
+
};
|
|
17632
|
+
const json = await relayerProvider.fetchPostUserDecrypt(payloadForRequest, options ?? instanceOptions);
|
|
17633
|
+
// const json = await fetchRelayerJsonRpcPost(
|
|
17634
|
+
// 'USER_DECRYPT',
|
|
17635
|
+
// `${relayerUrl}/v1/user-decrypt`,
|
|
17636
|
+
// payloadForRequest,
|
|
17637
|
+
// instanceOptions ?? options,
|
|
17638
|
+
// );
|
|
17639
|
+
// assume the KMS Signers have the correct order
|
|
17640
|
+
let indexedKmsSigners = kmsSigners.map((signer, index) => {
|
|
17641
|
+
return TKMS.new_server_id_addr(index + 1, signer);
|
|
16939
17642
|
});
|
|
16940
17643
|
const client = TKMS.new_client(indexedKmsSigners, userAddress, 'default');
|
|
16941
17644
|
try {
|
|
@@ -16957,7 +17660,8 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
|
|
|
16957
17660
|
ciphertext_handles: handles.map((h) => h.handle.replace(/^0x/, '')),
|
|
16958
17661
|
eip712_verifying_contract: verifyingContractAddress,
|
|
16959
17662
|
};
|
|
16960
|
-
const decryption = TKMS.process_user_decryption_resp_from_js(client, payloadForVerification, eip712Domain, json.response,
|
|
17663
|
+
const decryption = TKMS.process_user_decryption_resp_from_js(client, payloadForVerification, eip712Domain, json, //json.response,
|
|
17664
|
+
pubKey, privKey, true);
|
|
16961
17665
|
const listBigIntDecryptions = decryption.map((d) => bytesToBigInt(d.bytes));
|
|
16962
17666
|
const results = buildUserDecryptResults(handles.map((h) => h.handle), listBigIntDecryptions);
|
|
16963
17667
|
return results;
|
|
@@ -16984,10 +17688,10 @@ const checkEncryptedValue = (value, bits) => {
|
|
|
16984
17688
|
}
|
|
16985
17689
|
};
|
|
16986
17690
|
const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKey, publicParams, contractAddress, userAddress, }) => {
|
|
16987
|
-
if (!isAddress(contractAddress)) {
|
|
17691
|
+
if (!isAddress$1(contractAddress)) {
|
|
16988
17692
|
throw new Error('Contract address is not a valid address.');
|
|
16989
17693
|
}
|
|
16990
|
-
if (!isAddress(userAddress)) {
|
|
17694
|
+
if (!isAddress$1(userAddress)) {
|
|
16991
17695
|
throw new Error('User address is not a valid address.');
|
|
16992
17696
|
}
|
|
16993
17697
|
const publicKey = tfheCompactPublicKey;
|
|
@@ -17053,7 +17757,7 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
|
|
|
17053
17757
|
return this;
|
|
17054
17758
|
},
|
|
17055
17759
|
addAddress(value) {
|
|
17056
|
-
if (!isAddress(value)) {
|
|
17760
|
+
if (!isAddress$1(value)) {
|
|
17057
17761
|
throw new Error('The value must be a valid address.');
|
|
17058
17762
|
}
|
|
17059
17763
|
checkLimit(160);
|
|
@@ -17084,10 +17788,10 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
|
|
|
17084
17788
|
};
|
|
17085
17789
|
const closestPP = getClosestPP();
|
|
17086
17790
|
const pp = publicParams[closestPP].publicParams;
|
|
17087
|
-
const buffContract =
|
|
17088
|
-
const buffUser =
|
|
17089
|
-
const buffAcl =
|
|
17090
|
-
const buffChainId =
|
|
17791
|
+
const buffContract = hexToBytes(contractAddress);
|
|
17792
|
+
const buffUser = hexToBytes(userAddress);
|
|
17793
|
+
const buffAcl = hexToBytes(aclContractAddress);
|
|
17794
|
+
const buffChainId = hexToBytes(chainId.toString(16).padStart(64, '0'));
|
|
17091
17795
|
const auxData = new Uint8Array(buffContract.length + buffUser.length + buffAcl.length + 32);
|
|
17092
17796
|
auxData.set(buffContract, 0);
|
|
17093
17797
|
auxData.set(buffUser, 20);
|
|
@@ -17100,93 +17804,340 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
|
|
|
17100
17804
|
};
|
|
17101
17805
|
};
|
|
17102
17806
|
|
|
17103
|
-
/**
|
|
17104
|
-
* **FHE Type Mapping for Input Builders**
|
|
17105
|
-
* * Maps the **number of encrypted bits** used by a FHEVM primary type
|
|
17106
|
-
* to its corresponding **FheTypeId**. This constant is primarily used by
|
|
17107
|
-
* `EncryptedInput` and `RelayerEncryptedInput` builders to determine the correct
|
|
17108
|
-
* input type and calculate the total required bit-length.
|
|
17109
|
-
*
|
|
17110
|
-
* **Structure: \{ Encrypted Bit Length: FheTypeId \}**
|
|
17111
|
-
*
|
|
17112
|
-
* | Bits | FheTypeId | FHE Type Name | Note |
|
|
17113
|
-
* | :--- | :-------- | :------------ | :--- |
|
|
17114
|
-
* | 2 | 0 | `ebool` | The boolean type. |
|
|
17115
|
-
* | (N/A)| 1 | `euint4` | **Deprecated** and omitted from this map. |
|
|
17116
|
-
* | 8 | 2 | `euint8` | |
|
|
17117
|
-
* | 16 | 3 | `euint16` | |
|
|
17118
|
-
* | 32 | 4 | `euint32` | |
|
|
17119
|
-
* | 64 | 5 | `euint64` | |
|
|
17120
|
-
* | 128 | 6 | `euint128` | |
|
|
17121
|
-
* | 160 | 7 | `eaddress` | Used for encrypted Ethereum addresses. |
|
|
17122
|
-
* | 256 | 8 | `euint256` | The maximum supported integer size. |
|
|
17123
|
-
*/
|
|
17124
|
-
const ENCRYPTION_TYPES = {
|
|
17125
|
-
2: 0, // ebool (FheTypeId=0) is using 2 encrypted bits
|
|
17126
|
-
// euint4 (FheTypeId=1) is deprecated
|
|
17127
|
-
8: 2, // euint8 (FheTypeId=2) is using 8 encrypted bits
|
|
17128
|
-
16: 3, // euint16 (FheTypeId=3) is using 16 encrypted bits
|
|
17129
|
-
32: 4, // euint32 (FheTypeId=4) is using 32 encrypted bits
|
|
17130
|
-
64: 5, // euint64 (FheTypeId=5) is using 64 encrypted bits
|
|
17131
|
-
128: 6, // euint128 (FheTypeId=128) is using 128 encrypted bits
|
|
17132
|
-
160: 7, // eaddress (FheTypeId=7) is using 160 encrypted bits
|
|
17133
|
-
256: 8, // euint256 (FheTypeId=8) is using 256 encrypted bits
|
|
17134
|
-
};
|
|
17135
|
-
|
|
17136
17807
|
const MAX_UINT64 = BigInt('18446744073709551615'); // 2^64 - 1
|
|
17137
|
-
|
|
17138
|
-
const
|
|
17139
|
-
const
|
|
17140
|
-
|
|
17141
|
-
|
|
17142
|
-
|
|
17143
|
-
|
|
17144
|
-
|
|
17145
|
-
|
|
17146
|
-
|
|
17147
|
-
|
|
17148
|
-
const chainId32Bytes = Buffer.from(hex, 'hex');
|
|
17149
|
-
const handles = bitwidths.map((bitwidth, encryptionIndex) => {
|
|
17150
|
-
const encryptionType = ENCRYPTION_TYPES[bitwidth];
|
|
17151
|
-
const encryptionIndex1Byte = Buffer.from([encryptionIndex]);
|
|
17152
|
-
const handleHash = createHash('keccak256')
|
|
17153
|
-
.update(Buffer.from(HANDLE_HASH_DOMAIN_SEPARATOR))
|
|
17154
|
-
.update(blob_hash)
|
|
17155
|
-
.update(encryptionIndex1Byte)
|
|
17156
|
-
.update(aclContractAddress20Bytes)
|
|
17157
|
-
.update(chainId32Bytes)
|
|
17158
|
-
.digest();
|
|
17159
|
-
const dataInput = new Uint8Array(32);
|
|
17160
|
-
dataInput.set(handleHash, 0);
|
|
17161
|
-
// Check if chainId exceeds 8 bytes
|
|
17162
|
-
if (BigInt(chainId) > MAX_UINT64) {
|
|
17163
|
-
throw new Error('ChainId exceeds maximum allowed value (8 bytes)'); // fhevm assumes chainID is only taking up to 8 bytes
|
|
17164
|
-
}
|
|
17165
|
-
const chainId8Bytes = fromHexString(hex).slice(24, 32);
|
|
17166
|
-
dataInput[21] = encryptionIndex;
|
|
17167
|
-
dataInput.set(chainId8Bytes, 22);
|
|
17168
|
-
dataInput[30] = encryptionType;
|
|
17169
|
-
dataInput[31] = ciphertextVersion;
|
|
17170
|
-
return dataInput;
|
|
17171
|
-
});
|
|
17172
|
-
return handles;
|
|
17173
|
-
};
|
|
17174
|
-
|
|
17175
|
-
// Add type checking
|
|
17176
|
-
const getAddress = (value) => getAddress$2(value);
|
|
17177
|
-
const currentCiphertextVersion = () => {
|
|
17178
|
-
return 0;
|
|
17179
|
-
};
|
|
17180
|
-
function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold) {
|
|
17181
|
-
const addressMap = new Map();
|
|
17182
|
-
recoveredAddresses.forEach((address, index) => {
|
|
17183
|
-
if (addressMap.has(address)) {
|
|
17184
|
-
const duplicateValue = address;
|
|
17185
|
-
throw new Error(`Duplicate coprocessor signer address found: ${duplicateValue} appears multiple times in recovered addresses`);
|
|
17808
|
+
BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
|
17809
|
+
const MAX_UINT32 = 0xffffffff;
|
|
17810
|
+
const MAX_UINT8 = 0xff;
|
|
17811
|
+
function numberToHex(num) {
|
|
17812
|
+
let hex = num.toString(16);
|
|
17813
|
+
return hex.length % 2 ? '0' + hex : hex;
|
|
17814
|
+
}
|
|
17815
|
+
function isUintNumber(value) {
|
|
17816
|
+
if (typeof value === 'number') {
|
|
17817
|
+
if (value < 0) {
|
|
17818
|
+
return false;
|
|
17186
17819
|
}
|
|
17187
|
-
|
|
17188
|
-
}
|
|
17189
|
-
|
|
17820
|
+
return Number.isInteger(value);
|
|
17821
|
+
}
|
|
17822
|
+
return false;
|
|
17823
|
+
}
|
|
17824
|
+
function isUintBigInt(value) {
|
|
17825
|
+
if (typeof value === 'bigint') {
|
|
17826
|
+
return value >= 0;
|
|
17827
|
+
}
|
|
17828
|
+
return false;
|
|
17829
|
+
}
|
|
17830
|
+
function isUint(value) {
|
|
17831
|
+
if (isUintNumber(value)) {
|
|
17832
|
+
return true;
|
|
17833
|
+
}
|
|
17834
|
+
else if (isUintBigInt(value)) {
|
|
17835
|
+
return true;
|
|
17836
|
+
}
|
|
17837
|
+
return false;
|
|
17838
|
+
}
|
|
17839
|
+
function isUint8(value) {
|
|
17840
|
+
if (!isUint(value)) {
|
|
17841
|
+
return false;
|
|
17842
|
+
}
|
|
17843
|
+
return value <= MAX_UINT8;
|
|
17844
|
+
}
|
|
17845
|
+
function isUint32(value) {
|
|
17846
|
+
if (!isUint(value)) {
|
|
17847
|
+
return false;
|
|
17848
|
+
}
|
|
17849
|
+
return value <= MAX_UINT32;
|
|
17850
|
+
}
|
|
17851
|
+
function isUint64(value) {
|
|
17852
|
+
if (!isUint(value)) {
|
|
17853
|
+
return false;
|
|
17854
|
+
}
|
|
17855
|
+
return value <= MAX_UINT64;
|
|
17856
|
+
}
|
|
17857
|
+
function uint32ToBytes32(uint32) {
|
|
17858
|
+
if (!isUint32(uint32)) {
|
|
17859
|
+
throw new InvalidTypeError({ expectedType: 'Uint32' });
|
|
17860
|
+
}
|
|
17861
|
+
const buffer = new ArrayBuffer(32);
|
|
17862
|
+
const view = new DataView(buffer);
|
|
17863
|
+
view.setUint32(28, Number(uint32), false);
|
|
17864
|
+
return new Uint8Array(buffer);
|
|
17865
|
+
}
|
|
17866
|
+
function assertIsUint8(value) {
|
|
17867
|
+
if (!isUint8(value)) {
|
|
17868
|
+
throw new InvalidTypeError({
|
|
17869
|
+
type: typeof value,
|
|
17870
|
+
expectedType: 'Uint8',
|
|
17871
|
+
});
|
|
17872
|
+
}
|
|
17873
|
+
}
|
|
17874
|
+
function assertIsUint64(value) {
|
|
17875
|
+
if (!isUint64(value)) {
|
|
17876
|
+
throw new InvalidTypeError({
|
|
17877
|
+
type: typeof value,
|
|
17878
|
+
expectedType: 'Uint64',
|
|
17879
|
+
});
|
|
17880
|
+
}
|
|
17881
|
+
}
|
|
17882
|
+
function isRecordUintProperty(o, property) {
|
|
17883
|
+
if (!isNonNullableRecordProperty(o, property)) {
|
|
17884
|
+
return false;
|
|
17885
|
+
}
|
|
17886
|
+
return isUint(o[property]);
|
|
17887
|
+
}
|
|
17888
|
+
function assertRecordUintProperty(o, property, objName) {
|
|
17889
|
+
if (!isRecordUintProperty(o, property)) {
|
|
17890
|
+
throw new InvalidPropertyError({
|
|
17891
|
+
objName,
|
|
17892
|
+
property,
|
|
17893
|
+
type: typeofProperty(o, property),
|
|
17894
|
+
expectedType: 'Uint',
|
|
17895
|
+
});
|
|
17896
|
+
}
|
|
17897
|
+
}
|
|
17898
|
+
|
|
17899
|
+
class ChecksummedAddressError extends RelayerErrorBase {
|
|
17900
|
+
constructor({ address }) {
|
|
17901
|
+
super({
|
|
17902
|
+
message: `Checksummed address "${address}" is invalid.`,
|
|
17903
|
+
name: 'ChecksummedAddressError',
|
|
17904
|
+
});
|
|
17905
|
+
}
|
|
17906
|
+
}
|
|
17907
|
+
|
|
17908
|
+
function isChecksummedAddress(value) {
|
|
17909
|
+
if (typeof value !== 'string') {
|
|
17910
|
+
return false;
|
|
17911
|
+
}
|
|
17912
|
+
if (!value.startsWith('0x')) {
|
|
17913
|
+
return false;
|
|
17914
|
+
}
|
|
17915
|
+
if (value.length !== 42) {
|
|
17916
|
+
return false;
|
|
17917
|
+
}
|
|
17918
|
+
try {
|
|
17919
|
+
const a = getAddress$2(value);
|
|
17920
|
+
return a === value;
|
|
17921
|
+
}
|
|
17922
|
+
catch (e) {
|
|
17923
|
+
return false;
|
|
17924
|
+
}
|
|
17925
|
+
}
|
|
17926
|
+
function assertIsChecksummedAddress(value) {
|
|
17927
|
+
if (!isChecksummedAddress(value)) {
|
|
17928
|
+
throw new ChecksummedAddressError({ address: String(value) });
|
|
17929
|
+
}
|
|
17930
|
+
}
|
|
17931
|
+
function isAddress(value) {
|
|
17932
|
+
if (typeof value !== 'string') {
|
|
17933
|
+
return false;
|
|
17934
|
+
}
|
|
17935
|
+
if (!value.startsWith('0x')) {
|
|
17936
|
+
return false;
|
|
17937
|
+
}
|
|
17938
|
+
if (value.length !== 42) {
|
|
17939
|
+
return false;
|
|
17940
|
+
}
|
|
17941
|
+
if (!isAddress$1(value)) {
|
|
17942
|
+
return false;
|
|
17943
|
+
}
|
|
17944
|
+
return true;
|
|
17945
|
+
}
|
|
17946
|
+
function checksummedAddressToBytes20(address) {
|
|
17947
|
+
if (!isAddress(address)) {
|
|
17948
|
+
throw new InvalidTypeError({ expectedType: 'ChecksummedAddress' });
|
|
17949
|
+
}
|
|
17950
|
+
const hex = remove0x(address);
|
|
17951
|
+
const bytes = new Uint8Array(20);
|
|
17952
|
+
for (let i = 0; i < 20; i++) {
|
|
17953
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
17954
|
+
}
|
|
17955
|
+
return bytes;
|
|
17956
|
+
}
|
|
17957
|
+
|
|
17958
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
17959
|
+
// FhevmHandle
|
|
17960
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
17961
|
+
class FhevmHandle {
|
|
17962
|
+
_hash21;
|
|
17963
|
+
_chainId;
|
|
17964
|
+
_fheTypeId;
|
|
17965
|
+
_version;
|
|
17966
|
+
_computed;
|
|
17967
|
+
_index;
|
|
17968
|
+
static RAW_CT_HASH_DOMAIN_SEPARATOR = 'ZK-w_rct';
|
|
17969
|
+
static HANDLE_HASH_DOMAIN_SEPARATOR = 'ZK-w_hdl';
|
|
17970
|
+
static FheTypeIdToEncryptionBitwidths = {
|
|
17971
|
+
0: 2,
|
|
17972
|
+
2: 8,
|
|
17973
|
+
3: 16,
|
|
17974
|
+
4: 32,
|
|
17975
|
+
5: 64,
|
|
17976
|
+
6: 128,
|
|
17977
|
+
7: 160,
|
|
17978
|
+
8: 256,
|
|
17979
|
+
};
|
|
17980
|
+
static FheTypeEncryptionBitwidthsToId = {
|
|
17981
|
+
2: 0,
|
|
17982
|
+
8: 2,
|
|
17983
|
+
16: 3,
|
|
17984
|
+
32: 4,
|
|
17985
|
+
64: 5,
|
|
17986
|
+
128: 6,
|
|
17987
|
+
160: 7,
|
|
17988
|
+
256: 8,
|
|
17989
|
+
};
|
|
17990
|
+
static {
|
|
17991
|
+
Object.freeze(FhevmHandle.FheTypeIdToEncryptionBitwidths);
|
|
17992
|
+
Object.freeze(FhevmHandle.FheTypeEncryptionBitwidthsToId);
|
|
17993
|
+
}
|
|
17994
|
+
constructor(hash21, chainId, fheTypeId, version, computed, index) {
|
|
17995
|
+
if (BigInt(chainId) > MAX_UINT64) {
|
|
17996
|
+
// fhevm assumes chainID is only taking up to 8 bytes
|
|
17997
|
+
throw new Error('ChainId exceeds maximum allowed value (8 bytes)');
|
|
17998
|
+
}
|
|
17999
|
+
this._hash21 = hash21;
|
|
18000
|
+
this._chainId = chainId;
|
|
18001
|
+
this._fheTypeId = fheTypeId;
|
|
18002
|
+
this._version = version;
|
|
18003
|
+
this._computed = computed;
|
|
18004
|
+
if (index !== undefined) {
|
|
18005
|
+
this._index = index;
|
|
18006
|
+
}
|
|
18007
|
+
}
|
|
18008
|
+
get hash21() {
|
|
18009
|
+
return this._hash21;
|
|
18010
|
+
}
|
|
18011
|
+
get chainId() {
|
|
18012
|
+
return this._chainId;
|
|
18013
|
+
}
|
|
18014
|
+
get fheTypeId() {
|
|
18015
|
+
return this._fheTypeId;
|
|
18016
|
+
}
|
|
18017
|
+
get version() {
|
|
18018
|
+
return this._version;
|
|
18019
|
+
}
|
|
18020
|
+
get computed() {
|
|
18021
|
+
return this._computed;
|
|
18022
|
+
}
|
|
18023
|
+
get index() {
|
|
18024
|
+
return this._index;
|
|
18025
|
+
}
|
|
18026
|
+
static fromZKProof(params) {
|
|
18027
|
+
assertIsChecksummedAddress(params.aclAddress);
|
|
18028
|
+
assertIsUint64(params.chainId);
|
|
18029
|
+
assertIsUint8(params.ciphertextVersion);
|
|
18030
|
+
let fheTypeIds;
|
|
18031
|
+
if (params.fheTypeIds !== undefined) {
|
|
18032
|
+
fheTypeIds = params.fheTypeIds;
|
|
18033
|
+
}
|
|
18034
|
+
else if (params.fheTypeEncryptionBitwidths !== undefined) {
|
|
18035
|
+
fheTypeIds = params.fheTypeEncryptionBitwidths.map((w) => FhevmHandle.FheTypeEncryptionBitwidthsToId[w]);
|
|
18036
|
+
}
|
|
18037
|
+
else {
|
|
18038
|
+
throw new InternalError({
|
|
18039
|
+
message: 'createInputHandles requires either fheTypeIds or fheTypeEncryptionBitwidths',
|
|
18040
|
+
});
|
|
18041
|
+
}
|
|
18042
|
+
assertIsUint8(fheTypeIds.length);
|
|
18043
|
+
let ciphertextWithZKProof;
|
|
18044
|
+
if (typeof params.ciphertextWithZKProof === 'string') {
|
|
18045
|
+
ciphertextWithZKProof = hexToBytes(params.ciphertextWithZKProof);
|
|
18046
|
+
}
|
|
18047
|
+
else if (params.ciphertextWithZKProof instanceof Uint8Array) {
|
|
18048
|
+
ciphertextWithZKProof = params.ciphertextWithZKProof;
|
|
18049
|
+
}
|
|
18050
|
+
else {
|
|
18051
|
+
throw new InternalError({
|
|
18052
|
+
message: 'Invalid ciphertextWithZKProof argument',
|
|
18053
|
+
});
|
|
18054
|
+
}
|
|
18055
|
+
if (ciphertextWithZKProof.length === 0) {
|
|
18056
|
+
throw new InternalError({
|
|
18057
|
+
message: 'Invalid ciphertextWithZKProof argument',
|
|
18058
|
+
});
|
|
18059
|
+
}
|
|
18060
|
+
const encoder = new TextEncoder();
|
|
18061
|
+
const domainSepBytes = encoder.encode(FhevmHandle.RAW_CT_HASH_DOMAIN_SEPARATOR);
|
|
18062
|
+
const blobHashBytes32Hex = keccak256(concatBytes(domainSepBytes, ciphertextWithZKProof));
|
|
18063
|
+
const handles = [];
|
|
18064
|
+
for (let i = 0; i < fheTypeIds.length; ++i) {
|
|
18065
|
+
const hash21 = FhevmHandle._computeInputHash21(hexToBytes(blobHashBytes32Hex), params.aclAddress, params.chainId, i);
|
|
18066
|
+
handles.push(new FhevmHandle(hash21, params.chainId, fheTypeIds[i], params.ciphertextVersion, false, i));
|
|
18067
|
+
}
|
|
18068
|
+
return handles;
|
|
18069
|
+
}
|
|
18070
|
+
/**
|
|
18071
|
+
* blobHashBytes32 = keccak256(ciphertextWithZKProof)
|
|
18072
|
+
*/
|
|
18073
|
+
static _computeInputHash21(blobHashBytes32, aclAddress, chainId, index) {
|
|
18074
|
+
/*
|
|
18075
|
+
https://github.com/zama-ai/fhevm/blob/8ffbd5906ab3d57af178e049930e3fc065c9d4b3/coprocessor/fhevm-engine/zkproof-worker/src/verifier.rs#L431C7-L431C8
|
|
18076
|
+
|
|
18077
|
+
handle_hash = Bytes("ZK-w_hdl") + blobHash 32 Bytes + index 1 Byte + aclAddress 20 Bytes + chainId 32 bytes
|
|
18078
|
+
===========================================================================================================
|
|
18079
|
+
|
|
18080
|
+
const HANDLE_HASH_DOMAIN_SEPARATOR: [u8; 8] = *b"ZK-w_hdl";
|
|
18081
|
+
|
|
18082
|
+
let mut handle_hash = Keccak256::new();
|
|
18083
|
+
handle_hash.update(HANDLE_HASH_DOMAIN_SEPARATOR);
|
|
18084
|
+
handle_hash.update(blob_hash);
|
|
18085
|
+
handle_hash.update([ct_idx as u8]);
|
|
18086
|
+
handle_hash.update(
|
|
18087
|
+
Address::from_str(&aux_data.acl_contract_address)
|
|
18088
|
+
.expect("valid acl_contract_address")
|
|
18089
|
+
.into_array(),
|
|
18090
|
+
);
|
|
18091
|
+
handle_hash.update(chain_id_bytes);
|
|
18092
|
+
let mut handle = handle_hash.finalize().to_vec();
|
|
18093
|
+
assert_eq!(handle.len(), 32);
|
|
18094
|
+
|
|
18095
|
+
*/
|
|
18096
|
+
assertIsBytes32(blobHashBytes32);
|
|
18097
|
+
assertIsChecksummedAddress(aclAddress);
|
|
18098
|
+
assertIsUint8(index);
|
|
18099
|
+
assertIsUint64(chainId);
|
|
18100
|
+
const encryptionIndexByte1 = new Uint8Array([index]);
|
|
18101
|
+
const aclContractAddressBytes20 = checksummedAddressToBytes20(aclAddress);
|
|
18102
|
+
const chainIdBytes32 = uint32ToBytes32(chainId);
|
|
18103
|
+
const encoder = new TextEncoder();
|
|
18104
|
+
const domainSepBytes = encoder.encode(FhevmHandle.HANDLE_HASH_DOMAIN_SEPARATOR);
|
|
18105
|
+
return keccak256(concatBytes(domainSepBytes, blobHashBytes32, encryptionIndexByte1, aclContractAddressBytes20, chainIdBytes32));
|
|
18106
|
+
}
|
|
18107
|
+
toBytes32() {
|
|
18108
|
+
assertRelayer((this._index === undefined && this._computed) ||
|
|
18109
|
+
(this._index !== undefined && this._index < 255 && !this._computed));
|
|
18110
|
+
const chainId32Bytes = uint32ToBytes32(this._chainId);
|
|
18111
|
+
const chainId8Bytes = chainId32Bytes.subarray(24, 32);
|
|
18112
|
+
const handleHash = hexToBytes(this._hash21);
|
|
18113
|
+
const handleBytes32AsBytes = new Uint8Array(32);
|
|
18114
|
+
handleBytes32AsBytes.set(handleHash, 0);
|
|
18115
|
+
handleBytes32AsBytes[21] = this._index === undefined ? 255 : this._index;
|
|
18116
|
+
handleBytes32AsBytes.set(chainId8Bytes, 22);
|
|
18117
|
+
handleBytes32AsBytes[30] = this._fheTypeId;
|
|
18118
|
+
handleBytes32AsBytes[31] = this._version;
|
|
18119
|
+
return handleBytes32AsBytes;
|
|
18120
|
+
}
|
|
18121
|
+
toBytes32Hex() {
|
|
18122
|
+
return bytesToHex(this.toBytes32());
|
|
18123
|
+
}
|
|
18124
|
+
}
|
|
18125
|
+
|
|
18126
|
+
// Add type checking
|
|
18127
|
+
const getAddress = (value) => getAddress$2(value);
|
|
18128
|
+
const currentCiphertextVersion = () => {
|
|
18129
|
+
return 0;
|
|
18130
|
+
};
|
|
18131
|
+
function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold) {
|
|
18132
|
+
const addressMap = new Map();
|
|
18133
|
+
recoveredAddresses.forEach((address, index) => {
|
|
18134
|
+
if (addressMap.has(address)) {
|
|
18135
|
+
const duplicateValue = address;
|
|
18136
|
+
throw new Error(`Duplicate coprocessor signer address found: ${duplicateValue} appears multiple times in recovered addresses`);
|
|
18137
|
+
}
|
|
18138
|
+
addressMap.set(address, index);
|
|
18139
|
+
});
|
|
18140
|
+
for (const address of recoveredAddresses) {
|
|
17190
18141
|
if (!coprocessorSigners.includes(address)) {
|
|
17191
18142
|
throw new Error(`Invalid address found: ${address} is not in the list of coprocessor signers`);
|
|
17192
18143
|
}
|
|
@@ -17194,7 +18145,8 @@ function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold)
|
|
|
17194
18145
|
return recoveredAddresses.length >= threshold;
|
|
17195
18146
|
}
|
|
17196
18147
|
function isFhevmRelayerInputProofResponse(json) {
|
|
17197
|
-
const response = json
|
|
18148
|
+
const response = json;
|
|
18149
|
+
// const response = json.response as unknown;
|
|
17198
18150
|
if (typeof response !== 'object' || response === null) {
|
|
17199
18151
|
return false;
|
|
17200
18152
|
}
|
|
@@ -17207,11 +18159,13 @@ function isFhevmRelayerInputProofResponse(json) {
|
|
|
17207
18159
|
return (response.signatures.every((s) => typeof s === 'string') &&
|
|
17208
18160
|
response.handles.every((h) => typeof h === 'string'));
|
|
17209
18161
|
}
|
|
17210
|
-
const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId,
|
|
17211
|
-
|
|
18162
|
+
const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId,
|
|
18163
|
+
//relayerUrl: string,
|
|
18164
|
+
relayerProvider, tfheCompactPublicKey, publicParams, coprocessorSigners, thresholdCoprocessorSigners, instanceOptions) => (contractAddress, userAddress) => {
|
|
18165
|
+
if (!isAddress$1(contractAddress)) {
|
|
17212
18166
|
throw new Error('Contract address is not a valid address.');
|
|
17213
18167
|
}
|
|
17214
|
-
if (!isAddress(userAddress)) {
|
|
18168
|
+
if (!isAddress$1(userAddress)) {
|
|
17215
18169
|
throw new Error('User address is not a valid address.');
|
|
17216
18170
|
}
|
|
17217
18171
|
const input = createEncryptedInput({
|
|
@@ -17263,6 +18217,7 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
|
|
|
17263
18217
|
const extraData = '0x00';
|
|
17264
18218
|
const bits = input.getBits();
|
|
17265
18219
|
const ciphertext = input.encrypt();
|
|
18220
|
+
//console.log(`ciphertext=${toHexString(ciphertext)}`);
|
|
17266
18221
|
const payload = {
|
|
17267
18222
|
contractAddress: getAddress(contractAddress),
|
|
17268
18223
|
userAddress: getAddress(userAddress),
|
|
@@ -17270,14 +18225,29 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
|
|
|
17270
18225
|
contractChainId: ('0x' + chainId.toString(16)),
|
|
17271
18226
|
extraData,
|
|
17272
18227
|
};
|
|
17273
|
-
const json = await
|
|
18228
|
+
const json = await relayerProvider.fetchPostInputProof(payload, options ?? instanceOptions);
|
|
18229
|
+
// const json = await fetchRelayerJsonRpcPost(
|
|
18230
|
+
// 'INPUT_PROOF',
|
|
18231
|
+
// `${relayerUrl}/v1/input-proof`,
|
|
18232
|
+
// payload,
|
|
18233
|
+
// options ?? instanceOptions,
|
|
18234
|
+
// );
|
|
17274
18235
|
if (!isFhevmRelayerInputProofResponse(json)) {
|
|
17275
18236
|
throwRelayerInternalError('INPUT_PROOF', json);
|
|
17276
18237
|
}
|
|
17277
|
-
const
|
|
18238
|
+
const fhevmHandles = FhevmHandle.fromZKProof({
|
|
18239
|
+
ciphertextWithZKProof: ciphertext,
|
|
18240
|
+
chainId,
|
|
18241
|
+
aclAddress: aclContractAddress,
|
|
18242
|
+
ciphertextVersion: currentCiphertextVersion(),
|
|
18243
|
+
fheTypeEncryptionBitwidths: bits,
|
|
18244
|
+
});
|
|
18245
|
+
const handles = fhevmHandles.map((h) => h.toBytes32());
|
|
18246
|
+
//const result = json.response;
|
|
18247
|
+
const result = json;
|
|
17278
18248
|
// Note that the hex strings returned by the relayer do have have the 0x prefix
|
|
17279
|
-
if (
|
|
17280
|
-
const responseHandles =
|
|
18249
|
+
if (result.handles && result.handles.length > 0) {
|
|
18250
|
+
const responseHandles = result.handles.map(hexToBytes);
|
|
17281
18251
|
if (handles.length != responseHandles.length) {
|
|
17282
18252
|
throw new Error(`Incorrect Handles list sizes: (expected) ${handles.length} != ${responseHandles.length} (received)`);
|
|
17283
18253
|
}
|
|
@@ -17291,7 +18261,7 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
|
|
|
17291
18261
|
}
|
|
17292
18262
|
}
|
|
17293
18263
|
}
|
|
17294
|
-
const signatures =
|
|
18264
|
+
const signatures = result.signatures;
|
|
17295
18265
|
// verify signatures for inputs:
|
|
17296
18266
|
const domain = {
|
|
17297
18267
|
name: 'InputVerification',
|
|
@@ -17334,7 +18304,7 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
|
|
|
17334
18304
|
inputProof += extraData.slice(2);
|
|
17335
18305
|
return {
|
|
17336
18306
|
handles,
|
|
17337
|
-
inputProof:
|
|
18307
|
+
inputProof: hexToBytes(inputProof),
|
|
17338
18308
|
};
|
|
17339
18309
|
},
|
|
17340
18310
|
};
|
|
@@ -17460,14 +18430,16 @@ function deserializeClearValues(handles, decryptedResult) {
|
|
|
17460
18430
|
handles.forEach((handle, idx) => (results[handle] = rawValues[idx]));
|
|
17461
18431
|
return results;
|
|
17462
18432
|
}
|
|
17463
|
-
const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress,
|
|
18433
|
+
const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress,
|
|
18434
|
+
//relayerUrl: string,
|
|
18435
|
+
relayerProvider, provider, instanceOptions) => async (_handles, options) => {
|
|
17464
18436
|
const extraData = '0x00';
|
|
17465
18437
|
const acl = new ethers.Contract(aclContractAddress, aclABI, provider);
|
|
17466
18438
|
let handles;
|
|
17467
18439
|
try {
|
|
17468
18440
|
handles = await Promise.all(_handles.map(async (_handle) => {
|
|
17469
18441
|
const handle = typeof _handle === 'string'
|
|
17470
|
-
? toHexString(
|
|
18442
|
+
? toHexString(hexToBytes(_handle), true)
|
|
17471
18443
|
: toHexString(_handle, true);
|
|
17472
18444
|
const isAllowedForDecryption = await acl.isAllowedForDecryption(handle);
|
|
17473
18445
|
if (!isAllowedForDecryption) {
|
|
@@ -17485,7 +18457,13 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
|
|
|
17485
18457
|
ciphertextHandles: handles,
|
|
17486
18458
|
extraData,
|
|
17487
18459
|
};
|
|
17488
|
-
const json = await
|
|
18460
|
+
const json = await relayerProvider.fetchPostPublicDecrypt(payloadForRequest, options ?? instanceOptions);
|
|
18461
|
+
// const json = await fetchRelayerJsonRpcPost(
|
|
18462
|
+
// 'PUBLIC_DECRYPT',
|
|
18463
|
+
// `${relayerUrl}/v1/public-decrypt`,
|
|
18464
|
+
// payloadForRequest,
|
|
18465
|
+
// options ?? instanceOptions,
|
|
18466
|
+
// );
|
|
17489
18467
|
// verify signatures on decryption:
|
|
17490
18468
|
const domain = {
|
|
17491
18469
|
name: 'Decryption',
|
|
@@ -17500,10 +18478,11 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
|
|
|
17500
18478
|
{ name: 'extraData', type: 'bytes' },
|
|
17501
18479
|
],
|
|
17502
18480
|
};
|
|
17503
|
-
const result = json.response[0];
|
|
17504
|
-
const
|
|
18481
|
+
//const result = json.response[0];
|
|
18482
|
+
const result = json;
|
|
18483
|
+
const decryptedResult = ensure0x(result.decryptedValue);
|
|
17505
18484
|
const kmsSignatures = result.signatures.map(ensure0x);
|
|
17506
|
-
// TODO result.
|
|
18485
|
+
// TODO result.extraData (RelayerPublicDecryptJsonResponse)
|
|
17507
18486
|
const signedExtraData = '0x';
|
|
17508
18487
|
const recoveredAddresses = kmsSignatures.map((kmsSignature) => {
|
|
17509
18488
|
const recoveredAddress = ethers.verifyTypedData(domain, types, { ctHandles: handles, decryptedResult, extraData: signedExtraData }, kmsSignature);
|
|
@@ -17538,12 +18517,12 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
|
|
|
17538
18517
|
*/
|
|
17539
18518
|
const createEIP712 = (verifyingContract, contractsChainId) => (publicKey, contractAddresses, startTimestamp, durationDays, delegatedAccount) => {
|
|
17540
18519
|
const extraData = '0x00';
|
|
17541
|
-
if (delegatedAccount && !isAddress(delegatedAccount))
|
|
18520
|
+
if (delegatedAccount && !isAddress$1(delegatedAccount))
|
|
17542
18521
|
throw new Error('Invalid delegated account.');
|
|
17543
|
-
if (!isAddress(verifyingContract)) {
|
|
18522
|
+
if (!isAddress$1(verifyingContract)) {
|
|
17544
18523
|
throw new Error('Invalid verifying contract address.');
|
|
17545
18524
|
}
|
|
17546
|
-
if (!contractAddresses.every((c) => isAddress(c))) {
|
|
18525
|
+
if (!contractAddresses.every((c) => isAddress$1(c))) {
|
|
17547
18526
|
throw new Error('Invalid contract address.');
|
|
17548
18527
|
}
|
|
17549
18528
|
// Format the public key based on its type
|
|
@@ -17627,77 +18606,2669 @@ const generateKeypair = () => {
|
|
|
17627
18606
|
};
|
|
17628
18607
|
};
|
|
17629
18608
|
|
|
17630
|
-
|
|
17631
|
-
|
|
17632
|
-
|
|
17633
|
-
|
|
17634
|
-
// KMS_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
|
|
17635
|
-
kmsContractAddress: '0xbE0E383937d564D7FF0BC3b46c51f0bF8d5C311A',
|
|
17636
|
-
// INPUT_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
|
|
17637
|
-
inputVerifierContractAddress: '0xBBC1fFCdc7C316aAAd72E807D9b0272BE8F84DA0',
|
|
17638
|
-
// DECRYPTION_ADDRESS (Gateway chain)
|
|
17639
|
-
verifyingContractAddressDecryption: '0x5D8BD78e2ea6bbE41f26dFe9fdaEAa349e077478',
|
|
17640
|
-
// INPUT_VERIFICATION_ADDRESS (Gateway chain)
|
|
17641
|
-
verifyingContractAddressInputVerification: '0x483b9dE06E4E4C7D35CCf5837A1668487406D955',
|
|
17642
|
-
// FHEVM Host chain id
|
|
17643
|
-
chainId: 11155111,
|
|
17644
|
-
// Gateway chain id
|
|
17645
|
-
gatewayChainId: 10901,
|
|
17646
|
-
// Optional RPC provider to host chain
|
|
17647
|
-
network: 'https://ethereum-sepolia-rpc.publicnode.com',
|
|
17648
|
-
// Relayer URL
|
|
17649
|
-
relayerUrl: 'https://relayer.testnet.zama.org',
|
|
17650
|
-
};
|
|
17651
|
-
const createInstance = async (config) => {
|
|
17652
|
-
const { verifyingContractAddressDecryption, verifyingContractAddressInputVerification, publicKey, kmsContractAddress, aclContractAddress, gatewayChainId, auth, } = config;
|
|
17653
|
-
if (!kmsContractAddress || !isAddress(kmsContractAddress)) {
|
|
17654
|
-
throw new Error('KMS contract address is not valid or empty');
|
|
18609
|
+
class AbstractRelayerProvider {
|
|
18610
|
+
_relayerUrl;
|
|
18611
|
+
constructor(relayerUrl) {
|
|
18612
|
+
this._relayerUrl = relayerUrl;
|
|
17655
18613
|
}
|
|
17656
|
-
|
|
17657
|
-
|
|
17658
|
-
throw new Error('Verifying contract for Decryption address is not valid or empty');
|
|
18614
|
+
get url() {
|
|
18615
|
+
return this._relayerUrl;
|
|
17659
18616
|
}
|
|
17660
|
-
|
|
17661
|
-
|
|
17662
|
-
throw new Error('Verifying contract for InputVerification address is not valid or empty');
|
|
18617
|
+
get keyUrl() {
|
|
18618
|
+
return `${this.url}/keyurl`;
|
|
17663
18619
|
}
|
|
17664
|
-
|
|
17665
|
-
|
|
18620
|
+
get inputProof() {
|
|
18621
|
+
return `${this.url}/input-proof`;
|
|
17666
18622
|
}
|
|
17667
|
-
|
|
17668
|
-
|
|
17669
|
-
const provider = getProvider(config);
|
|
17670
|
-
if (!provider) {
|
|
17671
|
-
throw new Error('No network has been provided!');
|
|
18623
|
+
get publicDecrypt() {
|
|
18624
|
+
return `${this.url}/public-decrypt`;
|
|
17672
18625
|
}
|
|
17673
|
-
|
|
17674
|
-
|
|
17675
|
-
|
|
17676
|
-
|
|
17677
|
-
|
|
17678
|
-
|
|
17679
|
-
|
|
17680
|
-
|
|
17681
|
-
|
|
17682
|
-
|
|
17683
|
-
|
|
17684
|
-
|
|
17685
|
-
|
|
17686
|
-
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
|
|
17691
|
-
:
|
|
17692
|
-
|
|
17693
|
-
|
|
17694
|
-
|
|
17695
|
-
|
|
17696
|
-
|
|
17697
|
-
|
|
17698
|
-
|
|
17699
|
-
|
|
17700
|
-
|
|
18626
|
+
get userDecrypt() {
|
|
18627
|
+
return `${this.url}/user-decrypt`;
|
|
18628
|
+
}
|
|
18629
|
+
}
|
|
18630
|
+
function assertIsRelayerInputProofResult(value, name) {
|
|
18631
|
+
assertRecordBytes32HexArrayProperty(value, 'handles', name);
|
|
18632
|
+
assertRecordBytes65HexArrayProperty(value, 'signatures', name);
|
|
18633
|
+
}
|
|
18634
|
+
function assertIsRelayerPublicDecryptResult(value, name) {
|
|
18635
|
+
assertRecordBytesHexNo0xArrayProperty(value, 'signatures', name);
|
|
18636
|
+
assertRecordStringProperty(value, 'decryptedValue', name);
|
|
18637
|
+
assertRecordBytesHexProperty(value, 'extraData', name);
|
|
18638
|
+
}
|
|
18639
|
+
function assertIsRelayerUserDecryptResult(value, name) {
|
|
18640
|
+
if (!Array.isArray(value)) {
|
|
18641
|
+
throw InvalidPropertyError.invalidObject({
|
|
18642
|
+
objName: name,
|
|
18643
|
+
expectedType: 'Array',
|
|
18644
|
+
type: typeof value,
|
|
18645
|
+
});
|
|
18646
|
+
}
|
|
18647
|
+
for (let i = 0; i < value.length; ++i) {
|
|
18648
|
+
// Missing extraData
|
|
18649
|
+
assertRecordBytesHexNo0xProperty(value[i], 'payload', `${name}[i]`);
|
|
18650
|
+
assertRecordBytesHexNo0xProperty(value[i], 'signature', `${name}[i]`);
|
|
18651
|
+
}
|
|
18652
|
+
}
|
|
18653
|
+
|
|
18654
|
+
class RelayerV1Provider extends AbstractRelayerProvider {
|
|
18655
|
+
constructor(relayerUrl) {
|
|
18656
|
+
super(relayerUrl);
|
|
18657
|
+
}
|
|
18658
|
+
get version() {
|
|
18659
|
+
return 1;
|
|
18660
|
+
}
|
|
18661
|
+
async fetchGetKeyUrl() {
|
|
18662
|
+
const response = await fetchRelayerGet('KEY_URL', this.keyUrl);
|
|
18663
|
+
return response;
|
|
18664
|
+
}
|
|
18665
|
+
async fetchPostInputProof(payload, options) {
|
|
18666
|
+
/*
|
|
18667
|
+
Expected v1 format:
|
|
18668
|
+
===================
|
|
18669
|
+
{
|
|
18670
|
+
"response": {
|
|
18671
|
+
"handles": [
|
|
18672
|
+
"0xb0b1af7734450c2b7d944571af7e5b438cc62a2a26000000000000aa36a70400"
|
|
18673
|
+
],
|
|
18674
|
+
"signatures": [
|
|
18675
|
+
"0x70dcb78534f05c4448d3441b4704d3ff4a8478af56a3464497533c2e3c476d77165b09028847f0c3ed4b342b1e8b4252a93b521a3d8d07b724bcff740383e1361b"
|
|
18676
|
+
]
|
|
18677
|
+
}
|
|
18678
|
+
}
|
|
18679
|
+
*/
|
|
18680
|
+
const json = await fetchRelayerJsonRpcPost('INPUT_PROOF', this.inputProof, payload, options);
|
|
18681
|
+
assertIsRelayerInputProofResult(json.response, 'fetchPostInputProof()');
|
|
18682
|
+
return json.response;
|
|
18683
|
+
}
|
|
18684
|
+
async fetchPostPublicDecrypt(payload, options) {
|
|
18685
|
+
const json = await fetchRelayerJsonRpcPost('PUBLIC_DECRYPT', this.publicDecrypt, payload, options);
|
|
18686
|
+
const response = json.response[0];
|
|
18687
|
+
const result = {
|
|
18688
|
+
signatures: response.signatures,
|
|
18689
|
+
decryptedValue: response.decrypted_value,
|
|
18690
|
+
extraData: '0x',
|
|
18691
|
+
};
|
|
18692
|
+
assertIsRelayerPublicDecryptResult(result, 'fetchPostPublicDecrypt()');
|
|
18693
|
+
return result;
|
|
18694
|
+
}
|
|
18695
|
+
async fetchPostUserDecrypt(payload, options) {
|
|
18696
|
+
const json = await fetchRelayerJsonRpcPost('USER_DECRYPT', this.userDecrypt, payload, options);
|
|
18697
|
+
assertIsRelayerUserDecryptResult(json.response, 'RelayerUserDecryptResult()');
|
|
18698
|
+
return json.response;
|
|
18699
|
+
}
|
|
18700
|
+
}
|
|
18701
|
+
|
|
18702
|
+
function ensureError(e) {
|
|
18703
|
+
if (e instanceof Error) {
|
|
18704
|
+
return e;
|
|
18705
|
+
}
|
|
18706
|
+
const message = e.message ?? 'Non-Error value caught in exception handler';
|
|
18707
|
+
const name = e.name ?? 'ErrorWrapper';
|
|
18708
|
+
const cause = e.cause ?? e;
|
|
18709
|
+
const err = new Error(message, { cause });
|
|
18710
|
+
err.name = name;
|
|
18711
|
+
return err;
|
|
18712
|
+
}
|
|
18713
|
+
|
|
18714
|
+
class RelayerV2ProviderError extends RelayerErrorBase {
|
|
18715
|
+
_operation;
|
|
18716
|
+
constructor(params) {
|
|
18717
|
+
super(params);
|
|
18718
|
+
this._operation = params.operation;
|
|
18719
|
+
}
|
|
18720
|
+
get operation() {
|
|
18721
|
+
return this._operation;
|
|
18722
|
+
}
|
|
18723
|
+
}
|
|
18724
|
+
class RelayerV2GetKeyUrlError extends RelayerV2ProviderError {
|
|
18725
|
+
constructor({ cause }) {
|
|
18726
|
+
super({
|
|
18727
|
+
message: `Invalid relayer response.`,
|
|
18728
|
+
name: 'RelayerV2GetKeyUrlError',
|
|
18729
|
+
operation: 'KEY_URL',
|
|
18730
|
+
cause,
|
|
18731
|
+
});
|
|
18732
|
+
}
|
|
18733
|
+
}
|
|
18734
|
+
class RelayerV2GetKeyUrlInvalidResponseError extends RelayerV2GetKeyUrlError {
|
|
18735
|
+
constructor({ cause }) {
|
|
18736
|
+
super({ cause });
|
|
18737
|
+
}
|
|
18738
|
+
}
|
|
18739
|
+
|
|
18740
|
+
function assertIsRelayerV2ApiError400NoDetails(value, name) {
|
|
18741
|
+
assertRecordStringProperty(value, 'label', name);
|
|
18742
|
+
if (!(value.label === 'malformed_json' ||
|
|
18743
|
+
value.label === 'request_error' ||
|
|
18744
|
+
value.label === 'not_ready_for_decryption')) {
|
|
18745
|
+
throw new InvalidPropertyError({
|
|
18746
|
+
objName: name,
|
|
18747
|
+
property: 'label',
|
|
18748
|
+
expectedType: 'string',
|
|
18749
|
+
expectedValue: [
|
|
18750
|
+
'malformed_json',
|
|
18751
|
+
'request_error',
|
|
18752
|
+
'not_ready_for_decryption',
|
|
18753
|
+
],
|
|
18754
|
+
type: typeof value.label, // === "string"
|
|
18755
|
+
value: value.label,
|
|
18756
|
+
});
|
|
18757
|
+
}
|
|
18758
|
+
assertRecordStringProperty(value, 'message', name);
|
|
18759
|
+
}
|
|
18760
|
+
|
|
18761
|
+
/*
|
|
18762
|
+
type RelayerV2ApiError400WithDetails = {
|
|
18763
|
+
label: 'missing_fields' | 'validation_failed';
|
|
18764
|
+
message: string;
|
|
18765
|
+
details: Array<RelayerV2ErrorDetail>;
|
|
18766
|
+
};
|
|
18767
|
+
type RelayerV2ErrorDetail = {
|
|
18768
|
+
field: string;
|
|
18769
|
+
issue: string;
|
|
18770
|
+
}
|
|
18771
|
+
*/
|
|
18772
|
+
function assertIsRelayerV2ApiError400WithDetails(value, name) {
|
|
18773
|
+
assertRecordStringProperty(value, 'label', name);
|
|
18774
|
+
if (!(value.label === 'missing_fields' || value.label === 'validation_failed')) {
|
|
18775
|
+
throw new InvalidPropertyError({
|
|
18776
|
+
objName: name,
|
|
18777
|
+
property: 'label',
|
|
18778
|
+
expectedType: 'string',
|
|
18779
|
+
expectedValue: ['missing_fields', 'validation_failed'],
|
|
18780
|
+
type: typeof value.label,
|
|
18781
|
+
value: value.label,
|
|
18782
|
+
});
|
|
18783
|
+
}
|
|
18784
|
+
assertRecordStringProperty(value, 'message', name);
|
|
18785
|
+
assertRecordArrayProperty(value, 'details', name);
|
|
18786
|
+
const arr = value.details;
|
|
18787
|
+
for (let i = 0; i < arr.length; ++i) {
|
|
18788
|
+
const detail = arr[i];
|
|
18789
|
+
assertRecordStringProperty(detail, 'field', `${name}.details[${i}]`);
|
|
18790
|
+
assertRecordStringProperty(detail, 'issue', `${name}.details[${i}]`);
|
|
18791
|
+
}
|
|
18792
|
+
}
|
|
18793
|
+
|
|
18794
|
+
/*
|
|
18795
|
+
export type RelayerV2ApiError429 = {
|
|
18796
|
+
label: 'rate_limited';
|
|
18797
|
+
message: string;
|
|
18798
|
+
};
|
|
18799
|
+
*/
|
|
18800
|
+
function assertIsRelayerV2ApiError429(value, name) {
|
|
18801
|
+
assertRecordStringProperty(value, 'label', name, 'rate_limited');
|
|
18802
|
+
assertRecordStringProperty(value, 'message', name);
|
|
18803
|
+
}
|
|
18804
|
+
|
|
18805
|
+
/*
|
|
18806
|
+
export type RelayerV2ApiError500 = {
|
|
18807
|
+
label: 'internal_server_error';
|
|
18808
|
+
message: string;
|
|
18809
|
+
};
|
|
18810
|
+
*/
|
|
18811
|
+
function assertIsRelayerV2ApiError500(value, name) {
|
|
18812
|
+
assertRecordStringProperty(value, 'label', name, 'internal_server_error');
|
|
18813
|
+
assertRecordStringProperty(value, 'message', name);
|
|
18814
|
+
}
|
|
18815
|
+
|
|
18816
|
+
/*
|
|
18817
|
+
export type RelayerV2ApiError404 = {
|
|
18818
|
+
label: 'not_found';
|
|
18819
|
+
message: string;
|
|
18820
|
+
};
|
|
18821
|
+
*/
|
|
18822
|
+
function assertIsRelayerV2ApiError404(value, name) {
|
|
18823
|
+
assertRecordStringProperty(value, 'label', name, 'not_found');
|
|
18824
|
+
assertRecordStringProperty(value, 'message', name);
|
|
18825
|
+
}
|
|
18826
|
+
|
|
18827
|
+
/*
|
|
18828
|
+
export type RelayerV2ApiError503 = {
|
|
18829
|
+
label: "protocol_paused" | "gateway_not_reachable";
|
|
18830
|
+
message: string;
|
|
18831
|
+
};
|
|
18832
|
+
*/
|
|
18833
|
+
function assertIsRelayerV2ApiError503(value, name) {
|
|
18834
|
+
assertRecordStringProperty(value, 'label', name, [
|
|
18835
|
+
'protocol_paused',
|
|
18836
|
+
'gateway_not_reachable',
|
|
18837
|
+
]);
|
|
18838
|
+
assertRecordStringProperty(value, 'message', name);
|
|
18839
|
+
}
|
|
18840
|
+
|
|
18841
|
+
/*
|
|
18842
|
+
export type RelayerV2ApiError504 = {
|
|
18843
|
+
label: 'readiness_check_timedout' | 'response_timedout';
|
|
18844
|
+
message: string;
|
|
18845
|
+
};
|
|
18846
|
+
*/
|
|
18847
|
+
function assertIsRelayerV2ApiError504(value, name) {
|
|
18848
|
+
assertRecordStringProperty(value, 'label', name, [
|
|
18849
|
+
'readiness_check_timedout',
|
|
18850
|
+
'response_timedout',
|
|
18851
|
+
]);
|
|
18852
|
+
assertRecordStringProperty(value, 'message', name);
|
|
18853
|
+
}
|
|
18854
|
+
|
|
18855
|
+
function assertIsRelayerV2ResponseFailed(value, name) {
|
|
18856
|
+
assertRecordStringProperty(value, 'status', name, 'failed');
|
|
18857
|
+
assertNonNullableRecordProperty(value, 'error', name);
|
|
18858
|
+
assertIsRelayerV2ApiError(value.error, `${name}.error`);
|
|
18859
|
+
}
|
|
18860
|
+
function assertIsRelayerV2ApiError(value, name) {
|
|
18861
|
+
assertRecordStringProperty(value, 'label', name);
|
|
18862
|
+
// 400
|
|
18863
|
+
if (value.label === 'malformed_json' ||
|
|
18864
|
+
value.label === 'request_error' ||
|
|
18865
|
+
value.label === 'not_ready_for_decryption') {
|
|
18866
|
+
assertIsRelayerV2ApiError400NoDetails(value, name);
|
|
18867
|
+
}
|
|
18868
|
+
// 400 (with details)
|
|
18869
|
+
else if (value.label === 'missing_fields' ||
|
|
18870
|
+
value.label === 'validation_failed') {
|
|
18871
|
+
assertIsRelayerV2ApiError400WithDetails(value, name);
|
|
18872
|
+
}
|
|
18873
|
+
// 404
|
|
18874
|
+
else if (value.label === 'not_found') {
|
|
18875
|
+
assertIsRelayerV2ApiError404(value, name);
|
|
18876
|
+
}
|
|
18877
|
+
// 429
|
|
18878
|
+
else if (value.label === 'rate_limited') {
|
|
18879
|
+
assertIsRelayerV2ApiError429(value, name);
|
|
18880
|
+
}
|
|
18881
|
+
// 500
|
|
18882
|
+
else if (value.label === 'internal_server_error') {
|
|
18883
|
+
assertIsRelayerV2ApiError500(value, name);
|
|
18884
|
+
}
|
|
18885
|
+
// 503
|
|
18886
|
+
else if (value.label === 'protocol_paused' ||
|
|
18887
|
+
value.label === 'gateway_not_reachable') {
|
|
18888
|
+
assertIsRelayerV2ApiError503(value, name);
|
|
18889
|
+
}
|
|
18890
|
+
// 504
|
|
18891
|
+
else if (value.label === 'readiness_check_timedout' ||
|
|
18892
|
+
value.label === 'response_timedout') {
|
|
18893
|
+
assertIsRelayerV2ApiError504(value, name);
|
|
18894
|
+
}
|
|
18895
|
+
// Unsupported
|
|
18896
|
+
else {
|
|
18897
|
+
throw new InvalidPropertyError({
|
|
18898
|
+
objName: name,
|
|
18899
|
+
property: 'label',
|
|
18900
|
+
expectedType: 'string',
|
|
18901
|
+
expectedValue: [
|
|
18902
|
+
'malformed_json',
|
|
18903
|
+
'request_error',
|
|
18904
|
+
'not_ready_for_decryption',
|
|
18905
|
+
'missing_fields',
|
|
18906
|
+
'validation_failed',
|
|
18907
|
+
'rate_limited',
|
|
18908
|
+
'internal_server_error',
|
|
18909
|
+
'protocol_paused',
|
|
18910
|
+
'gateway_not_reachable',
|
|
18911
|
+
'readiness_check_timedout',
|
|
18912
|
+
'response_timedout',
|
|
18913
|
+
],
|
|
18914
|
+
type: typeof value.label,
|
|
18915
|
+
value: value.label,
|
|
18916
|
+
});
|
|
18917
|
+
}
|
|
18918
|
+
}
|
|
18919
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18920
|
+
// 400
|
|
18921
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18922
|
+
function assertIsRelayerV2ResponseFailedWithError400(value, name) {
|
|
18923
|
+
assertIsRelayerV2ResponseFailed(value, name);
|
|
18924
|
+
if (value.error.label === 'malformed_json' ||
|
|
18925
|
+
value.error.label === 'request_error' ||
|
|
18926
|
+
value.error.label === 'not_ready_for_decryption') {
|
|
18927
|
+
assertIsRelayerV2ApiError400NoDetails(value.error, `${name}.error`);
|
|
18928
|
+
}
|
|
18929
|
+
else if (value.error.label === 'missing_fields' ||
|
|
18930
|
+
value.error.label === 'validation_failed') {
|
|
18931
|
+
assertIsRelayerV2ApiError400WithDetails(value.error, `${name}.error`);
|
|
18932
|
+
}
|
|
18933
|
+
else {
|
|
18934
|
+
throw new InvalidPropertyError({
|
|
18935
|
+
objName: `${name}.error`,
|
|
18936
|
+
property: 'label',
|
|
18937
|
+
expectedType: 'string',
|
|
18938
|
+
expectedValue: [
|
|
18939
|
+
'malformed_json',
|
|
18940
|
+
'request_error',
|
|
18941
|
+
'not_ready_for_decryption',
|
|
18942
|
+
'missing_fields',
|
|
18943
|
+
'validation_failed',
|
|
18944
|
+
],
|
|
18945
|
+
type: typeof value.error.label,
|
|
18946
|
+
value: value.error.label,
|
|
18947
|
+
});
|
|
18948
|
+
}
|
|
18949
|
+
}
|
|
18950
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18951
|
+
// 404
|
|
18952
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18953
|
+
function assertIsRelayerV2ResponseFailedWithError404(value, name) {
|
|
18954
|
+
assertIsRelayerV2ResponseFailed(value, name);
|
|
18955
|
+
assertIsRelayerV2ApiError404(value.error, `${name}.error`);
|
|
18956
|
+
}
|
|
18957
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18958
|
+
// 429
|
|
18959
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18960
|
+
function assertIsRelayerV2ResponseFailedWithError429(value, name) {
|
|
18961
|
+
assertIsRelayerV2ResponseFailed(value, name);
|
|
18962
|
+
assertIsRelayerV2ApiError429(value.error, `${name}.error`);
|
|
18963
|
+
}
|
|
18964
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18965
|
+
// 500
|
|
18966
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18967
|
+
function assertIsRelayerV2ResponseFailedWithError500(value, name) {
|
|
18968
|
+
assertIsRelayerV2ResponseFailed(value, name);
|
|
18969
|
+
assertIsRelayerV2ApiError500(value.error, `${name}.error`);
|
|
18970
|
+
}
|
|
18971
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18972
|
+
// 503
|
|
18973
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18974
|
+
function assertIsRelayerV2ResponseFailedWithError503(value, name) {
|
|
18975
|
+
assertIsRelayerV2ResponseFailed(value, name);
|
|
18976
|
+
assertIsRelayerV2ApiError503(value.error, `${name}.error`);
|
|
18977
|
+
}
|
|
18978
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18979
|
+
// 504
|
|
18980
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
18981
|
+
function assertIsRelayerV2ResponseFailedWithError504(value, name) {
|
|
18982
|
+
assertIsRelayerV2ResponseFailed(value, name);
|
|
18983
|
+
assertIsRelayerV2ApiError504(value.error, `${name}.error`);
|
|
18984
|
+
}
|
|
18985
|
+
|
|
18986
|
+
class RelayerV2FetchErrorBase extends RelayerErrorBase {
|
|
18987
|
+
_fetchMethod;
|
|
18988
|
+
_url;
|
|
18989
|
+
_jobId;
|
|
18990
|
+
_operation;
|
|
18991
|
+
_retryCount;
|
|
18992
|
+
_elapsed;
|
|
18993
|
+
_state;
|
|
18994
|
+
constructor(params) {
|
|
18995
|
+
super({
|
|
18996
|
+
...params,
|
|
18997
|
+
name: params.name ?? 'RelayerV2FetchErrorBase',
|
|
18998
|
+
});
|
|
18999
|
+
this._fetchMethod = params.fetchMethod;
|
|
19000
|
+
this._url = params.url;
|
|
19001
|
+
this._operation = params.operation;
|
|
19002
|
+
this._elapsed = params.elapsed;
|
|
19003
|
+
this._retryCount = params.retryCount;
|
|
19004
|
+
this._state = params.state;
|
|
19005
|
+
this._jobId = params.jobId;
|
|
19006
|
+
}
|
|
19007
|
+
get url() {
|
|
19008
|
+
return this._url;
|
|
19009
|
+
}
|
|
19010
|
+
get operation() {
|
|
19011
|
+
return this._operation;
|
|
19012
|
+
}
|
|
19013
|
+
get fetchMethod() {
|
|
19014
|
+
return this._fetchMethod;
|
|
19015
|
+
}
|
|
19016
|
+
get jobId() {
|
|
19017
|
+
return this._jobId;
|
|
19018
|
+
}
|
|
19019
|
+
get retryCount() {
|
|
19020
|
+
return this._retryCount;
|
|
19021
|
+
}
|
|
19022
|
+
get elapsed() {
|
|
19023
|
+
return this._elapsed;
|
|
19024
|
+
}
|
|
19025
|
+
get state() {
|
|
19026
|
+
return this._state;
|
|
19027
|
+
}
|
|
19028
|
+
get isAbort() {
|
|
19029
|
+
// AbortError is not an instance of Error!
|
|
19030
|
+
return this.cause ? this.cause.name === 'AbortError' : false;
|
|
19031
|
+
}
|
|
19032
|
+
}
|
|
19033
|
+
|
|
19034
|
+
class RelayerV2ResponseErrorBase extends RelayerV2FetchErrorBase {
|
|
19035
|
+
_status;
|
|
19036
|
+
constructor(params) {
|
|
19037
|
+
super({
|
|
19038
|
+
...params,
|
|
19039
|
+
name: params.name ?? 'RelayerV2ResponseErrorBase',
|
|
19040
|
+
});
|
|
19041
|
+
this._status = params.status;
|
|
19042
|
+
}
|
|
19043
|
+
get status() {
|
|
19044
|
+
return this._status;
|
|
19045
|
+
}
|
|
19046
|
+
}
|
|
19047
|
+
|
|
19048
|
+
class RelayerV2ResponseInvalidBodyError extends RelayerV2ResponseErrorBase {
|
|
19049
|
+
constructor(params) {
|
|
19050
|
+
super({
|
|
19051
|
+
...params,
|
|
19052
|
+
cause: ensureError(params.cause),
|
|
19053
|
+
name: 'RelayerV2ResponseInvalidBodyError',
|
|
19054
|
+
message: `fetchMethod: ${params.fetchMethod} status:${params.status} url:${params.url} operation:${params.operation}`,
|
|
19055
|
+
});
|
|
19056
|
+
}
|
|
19057
|
+
}
|
|
19058
|
+
|
|
19059
|
+
class RelayerV2ResponseStatusError extends RelayerV2ResponseErrorBase {
|
|
19060
|
+
constructor(params) {
|
|
19061
|
+
super({
|
|
19062
|
+
...params,
|
|
19063
|
+
name: 'RelayerV2ResponseStatusError',
|
|
19064
|
+
message: `fetchMethod: ${params.fetchMethod} status:${params.status} url:${params.url} operation:${params.operation}`,
|
|
19065
|
+
});
|
|
19066
|
+
}
|
|
19067
|
+
}
|
|
19068
|
+
|
|
19069
|
+
function assertIsRelayerV2ResultInputProof(value, name) {
|
|
19070
|
+
assertRecordBooleanProperty(value, 'accepted', name);
|
|
19071
|
+
if (value.accepted) {
|
|
19072
|
+
assertIsRelayerV2ResultInputProofAccepted(value, name);
|
|
19073
|
+
}
|
|
19074
|
+
else {
|
|
19075
|
+
assertIsRelayerV2ResultInputProofRejected(value, name);
|
|
19076
|
+
}
|
|
19077
|
+
}
|
|
19078
|
+
/*
|
|
19079
|
+
type RelayerV2ResultInputProofAccepted = {
|
|
19080
|
+
accepted: true;
|
|
19081
|
+
extra_data: BytesHex;
|
|
19082
|
+
handles: Bytes32Hex[];
|
|
19083
|
+
signatures: BytesHex[];
|
|
19084
|
+
}
|
|
19085
|
+
*/
|
|
19086
|
+
function assertIsRelayerV2ResultInputProofAccepted(value, name) {
|
|
19087
|
+
assertRecordBooleanProperty(value, 'accepted', name, true);
|
|
19088
|
+
assertRecordBytes32HexArrayProperty(value, 'handles', name);
|
|
19089
|
+
assertRecordBytesHexArrayProperty(value, 'signatures', name);
|
|
19090
|
+
assertRecordBytesHexProperty(value, 'extraData', name);
|
|
19091
|
+
}
|
|
19092
|
+
/*
|
|
19093
|
+
type RelayerV2ResultInputProofRejected = {
|
|
19094
|
+
accepted: false;
|
|
19095
|
+
extra_data: BytesHex;
|
|
19096
|
+
}
|
|
19097
|
+
*/
|
|
19098
|
+
function assertIsRelayerV2ResultInputProofRejected(value, name) {
|
|
19099
|
+
assertRecordBooleanProperty(value, 'accepted', name, false);
|
|
19100
|
+
assertRecordBytesHexProperty(value, 'extraData', name);
|
|
19101
|
+
}
|
|
19102
|
+
|
|
19103
|
+
function assertIsRelayerV2GetResponseInputProofSucceeded(value, name) {
|
|
19104
|
+
assertNonNullableRecordProperty(value, 'result', name);
|
|
19105
|
+
assertRecordStringProperty(value, 'status', name, 'succeeded');
|
|
19106
|
+
assertRecordStringProperty(value, 'requestId', name);
|
|
19107
|
+
assertIsRelayerV2ResultInputProof(value.result, `${name}.result`);
|
|
19108
|
+
}
|
|
19109
|
+
|
|
19110
|
+
function assertIsRelayerV2ResultPublicDecrypt(value, name) {
|
|
19111
|
+
assertRecordBytesHexNo0xArrayProperty(value, 'signatures', name);
|
|
19112
|
+
assertRecordBytesHexNo0xProperty(value, 'decryptedValue', name);
|
|
19113
|
+
assertRecordBytesHexProperty(value, 'extraData', name);
|
|
19114
|
+
}
|
|
19115
|
+
|
|
19116
|
+
function assertIsRelayerV2GetResponsePublicDecryptSucceeded(value, name) {
|
|
19117
|
+
assertNonNullableRecordProperty(value, 'result', name);
|
|
19118
|
+
assertRecordStringProperty(value, 'status', name, 'succeeded');
|
|
19119
|
+
assertRecordStringProperty(value, 'requestId', name);
|
|
19120
|
+
assertIsRelayerV2ResultPublicDecrypt(value.result, `${name}.result`);
|
|
19121
|
+
}
|
|
19122
|
+
|
|
19123
|
+
/**
|
|
19124
|
+
* Assertion function that validates a value is a valid `RelayerV2ResultUserDecrypt` object.
|
|
19125
|
+
* Validates the structure returned from the relayer for user decryption operations.
|
|
19126
|
+
* Throws an `InvalidPropertyError` if validation fails.
|
|
19127
|
+
*
|
|
19128
|
+
* @param value - The value to validate (can be any type)
|
|
19129
|
+
* @param name - The name of the value being validated (used in error messages)
|
|
19130
|
+
* @throws {InvalidPropertyError} When any required property is missing or has an invalid format
|
|
19131
|
+
* @throws {never} No other errors are thrown
|
|
19132
|
+
*/
|
|
19133
|
+
function assertIsRelayerV2ResultUserDecrypt(value, name) {
|
|
19134
|
+
assertRecordArrayProperty(value, 'result', name);
|
|
19135
|
+
for (let i = 0; i < value.result.length; ++i) {
|
|
19136
|
+
// Missing extraData
|
|
19137
|
+
assertRecordBytesHexNo0xProperty(value.result[i], 'payload', `${name}.result[${i}]`);
|
|
19138
|
+
assertRecordBytesHexNo0xProperty(value.result[i], 'signature', `${name}.result[${i}]`);
|
|
19139
|
+
}
|
|
19140
|
+
}
|
|
19141
|
+
|
|
19142
|
+
function assertIsRelayerV2GetResponseUserDecryptSucceeded(value, name) {
|
|
19143
|
+
assertNonNullableRecordProperty(value, 'result', name);
|
|
19144
|
+
assertRecordStringProperty(value, 'status', name, 'succeeded');
|
|
19145
|
+
assertRecordStringProperty(value, 'requestId', name);
|
|
19146
|
+
assertIsRelayerV2ResultUserDecrypt(value.result, `${name}.result`);
|
|
19147
|
+
}
|
|
19148
|
+
|
|
19149
|
+
class RelayerV2RequestErrorBase extends RelayerErrorBase {
|
|
19150
|
+
_url;
|
|
19151
|
+
_operation;
|
|
19152
|
+
_jobId;
|
|
19153
|
+
constructor(params) {
|
|
19154
|
+
super({ ...params, name: params.name ?? 'RelayerV2RequestErrorBase' });
|
|
19155
|
+
this._url = params.url;
|
|
19156
|
+
this._operation = params.operation;
|
|
19157
|
+
this._jobId = params.jobId;
|
|
19158
|
+
}
|
|
19159
|
+
get url() {
|
|
19160
|
+
return this._url;
|
|
19161
|
+
}
|
|
19162
|
+
get jobId() {
|
|
19163
|
+
return this._jobId;
|
|
19164
|
+
}
|
|
19165
|
+
get operation() {
|
|
19166
|
+
return this._operation;
|
|
19167
|
+
}
|
|
19168
|
+
}
|
|
19169
|
+
|
|
19170
|
+
class RelayerV2RequestInternalError extends RelayerV2RequestErrorBase {
|
|
19171
|
+
_status;
|
|
19172
|
+
_state;
|
|
19173
|
+
constructor(params) {
|
|
19174
|
+
super({
|
|
19175
|
+
...params,
|
|
19176
|
+
name: 'RelayerV2RequestInternalError',
|
|
19177
|
+
message: params.message ?? 'internal error',
|
|
19178
|
+
});
|
|
19179
|
+
this._status = params.status;
|
|
19180
|
+
this._state = params.state;
|
|
19181
|
+
}
|
|
19182
|
+
get status() {
|
|
19183
|
+
return this._status;
|
|
19184
|
+
}
|
|
19185
|
+
get state() {
|
|
19186
|
+
return this._state;
|
|
19187
|
+
}
|
|
19188
|
+
}
|
|
19189
|
+
|
|
19190
|
+
function assertIsRelayerV2ResultQueued(value, name) {
|
|
19191
|
+
assertRecordStringProperty(value, 'jobId', name);
|
|
19192
|
+
}
|
|
19193
|
+
|
|
19194
|
+
/*
|
|
19195
|
+
type RelayerV2ResponseQueued = {
|
|
19196
|
+
status: "queued";
|
|
19197
|
+
result: RelayerV2ResultQueued;
|
|
19198
|
+
}
|
|
19199
|
+
*/
|
|
19200
|
+
function assertIsRelayerV2ResponseQueued(value, name) {
|
|
19201
|
+
assertRecordStringProperty(value, 'status', name, 'queued');
|
|
19202
|
+
assertNonNullableRecordProperty(value, 'result', name);
|
|
19203
|
+
assertIsRelayerV2ResultQueued(value.result, `${name}.result`);
|
|
19204
|
+
}
|
|
19205
|
+
|
|
19206
|
+
class RelayerV2ResponseApiError extends RelayerV2ResponseErrorBase {
|
|
19207
|
+
constructor(params) {
|
|
19208
|
+
super({
|
|
19209
|
+
...params,
|
|
19210
|
+
name: 'RelayerV2ResponseApiError',
|
|
19211
|
+
message: params.relayerApiError.message,
|
|
19212
|
+
});
|
|
19213
|
+
}
|
|
19214
|
+
}
|
|
19215
|
+
|
|
19216
|
+
class RelayerV2FetchError extends RelayerV2FetchErrorBase {
|
|
19217
|
+
constructor(params) {
|
|
19218
|
+
super({
|
|
19219
|
+
...params,
|
|
19220
|
+
name: 'RelayerV2FetchError',
|
|
19221
|
+
message: `Fetch ${params.fetchMethod} error`,
|
|
19222
|
+
cause: ensureError(params.cause),
|
|
19223
|
+
});
|
|
19224
|
+
}
|
|
19225
|
+
}
|
|
19226
|
+
|
|
19227
|
+
class RelayerV2ResponseInputProofRejectedError extends RelayerV2ResponseErrorBase {
|
|
19228
|
+
_result;
|
|
19229
|
+
constructor(params) {
|
|
19230
|
+
super({
|
|
19231
|
+
...params,
|
|
19232
|
+
name: 'RelayerV2ResponseInputProofRejectedError',
|
|
19233
|
+
message: `InputProof rejected`,
|
|
19234
|
+
});
|
|
19235
|
+
this._result = params.result;
|
|
19236
|
+
}
|
|
19237
|
+
get result() {
|
|
19238
|
+
return this._result;
|
|
19239
|
+
}
|
|
19240
|
+
}
|
|
19241
|
+
|
|
19242
|
+
class RelayerV2StateError extends RelayerErrorBase {
|
|
19243
|
+
_state;
|
|
19244
|
+
constructor(params) {
|
|
19245
|
+
super({
|
|
19246
|
+
...params,
|
|
19247
|
+
name: 'RelayerV2StateError',
|
|
19248
|
+
});
|
|
19249
|
+
this._state = { ...params.state };
|
|
19250
|
+
Object.freeze(this._state);
|
|
19251
|
+
}
|
|
19252
|
+
get state() {
|
|
19253
|
+
return this._state;
|
|
19254
|
+
}
|
|
19255
|
+
}
|
|
19256
|
+
|
|
19257
|
+
class RelayerV2MaxRetryError extends RelayerV2FetchErrorBase {
|
|
19258
|
+
constructor(params) {
|
|
19259
|
+
super({
|
|
19260
|
+
...params,
|
|
19261
|
+
name: 'RelayerV2MaxRetryError',
|
|
19262
|
+
message: 'max retry error',
|
|
19263
|
+
});
|
|
19264
|
+
}
|
|
19265
|
+
}
|
|
19266
|
+
|
|
19267
|
+
class RelayerV2AsyncRequest {
|
|
19268
|
+
_jobId;
|
|
19269
|
+
_jobIdTimestamp;
|
|
19270
|
+
_state;
|
|
19271
|
+
_relayerOperation;
|
|
19272
|
+
_publicAPINoReentrancy;
|
|
19273
|
+
_internalAbortController;
|
|
19274
|
+
_internalAbortSignal;
|
|
19275
|
+
_externalAbortSignal;
|
|
19276
|
+
_terminateReason;
|
|
19277
|
+
_terminateError;
|
|
19278
|
+
_retryCount;
|
|
19279
|
+
_retryAfterTimeoutID;
|
|
19280
|
+
_url;
|
|
19281
|
+
_payload;
|
|
19282
|
+
_fhevmInstanceOptions;
|
|
19283
|
+
_retryAfterTimeoutPromiseFuncReject;
|
|
19284
|
+
_onProgress;
|
|
19285
|
+
_requestMaxDurationInSecs;
|
|
19286
|
+
_requestStartTimestamp;
|
|
19287
|
+
_requestGlobalTimeoutID;
|
|
19288
|
+
_throwErrorIfNoRetryAfter;
|
|
19289
|
+
static DEFAULT_RETRY_AFTER_SECS = 2;
|
|
19290
|
+
static DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS = 60 * 60;
|
|
19291
|
+
static MAX_GET_RETRY = 100;
|
|
19292
|
+
static MAX_POST_RETRY = 100;
|
|
19293
|
+
constructor(params) {
|
|
19294
|
+
if (params.relayerOperation !== 'INPUT_PROOF' &&
|
|
19295
|
+
params.relayerOperation !== 'PUBLIC_DECRYPT' &&
|
|
19296
|
+
params.relayerOperation !== 'USER_DECRYPT') {
|
|
19297
|
+
throw new InvalidPropertyError({
|
|
19298
|
+
objName: 'RelayerV2AsyncRequestParams',
|
|
19299
|
+
property: 'relayerOperation',
|
|
19300
|
+
expectedType: 'string',
|
|
19301
|
+
value: params.relayerOperation,
|
|
19302
|
+
expectedValue: 'INPUT_PROOF | PUBLIC_DECRYPT | USER_DECRYPT',
|
|
19303
|
+
});
|
|
19304
|
+
}
|
|
19305
|
+
this._relayerOperation = params.relayerOperation;
|
|
19306
|
+
this._internalAbortController = new AbortController();
|
|
19307
|
+
this._internalAbortSignal = this._internalAbortController.signal;
|
|
19308
|
+
this._internalAbortSignal.addEventListener('abort', this._handleInternalSignalAbort);
|
|
19309
|
+
this._externalAbortSignal = params.signal;
|
|
19310
|
+
if (this._externalAbortSignal) {
|
|
19311
|
+
this._externalAbortSignal.addEventListener('abort', this._handleExternalSignalAbort);
|
|
19312
|
+
}
|
|
19313
|
+
this._url = params.url;
|
|
19314
|
+
this._payload = params.payload;
|
|
19315
|
+
this._fhevmInstanceOptions = params.instanceOptions;
|
|
19316
|
+
this._onProgress = params.onProgress;
|
|
19317
|
+
this._state = {
|
|
19318
|
+
aborted: false,
|
|
19319
|
+
canceled: false,
|
|
19320
|
+
failed: false,
|
|
19321
|
+
fetching: false,
|
|
19322
|
+
running: false,
|
|
19323
|
+
succeeded: false,
|
|
19324
|
+
terminated: false,
|
|
19325
|
+
};
|
|
19326
|
+
this._retryCount = 0;
|
|
19327
|
+
this._retryAfterTimeoutID = undefined;
|
|
19328
|
+
this._requestGlobalTimeoutID = undefined;
|
|
19329
|
+
this._terminateReason = undefined;
|
|
19330
|
+
this._publicAPINoReentrancy = false;
|
|
19331
|
+
this._throwErrorIfNoRetryAfter = params.throwErrorIfNoRetryAfter ?? false;
|
|
19332
|
+
this._requestMaxDurationInSecs =
|
|
19333
|
+
params.timeoutInSeconds ??
|
|
19334
|
+
RelayerV2AsyncRequest.DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS;
|
|
19335
|
+
}
|
|
19336
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19337
|
+
// Public API: run
|
|
19338
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19339
|
+
async run() {
|
|
19340
|
+
if (this._publicAPINoReentrancy) {
|
|
19341
|
+
throw new RelayerV2StateError({
|
|
19342
|
+
message: `Relayer.run() failed. Call not permitted.`,
|
|
19343
|
+
state: { ...this._state },
|
|
19344
|
+
});
|
|
19345
|
+
}
|
|
19346
|
+
if (this._state.terminated) {
|
|
19347
|
+
throw new RelayerV2StateError({
|
|
19348
|
+
message: `Relayer.run() failed. Request already terminated.`,
|
|
19349
|
+
state: { ...this._state },
|
|
19350
|
+
});
|
|
19351
|
+
}
|
|
19352
|
+
if (this._state.canceled) {
|
|
19353
|
+
throw new RelayerV2StateError({
|
|
19354
|
+
message: `Relayer.run() failed. Request already canceled.`,
|
|
19355
|
+
state: { ...this._state },
|
|
19356
|
+
});
|
|
19357
|
+
}
|
|
19358
|
+
if (this._state.succeeded) {
|
|
19359
|
+
throw new RelayerV2StateError({
|
|
19360
|
+
message: `Relayer.run() failed. Request already succeeded.`,
|
|
19361
|
+
state: { ...this._state },
|
|
19362
|
+
});
|
|
19363
|
+
}
|
|
19364
|
+
if (this._state.failed) {
|
|
19365
|
+
throw new RelayerV2StateError({
|
|
19366
|
+
message: `Relayer.run() failed. Request already failed.`,
|
|
19367
|
+
state: { ...this._state },
|
|
19368
|
+
});
|
|
19369
|
+
}
|
|
19370
|
+
if (this._state.aborted) {
|
|
19371
|
+
throw new RelayerV2StateError({
|
|
19372
|
+
message: `Relayer.run() failed. Request already aborted.`,
|
|
19373
|
+
state: { ...this._state },
|
|
19374
|
+
});
|
|
19375
|
+
}
|
|
19376
|
+
if (this._externalAbortSignal?.aborted === true) {
|
|
19377
|
+
throw new RelayerV2StateError({
|
|
19378
|
+
message: `Relayer.run() failed. External AbortSignal already aborted (reason:${this._externalAbortSignal?.reason}).`,
|
|
19379
|
+
state: { ...this._state },
|
|
19380
|
+
});
|
|
19381
|
+
}
|
|
19382
|
+
if (this._internalAbortSignal?.aborted === true) {
|
|
19383
|
+
throw new RelayerV2StateError({
|
|
19384
|
+
message: `Relayer.run() failed. Internal AbortSignal already aborted (reason:${this._internalAbortSignal?.reason}).`,
|
|
19385
|
+
state: { ...this._state },
|
|
19386
|
+
});
|
|
19387
|
+
}
|
|
19388
|
+
if (this._state.running) {
|
|
19389
|
+
throw new RelayerV2StateError({
|
|
19390
|
+
message: `Relayer.run() failed. Request already running.`,
|
|
19391
|
+
state: { ...this._state },
|
|
19392
|
+
});
|
|
19393
|
+
}
|
|
19394
|
+
this._state.running = true;
|
|
19395
|
+
this._requestStartTimestamp = Date.now();
|
|
19396
|
+
this._setGlobalRequestTimeout(this._requestMaxDurationInSecs * 1000);
|
|
19397
|
+
try {
|
|
19398
|
+
const json = await this._runPostLoop();
|
|
19399
|
+
this._state.succeeded = true;
|
|
19400
|
+
this._terminate('succeeded');
|
|
19401
|
+
return json;
|
|
19402
|
+
}
|
|
19403
|
+
catch (e) {
|
|
19404
|
+
this._state.failed = true;
|
|
19405
|
+
if (e.name === 'AbortError') {
|
|
19406
|
+
this._assert(this._state.aborted, 'this._state.aborted');
|
|
19407
|
+
this._assert(this._state.terminated, 'this._state.terminated');
|
|
19408
|
+
}
|
|
19409
|
+
// Ignored if already terminated. For example, if abort has been previously called.
|
|
19410
|
+
this._terminate('failed', e);
|
|
19411
|
+
throw e;
|
|
19412
|
+
}
|
|
19413
|
+
}
|
|
19414
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19415
|
+
// Public API: cancel
|
|
19416
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19417
|
+
_canContinue() {
|
|
19418
|
+
return !(this._state.canceled ||
|
|
19419
|
+
this._state.terminated ||
|
|
19420
|
+
this._state.succeeded ||
|
|
19421
|
+
this._state.aborted);
|
|
19422
|
+
}
|
|
19423
|
+
cancel() {
|
|
19424
|
+
if (this._publicAPINoReentrancy) {
|
|
19425
|
+
throw new RelayerV2StateError({
|
|
19426
|
+
message: `Relayer.cancel() failed. Call not permitted.`,
|
|
19427
|
+
state: { ...this._state },
|
|
19428
|
+
});
|
|
19429
|
+
}
|
|
19430
|
+
if (!this._canContinue()) {
|
|
19431
|
+
this._trace('cancel', '!this._canContinue()');
|
|
19432
|
+
return;
|
|
19433
|
+
}
|
|
19434
|
+
this._state.canceled = true;
|
|
19435
|
+
this._internalAbortController?.abort('cancel');
|
|
19436
|
+
// Debug
|
|
19437
|
+
this._assert(this._state.aborted, 'this._state.aborted');
|
|
19438
|
+
this._assert(this._state.terminated, 'this._state.terminated');
|
|
19439
|
+
}
|
|
19440
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19441
|
+
// Public API: getters
|
|
19442
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19443
|
+
get state() {
|
|
19444
|
+
return { ...this._state };
|
|
19445
|
+
}
|
|
19446
|
+
get canceled() {
|
|
19447
|
+
return this._state.canceled;
|
|
19448
|
+
}
|
|
19449
|
+
get terminated() {
|
|
19450
|
+
return this._state.terminated;
|
|
19451
|
+
}
|
|
19452
|
+
get terminateReason() {
|
|
19453
|
+
return this._terminateReason;
|
|
19454
|
+
}
|
|
19455
|
+
get terminateError() {
|
|
19456
|
+
return this._terminateError;
|
|
19457
|
+
}
|
|
19458
|
+
get running() {
|
|
19459
|
+
return this._state.running;
|
|
19460
|
+
}
|
|
19461
|
+
get fetching() {
|
|
19462
|
+
return this._state.fetching;
|
|
19463
|
+
}
|
|
19464
|
+
get failed() {
|
|
19465
|
+
return this._state.failed;
|
|
19466
|
+
}
|
|
19467
|
+
get succeeded() {
|
|
19468
|
+
return this._state.succeeded;
|
|
19469
|
+
}
|
|
19470
|
+
get startTimeMs() {
|
|
19471
|
+
return this._requestStartTimestamp;
|
|
19472
|
+
}
|
|
19473
|
+
get elapsedTimeMs() {
|
|
19474
|
+
if (this._requestStartTimestamp === undefined) {
|
|
19475
|
+
return undefined;
|
|
19476
|
+
}
|
|
19477
|
+
return Date.now() - this._requestStartTimestamp;
|
|
19478
|
+
}
|
|
19479
|
+
get retryCount() {
|
|
19480
|
+
return this._retryCount;
|
|
19481
|
+
}
|
|
19482
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19483
|
+
// Post Loop
|
|
19484
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19485
|
+
// POST : 202 | 400 | 429 | 500 | 503
|
|
19486
|
+
async _runPostLoop() {
|
|
19487
|
+
// No infinite loop!
|
|
19488
|
+
let i = 0;
|
|
19489
|
+
while (i < RelayerV2AsyncRequest.MAX_POST_RETRY) {
|
|
19490
|
+
++i;
|
|
19491
|
+
this._assertCanContinueAfterAwait();
|
|
19492
|
+
// At this stage: `terminated` is guaranteed to be `false`.
|
|
19493
|
+
// However, the `fetch` call can potentially throw an `AbortError`. In this case
|
|
19494
|
+
// in the error catch the `terminated` flag will be `true`! But, that's ok because the
|
|
19495
|
+
// next part of the function will never be executed (thrown error).
|
|
19496
|
+
const elapsed = this._jobId ? Date.now() - this._jobIdTimestamp : 0;
|
|
19497
|
+
const response = await this._fetchPost(elapsed);
|
|
19498
|
+
// At this stage: `terminated` is guaranteed to be `false`.
|
|
19499
|
+
const responseStatus = response.status;
|
|
19500
|
+
switch (responseStatus) {
|
|
19501
|
+
// RelayerV2ResponseQueued
|
|
19502
|
+
case 202: {
|
|
19503
|
+
// response.json() errors:
|
|
19504
|
+
// 1. if body is already read (call json() 2 times)
|
|
19505
|
+
// - TypeError: Body is unusable: Body has already been read
|
|
19506
|
+
// 2. if body is invalid JSON
|
|
19507
|
+
// - SyntaxError: Unexpected end of JSON input
|
|
19508
|
+
// - SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2) at JSON.parse (<anonymous>)
|
|
19509
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19510
|
+
try {
|
|
19511
|
+
assertIsRelayerV2ResponseQueued(bodyJson, 'body');
|
|
19512
|
+
}
|
|
19513
|
+
catch (cause) {
|
|
19514
|
+
this._throwInvalidResponseError({
|
|
19515
|
+
fetchMethod: 'POST',
|
|
19516
|
+
status: responseStatus,
|
|
19517
|
+
cause: cause,
|
|
19518
|
+
elapsed,
|
|
19519
|
+
});
|
|
19520
|
+
}
|
|
19521
|
+
let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
|
|
19522
|
+
if (retry_after_sec < 1)
|
|
19523
|
+
retry_after_sec = 1;
|
|
19524
|
+
// Debug: will throw an assert failed error if jobId has already been set
|
|
19525
|
+
this._setJobIdOnce(bodyJson.result.jobId);
|
|
19526
|
+
// Async onProgress callback
|
|
19527
|
+
this._postAsyncOnProgressCallback({
|
|
19528
|
+
type: 'queued',
|
|
19529
|
+
url: this._url,
|
|
19530
|
+
method: 'POST',
|
|
19531
|
+
status: responseStatus,
|
|
19532
|
+
requestId: bodyJson.requestId,
|
|
19533
|
+
jobId: this.jobId,
|
|
19534
|
+
operation: this._relayerOperation,
|
|
19535
|
+
retryCount: this._retryCount,
|
|
19536
|
+
retryAfter: retry_after_sec,
|
|
19537
|
+
elapsed,
|
|
19538
|
+
});
|
|
19539
|
+
// Wait if needed (minimum 1s)
|
|
19540
|
+
await this._setRetryAfterTimeout(retry_after_sec * 1000);
|
|
19541
|
+
const json = await this._runGetLoop();
|
|
19542
|
+
return json;
|
|
19543
|
+
}
|
|
19544
|
+
// RelayerV2ResponseFailed
|
|
19545
|
+
// RelayerV2ApiError400
|
|
19546
|
+
// RelayerV2ApiError400WithDetails
|
|
19547
|
+
case 400: {
|
|
19548
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19549
|
+
try {
|
|
19550
|
+
assertIsRelayerV2ResponseFailedWithError400(bodyJson, 'body');
|
|
19551
|
+
}
|
|
19552
|
+
catch (cause) {
|
|
19553
|
+
this._throwInvalidResponseError({
|
|
19554
|
+
fetchMethod: 'POST',
|
|
19555
|
+
status: responseStatus,
|
|
19556
|
+
cause: cause,
|
|
19557
|
+
elapsed,
|
|
19558
|
+
});
|
|
19559
|
+
}
|
|
19560
|
+
this._throwRelayerV2ResponseApiError({
|
|
19561
|
+
fetchMethod: 'POST',
|
|
19562
|
+
status: responseStatus,
|
|
19563
|
+
relayerApiError: bodyJson.error,
|
|
19564
|
+
elapsed,
|
|
19565
|
+
});
|
|
19566
|
+
}
|
|
19567
|
+
// RelayerV2ResponseFailed
|
|
19568
|
+
// RelayerV2ApiError429
|
|
19569
|
+
case 429: {
|
|
19570
|
+
// Retry
|
|
19571
|
+
// Rate Limit error (Cloudflare/Kong/Relayer), reason in message
|
|
19572
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19573
|
+
try {
|
|
19574
|
+
assertIsRelayerV2ResponseFailedWithError429(bodyJson, 'body');
|
|
19575
|
+
}
|
|
19576
|
+
catch (cause) {
|
|
19577
|
+
this._throwInvalidResponseError({
|
|
19578
|
+
fetchMethod: 'POST',
|
|
19579
|
+
status: responseStatus,
|
|
19580
|
+
cause: cause,
|
|
19581
|
+
elapsed,
|
|
19582
|
+
});
|
|
19583
|
+
}
|
|
19584
|
+
let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
|
|
19585
|
+
if (retry_after_sec < 1)
|
|
19586
|
+
retry_after_sec = 1;
|
|
19587
|
+
// Async onProgress callback
|
|
19588
|
+
this._postAsyncOnProgressCallback({
|
|
19589
|
+
type: 'ratelimited',
|
|
19590
|
+
url: this._url,
|
|
19591
|
+
method: 'POST',
|
|
19592
|
+
status: responseStatus,
|
|
19593
|
+
retryAfter: retry_after_sec,
|
|
19594
|
+
retryCount: this._retryCount,
|
|
19595
|
+
elapsed,
|
|
19596
|
+
message: bodyJson.error.message,
|
|
19597
|
+
});
|
|
19598
|
+
// Wait if needed (minimum 1s)
|
|
19599
|
+
await this._setRetryAfterTimeout(retry_after_sec * 1000);
|
|
19600
|
+
continue;
|
|
19601
|
+
}
|
|
19602
|
+
// RelayerV2ResponseFailed
|
|
19603
|
+
// RelayerV2ApiError500
|
|
19604
|
+
case 500: {
|
|
19605
|
+
// Abort
|
|
19606
|
+
// Relayer internal error
|
|
19607
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19608
|
+
try {
|
|
19609
|
+
assertIsRelayerV2ResponseFailedWithError500(bodyJson, 'body');
|
|
19610
|
+
}
|
|
19611
|
+
catch (cause) {
|
|
19612
|
+
this._throwInvalidResponseError({
|
|
19613
|
+
fetchMethod: 'POST',
|
|
19614
|
+
status: responseStatus,
|
|
19615
|
+
cause: cause,
|
|
19616
|
+
elapsed,
|
|
19617
|
+
});
|
|
19618
|
+
}
|
|
19619
|
+
this._throwRelayerV2ResponseApiError({
|
|
19620
|
+
fetchMethod: 'POST',
|
|
19621
|
+
status: responseStatus,
|
|
19622
|
+
relayerApiError: bodyJson.error,
|
|
19623
|
+
elapsed,
|
|
19624
|
+
});
|
|
19625
|
+
}
|
|
19626
|
+
// RelayerV2ResponseFailed
|
|
19627
|
+
// RelayerV2ApiError503
|
|
19628
|
+
case 503: {
|
|
19629
|
+
// Abort
|
|
19630
|
+
// Possible Reasons: Gateway has some internal error (unknown)
|
|
19631
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19632
|
+
try {
|
|
19633
|
+
assertIsRelayerV2ResponseFailedWithError503(bodyJson, 'body');
|
|
19634
|
+
}
|
|
19635
|
+
catch (cause) {
|
|
19636
|
+
this._throwInvalidResponseError({
|
|
19637
|
+
fetchMethod: 'POST',
|
|
19638
|
+
status: responseStatus,
|
|
19639
|
+
cause: cause,
|
|
19640
|
+
elapsed,
|
|
19641
|
+
});
|
|
19642
|
+
}
|
|
19643
|
+
this._throwRelayerV2ResponseApiError({
|
|
19644
|
+
fetchMethod: 'POST',
|
|
19645
|
+
status: responseStatus,
|
|
19646
|
+
relayerApiError: bodyJson.error,
|
|
19647
|
+
elapsed,
|
|
19648
|
+
});
|
|
19649
|
+
}
|
|
19650
|
+
default: {
|
|
19651
|
+
// Use TS compiler + `never` to guarantee the switch integrity
|
|
19652
|
+
const throwUnsupportedStatus = (unsupportedStatus) => {
|
|
19653
|
+
throw new RelayerV2ResponseStatusError({
|
|
19654
|
+
fetchMethod: 'POST',
|
|
19655
|
+
status: unsupportedStatus,
|
|
19656
|
+
url: this._url,
|
|
19657
|
+
operation: this._relayerOperation,
|
|
19658
|
+
elapsed,
|
|
19659
|
+
retryCount: this._retryCount,
|
|
19660
|
+
state: { ...this._state },
|
|
19661
|
+
});
|
|
19662
|
+
};
|
|
19663
|
+
throwUnsupportedStatus(responseStatus);
|
|
19664
|
+
}
|
|
19665
|
+
}
|
|
19666
|
+
}
|
|
19667
|
+
// Max retry error
|
|
19668
|
+
this._throwMaxRetryError({ fetchMethod: 'POST' });
|
|
19669
|
+
}
|
|
19670
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19671
|
+
// Get Loop
|
|
19672
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19673
|
+
// GET: 200 | 202 | 404 | 500 | 503 | 504
|
|
19674
|
+
// GET is not rate-limited, therefore there is not 429 error
|
|
19675
|
+
async _runGetLoop() {
|
|
19676
|
+
this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
|
|
19677
|
+
this._assert(this._jobIdTimestamp !== undefined, 'this._jobIdTimestamp !== undefined');
|
|
19678
|
+
let i = 0;
|
|
19679
|
+
while (i < RelayerV2AsyncRequest.MAX_GET_RETRY) {
|
|
19680
|
+
++i;
|
|
19681
|
+
this._assertCanContinueAfterAwait();
|
|
19682
|
+
const elapsed = Date.now() - this._jobIdTimestamp;
|
|
19683
|
+
const response = await this._fetchGet(elapsed);
|
|
19684
|
+
// At this stage: `terminated` is guaranteed to be `false`.
|
|
19685
|
+
const responseStatus = response.status;
|
|
19686
|
+
switch (responseStatus) {
|
|
19687
|
+
// RelayerV2GetResponseSucceeded
|
|
19688
|
+
case 200: {
|
|
19689
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19690
|
+
try {
|
|
19691
|
+
if (this._relayerOperation === 'INPUT_PROOF') {
|
|
19692
|
+
assertIsRelayerV2GetResponseInputProofSucceeded(bodyJson, 'body');
|
|
19693
|
+
// Async onProgress callback
|
|
19694
|
+
this._postAsyncOnProgressCallback({
|
|
19695
|
+
type: 'succeeded',
|
|
19696
|
+
url: this._url,
|
|
19697
|
+
method: 'GET',
|
|
19698
|
+
status: responseStatus,
|
|
19699
|
+
jobId: this.jobId,
|
|
19700
|
+
requestId: bodyJson.requestId,
|
|
19701
|
+
operation: this._relayerOperation,
|
|
19702
|
+
retryCount: this._retryCount,
|
|
19703
|
+
elapsed,
|
|
19704
|
+
result: bodyJson.result,
|
|
19705
|
+
});
|
|
19706
|
+
if (!bodyJson.result.accepted) {
|
|
19707
|
+
const e = new RelayerV2ResponseInputProofRejectedError({
|
|
19708
|
+
url: this._url,
|
|
19709
|
+
fetchMethod: 'GET',
|
|
19710
|
+
jobId: this.jobId,
|
|
19711
|
+
operation: this._relayerOperation,
|
|
19712
|
+
retryCount: this._retryCount,
|
|
19713
|
+
status: responseStatus,
|
|
19714
|
+
state: { ...this._state },
|
|
19715
|
+
elapsed,
|
|
19716
|
+
result: bodyJson.result,
|
|
19717
|
+
});
|
|
19718
|
+
throw e;
|
|
19719
|
+
}
|
|
19720
|
+
}
|
|
19721
|
+
else if (this._relayerOperation === 'PUBLIC_DECRYPT') {
|
|
19722
|
+
assertIsRelayerV2GetResponsePublicDecryptSucceeded(bodyJson, 'body');
|
|
19723
|
+
// Async onProgress callback
|
|
19724
|
+
this._postAsyncOnProgressCallback({
|
|
19725
|
+
type: 'succeeded',
|
|
19726
|
+
url: this._url,
|
|
19727
|
+
method: 'GET',
|
|
19728
|
+
status: responseStatus,
|
|
19729
|
+
jobId: this.jobId,
|
|
19730
|
+
requestId: bodyJson.requestId,
|
|
19731
|
+
operation: this._relayerOperation,
|
|
19732
|
+
retryCount: this._retryCount,
|
|
19733
|
+
elapsed,
|
|
19734
|
+
result: bodyJson.result,
|
|
19735
|
+
});
|
|
19736
|
+
}
|
|
19737
|
+
else if (this._relayerOperation === 'USER_DECRYPT') {
|
|
19738
|
+
assertIsRelayerV2GetResponseUserDecryptSucceeded(bodyJson, 'body');
|
|
19739
|
+
// Async onProgress callback
|
|
19740
|
+
this._postAsyncOnProgressCallback({
|
|
19741
|
+
type: 'succeeded',
|
|
19742
|
+
url: this._url,
|
|
19743
|
+
method: 'GET',
|
|
19744
|
+
status: responseStatus,
|
|
19745
|
+
jobId: this.jobId,
|
|
19746
|
+
requestId: bodyJson.requestId,
|
|
19747
|
+
operation: this._relayerOperation,
|
|
19748
|
+
retryCount: this._retryCount,
|
|
19749
|
+
elapsed,
|
|
19750
|
+
result: bodyJson.result,
|
|
19751
|
+
});
|
|
19752
|
+
}
|
|
19753
|
+
}
|
|
19754
|
+
catch (cause) {
|
|
19755
|
+
// Special case for InputProof rejected
|
|
19756
|
+
if (cause instanceof RelayerV2ResponseInputProofRejectedError) {
|
|
19757
|
+
throw cause;
|
|
19758
|
+
}
|
|
19759
|
+
this._throwResponseInvalidBodyError({
|
|
19760
|
+
fetchMethod: 'GET',
|
|
19761
|
+
status: responseStatus,
|
|
19762
|
+
elapsed,
|
|
19763
|
+
cause: cause,
|
|
19764
|
+
});
|
|
19765
|
+
}
|
|
19766
|
+
// RelayerV2ResultPublicDecrypt
|
|
19767
|
+
// RelayerV2ResultUserDecrypt
|
|
19768
|
+
// RelayerV2ResultInputProof;
|
|
19769
|
+
return bodyJson.result;
|
|
19770
|
+
}
|
|
19771
|
+
// RelayerV2ResponseQueued
|
|
19772
|
+
case 202: {
|
|
19773
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19774
|
+
try {
|
|
19775
|
+
assertIsRelayerV2ResponseQueued(bodyJson, 'body');
|
|
19776
|
+
}
|
|
19777
|
+
catch (cause) {
|
|
19778
|
+
this._throwResponseInvalidBodyError({
|
|
19779
|
+
fetchMethod: 'GET',
|
|
19780
|
+
status: responseStatus,
|
|
19781
|
+
elapsed,
|
|
19782
|
+
cause: cause,
|
|
19783
|
+
});
|
|
19784
|
+
}
|
|
19785
|
+
let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
|
|
19786
|
+
if (retry_after_sec < 1)
|
|
19787
|
+
retry_after_sec = 1;
|
|
19788
|
+
// Async onProgress callback
|
|
19789
|
+
this._postAsyncOnProgressCallback({
|
|
19790
|
+
type: 'queued',
|
|
19791
|
+
url: this._url,
|
|
19792
|
+
method: 'GET',
|
|
19793
|
+
status: responseStatus,
|
|
19794
|
+
requestId: bodyJson.requestId,
|
|
19795
|
+
operation: this._relayerOperation,
|
|
19796
|
+
jobId: this.jobId,
|
|
19797
|
+
retryAfter: retry_after_sec,
|
|
19798
|
+
retryCount: this._retryCount,
|
|
19799
|
+
elapsed,
|
|
19800
|
+
});
|
|
19801
|
+
// Wait if needed (minimum 1s)
|
|
19802
|
+
await this._setRetryAfterTimeout(retry_after_sec * 1000);
|
|
19803
|
+
continue;
|
|
19804
|
+
}
|
|
19805
|
+
case 400: {
|
|
19806
|
+
// Abort
|
|
19807
|
+
// Wrong jobId, incorrect format or unknown value etc.
|
|
19808
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19809
|
+
try {
|
|
19810
|
+
assertIsRelayerV2ResponseFailedWithError400(bodyJson, 'body');
|
|
19811
|
+
}
|
|
19812
|
+
catch (cause) {
|
|
19813
|
+
this._throwResponseInvalidBodyError({
|
|
19814
|
+
fetchMethod: 'GET',
|
|
19815
|
+
status: responseStatus,
|
|
19816
|
+
elapsed,
|
|
19817
|
+
cause: cause,
|
|
19818
|
+
});
|
|
19819
|
+
}
|
|
19820
|
+
this._throwRelayerV2ResponseApiError({
|
|
19821
|
+
fetchMethod: 'GET',
|
|
19822
|
+
status: responseStatus,
|
|
19823
|
+
relayerApiError: bodyJson.error,
|
|
19824
|
+
elapsed,
|
|
19825
|
+
});
|
|
19826
|
+
}
|
|
19827
|
+
case 404: {
|
|
19828
|
+
// Abort
|
|
19829
|
+
// Wrong jobId, incorrect format or unknown value etc.
|
|
19830
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19831
|
+
try {
|
|
19832
|
+
assertIsRelayerV2ResponseFailedWithError404(bodyJson, 'body');
|
|
19833
|
+
}
|
|
19834
|
+
catch (cause) {
|
|
19835
|
+
this._throwResponseInvalidBodyError({
|
|
19836
|
+
fetchMethod: 'GET',
|
|
19837
|
+
status: responseStatus,
|
|
19838
|
+
elapsed,
|
|
19839
|
+
cause: cause,
|
|
19840
|
+
});
|
|
19841
|
+
}
|
|
19842
|
+
this._throwRelayerV2ResponseApiError({
|
|
19843
|
+
fetchMethod: 'GET',
|
|
19844
|
+
status: responseStatus,
|
|
19845
|
+
relayerApiError: bodyJson.error,
|
|
19846
|
+
elapsed,
|
|
19847
|
+
});
|
|
19848
|
+
}
|
|
19849
|
+
// RelayerV2ResponseFailed
|
|
19850
|
+
// RelayerV2ApiError500
|
|
19851
|
+
case 500: {
|
|
19852
|
+
// Abort
|
|
19853
|
+
// Relayer internal error
|
|
19854
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19855
|
+
try {
|
|
19856
|
+
assertIsRelayerV2ResponseFailedWithError500(bodyJson, 'body');
|
|
19857
|
+
}
|
|
19858
|
+
catch (cause) {
|
|
19859
|
+
this._throwResponseInvalidBodyError({
|
|
19860
|
+
fetchMethod: 'GET',
|
|
19861
|
+
status: responseStatus,
|
|
19862
|
+
elapsed,
|
|
19863
|
+
cause: cause,
|
|
19864
|
+
});
|
|
19865
|
+
}
|
|
19866
|
+
this._throwRelayerV2ResponseApiError({
|
|
19867
|
+
fetchMethod: 'GET',
|
|
19868
|
+
status: responseStatus,
|
|
19869
|
+
relayerApiError: bodyJson.error,
|
|
19870
|
+
elapsed,
|
|
19871
|
+
});
|
|
19872
|
+
}
|
|
19873
|
+
// RelayerV2ResponseFailed
|
|
19874
|
+
// RelayerV2ApiError503
|
|
19875
|
+
case 503: {
|
|
19876
|
+
// Abort
|
|
19877
|
+
// Possible Reasons: Gateway has some internal error (unknown)
|
|
19878
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19879
|
+
try {
|
|
19880
|
+
assertIsRelayerV2ResponseFailedWithError503(bodyJson, 'body');
|
|
19881
|
+
}
|
|
19882
|
+
catch (cause) {
|
|
19883
|
+
this._throwResponseInvalidBodyError({
|
|
19884
|
+
fetchMethod: 'GET',
|
|
19885
|
+
status: responseStatus,
|
|
19886
|
+
elapsed,
|
|
19887
|
+
cause: cause,
|
|
19888
|
+
});
|
|
19889
|
+
}
|
|
19890
|
+
this._throwRelayerV2ResponseApiError({
|
|
19891
|
+
fetchMethod: 'GET',
|
|
19892
|
+
status: responseStatus,
|
|
19893
|
+
relayerApiError: bodyJson.error,
|
|
19894
|
+
elapsed,
|
|
19895
|
+
});
|
|
19896
|
+
}
|
|
19897
|
+
// RelayerV2ResponseFailed
|
|
19898
|
+
// RelayerV2ApiError504
|
|
19899
|
+
case 504: {
|
|
19900
|
+
// Abort
|
|
19901
|
+
// Possible Reasons: Gateway has not responded in time (gateway timeout)
|
|
19902
|
+
const bodyJson = await this._getResponseJson(response);
|
|
19903
|
+
try {
|
|
19904
|
+
assertIsRelayerV2ResponseFailedWithError504(bodyJson, 'body');
|
|
19905
|
+
}
|
|
19906
|
+
catch (cause) {
|
|
19907
|
+
this._throwResponseInvalidBodyError({
|
|
19908
|
+
fetchMethod: 'GET',
|
|
19909
|
+
status: responseStatus,
|
|
19910
|
+
elapsed,
|
|
19911
|
+
cause: cause,
|
|
19912
|
+
});
|
|
19913
|
+
}
|
|
19914
|
+
this._throwRelayerV2ResponseApiError({
|
|
19915
|
+
fetchMethod: 'GET',
|
|
19916
|
+
status: responseStatus,
|
|
19917
|
+
relayerApiError: bodyJson.error,
|
|
19918
|
+
elapsed,
|
|
19919
|
+
});
|
|
19920
|
+
}
|
|
19921
|
+
default: {
|
|
19922
|
+
// Use TS compiler + `never` to guarantee the switch integrity
|
|
19923
|
+
const throwUnsupportedStatus = (unsupportedStatus) => {
|
|
19924
|
+
throw new RelayerV2ResponseStatusError({
|
|
19925
|
+
fetchMethod: 'GET',
|
|
19926
|
+
status: unsupportedStatus,
|
|
19927
|
+
url: this._url,
|
|
19928
|
+
jobId: this.jobId,
|
|
19929
|
+
operation: this._relayerOperation,
|
|
19930
|
+
elapsed,
|
|
19931
|
+
retryCount: this._retryCount,
|
|
19932
|
+
state: { ...this._state },
|
|
19933
|
+
});
|
|
19934
|
+
};
|
|
19935
|
+
throwUnsupportedStatus(responseStatus);
|
|
19936
|
+
}
|
|
19937
|
+
}
|
|
19938
|
+
}
|
|
19939
|
+
// Max retry error
|
|
19940
|
+
this._throwMaxRetryError({ fetchMethod: 'GET' });
|
|
19941
|
+
}
|
|
19942
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19943
|
+
async _getResponseJson(response) {
|
|
19944
|
+
const bodyJson = await response.json();
|
|
19945
|
+
this._assertCanContinueAfterAwait();
|
|
19946
|
+
return bodyJson;
|
|
19947
|
+
}
|
|
19948
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19949
|
+
_getRetryAfterHeaderValueInSecs(response) {
|
|
19950
|
+
if (!response.headers.has('Retry-After')) {
|
|
19951
|
+
if (this._throwErrorIfNoRetryAfter) {
|
|
19952
|
+
throw new Error(`Missing 'Retry-After' header key`);
|
|
19953
|
+
}
|
|
19954
|
+
return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
|
|
19955
|
+
}
|
|
19956
|
+
try {
|
|
19957
|
+
const n = Number.parseInt(response.headers.get('Retry-After'));
|
|
19958
|
+
if (isUint(n)) {
|
|
19959
|
+
return n;
|
|
19960
|
+
}
|
|
19961
|
+
}
|
|
19962
|
+
catch {
|
|
19963
|
+
//
|
|
19964
|
+
}
|
|
19965
|
+
if (this._throwErrorIfNoRetryAfter) {
|
|
19966
|
+
throw new Error(`Invalid 'Retry-After' header key`);
|
|
19967
|
+
}
|
|
19968
|
+
return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
|
|
19969
|
+
}
|
|
19970
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19971
|
+
// JobId
|
|
19972
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19973
|
+
/**
|
|
19974
|
+
* Sets the unique job identifier for this request.
|
|
19975
|
+
*
|
|
19976
|
+
* This function enforces a strict initialization constraint: the jobId must be
|
|
19977
|
+
* set exactly once during the entire lifecycle of the state machine instance.
|
|
19978
|
+
*
|
|
19979
|
+
* This immutability ensures that all subsequent operations, logging, and state
|
|
19980
|
+
* transitions are consistently associated with the correct external request.
|
|
19981
|
+
*
|
|
19982
|
+
* @param jobId - The unique identifier associated with the asynchronous job request.
|
|
19983
|
+
* @private
|
|
19984
|
+
* @throws {RelayerV2RequestInternalError} Thrown if jobId is undefined or if the jobId has already been set.
|
|
19985
|
+
*/
|
|
19986
|
+
_setJobIdOnce(jobId) {
|
|
19987
|
+
this._assert(jobId !== undefined, 'jobId !== undefined');
|
|
19988
|
+
this._assert(this._jobId === undefined, 'this._jobId === undefined');
|
|
19989
|
+
this._jobId = jobId;
|
|
19990
|
+
this._jobIdTimestamp = Date.now();
|
|
19991
|
+
}
|
|
19992
|
+
get jobId() {
|
|
19993
|
+
this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
|
|
19994
|
+
return this._jobId;
|
|
19995
|
+
}
|
|
19996
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19997
|
+
// Fetch functions
|
|
19998
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
19999
|
+
async _fetchPost(elapsed) {
|
|
20000
|
+
// Debug state-check guards:
|
|
20001
|
+
// - the jobId is guaranteed to be undefined.
|
|
20002
|
+
// - `terminated` is guaranteed to be `false`
|
|
20003
|
+
// - `fetching` is guaranteed to be `false`
|
|
20004
|
+
this._assert(this._jobId === undefined, 'this._jobId === undefined');
|
|
20005
|
+
this._assert(!this._state.terminated, '!this._state.terminated');
|
|
20006
|
+
this._assert(!this._state.fetching, '!this._state.fetching');
|
|
20007
|
+
this._trace('_fetchPost', 'enter');
|
|
20008
|
+
const init = setAuth({
|
|
20009
|
+
method: 'POST',
|
|
20010
|
+
headers: {
|
|
20011
|
+
'Content-Type': 'application/json',
|
|
20012
|
+
},
|
|
20013
|
+
body: JSON.stringify(this._payload),
|
|
20014
|
+
...(this._internalAbortSignal
|
|
20015
|
+
? { signal: this._internalAbortSignal }
|
|
20016
|
+
: {}),
|
|
20017
|
+
}, this._fhevmInstanceOptions?.auth);
|
|
20018
|
+
this._state.fetching = true;
|
|
20019
|
+
let response;
|
|
20020
|
+
try {
|
|
20021
|
+
response = await fetch(this._url, init);
|
|
20022
|
+
}
|
|
20023
|
+
catch (cause) {
|
|
20024
|
+
this._state.fetching = false;
|
|
20025
|
+
// Warning: `terminated` can be `true` here!
|
|
20026
|
+
// (ex: if `controller.abort()` has been called from the outside while still executing `fetch`)
|
|
20027
|
+
this._trace('_fetchPost', 'catch(e) + throw e: ' + cause);
|
|
20028
|
+
// Keep the standard 'AbortError'
|
|
20029
|
+
if (cause.name === 'AbortError') {
|
|
20030
|
+
throw cause;
|
|
20031
|
+
}
|
|
20032
|
+
else {
|
|
20033
|
+
this._throwFetchError({
|
|
20034
|
+
fetchMethod: 'POST',
|
|
20035
|
+
cause,
|
|
20036
|
+
elapsed,
|
|
20037
|
+
});
|
|
20038
|
+
}
|
|
20039
|
+
}
|
|
20040
|
+
this._state.fetching = false;
|
|
20041
|
+
// Debug state-check guards:
|
|
20042
|
+
// - the jobId is guaranteed to be undefined.
|
|
20043
|
+
// - `terminated` is guaranteed to be `false`
|
|
20044
|
+
this._assert(this._jobId === undefined, 'this._jobId === undefined');
|
|
20045
|
+
this._assert(!this._state.terminated, '!this._state.terminated');
|
|
20046
|
+
// Debug
|
|
20047
|
+
this._assertCanContinueAfterAwait();
|
|
20048
|
+
this._trace('_fetchPost', 'return response Ok');
|
|
20049
|
+
return response;
|
|
20050
|
+
}
|
|
20051
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20052
|
+
async _fetchGet(elapsed) {
|
|
20053
|
+
// Debug state-check guards:
|
|
20054
|
+
// - the jobId is guaranteed to be set.
|
|
20055
|
+
// - `terminated` is guaranteed to be `false`
|
|
20056
|
+
// - `fetching` is guaranteed to be `false`
|
|
20057
|
+
this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
|
|
20058
|
+
this._assert(!this._state.terminated, '!this._state.terminated');
|
|
20059
|
+
this._assert(!this._state.fetching, '!this._state.fetching');
|
|
20060
|
+
this._trace('_fetchGet', `jobId=${this.jobId}`);
|
|
20061
|
+
const init = this._internalAbortSignal
|
|
20062
|
+
? { signal: this._internalAbortSignal }
|
|
20063
|
+
: undefined;
|
|
20064
|
+
this._state.fetching = true;
|
|
20065
|
+
let response;
|
|
20066
|
+
try {
|
|
20067
|
+
response = await fetch(`${this._url}/${this.jobId}`, init);
|
|
20068
|
+
}
|
|
20069
|
+
catch (cause) {
|
|
20070
|
+
this._state.fetching = false;
|
|
20071
|
+
// Warning: `terminated` can be `true` here!
|
|
20072
|
+
// (ex: if `controller.abort()` has been called from the outside while still executing `fetch`)
|
|
20073
|
+
this._trace('_fetchGet', `jobId=${this.jobId}, catch(e) + throw e: ${cause}`);
|
|
20074
|
+
// Keep the standard 'AbortError'
|
|
20075
|
+
if (cause.name === 'AbortError') {
|
|
20076
|
+
throw cause;
|
|
20077
|
+
}
|
|
20078
|
+
else {
|
|
20079
|
+
this._throwFetchError({
|
|
20080
|
+
fetchMethod: 'GET',
|
|
20081
|
+
cause,
|
|
20082
|
+
elapsed,
|
|
20083
|
+
});
|
|
20084
|
+
}
|
|
20085
|
+
}
|
|
20086
|
+
this._state.fetching = false;
|
|
20087
|
+
// Debug state-check guards:
|
|
20088
|
+
// - the jobId is guaranteed to be set.
|
|
20089
|
+
// - `terminated` is guaranteed to be `false`
|
|
20090
|
+
this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
|
|
20091
|
+
this._assert(!this._state.terminated, '!this._state.terminated');
|
|
20092
|
+
// Debug
|
|
20093
|
+
this._assertCanContinueAfterAwait();
|
|
20094
|
+
this._trace('_fetchGet', `jobId=${this.jobId}, return response Ok, status=${response.status}`);
|
|
20095
|
+
return response;
|
|
20096
|
+
}
|
|
20097
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20098
|
+
// AbortSignal
|
|
20099
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20100
|
+
// Warning: Use arrow function only!
|
|
20101
|
+
_handleExternalSignalAbort = (ev) => {
|
|
20102
|
+
const signal = ev.currentTarget;
|
|
20103
|
+
// TESTING: the following sequences must be extensively tested:
|
|
20104
|
+
// ============================================================
|
|
20105
|
+
//
|
|
20106
|
+
// Each steps could potentially be called synchronously one after the other
|
|
20107
|
+
// or asynchronously: step 2 is called from the next microtick
|
|
20108
|
+
//
|
|
20109
|
+
// 1. externalSignal.abort();
|
|
20110
|
+
// 2. request.cancel();
|
|
20111
|
+
//
|
|
20112
|
+
// 1. externalSignal.abort();
|
|
20113
|
+
// 2. externalSignal.abort();
|
|
20114
|
+
//
|
|
20115
|
+
// 1. request.cancel();
|
|
20116
|
+
// 2. externalSignal.abort();
|
|
20117
|
+
// Debug state-check guards:
|
|
20118
|
+
this._assert(this instanceof RelayerV2AsyncRequest, `this instanceof RelayerV2AsyncRequest`);
|
|
20119
|
+
this._assert(signal === this._externalAbortSignal, 'signal === this._externalAbortSignal');
|
|
20120
|
+
this._assert(!this._state.terminated, `!this._state.terminated`);
|
|
20121
|
+
this._assert(!this._state.aborted, '!this._state.aborted');
|
|
20122
|
+
this._assert(!this._state.canceled, '!this._state.canceled');
|
|
20123
|
+
this.cancel();
|
|
20124
|
+
};
|
|
20125
|
+
// Warning: Use arrow function only!
|
|
20126
|
+
_handleInternalSignalAbort = (ev) => {
|
|
20127
|
+
const signal = ev.currentTarget;
|
|
20128
|
+
// Debug state-check guards:
|
|
20129
|
+
this._assert(this instanceof RelayerV2AsyncRequest, `this instanceof RelayerV2AsyncRequest`);
|
|
20130
|
+
this._assert(signal === this._internalAbortSignal, 'signal === this._internalAbortSignal');
|
|
20131
|
+
this._assert(!this._state.terminated, `!this._state.terminated`);
|
|
20132
|
+
this._assert(!this._state.aborted, '!this._state.aborted');
|
|
20133
|
+
this._state.aborted = true;
|
|
20134
|
+
if (signal.reason !== 'cancel') {
|
|
20135
|
+
this._assert(!this._state.canceled, '!this._state.canceled');
|
|
20136
|
+
}
|
|
20137
|
+
this._terminate('abort');
|
|
20138
|
+
};
|
|
20139
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20140
|
+
// Terminate
|
|
20141
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20142
|
+
/**
|
|
20143
|
+
* Can be called multiple times
|
|
20144
|
+
*/
|
|
20145
|
+
_terminate(reason, error) {
|
|
20146
|
+
// Warning: this._state.fetching can be true
|
|
20147
|
+
// ex: call cancel while fetch is running
|
|
20148
|
+
if (this._state.terminated) {
|
|
20149
|
+
this._trace(`_terminate`, `reason=${reason}. Already terminated with reason='${this._terminateReason}'. IGNORE`);
|
|
20150
|
+
this._assert(this._terminateReason !== undefined, 'this._terminateReason !== undefined');
|
|
20151
|
+
this._assert(this._internalAbortSignal === undefined, 'this._signal === undefined');
|
|
20152
|
+
this._assert(this._requestGlobalTimeoutID === undefined, 'this._requestGlobalTimeoutID === undefined');
|
|
20153
|
+
this._assert(this._retryAfterTimeoutID === undefined, 'this._retryAfterTimeoutID === undefined');
|
|
20154
|
+
this._assert(this._retryAfterTimeoutPromiseFuncReject === undefined, 'this._retryAfterTimeoutPromiseFuncReject === undefined');
|
|
20155
|
+
return;
|
|
20156
|
+
}
|
|
20157
|
+
this._trace('_terminate', `reason=${reason}`);
|
|
20158
|
+
this._terminateReason = reason;
|
|
20159
|
+
this._terminateError = error;
|
|
20160
|
+
this._state.terminated = true;
|
|
20161
|
+
this._tryClearRetryAfterTimeout();
|
|
20162
|
+
this._tryClearGlobalRequestTimeout();
|
|
20163
|
+
const is = this._internalAbortSignal;
|
|
20164
|
+
const es = this._externalAbortSignal;
|
|
20165
|
+
this._externalAbortSignal = undefined;
|
|
20166
|
+
this._internalAbortSignal = undefined;
|
|
20167
|
+
this._internalAbortController = undefined;
|
|
20168
|
+
if (es) {
|
|
20169
|
+
es.removeEventListener('abort', this._handleExternalSignalAbort);
|
|
20170
|
+
}
|
|
20171
|
+
if (is) {
|
|
20172
|
+
is.removeEventListener('abort', this._handleInternalSignalAbort);
|
|
20173
|
+
}
|
|
20174
|
+
this._trace('_terminate', `reason=${reason} completed.`);
|
|
20175
|
+
}
|
|
20176
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20177
|
+
// Retry-After timeout
|
|
20178
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20179
|
+
async _setRetryAfterTimeout(delayMs) {
|
|
20180
|
+
// Debug
|
|
20181
|
+
this._assert(!this._state.terminated, '!this._state.terminated');
|
|
20182
|
+
this._assert(this._retryAfterTimeoutID === undefined, 'this._retryAfterTimeoutID === undefined');
|
|
20183
|
+
this._assert(delayMs >= 1000, 'delayMs >= 1000');
|
|
20184
|
+
this._trace('_setRetryAfterTimeout', `delayMs=${delayMs}`);
|
|
20185
|
+
if (this._retryAfterTimeoutID !== undefined) {
|
|
20186
|
+
return Promise.reject(new Error(`retry-after already running.`));
|
|
20187
|
+
}
|
|
20188
|
+
const p = new Promise((resolve, reject) => {
|
|
20189
|
+
this._retryAfterTimeoutPromiseFuncReject = reject;
|
|
20190
|
+
const callback = () => {
|
|
20191
|
+
this._retryAfterTimeoutID = undefined;
|
|
20192
|
+
this._retryAfterTimeoutPromiseFuncReject = undefined;
|
|
20193
|
+
resolve();
|
|
20194
|
+
};
|
|
20195
|
+
this._retryCount++;
|
|
20196
|
+
this._retryAfterTimeoutID = setTimeout(callback, delayMs);
|
|
20197
|
+
});
|
|
20198
|
+
this._assert(this._retryAfterTimeoutID !== undefined, 'this._retryAfterTimeoutID !== undefined');
|
|
20199
|
+
this._assert(this._retryAfterTimeoutPromiseFuncReject !== undefined, 'this._retryAfterTimeoutPromiseFuncReject !== undefined');
|
|
20200
|
+
return p;
|
|
20201
|
+
}
|
|
20202
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20203
|
+
_tryClearRetryAfterTimeout() {
|
|
20204
|
+
if (this._retryAfterTimeoutID === undefined) {
|
|
20205
|
+
// Debug
|
|
20206
|
+
this._assert(this._retryAfterTimeoutPromiseFuncReject === undefined, 'this._retryAfterTimeoutPromiseFuncReject === undefined');
|
|
20207
|
+
return;
|
|
20208
|
+
}
|
|
20209
|
+
const reject = this._retryAfterTimeoutPromiseFuncReject;
|
|
20210
|
+
const tid = this._retryAfterTimeoutID;
|
|
20211
|
+
this._retryAfterTimeoutID = undefined;
|
|
20212
|
+
this._retryAfterTimeoutPromiseFuncReject = undefined;
|
|
20213
|
+
clearTimeout(tid);
|
|
20214
|
+
reject(new Error('_tryClearRetryAfterTimeout'));
|
|
20215
|
+
}
|
|
20216
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20217
|
+
// Global Request Timeout
|
|
20218
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20219
|
+
_setGlobalRequestTimeout(delayMs) {
|
|
20220
|
+
// Debug
|
|
20221
|
+
this._assert(this._requestGlobalTimeoutID === undefined, 'this._requestGlobalTimeoutID === undefined');
|
|
20222
|
+
const callback = () => {
|
|
20223
|
+
this._requestGlobalTimeoutID = undefined;
|
|
20224
|
+
this._handleGlobalRequestTimeout();
|
|
20225
|
+
};
|
|
20226
|
+
this._requestGlobalTimeoutID = setTimeout(callback, delayMs);
|
|
20227
|
+
}
|
|
20228
|
+
_handleGlobalRequestTimeout() {
|
|
20229
|
+
this._terminate('timeout');
|
|
20230
|
+
}
|
|
20231
|
+
_tryClearGlobalRequestTimeout() {
|
|
20232
|
+
if (this._requestGlobalTimeoutID === undefined) {
|
|
20233
|
+
return;
|
|
20234
|
+
}
|
|
20235
|
+
const tid = this._requestGlobalTimeoutID;
|
|
20236
|
+
this._requestGlobalTimeoutID = undefined;
|
|
20237
|
+
clearTimeout(tid);
|
|
20238
|
+
}
|
|
20239
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20240
|
+
// Progress
|
|
20241
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20242
|
+
_postAsyncOnProgressCallback(args) {
|
|
20243
|
+
const onProgressFunc = this._onProgress;
|
|
20244
|
+
if (onProgressFunc) {
|
|
20245
|
+
// setTimeout(() => {
|
|
20246
|
+
// onProgressFunc(args);
|
|
20247
|
+
// }, 0);
|
|
20248
|
+
// onProgressFunc() will execute asynchronously in the next cycle of
|
|
20249
|
+
// the JavaScript event loop (the microtask queue).
|
|
20250
|
+
// Promise.resolve().then(() => {
|
|
20251
|
+
// onProgressFunc(args);
|
|
20252
|
+
// });
|
|
20253
|
+
queueMicrotask(() => {
|
|
20254
|
+
onProgressFunc(args);
|
|
20255
|
+
});
|
|
20256
|
+
}
|
|
20257
|
+
}
|
|
20258
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20259
|
+
// Errors
|
|
20260
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20261
|
+
_throwRelayerV2ResponseApiError(params) {
|
|
20262
|
+
// Clone
|
|
20263
|
+
const clonedRelayerApiError = JSON.parse(JSON.stringify(params.relayerApiError));
|
|
20264
|
+
// Async onProgress callback
|
|
20265
|
+
this._postAsyncOnProgressCallback({
|
|
20266
|
+
type: 'failed',
|
|
20267
|
+
url: this._url,
|
|
20268
|
+
method: params.fetchMethod,
|
|
20269
|
+
status: params.status,
|
|
20270
|
+
...(this._jobId ? { jobId: this._jobId } : {}),
|
|
20271
|
+
operation: this._relayerOperation,
|
|
20272
|
+
retryCount: this._retryCount,
|
|
20273
|
+
elapsed: params.elapsed,
|
|
20274
|
+
relayerApiError: clonedRelayerApiError,
|
|
20275
|
+
});
|
|
20276
|
+
throw new RelayerV2ResponseApiError({
|
|
20277
|
+
url: this._url,
|
|
20278
|
+
fetchMethod: params.fetchMethod,
|
|
20279
|
+
status: params.status,
|
|
20280
|
+
jobId: this._jobId,
|
|
20281
|
+
operation: this._relayerOperation,
|
|
20282
|
+
retryCount: this._retryCount,
|
|
20283
|
+
elapsed: params.elapsed,
|
|
20284
|
+
relayerApiError: params.relayerApiError,
|
|
20285
|
+
state: { ...this._state },
|
|
20286
|
+
});
|
|
20287
|
+
}
|
|
20288
|
+
_assert(condition, message) {
|
|
20289
|
+
if (!condition) {
|
|
20290
|
+
this._throwInternalError(`Assertion failed: ${message}`);
|
|
20291
|
+
}
|
|
20292
|
+
}
|
|
20293
|
+
_throwInternalError(message) {
|
|
20294
|
+
throw new RelayerV2RequestInternalError({
|
|
20295
|
+
operation: this._relayerOperation,
|
|
20296
|
+
url: this._url,
|
|
20297
|
+
message,
|
|
20298
|
+
state: JSON.stringify(this._state),
|
|
20299
|
+
jobId: this._jobId, // internal value
|
|
20300
|
+
});
|
|
20301
|
+
}
|
|
20302
|
+
_throwMaxRetryError(params) {
|
|
20303
|
+
const elapsed = this._jobIdTimestamp
|
|
20304
|
+
? Date.now() - this._jobIdTimestamp
|
|
20305
|
+
: 0;
|
|
20306
|
+
throw new RelayerV2MaxRetryError({
|
|
20307
|
+
operation: this._relayerOperation,
|
|
20308
|
+
url: this._url,
|
|
20309
|
+
state: { ...this._state },
|
|
20310
|
+
retryCount: this._retryCount,
|
|
20311
|
+
jobId: this._jobId, // internal value
|
|
20312
|
+
fetchMethod: params.fetchMethod,
|
|
20313
|
+
elapsed,
|
|
20314
|
+
});
|
|
20315
|
+
}
|
|
20316
|
+
_throwInvalidResponseError(params) {
|
|
20317
|
+
throw new RelayerV2ResponseInvalidBodyError({
|
|
20318
|
+
...params,
|
|
20319
|
+
url: this._url,
|
|
20320
|
+
operation: this._relayerOperation,
|
|
20321
|
+
state: { ...this._state },
|
|
20322
|
+
retryCount: this._retryCount,
|
|
20323
|
+
});
|
|
20324
|
+
}
|
|
20325
|
+
_throwResponseInvalidBodyError(params) {
|
|
20326
|
+
throw new RelayerV2ResponseInvalidBodyError({
|
|
20327
|
+
...params,
|
|
20328
|
+
url: this._url,
|
|
20329
|
+
jobId: this.jobId,
|
|
20330
|
+
operation: this._relayerOperation,
|
|
20331
|
+
state: { ...this._state },
|
|
20332
|
+
retryCount: this._retryCount,
|
|
20333
|
+
});
|
|
20334
|
+
}
|
|
20335
|
+
_throwFetchError(params) {
|
|
20336
|
+
throw new RelayerV2FetchError({
|
|
20337
|
+
...params,
|
|
20338
|
+
url: this._url,
|
|
20339
|
+
jobId: this._jobId,
|
|
20340
|
+
operation: this._relayerOperation,
|
|
20341
|
+
state: { ...this._state },
|
|
20342
|
+
retryCount: this._retryCount,
|
|
20343
|
+
});
|
|
20344
|
+
}
|
|
20345
|
+
/**
|
|
20346
|
+
* Assert Continuation Guard
|
|
20347
|
+
*
|
|
20348
|
+
* This internal method implements a state-check guard to ensure the state machine
|
|
20349
|
+
* can safely proceed after an asynchronous operation has completed.
|
|
20350
|
+
*
|
|
20351
|
+
* In a state machine with asynchronous calls (e.g., fetch, timer delays), the system's
|
|
20352
|
+
* state (e.g., this._state) might change externally during the 'await' pause
|
|
20353
|
+
* (e.g., due to a timeout, an external abort signal, or a concurrent state transition).
|
|
20354
|
+
*
|
|
20355
|
+
* If the internal check (this._canContinue()) returns false, it means the current
|
|
20356
|
+
* operation is no longer valid, and execution must stop immediately to prevent state corruption.
|
|
20357
|
+
* This pattern is essential for reliable asynchronous state machines.
|
|
20358
|
+
*
|
|
20359
|
+
* @throws {RelayerV2RequestInternalError} Thrown if the state check fails (i.e., this._canContinue() is false).
|
|
20360
|
+
* The error includes relevant state information (like current state and jobId)
|
|
20361
|
+
* to aid in debugging the exact point of the integrity failure.
|
|
20362
|
+
*/
|
|
20363
|
+
_assertCanContinueAfterAwait() {
|
|
20364
|
+
if (!this._canContinue()) {
|
|
20365
|
+
this._throwInternalError('cannot continue.');
|
|
20366
|
+
}
|
|
20367
|
+
}
|
|
20368
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20369
|
+
// Trace
|
|
20370
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
20371
|
+
_trace(functionName, message) {
|
|
20372
|
+
console.log(`[RelayerV2AsyncRequest]:${functionName}: ${message}`);
|
|
20373
|
+
}
|
|
20374
|
+
}
|
|
20375
|
+
|
|
20376
|
+
function assertIsRelayerV2GetResponseKeyUrl(value, name) {
|
|
20377
|
+
assertNonNullableRecordProperty(value, 'response', name);
|
|
20378
|
+
// crs
|
|
20379
|
+
assertNonNullableRecordProperty(value.response, 'crs', `${name}.response`);
|
|
20380
|
+
const crs = value.response.crs;
|
|
20381
|
+
const keys = Object.keys(crs);
|
|
20382
|
+
for (let i = 0; i < keys.length; ++i) {
|
|
20383
|
+
// RelayerV2KeyData
|
|
20384
|
+
assertIsRelayerV2KeyData(crs[keys[i]], `${name}.response.crs.${keys[i]}`);
|
|
20385
|
+
}
|
|
20386
|
+
// fhe_key_info
|
|
20387
|
+
assertRecordArrayProperty(value.response, 'fheKeyInfo', `${name}.response`);
|
|
20388
|
+
const fheKeyInfo = value.response.fheKeyInfo;
|
|
20389
|
+
for (let i = 0; i < fheKeyInfo.length; ++i) {
|
|
20390
|
+
const ki = fheKeyInfo[i];
|
|
20391
|
+
const kiName = `${name}.response.fheKeyInfo[${i}]`;
|
|
20392
|
+
assertNonNullableRecordProperty(ki, 'fhePublicKey', kiName);
|
|
20393
|
+
assertIsRelayerV2KeyData(ki.fhePublicKey, `${kiName}.fhePublicKey`);
|
|
20394
|
+
}
|
|
20395
|
+
}
|
|
20396
|
+
function assertIsRelayerV2KeyData(value, name) {
|
|
20397
|
+
assertRecordStringProperty(value, 'dataId', name);
|
|
20398
|
+
assertRecordStringArrayProperty(value, 'urls', name);
|
|
20399
|
+
}
|
|
20400
|
+
function toRelayerKeyUrlResponse(response) {
|
|
20401
|
+
const fheKeyInfoV1 = response.response.fheKeyInfo.map((v2Info) => ({
|
|
20402
|
+
fhe_public_key: {
|
|
20403
|
+
data_id: v2Info.fhePublicKey.dataId,
|
|
20404
|
+
urls: v2Info.fhePublicKey.urls,
|
|
20405
|
+
},
|
|
20406
|
+
}));
|
|
20407
|
+
const crsV1 = {};
|
|
20408
|
+
for (const [key, v2Data] of Object.entries(response.response.crs)) {
|
|
20409
|
+
crsV1[key] = {
|
|
20410
|
+
data_id: v2Data.dataId,
|
|
20411
|
+
urls: v2Data.urls,
|
|
20412
|
+
};
|
|
20413
|
+
}
|
|
20414
|
+
return {
|
|
20415
|
+
response: {
|
|
20416
|
+
fhe_key_info: fheKeyInfoV1,
|
|
20417
|
+
crs: crsV1,
|
|
20418
|
+
},
|
|
20419
|
+
};
|
|
20420
|
+
}
|
|
20421
|
+
|
|
20422
|
+
class RelayerV2Provider extends AbstractRelayerProvider {
|
|
20423
|
+
constructor(relayerUrl) {
|
|
20424
|
+
super(relayerUrl);
|
|
20425
|
+
}
|
|
20426
|
+
get version() {
|
|
20427
|
+
return 2;
|
|
20428
|
+
}
|
|
20429
|
+
async fetchGetKeyUrlV2() {
|
|
20430
|
+
const response = await fetchRelayerGet('KEY_URL', this.keyUrl);
|
|
20431
|
+
// Relayer error
|
|
20432
|
+
try {
|
|
20433
|
+
assertIsRelayerV2GetResponseKeyUrl(response, 'fetchGetKeyUrl()');
|
|
20434
|
+
}
|
|
20435
|
+
catch (e) {
|
|
20436
|
+
throw new RelayerV2GetKeyUrlInvalidResponseError({
|
|
20437
|
+
cause: ensureError(e),
|
|
20438
|
+
});
|
|
20439
|
+
}
|
|
20440
|
+
return response;
|
|
20441
|
+
}
|
|
20442
|
+
async fetchGetKeyUrl() {
|
|
20443
|
+
const response = await this.fetchGetKeyUrlV2();
|
|
20444
|
+
return toRelayerKeyUrlResponse(response);
|
|
20445
|
+
}
|
|
20446
|
+
async fetchPostInputProof(payload, instanceOptions, fetchOptions) {
|
|
20447
|
+
const request = new RelayerV2AsyncRequest({
|
|
20448
|
+
relayerOperation: 'INPUT_PROOF',
|
|
20449
|
+
url: this.inputProof,
|
|
20450
|
+
payload,
|
|
20451
|
+
instanceOptions,
|
|
20452
|
+
...fetchOptions,
|
|
20453
|
+
});
|
|
20454
|
+
const result = (await request.run());
|
|
20455
|
+
assertIsRelayerInputProofResult(result, 'fetchPostInputProof()');
|
|
20456
|
+
return result;
|
|
20457
|
+
}
|
|
20458
|
+
async fetchPostPublicDecrypt(payload, instanceOptions, fetchOptions) {
|
|
20459
|
+
const request = new RelayerV2AsyncRequest({
|
|
20460
|
+
relayerOperation: 'PUBLIC_DECRYPT',
|
|
20461
|
+
url: this.publicDecrypt,
|
|
20462
|
+
payload,
|
|
20463
|
+
instanceOptions,
|
|
20464
|
+
...fetchOptions,
|
|
20465
|
+
});
|
|
20466
|
+
const result = await request.run();
|
|
20467
|
+
assertIsRelayerPublicDecryptResult(result, 'fetchPostPublicDecrypt()');
|
|
20468
|
+
return result;
|
|
20469
|
+
}
|
|
20470
|
+
async fetchPostUserDecrypt(payload, instanceOptions, fetchOptions) {
|
|
20471
|
+
const request = new RelayerV2AsyncRequest({
|
|
20472
|
+
relayerOperation: 'USER_DECRYPT',
|
|
20473
|
+
url: this.userDecrypt,
|
|
20474
|
+
payload,
|
|
20475
|
+
instanceOptions,
|
|
20476
|
+
...fetchOptions,
|
|
20477
|
+
});
|
|
20478
|
+
const result = (await request.run());
|
|
20479
|
+
assertIsRelayerUserDecryptResult(result.result, 'fetchPostUserDecrypt()');
|
|
20480
|
+
return result.result;
|
|
20481
|
+
}
|
|
20482
|
+
}
|
|
20483
|
+
|
|
20484
|
+
class AbstractRelayerFhevm {
|
|
20485
|
+
}
|
|
20486
|
+
|
|
20487
|
+
class TFHECrs {
|
|
20488
|
+
_id;
|
|
20489
|
+
_compactPkeCrs;
|
|
20490
|
+
_bits;
|
|
20491
|
+
_srcUrl;
|
|
20492
|
+
constructor(params) {
|
|
20493
|
+
this._id = params.id;
|
|
20494
|
+
this._compactPkeCrs = params.compactPkeCrs;
|
|
20495
|
+
this._bits = params.bits;
|
|
20496
|
+
this._srcUrl = params.srcUrl;
|
|
20497
|
+
}
|
|
20498
|
+
/*
|
|
20499
|
+
{
|
|
20500
|
+
id: string,
|
|
20501
|
+
data: Uint8Array,
|
|
20502
|
+
bits: number
|
|
20503
|
+
srcUrl?: string
|
|
20504
|
+
}
|
|
20505
|
+
*/
|
|
20506
|
+
static isKeyBytesType(value) {
|
|
20507
|
+
try {
|
|
20508
|
+
TFHECrs.assertKeyBytesType(value, '');
|
|
20509
|
+
return true;
|
|
20510
|
+
}
|
|
20511
|
+
catch {
|
|
20512
|
+
return false;
|
|
20513
|
+
}
|
|
20514
|
+
}
|
|
20515
|
+
/*
|
|
20516
|
+
{
|
|
20517
|
+
id: string,
|
|
20518
|
+
bits: number
|
|
20519
|
+
srcUrl: string
|
|
20520
|
+
}
|
|
20521
|
+
*/
|
|
20522
|
+
static isKeyUrlType(value) {
|
|
20523
|
+
try {
|
|
20524
|
+
TFHECrs.assertKeyUrlType(value, '');
|
|
20525
|
+
return true;
|
|
20526
|
+
}
|
|
20527
|
+
catch {
|
|
20528
|
+
return false;
|
|
20529
|
+
}
|
|
20530
|
+
}
|
|
20531
|
+
/*
|
|
20532
|
+
{
|
|
20533
|
+
id: string,
|
|
20534
|
+
data: Uint8Array,
|
|
20535
|
+
bits: number
|
|
20536
|
+
srcUrl?: string
|
|
20537
|
+
}
|
|
20538
|
+
*/
|
|
20539
|
+
static assertKeyBytesType(value, name) {
|
|
20540
|
+
assertRecordStringProperty(value, 'id', name);
|
|
20541
|
+
assertUint8ArrayProperty(value, 'data', name);
|
|
20542
|
+
assertRecordUintProperty(value, 'bits', name);
|
|
20543
|
+
if (isNonNullableRecordProperty(value, 'srcUrl')) {
|
|
20544
|
+
assertRecordStringProperty(value, 'srcUrl', name);
|
|
20545
|
+
}
|
|
20546
|
+
}
|
|
20547
|
+
/*
|
|
20548
|
+
{
|
|
20549
|
+
id: string,
|
|
20550
|
+
bits: number
|
|
20551
|
+
srcUrl: string
|
|
20552
|
+
}
|
|
20553
|
+
*/
|
|
20554
|
+
static assertKeyUrlType(value, name) {
|
|
20555
|
+
assertRecordStringProperty(value, 'id', name);
|
|
20556
|
+
assertRecordUintProperty(value, 'bits', name);
|
|
20557
|
+
assertRecordStringProperty(value, 'srcUrl', name);
|
|
20558
|
+
}
|
|
20559
|
+
/*
|
|
20560
|
+
{
|
|
20561
|
+
2048: {
|
|
20562
|
+
publicParamsId: string,
|
|
20563
|
+
publicParams: Uint8Array
|
|
20564
|
+
}
|
|
20565
|
+
}
|
|
20566
|
+
*/
|
|
20567
|
+
static assertIsPublicParams2048BytesType(value, name) {
|
|
20568
|
+
assertNonNullableRecordProperty(value, '2048', name);
|
|
20569
|
+
assertRecordStringProperty(value['2048'], 'publicParamsId', `${name}.2048`);
|
|
20570
|
+
assertUint8ArrayProperty(value['2048'], 'publicParams', `${name}.2048`);
|
|
20571
|
+
}
|
|
20572
|
+
/*
|
|
20573
|
+
{
|
|
20574
|
+
2048: {
|
|
20575
|
+
publicParamsId: string,
|
|
20576
|
+
publicParams: Uint8Array
|
|
20577
|
+
}
|
|
20578
|
+
}
|
|
20579
|
+
*/
|
|
20580
|
+
static isPublicParams2048BytesType(value) {
|
|
20581
|
+
try {
|
|
20582
|
+
TFHECrs.assertIsPublicParams2048BytesType(value, '');
|
|
20583
|
+
return true;
|
|
20584
|
+
}
|
|
20585
|
+
catch {
|
|
20586
|
+
return false;
|
|
20587
|
+
}
|
|
20588
|
+
}
|
|
20589
|
+
static async fromBytesOrUrl(params) {
|
|
20590
|
+
if (TFHECrs.isKeyBytesType(params)) {
|
|
20591
|
+
return TFHECrs._fromBytes(params);
|
|
20592
|
+
}
|
|
20593
|
+
else if (TFHECrs.isPublicParams2048BytesType(params)) {
|
|
20594
|
+
return TFHECrs._fromPublicParamsBytes(params);
|
|
20595
|
+
}
|
|
20596
|
+
else if (TFHECrs.isKeyUrlType(params)) {
|
|
20597
|
+
return TFHECrs._fromUrl(params);
|
|
20598
|
+
}
|
|
20599
|
+
else {
|
|
20600
|
+
throw new Error('Invalid public key (deserialization failed)');
|
|
20601
|
+
}
|
|
20602
|
+
}
|
|
20603
|
+
static fromBytes(params) {
|
|
20604
|
+
try {
|
|
20605
|
+
TFHECrs.assertKeyBytesType(params, 'arg');
|
|
20606
|
+
return TFHECrs._fromBytes(params);
|
|
20607
|
+
}
|
|
20608
|
+
catch (e) {
|
|
20609
|
+
throw new Error('Invalid public key (deserialization failed)', {
|
|
20610
|
+
cause: e,
|
|
20611
|
+
});
|
|
20612
|
+
}
|
|
20613
|
+
}
|
|
20614
|
+
static _fromBytes(params) {
|
|
20615
|
+
const _params = {
|
|
20616
|
+
compactPkeCrs: TFHE.CompactPkeCrs.safe_deserialize(params.data, SERIALIZED_SIZE_LIMIT_CRS),
|
|
20617
|
+
id: params.id,
|
|
20618
|
+
bits: params.bits,
|
|
20619
|
+
srcUrl: params.srcUrl,
|
|
20620
|
+
};
|
|
20621
|
+
return new TFHECrs(_params);
|
|
20622
|
+
}
|
|
20623
|
+
static fromPublicParamsBytes(params) {
|
|
20624
|
+
try {
|
|
20625
|
+
TFHECrs.assertIsPublicParams2048BytesType(params, 'arg');
|
|
20626
|
+
return TFHECrs._fromPublicParamsBytes(params);
|
|
20627
|
+
}
|
|
20628
|
+
catch (e) {
|
|
20629
|
+
throw new Error('Invalid public key (deserialization failed)', {
|
|
20630
|
+
cause: e,
|
|
20631
|
+
});
|
|
20632
|
+
}
|
|
20633
|
+
}
|
|
20634
|
+
static _fromPublicParamsBytes(params) {
|
|
20635
|
+
return TFHECrs._fromBytes({
|
|
20636
|
+
bits: 2048,
|
|
20637
|
+
data: params['2048'].publicParams,
|
|
20638
|
+
id: params['2048'].publicParamsId,
|
|
20639
|
+
});
|
|
20640
|
+
}
|
|
20641
|
+
static async fromUrl(params) {
|
|
20642
|
+
try {
|
|
20643
|
+
TFHECrs.assertKeyUrlType(params, 'arg');
|
|
20644
|
+
return TFHECrs._fromUrl(params);
|
|
20645
|
+
}
|
|
20646
|
+
catch (e) {
|
|
20647
|
+
throw new Error('Impossible to fetch public key: wrong relayer url.', {
|
|
20648
|
+
cause: e,
|
|
20649
|
+
});
|
|
20650
|
+
}
|
|
20651
|
+
}
|
|
20652
|
+
static async _fromUrl(params) {
|
|
20653
|
+
TFHECrs.assertKeyUrlType(params, 'arg');
|
|
20654
|
+
const compactPkeCrsBytes = await fetchBytes(params.srcUrl);
|
|
20655
|
+
return TFHECrs.fromBytes({
|
|
20656
|
+
data: compactPkeCrsBytes,
|
|
20657
|
+
id: params.id,
|
|
20658
|
+
bits: params.bits,
|
|
20659
|
+
srcUrl: params.srcUrl,
|
|
20660
|
+
});
|
|
20661
|
+
}
|
|
20662
|
+
/*
|
|
20663
|
+
{
|
|
20664
|
+
id: string,
|
|
20665
|
+
bits: number,
|
|
20666
|
+
data: Uint8Array,
|
|
20667
|
+
srcUrl?: string
|
|
20668
|
+
}
|
|
20669
|
+
*/
|
|
20670
|
+
toBytes() {
|
|
20671
|
+
return {
|
|
20672
|
+
data: this._compactPkeCrs.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
|
|
20673
|
+
id: this._id,
|
|
20674
|
+
bits: this._bits,
|
|
20675
|
+
...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
|
|
20676
|
+
};
|
|
20677
|
+
}
|
|
20678
|
+
/*
|
|
20679
|
+
{
|
|
20680
|
+
2048: {
|
|
20681
|
+
publicParamsId: string,
|
|
20682
|
+
publicParams: TFHE.CompactPkeCrs
|
|
20683
|
+
}
|
|
20684
|
+
}
|
|
20685
|
+
*/
|
|
20686
|
+
toPublicParamsWasm() {
|
|
20687
|
+
if (this._bits !== 2048) {
|
|
20688
|
+
throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
|
|
20689
|
+
}
|
|
20690
|
+
const pp = {
|
|
20691
|
+
2048: {
|
|
20692
|
+
publicParams: this._compactPkeCrs,
|
|
20693
|
+
publicParamsId: this._id,
|
|
20694
|
+
},
|
|
20695
|
+
};
|
|
20696
|
+
return pp;
|
|
20697
|
+
}
|
|
20698
|
+
/*
|
|
20699
|
+
{
|
|
20700
|
+
2048: {
|
|
20701
|
+
publicParamsId: string,
|
|
20702
|
+
publicParams: Uint8Array
|
|
20703
|
+
}
|
|
20704
|
+
}
|
|
20705
|
+
*/
|
|
20706
|
+
toPublicParamsBytes() {
|
|
20707
|
+
if (this._bits !== 2048) {
|
|
20708
|
+
throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
|
|
20709
|
+
}
|
|
20710
|
+
const pp = {
|
|
20711
|
+
2048: {
|
|
20712
|
+
publicParams: this.toBytes().data,
|
|
20713
|
+
publicParamsId: this._id,
|
|
20714
|
+
},
|
|
20715
|
+
};
|
|
20716
|
+
return pp;
|
|
20717
|
+
}
|
|
20718
|
+
}
|
|
20719
|
+
|
|
20720
|
+
class TFHEPublicKey {
|
|
20721
|
+
_id;
|
|
20722
|
+
_tfheCompactPublicKey;
|
|
20723
|
+
_srcUrl;
|
|
20724
|
+
constructor(params) {
|
|
20725
|
+
this._id = params.id;
|
|
20726
|
+
this._tfheCompactPublicKey = params.tfheCompactPublicKey;
|
|
20727
|
+
this._srcUrl = params.srcUrl;
|
|
20728
|
+
}
|
|
20729
|
+
/*
|
|
20730
|
+
{
|
|
20731
|
+
id: string,
|
|
20732
|
+
data: Uint8Array,
|
|
20733
|
+
srcUrl?: string
|
|
20734
|
+
}
|
|
20735
|
+
*/
|
|
20736
|
+
static isKeyBytesType(value) {
|
|
20737
|
+
try {
|
|
20738
|
+
TFHEPublicKey.assertKeyBytesType(value, '');
|
|
20739
|
+
return true;
|
|
20740
|
+
}
|
|
20741
|
+
catch {
|
|
20742
|
+
return false;
|
|
20743
|
+
}
|
|
20744
|
+
}
|
|
20745
|
+
/*
|
|
20746
|
+
{
|
|
20747
|
+
id: string,
|
|
20748
|
+
srcUrl: string
|
|
20749
|
+
}
|
|
20750
|
+
*/
|
|
20751
|
+
static isKeyUrlType(value) {
|
|
20752
|
+
try {
|
|
20753
|
+
TFHEPublicKey.assertKeyUrlType(value, '');
|
|
20754
|
+
return true;
|
|
20755
|
+
}
|
|
20756
|
+
catch {
|
|
20757
|
+
return false;
|
|
20758
|
+
}
|
|
20759
|
+
}
|
|
20760
|
+
/*
|
|
20761
|
+
{
|
|
20762
|
+
id: string,
|
|
20763
|
+
data: Uint8Array,
|
|
20764
|
+
srcUrl?: string
|
|
20765
|
+
}
|
|
20766
|
+
*/
|
|
20767
|
+
static assertKeyBytesType(value, name) {
|
|
20768
|
+
assertRecordStringProperty(value, 'id', name);
|
|
20769
|
+
assertUint8ArrayProperty(value, 'data', name);
|
|
20770
|
+
if (isNonNullableRecordProperty(value, 'srcUrl')) {
|
|
20771
|
+
assertRecordStringProperty(value, 'srcUrl', name);
|
|
20772
|
+
}
|
|
20773
|
+
}
|
|
20774
|
+
/*
|
|
20775
|
+
{
|
|
20776
|
+
id: string,
|
|
20777
|
+
srcUrl: string
|
|
20778
|
+
}
|
|
20779
|
+
*/
|
|
20780
|
+
static assertKeyUrlType(value, name) {
|
|
20781
|
+
assertRecordStringProperty(value, 'id', name);
|
|
20782
|
+
assertRecordStringProperty(value, 'srcUrl', name);
|
|
20783
|
+
}
|
|
20784
|
+
static async fromBytesOrUrl(params) {
|
|
20785
|
+
if (TFHEPublicKey.isKeyBytesType(params)) {
|
|
20786
|
+
return TFHEPublicKey._fromBytes(params);
|
|
20787
|
+
}
|
|
20788
|
+
else if (TFHEPublicKey.isKeyUrlType(params)) {
|
|
20789
|
+
return TFHEPublicKey._fromUrl(params);
|
|
20790
|
+
}
|
|
20791
|
+
else {
|
|
20792
|
+
throw new Error('Invalid public key (deserialization failed)');
|
|
20793
|
+
}
|
|
20794
|
+
}
|
|
20795
|
+
/*
|
|
20796
|
+
{
|
|
20797
|
+
id: string,
|
|
20798
|
+
data: Uint8Array,
|
|
20799
|
+
srcUrl?: string
|
|
20800
|
+
}
|
|
20801
|
+
*/
|
|
20802
|
+
static fromBytes(params) {
|
|
20803
|
+
try {
|
|
20804
|
+
TFHEPublicKey.assertKeyBytesType(params, 'arg');
|
|
20805
|
+
return TFHEPublicKey._fromBytes(params);
|
|
20806
|
+
}
|
|
20807
|
+
catch (e) {
|
|
20808
|
+
throw new Error('Invalid public key (deserialization failed)', {
|
|
20809
|
+
cause: e,
|
|
20810
|
+
});
|
|
20811
|
+
}
|
|
20812
|
+
}
|
|
20813
|
+
/*
|
|
20814
|
+
{
|
|
20815
|
+
id: string,
|
|
20816
|
+
data: Uint8Array,
|
|
20817
|
+
srcUrl?: string
|
|
20818
|
+
}
|
|
20819
|
+
*/
|
|
20820
|
+
static _fromBytes(params) {
|
|
20821
|
+
const _params = {
|
|
20822
|
+
tfheCompactPublicKey: TFHE.TfheCompactPublicKey.safe_deserialize(params.data, SERIALIZED_SIZE_LIMIT_PK),
|
|
20823
|
+
id: params.id,
|
|
20824
|
+
srcUrl: params.srcUrl,
|
|
20825
|
+
};
|
|
20826
|
+
return new TFHEPublicKey(_params);
|
|
20827
|
+
}
|
|
20828
|
+
/*
|
|
20829
|
+
{
|
|
20830
|
+
id: string,
|
|
20831
|
+
srcUrl: string
|
|
20832
|
+
}
|
|
20833
|
+
*/
|
|
20834
|
+
static async fromUrl(params) {
|
|
20835
|
+
try {
|
|
20836
|
+
TFHEPublicKey.assertKeyUrlType(params, 'arg');
|
|
20837
|
+
return TFHEPublicKey._fromUrl(params);
|
|
20838
|
+
}
|
|
20839
|
+
catch (e) {
|
|
20840
|
+
throw new Error('Impossible to fetch public key: wrong relayer url.', {
|
|
20841
|
+
cause: e,
|
|
20842
|
+
});
|
|
20843
|
+
}
|
|
20844
|
+
}
|
|
20845
|
+
/*
|
|
20846
|
+
{
|
|
20847
|
+
id: string,
|
|
20848
|
+
srcUrl: string
|
|
20849
|
+
}
|
|
20850
|
+
*/
|
|
20851
|
+
static async _fromUrl(params) {
|
|
20852
|
+
const tfheCompactPublicKeyBytes = await fetchBytes(params.srcUrl);
|
|
20853
|
+
return TFHEPublicKey.fromBytes({
|
|
20854
|
+
data: tfheCompactPublicKeyBytes,
|
|
20855
|
+
id: params.id,
|
|
20856
|
+
srcUrl: params.srcUrl,
|
|
20857
|
+
});
|
|
20858
|
+
}
|
|
20859
|
+
/*
|
|
20860
|
+
{
|
|
20861
|
+
id: string,
|
|
20862
|
+
data: Uint8Array,
|
|
20863
|
+
srcUrl?: string
|
|
20864
|
+
}
|
|
20865
|
+
*/
|
|
20866
|
+
toBytes() {
|
|
20867
|
+
return {
|
|
20868
|
+
data: this._tfheCompactPublicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK),
|
|
20869
|
+
id: this._id,
|
|
20870
|
+
...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
|
|
20871
|
+
};
|
|
20872
|
+
}
|
|
20873
|
+
/*
|
|
20874
|
+
{
|
|
20875
|
+
publicKey: TFHE.TfheCompactPublicKey
|
|
20876
|
+
publicKeyId: string
|
|
20877
|
+
}
|
|
20878
|
+
*/
|
|
20879
|
+
toPublicKeyWasm() {
|
|
20880
|
+
return {
|
|
20881
|
+
publicKey: this._tfheCompactPublicKey,
|
|
20882
|
+
publicKeyId: this._id,
|
|
20883
|
+
};
|
|
20884
|
+
}
|
|
20885
|
+
/*
|
|
20886
|
+
{
|
|
20887
|
+
publicKey: Uint8Array
|
|
20888
|
+
publicKeyId: string
|
|
20889
|
+
}
|
|
20890
|
+
*/
|
|
20891
|
+
toPublicKeyBytes() {
|
|
20892
|
+
return {
|
|
20893
|
+
publicKey: this.toBytes().data,
|
|
20894
|
+
publicKeyId: this._id,
|
|
20895
|
+
};
|
|
20896
|
+
}
|
|
20897
|
+
}
|
|
20898
|
+
|
|
20899
|
+
//const __KEY_URL_CACHE__: Record<string, RelayerV2PublicKey> = {};
|
|
20900
|
+
class RelayerV2PublicKey {
|
|
20901
|
+
_crs2048;
|
|
20902
|
+
_publicKey;
|
|
20903
|
+
constructor(params) {
|
|
20904
|
+
this._publicKey = params.publicKey;
|
|
20905
|
+
this._crs2048 = params.crs2048;
|
|
20906
|
+
}
|
|
20907
|
+
static tryFromBytes(value) {
|
|
20908
|
+
if (!isNonNullableRecordProperty(value, 'publicParams')) {
|
|
20909
|
+
return null;
|
|
20910
|
+
}
|
|
20911
|
+
if (!TFHECrs.isPublicParams2048BytesType(value.publicParams)) {
|
|
20912
|
+
return null;
|
|
20913
|
+
}
|
|
20914
|
+
if (!isNonNullableRecordProperty(value, 'publicKey')) {
|
|
20915
|
+
return null;
|
|
20916
|
+
}
|
|
20917
|
+
if (!TFHEPublicKey.isKeyBytesType(value.publicKey)) {
|
|
20918
|
+
return null;
|
|
20919
|
+
}
|
|
20920
|
+
const publicKey = value.publicKey;
|
|
20921
|
+
const publicParams = value.publicParams;
|
|
20922
|
+
return RelayerV2PublicKey.fromBytes({
|
|
20923
|
+
publicKey,
|
|
20924
|
+
publicParams,
|
|
20925
|
+
});
|
|
20926
|
+
}
|
|
20927
|
+
static fromBytes(params) {
|
|
20928
|
+
TFHECrs.assertIsPublicParams2048BytesType(params.publicParams, 'arg.publicParams');
|
|
20929
|
+
const publicKey = TFHEPublicKey.fromBytes(params.publicKey);
|
|
20930
|
+
const crs2048 = TFHECrs.fromBytes({
|
|
20931
|
+
id: params.publicParams[2048].publicParamsId,
|
|
20932
|
+
data: params.publicParams[2048].publicParams,
|
|
20933
|
+
bits: 2048,
|
|
20934
|
+
});
|
|
20935
|
+
return new RelayerV2PublicKey({ publicKey, crs2048 });
|
|
20936
|
+
}
|
|
20937
|
+
static async fromRelayerResponse(response) {
|
|
20938
|
+
try {
|
|
20939
|
+
assertIsRelayerV2GetResponseKeyUrl(response, 'RelayerV2GetResponseKeyUrl');
|
|
20940
|
+
const pub_key_0 = response.response.fheKeyInfo[0].fhePublicKey;
|
|
20941
|
+
const tfheCompactPublicKeyId = pub_key_0.dataId;
|
|
20942
|
+
const tfheCompactPublicKeyUrl = pub_key_0.urls[0];
|
|
20943
|
+
const crs_2048 = response.response.crs['2048'];
|
|
20944
|
+
const compactPkeCrs2048Id = crs_2048.dataId;
|
|
20945
|
+
const compactPkeCrs2048Url = crs_2048.urls[0];
|
|
20946
|
+
const publicKey = await TFHEPublicKey.fromUrl({
|
|
20947
|
+
id: tfheCompactPublicKeyId,
|
|
20948
|
+
srcUrl: tfheCompactPublicKeyUrl,
|
|
20949
|
+
});
|
|
20950
|
+
const crs = await TFHECrs.fromUrl({
|
|
20951
|
+
id: compactPkeCrs2048Id,
|
|
20952
|
+
bits: 2048,
|
|
20953
|
+
srcUrl: compactPkeCrs2048Url,
|
|
20954
|
+
});
|
|
20955
|
+
return new RelayerV2PublicKey({ publicKey, crs2048: crs });
|
|
20956
|
+
}
|
|
20957
|
+
catch (e) {
|
|
20958
|
+
throw new Error('Impossible to fetch public key: wrong relayer url.', {
|
|
20959
|
+
cause: e,
|
|
20960
|
+
});
|
|
20961
|
+
}
|
|
20962
|
+
}
|
|
20963
|
+
getTFHEPublicKey() {
|
|
20964
|
+
return this._publicKey;
|
|
20965
|
+
}
|
|
20966
|
+
getTFHECrs() {
|
|
20967
|
+
return this._crs2048;
|
|
20968
|
+
}
|
|
20969
|
+
toBytes() {
|
|
20970
|
+
return {
|
|
20971
|
+
publicKey: this._publicKey.toBytes(),
|
|
20972
|
+
publicParams: this._crs2048.toPublicParamsBytes(),
|
|
20973
|
+
};
|
|
20974
|
+
}
|
|
20975
|
+
}
|
|
20976
|
+
|
|
20977
|
+
class RelayerV2Fhevm extends AbstractRelayerFhevm {
|
|
20978
|
+
_relayerProvider;
|
|
20979
|
+
_relayerPublicKey;
|
|
20980
|
+
constructor(params) {
|
|
20981
|
+
super();
|
|
20982
|
+
this._relayerProvider = params.relayerProvider;
|
|
20983
|
+
this._relayerPublicKey = params.relayerPublicKey;
|
|
20984
|
+
}
|
|
20985
|
+
get version() {
|
|
20986
|
+
return 2;
|
|
20987
|
+
}
|
|
20988
|
+
get relayerVersionUrl() {
|
|
20989
|
+
return this.relayerProvider.url;
|
|
20990
|
+
}
|
|
20991
|
+
static async fromConfig(config) {
|
|
20992
|
+
const relayerProvider = new RelayerV2Provider(config.relayerVersionUrl);
|
|
20993
|
+
let relayerPublicKey = RelayerV2PublicKey.tryFromBytes(config);
|
|
20994
|
+
if (!relayerPublicKey) {
|
|
20995
|
+
const response = await relayerProvider.fetchGetKeyUrlV2();
|
|
20996
|
+
relayerPublicKey = await RelayerV2PublicKey.fromRelayerResponse(response);
|
|
20997
|
+
}
|
|
20998
|
+
return new RelayerV2Fhevm({
|
|
20999
|
+
relayerProvider,
|
|
21000
|
+
relayerPublicKey,
|
|
21001
|
+
});
|
|
21002
|
+
}
|
|
21003
|
+
get relayerProvider() {
|
|
21004
|
+
return this._relayerProvider;
|
|
21005
|
+
}
|
|
21006
|
+
getPublicKeyBytes() {
|
|
21007
|
+
return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyBytes();
|
|
21008
|
+
}
|
|
21009
|
+
getPublicKeyWasm() {
|
|
21010
|
+
return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyWasm();
|
|
21011
|
+
}
|
|
21012
|
+
getPublicParamsBytes(bits) {
|
|
21013
|
+
if (bits !== 2048) {
|
|
21014
|
+
throw new Error(`Unsupported PublicParams bits format ${bits}`);
|
|
21015
|
+
}
|
|
21016
|
+
return this._relayerPublicKey.getTFHECrs().toPublicParamsBytes()['2048'];
|
|
21017
|
+
}
|
|
21018
|
+
getPublicParamsWasm(bits) {
|
|
21019
|
+
if (bits !== 2048) {
|
|
21020
|
+
throw new Error(`Unsupported PublicParams bits format ${bits}`);
|
|
21021
|
+
}
|
|
21022
|
+
return this._relayerPublicKey.getTFHECrs().toPublicParamsWasm()['2048'];
|
|
21023
|
+
}
|
|
21024
|
+
}
|
|
21025
|
+
|
|
21026
|
+
class RelayerV1Fhevm extends AbstractRelayerFhevm {
|
|
21027
|
+
_relayerProvider;
|
|
21028
|
+
_publicKeyData;
|
|
21029
|
+
_publicParamsData;
|
|
21030
|
+
constructor(params) {
|
|
21031
|
+
super();
|
|
21032
|
+
this._relayerProvider = params.relayerProvider;
|
|
21033
|
+
this._publicKeyData = params.publicKeyData;
|
|
21034
|
+
this._publicParamsData = params.publicParamsData;
|
|
21035
|
+
}
|
|
21036
|
+
get version() {
|
|
21037
|
+
return 1;
|
|
21038
|
+
}
|
|
21039
|
+
get relayerVersionUrl() {
|
|
21040
|
+
return this.relayerProvider.url;
|
|
21041
|
+
}
|
|
21042
|
+
static async fromConfig(config) {
|
|
21043
|
+
const relayerProvider = new RelayerV1Provider(config.relayerVersionUrl);
|
|
21044
|
+
const publicKeyData = await getTfheCompactPublicKey(config);
|
|
21045
|
+
const publicParamsData = await getPublicParams(config);
|
|
21046
|
+
return new RelayerV1Fhevm({
|
|
21047
|
+
relayerProvider,
|
|
21048
|
+
publicKeyData,
|
|
21049
|
+
publicParamsData,
|
|
21050
|
+
});
|
|
21051
|
+
}
|
|
21052
|
+
get relayerProvider() {
|
|
21053
|
+
return this._relayerProvider;
|
|
21054
|
+
}
|
|
21055
|
+
getPublicKeyBytes() {
|
|
21056
|
+
return {
|
|
21057
|
+
publicKey: this._publicKeyData.publicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK),
|
|
21058
|
+
publicKeyId: this._publicKeyData.publicKeyId,
|
|
21059
|
+
};
|
|
21060
|
+
}
|
|
21061
|
+
getPublicKeyWasm() {
|
|
21062
|
+
return {
|
|
21063
|
+
publicKey: this._publicKeyData.publicKey,
|
|
21064
|
+
publicKeyId: this._publicKeyData.publicKeyId,
|
|
21065
|
+
};
|
|
21066
|
+
}
|
|
21067
|
+
getPublicParamsBytes(bits) {
|
|
21068
|
+
if (bits !== 2048) {
|
|
21069
|
+
throw new Error(`Unsupported PublicParams bits format ${bits}`);
|
|
21070
|
+
}
|
|
21071
|
+
return {
|
|
21072
|
+
publicParams: this._publicParamsData['2048'].publicParams.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
|
|
21073
|
+
publicParamsId: this._publicParamsData['2048'].publicParamsId,
|
|
21074
|
+
};
|
|
21075
|
+
}
|
|
21076
|
+
getPublicParamsWasm(bits) {
|
|
21077
|
+
if (bits !== 2048) {
|
|
21078
|
+
throw new Error(`Unsupported PublicParams bits format ${bits}`);
|
|
21079
|
+
}
|
|
21080
|
+
return {
|
|
21081
|
+
publicParams: this._publicParamsData['2048'].publicParams,
|
|
21082
|
+
publicParamsId: this._publicParamsData['2048'].publicParamsId,
|
|
21083
|
+
};
|
|
21084
|
+
}
|
|
21085
|
+
}
|
|
21086
|
+
|
|
21087
|
+
async function createRelayerFhevm(config) {
|
|
21088
|
+
const resolved = _resolveRelayerUrl(config.relayerUrl, config.defaultRelayerVersion);
|
|
21089
|
+
if (!resolved) {
|
|
21090
|
+
throw new Error(`Invalid relayerUrl: ${config.relayerUrl}`);
|
|
21091
|
+
}
|
|
21092
|
+
if (resolved.version === 2) {
|
|
21093
|
+
return RelayerV2Fhevm.fromConfig({
|
|
21094
|
+
relayerVersionUrl: resolved.url,
|
|
21095
|
+
publicKey: config.publicKey,
|
|
21096
|
+
publicParams: config.publicParams,
|
|
21097
|
+
});
|
|
21098
|
+
}
|
|
21099
|
+
return RelayerV1Fhevm.fromConfig({
|
|
21100
|
+
relayerVersionUrl: resolved.url,
|
|
21101
|
+
publicKey: config.publicKey,
|
|
21102
|
+
publicParams: config.publicParams,
|
|
21103
|
+
});
|
|
21104
|
+
}
|
|
21105
|
+
function _resolveRelayerUrl(value, defaultVersion) {
|
|
21106
|
+
if (!value || typeof value !== 'string') {
|
|
21107
|
+
return null;
|
|
21108
|
+
}
|
|
21109
|
+
const urlNoSlash = removeSuffix(value, '/');
|
|
21110
|
+
if (!URL.canParse(urlNoSlash)) {
|
|
21111
|
+
return null;
|
|
21112
|
+
}
|
|
21113
|
+
if (urlNoSlash.endsWith('/v1')) {
|
|
21114
|
+
return {
|
|
21115
|
+
url: value,
|
|
21116
|
+
version: 1,
|
|
21117
|
+
};
|
|
21118
|
+
}
|
|
21119
|
+
if (urlNoSlash.endsWith('/v2')) {
|
|
21120
|
+
return {
|
|
21121
|
+
url: value,
|
|
21122
|
+
version: 2,
|
|
21123
|
+
};
|
|
21124
|
+
}
|
|
21125
|
+
if (typeof defaultVersion !== 'number') {
|
|
21126
|
+
throw new Error(`relayerUrl cannot be resolved. (value=${value})`);
|
|
21127
|
+
}
|
|
21128
|
+
return {
|
|
21129
|
+
url: `${urlNoSlash}/v${defaultVersion}`,
|
|
21130
|
+
version: defaultVersion,
|
|
21131
|
+
};
|
|
21132
|
+
}
|
|
21133
|
+
|
|
21134
|
+
/**
|
|
21135
|
+
* **FHE Type Mapping for Input Builders**
|
|
21136
|
+
* * Maps the **number of encrypted bits** used by a FHEVM primary type
|
|
21137
|
+
* to its corresponding **FheTypeId**. This constant is primarily used by
|
|
21138
|
+
* `EncryptedInput` and `RelayerEncryptedInput` builders to determine the correct
|
|
21139
|
+
* input type and calculate the total required bit-length.
|
|
21140
|
+
*
|
|
21141
|
+
* **Structure: \{ Encrypted Bit Length: FheTypeId \}**
|
|
21142
|
+
*
|
|
21143
|
+
* | Bits | FheTypeId | FHE Type Name | Note |
|
|
21144
|
+
* | :--- | :-------- | :------------ | :--- |
|
|
21145
|
+
* | 2 | 0 | `ebool` | The boolean type. |
|
|
21146
|
+
* | (N/A)| 1 | `euint4` | **Deprecated** and omitted from this map. |
|
|
21147
|
+
* | 8 | 2 | `euint8` | |
|
|
21148
|
+
* | 16 | 3 | `euint16` | |
|
|
21149
|
+
* | 32 | 4 | `euint32` | |
|
|
21150
|
+
* | 64 | 5 | `euint64` | |
|
|
21151
|
+
* | 128 | 6 | `euint128` | |
|
|
21152
|
+
* | 160 | 7 | `eaddress` | Used for encrypted Ethereum addresses. |
|
|
21153
|
+
* | 256 | 8 | `euint256` | The maximum supported integer size. |
|
|
21154
|
+
*/
|
|
21155
|
+
const ENCRYPTION_TYPES = {
|
|
21156
|
+
2: 0, // ebool (FheTypeId=0) is using 2 encrypted bits
|
|
21157
|
+
// euint4 (FheTypeId=1) is deprecated
|
|
21158
|
+
8: 2, // euint8 (FheTypeId=2) is using 8 encrypted bits
|
|
21159
|
+
16: 3, // euint16 (FheTypeId=3) is using 16 encrypted bits
|
|
21160
|
+
32: 4, // euint32 (FheTypeId=4) is using 32 encrypted bits
|
|
21161
|
+
64: 5, // euint64 (FheTypeId=5) is using 64 encrypted bits
|
|
21162
|
+
128: 6, // euint128 (FheTypeId=128) is using 128 encrypted bits
|
|
21163
|
+
160: 7, // eaddress (FheTypeId=7) is using 160 encrypted bits
|
|
21164
|
+
256: 8, // euint256 (FheTypeId=8) is using 256 encrypted bits
|
|
21165
|
+
};
|
|
21166
|
+
|
|
21167
|
+
const SepoliaConfig = {
|
|
21168
|
+
// ACL_CONTRACT_ADDRESS (FHEVM Host chain)
|
|
21169
|
+
aclContractAddress: '0xf0Ffdc93b7E186bC2f8CB3dAA75D86d1930A433D',
|
|
21170
|
+
// KMS_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
|
|
21171
|
+
kmsContractAddress: '0xbE0E383937d564D7FF0BC3b46c51f0bF8d5C311A',
|
|
21172
|
+
// INPUT_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
|
|
21173
|
+
inputVerifierContractAddress: '0xBBC1fFCdc7C316aAAd72E807D9b0272BE8F84DA0',
|
|
21174
|
+
// DECRYPTION_ADDRESS (Gateway chain)
|
|
21175
|
+
verifyingContractAddressDecryption: '0x5D8BD78e2ea6bbE41f26dFe9fdaEAa349e077478',
|
|
21176
|
+
// INPUT_VERIFICATION_ADDRESS (Gateway chain)
|
|
21177
|
+
verifyingContractAddressInputVerification: '0x483b9dE06E4E4C7D35CCf5837A1668487406D955',
|
|
21178
|
+
// FHEVM Host chain id
|
|
21179
|
+
chainId: 11155111,
|
|
21180
|
+
// Gateway chain id
|
|
21181
|
+
gatewayChainId: 10901,
|
|
21182
|
+
// Optional RPC provider to host chain
|
|
21183
|
+
network: 'https://ethereum-sepolia-rpc.publicnode.com',
|
|
21184
|
+
// Relayer URL
|
|
21185
|
+
relayerUrl: 'https://relayer.testnet.zama.org',
|
|
21186
|
+
};
|
|
21187
|
+
Object.freeze(SepoliaConfig);
|
|
21188
|
+
const createInstance = async (config) => {
|
|
21189
|
+
const { verifyingContractAddressDecryption, verifyingContractAddressInputVerification, publicKey, inputVerifierContractAddress, kmsContractAddress, aclContractAddress, gatewayChainId, auth, } = config;
|
|
21190
|
+
if (!isChecksummedAddress(aclContractAddress)) {
|
|
21191
|
+
throw new Error('ACL contract address is not valid or empty');
|
|
21192
|
+
}
|
|
21193
|
+
if (!isChecksummedAddress(inputVerifierContractAddress)) {
|
|
21194
|
+
throw new Error('InputVerifier contract address is not valid or empty');
|
|
21195
|
+
}
|
|
21196
|
+
if (!isChecksummedAddress(kmsContractAddress)) {
|
|
21197
|
+
throw new Error('KMS contract address is not valid or empty');
|
|
21198
|
+
}
|
|
21199
|
+
if (!isChecksummedAddress(verifyingContractAddressDecryption)) {
|
|
21200
|
+
throw new Error('Verifying contract for Decryption address is not valid or empty');
|
|
21201
|
+
}
|
|
21202
|
+
if (!isChecksummedAddress(verifyingContractAddressInputVerification)) {
|
|
21203
|
+
throw new Error('Verifying contract for InputVerification address is not valid or empty');
|
|
21204
|
+
}
|
|
21205
|
+
if (publicKey && !(publicKey.data instanceof Uint8Array)) {
|
|
21206
|
+
throw new Error('publicKey must be a Uint8Array');
|
|
21207
|
+
}
|
|
21208
|
+
// TODO change argument
|
|
21209
|
+
// provider is never undefined | null here!
|
|
21210
|
+
const provider = getProvider(config.network);
|
|
21211
|
+
const relayerUrl = config.relayerUrl ?? SepoliaConfig.relayerUrl;
|
|
21212
|
+
const relayerFhevm = await createRelayerFhevm({
|
|
21213
|
+
relayerUrl,
|
|
21214
|
+
publicKey: config.publicKey,
|
|
21215
|
+
publicParams: config.publicParams,
|
|
21216
|
+
defaultRelayerVersion: 1,
|
|
21217
|
+
});
|
|
21218
|
+
const chainId = await getChainId(provider, config);
|
|
21219
|
+
// const relayerVersionUrl = `${config.relayerUrl!}/v1`;
|
|
21220
|
+
// const publicKeyData = await getTfheCompactPublicKey({
|
|
21221
|
+
// relayerVersionUrl: relayerFhevm.relayerVersionUrl,
|
|
21222
|
+
// publicKey: config.publicKey,
|
|
21223
|
+
// });
|
|
21224
|
+
//const aaa = relayerFhevm.getPublicKey();
|
|
21225
|
+
// const publicParamsData = await getPublicParams({
|
|
21226
|
+
// relayerVersionUrl,
|
|
21227
|
+
// publicParams: config.publicParams,
|
|
21228
|
+
// });
|
|
21229
|
+
const kmsSigners = await getKMSSigners(provider, kmsContractAddress);
|
|
21230
|
+
const thresholdKMSSigners = await getKMSSignersThreshold(provider, kmsContractAddress);
|
|
21231
|
+
const coprocessorSigners = await getCoprocessorSigners(provider, inputVerifierContractAddress);
|
|
21232
|
+
const thresholdCoprocessorSigners = await getCoprocessorSignersThreshold(provider, inputVerifierContractAddress);
|
|
21233
|
+
return {
|
|
21234
|
+
createEncryptedInput: createRelayerEncryptedInput(aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId,
|
|
21235
|
+
//cleanURL(config.relayerUrl),
|
|
21236
|
+
//relayerFhevm.relayerVersionUrl,
|
|
21237
|
+
relayerFhevm.relayerProvider,
|
|
21238
|
+
//publicKeyData.publicKey,
|
|
21239
|
+
relayerFhevm.getPublicKeyWasm().publicKey,
|
|
21240
|
+
//publicParamsData,
|
|
21241
|
+
{ 2048: relayerFhevm.getPublicParamsWasm(2048) }, coprocessorSigners, thresholdCoprocessorSigners),
|
|
21242
|
+
generateKeypair,
|
|
21243
|
+
createEIP712: createEIP712(verifyingContractAddressDecryption, chainId),
|
|
21244
|
+
publicDecrypt: publicDecryptRequest(kmsSigners, thresholdKMSSigners, gatewayChainId, verifyingContractAddressDecryption, aclContractAddress,
|
|
21245
|
+
//cleanURL(config.relayerUrl),
|
|
21246
|
+
relayerFhevm.relayerProvider, provider, auth && { auth }),
|
|
21247
|
+
userDecrypt: userDecryptRequest(kmsSigners, gatewayChainId, chainId, verifyingContractAddressDecryption, aclContractAddress,
|
|
21248
|
+
//cleanURL(config.relayerUrl),
|
|
21249
|
+
relayerFhevm.relayerProvider, provider, auth && { auth }),
|
|
21250
|
+
getPublicKey: () => relayerFhevm.getPublicKeyBytes(),
|
|
21251
|
+
getPublicParams: (bits) => relayerFhevm.getPublicParamsBytes(bits),
|
|
21252
|
+
// getPublicKey: () =>
|
|
21253
|
+
// publicKeyData.publicKey
|
|
21254
|
+
// ? {
|
|
21255
|
+
// publicKey: publicKeyData.publicKey.safe_serialize(
|
|
21256
|
+
// SERIALIZED_SIZE_LIMIT_PK,
|
|
21257
|
+
// ),
|
|
21258
|
+
// publicKeyId: publicKeyData.publicKeyId,
|
|
21259
|
+
// }
|
|
21260
|
+
// : null,
|
|
21261
|
+
// getPublicParams: (bits: keyof PublicParams) => {
|
|
21262
|
+
// if (publicParamsData[bits]) {
|
|
21263
|
+
// return {
|
|
21264
|
+
// publicParams: publicParamsData[bits]!.publicParams.safe_serialize(
|
|
21265
|
+
// SERIALIZED_SIZE_LIMIT_CRS,
|
|
21266
|
+
// ),
|
|
21267
|
+
// publicParamsId: publicParamsData[bits]!.publicParamsId,
|
|
21268
|
+
// };
|
|
21269
|
+
// }
|
|
21270
|
+
// return null;
|
|
21271
|
+
// },
|
|
17701
21272
|
};
|
|
17702
21273
|
};
|
|
17703
21274
|
|