@oma3/omatrust 0.1.0-alpha.10 → 0.1.0-alpha.11
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/dist/identity/index.cjs +543 -0
- package/dist/identity/index.cjs.map +1 -1
- package/dist/identity/index.d.cts +2 -1
- package/dist/identity/index.d.ts +2 -1
- package/dist/identity/index.js +527 -1
- package/dist/identity/index.js.map +1 -1
- package/dist/index-BOvk-7Ku.d.cts +235 -0
- package/dist/index-C2w5EvFH.d.ts +329 -0
- package/dist/index-QueRiudB.d.cts +329 -0
- package/dist/index-R78TpAhN.d.ts +235 -0
- package/dist/index.cjs +1475 -165
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1476 -166
- package/dist/index.js.map +1 -1
- package/dist/reputation/index.browser.cjs +943 -66
- package/dist/reputation/index.browser.cjs.map +1 -1
- package/dist/reputation/index.browser.d.cts +2 -2
- package/dist/reputation/index.browser.d.ts +2 -2
- package/dist/reputation/index.browser.js +941 -66
- package/dist/reputation/index.browser.js.map +1 -1
- package/dist/reputation/index.cjs +1393 -217
- package/dist/reputation/index.cjs.map +1 -1
- package/dist/reputation/index.d.cts +3 -2
- package/dist/reputation/index.d.ts +3 -2
- package/dist/reputation/index.js +1378 -217
- package/dist/reputation/index.js.map +1 -1
- package/dist/{subject-ownership-CXvzEjpH.d.cts → subject-ownership-B7cFlm8c.d.cts} +256 -26
- package/dist/{subject-ownership-CXvzEjpH.d.ts → subject-ownership-B7cFlm8c.d.ts} +256 -26
- package/dist/types-dpYxRq8N.d.cts +281 -0
- package/dist/types-dpYxRq8N.d.ts +281 -0
- package/dist/widgets/index.cjs +70 -27
- package/dist/widgets/index.cjs.map +1 -1
- package/dist/widgets/index.d.cts +42 -18
- package/dist/widgets/index.d.ts +42 -18
- package/dist/widgets/index.js +67 -26
- package/dist/widgets/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/index-B9KW02US.d.cts +0 -119
- package/dist/index-DXrwBex9.d.ts +0 -119
- package/dist/index-QZDExA4I.d.cts +0 -90
- package/dist/index-QZDExA4I.d.ts +0 -90
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var ethers = require('ethers');
|
|
4
|
+
var jose = require('jose');
|
|
4
5
|
var easSdk = require('@ethereum-attestation-service/eas-sdk');
|
|
5
6
|
var canonicalize = require('canonicalize');
|
|
6
7
|
|
|
@@ -41,6 +42,11 @@ function assertString(value, name, code = "INVALID_INPUT") {
|
|
|
41
42
|
throw new OmaTrustError(code, `${name} must be a non-empty string`, { value });
|
|
42
43
|
}
|
|
43
44
|
}
|
|
45
|
+
function assertObject(value, name, code = "INVALID_INPUT") {
|
|
46
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
47
|
+
throw new OmaTrustError(code, `${name} must be an object`, { value });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
44
50
|
var init_assert = __esm({
|
|
45
51
|
"src/shared/assert.ts"() {
|
|
46
52
|
init_errors();
|
|
@@ -156,6 +162,8 @@ function normalizeDid(input) {
|
|
|
156
162
|
return normalizeDidHandle(trimmed);
|
|
157
163
|
case "key":
|
|
158
164
|
return normalizeDidKey(trimmed);
|
|
165
|
+
case "jwk":
|
|
166
|
+
return normalizeDidJwk(trimmed);
|
|
159
167
|
default:
|
|
160
168
|
return trimmed;
|
|
161
169
|
}
|
|
@@ -245,13 +253,226 @@ function extractAddressFromDid(identifier) {
|
|
|
245
253
|
}
|
|
246
254
|
throw new OmaTrustError("INVALID_DID", "Unsupported identifier format", { identifier });
|
|
247
255
|
}
|
|
248
|
-
|
|
256
|
+
function validateDidJwk(did) {
|
|
257
|
+
const parts = did.split(":");
|
|
258
|
+
if (parts.length !== 3) {
|
|
259
|
+
return {
|
|
260
|
+
valid: false,
|
|
261
|
+
method: "jwk",
|
|
262
|
+
error: `did:jwk must have exactly 3 colon-separated parts, got ${parts.length}`
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
const [, , encoded] = parts;
|
|
266
|
+
if (!encoded || encoded.length === 0) {
|
|
267
|
+
return { valid: false, method: "jwk", error: "Missing base64url-encoded JWK identifier" };
|
|
268
|
+
}
|
|
269
|
+
if (!BASE64URL_REGEX.test(encoded)) {
|
|
270
|
+
return {
|
|
271
|
+
valid: false,
|
|
272
|
+
method: "jwk",
|
|
273
|
+
error: "Identifier contains invalid base64url characters"
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
let decoded;
|
|
277
|
+
try {
|
|
278
|
+
const bytes = jose.base64url.decode(encoded);
|
|
279
|
+
decoded = new TextDecoder().decode(bytes);
|
|
280
|
+
} catch {
|
|
281
|
+
return { valid: false, method: "jwk", error: "Failed to base64url-decode identifier" };
|
|
282
|
+
}
|
|
283
|
+
let jwk;
|
|
284
|
+
try {
|
|
285
|
+
jwk = JSON.parse(decoded);
|
|
286
|
+
} catch {
|
|
287
|
+
return { valid: false, method: "jwk", error: "Decoded identifier is not valid JSON" };
|
|
288
|
+
}
|
|
289
|
+
if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
|
|
290
|
+
return { valid: false, method: "jwk", error: "Decoded JWK must be a JSON object" };
|
|
291
|
+
}
|
|
292
|
+
const kty = jwk.kty;
|
|
293
|
+
if (typeof kty !== "string" || !VALID_JWK_KTY.has(kty)) {
|
|
294
|
+
return {
|
|
295
|
+
valid: false,
|
|
296
|
+
method: "jwk",
|
|
297
|
+
error: `Invalid or missing kty field (must be one of: EC, OKP, RSA)`
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
if ("d" in jwk) {
|
|
301
|
+
return {
|
|
302
|
+
valid: false,
|
|
303
|
+
method: "jwk",
|
|
304
|
+
error: "DID must reference a public key \u2014 private key component (d) is not allowed"
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
return { valid: true, method: "jwk" };
|
|
308
|
+
}
|
|
309
|
+
function normalizeDidJwk(input) {
|
|
310
|
+
assertString(input, "input", "INVALID_DID");
|
|
311
|
+
const trimmed = input.trim();
|
|
312
|
+
if (!trimmed.startsWith("did:jwk:")) {
|
|
313
|
+
throw new OmaTrustError("INVALID_DID", "Expected did:jwk DID", { input });
|
|
314
|
+
}
|
|
315
|
+
const result = validateDidJwk(trimmed);
|
|
316
|
+
if (!result.valid) {
|
|
317
|
+
throw new OmaTrustError("INVALID_DID", result.error ?? "Invalid did:jwk", { input });
|
|
318
|
+
}
|
|
319
|
+
return trimmed;
|
|
320
|
+
}
|
|
321
|
+
var DID_REGEX, BASE64URL_REGEX, VALID_JWK_KTY;
|
|
249
322
|
var init_did = __esm({
|
|
250
323
|
"src/identity/did.ts"() {
|
|
251
324
|
init_errors();
|
|
252
325
|
init_assert();
|
|
253
326
|
init_caip();
|
|
254
327
|
DID_REGEX = /^did:[a-z0-9]+:.+$/i;
|
|
328
|
+
BASE64URL_REGEX = /^[A-Za-z0-9_-]+$/;
|
|
329
|
+
VALID_JWK_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
function validatePublicJwk(jwk) {
|
|
333
|
+
if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
|
|
334
|
+
return { valid: false, error: "JWK must be a non-null object" };
|
|
335
|
+
}
|
|
336
|
+
const obj = jwk;
|
|
337
|
+
const kty = obj.kty;
|
|
338
|
+
if (typeof kty !== "string" || !VALID_KTY.has(kty)) {
|
|
339
|
+
return {
|
|
340
|
+
valid: false,
|
|
341
|
+
error: `Invalid or missing kty (must be one of: ${[...VALID_KTY].join(", ")})`
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
for (const field of PRIVATE_KEY_FIELDS) {
|
|
345
|
+
if (field in obj) {
|
|
346
|
+
return {
|
|
347
|
+
valid: false,
|
|
348
|
+
error: `JWK contains private key field "${field}" \u2014 only public keys are allowed`
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const required = REQUIRED_PUBLIC_FIELDS[kty];
|
|
353
|
+
if (required) {
|
|
354
|
+
for (const field of required) {
|
|
355
|
+
if (!(field in obj) || obj[field] === void 0 || obj[field] === null || obj[field] === "") {
|
|
356
|
+
return {
|
|
357
|
+
valid: false,
|
|
358
|
+
error: `Missing required public key field "${field}" for kty="${kty}"`
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return { valid: true };
|
|
364
|
+
}
|
|
365
|
+
function canonicalizeJwkJson(jwk) {
|
|
366
|
+
const sorted = Object.keys(jwk).sort();
|
|
367
|
+
const obj = {};
|
|
368
|
+
for (const key of sorted) {
|
|
369
|
+
obj[key] = jwk[key];
|
|
370
|
+
}
|
|
371
|
+
return JSON.stringify(obj);
|
|
372
|
+
}
|
|
373
|
+
function jwkToDidJwk(jwk) {
|
|
374
|
+
assertObject(jwk, "jwk", "INVALID_JWK");
|
|
375
|
+
const validation = validatePublicJwk(jwk);
|
|
376
|
+
if (!validation.valid) {
|
|
377
|
+
throw new OmaTrustError("INVALID_JWK", validation.error ?? "Invalid public JWK", { jwk });
|
|
378
|
+
}
|
|
379
|
+
const canonical = canonicalizeJwkJson(jwk);
|
|
380
|
+
const encoded = jose.base64url.encode(new TextEncoder().encode(canonical));
|
|
381
|
+
return `did:jwk:${encoded}`;
|
|
382
|
+
}
|
|
383
|
+
function didJwkToJwk(didJwk) {
|
|
384
|
+
if (typeof didJwk !== "string" || !didJwk.startsWith("did:jwk:")) {
|
|
385
|
+
throw new OmaTrustError("INVALID_DID", "Expected a did:jwk DID", { input: didJwk });
|
|
386
|
+
}
|
|
387
|
+
const parts = didJwk.split(":");
|
|
388
|
+
if (parts.length !== 3) {
|
|
389
|
+
throw new OmaTrustError("INVALID_DID", "did:jwk must have exactly 3 colon-separated parts", {
|
|
390
|
+
input: didJwk
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
const encoded = parts[2];
|
|
394
|
+
if (!encoded || encoded.length === 0) {
|
|
395
|
+
throw new OmaTrustError("INVALID_DID", "Missing base64url-encoded JWK identifier", {
|
|
396
|
+
input: didJwk
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
let decoded;
|
|
400
|
+
try {
|
|
401
|
+
const bytes = jose.base64url.decode(encoded);
|
|
402
|
+
decoded = new TextDecoder().decode(bytes);
|
|
403
|
+
} catch {
|
|
404
|
+
throw new OmaTrustError("INVALID_DID", "Failed to base64url-decode did:jwk identifier", {
|
|
405
|
+
input: didJwk
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
let jwk;
|
|
409
|
+
try {
|
|
410
|
+
jwk = JSON.parse(decoded);
|
|
411
|
+
} catch {
|
|
412
|
+
throw new OmaTrustError("INVALID_DID", "Decoded did:jwk identifier is not valid JSON", {
|
|
413
|
+
input: didJwk
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
|
|
417
|
+
throw new OmaTrustError("INVALID_DID", "Decoded did:jwk must be a JSON object", {
|
|
418
|
+
input: didJwk
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
const validation = validatePublicJwk(jwk);
|
|
422
|
+
if (!validation.valid) {
|
|
423
|
+
throw new OmaTrustError("INVALID_DID", validation.error ?? "Invalid public JWK in did:jwk", {
|
|
424
|
+
input: didJwk
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
return jwk;
|
|
428
|
+
}
|
|
429
|
+
function extractPublicKeyFields(jwk) {
|
|
430
|
+
const result = {};
|
|
431
|
+
const metadataFields = /* @__PURE__ */ new Set(["kid", "use", "key_ops", "alg", "ext"]);
|
|
432
|
+
for (const [key, value] of Object.entries(jwk)) {
|
|
433
|
+
if (PRIVATE_KEY_FIELDS.has(key)) continue;
|
|
434
|
+
if (metadataFields.has(key)) continue;
|
|
435
|
+
result[key] = value;
|
|
436
|
+
}
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
function publicJwkEquals(a, b) {
|
|
440
|
+
assertObject(a, "a", "INVALID_JWK");
|
|
441
|
+
assertObject(b, "b", "INVALID_JWK");
|
|
442
|
+
const aObj = a;
|
|
443
|
+
const bObj = b;
|
|
444
|
+
for (const field of PRIVATE_KEY_FIELDS) {
|
|
445
|
+
if (field in aObj) {
|
|
446
|
+
throw new OmaTrustError(
|
|
447
|
+
"INVALID_JWK",
|
|
448
|
+
`First JWK contains private key field "${field}"`,
|
|
449
|
+
{ field }
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
if (field in bObj) {
|
|
453
|
+
throw new OmaTrustError(
|
|
454
|
+
"INVALID_JWK",
|
|
455
|
+
`Second JWK contains private key field "${field}"`,
|
|
456
|
+
{ field }
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
const aPublic = extractPublicKeyFields(aObj);
|
|
461
|
+
const bPublic = extractPublicKeyFields(bObj);
|
|
462
|
+
return canonicalizeJwkJson(aPublic) === canonicalizeJwkJson(bPublic);
|
|
463
|
+
}
|
|
464
|
+
var VALID_KTY, PRIVATE_KEY_FIELDS, REQUIRED_PUBLIC_FIELDS;
|
|
465
|
+
var init_jwk = __esm({
|
|
466
|
+
"src/identity/jwk.ts"() {
|
|
467
|
+
init_errors();
|
|
468
|
+
init_assert();
|
|
469
|
+
VALID_KTY = /* @__PURE__ */ new Set(["EC", "OKP", "RSA"]);
|
|
470
|
+
PRIVATE_KEY_FIELDS = /* @__PURE__ */ new Set(["d", "p", "q", "dp", "dq", "qi", "oth"]);
|
|
471
|
+
REQUIRED_PUBLIC_FIELDS = {
|
|
472
|
+
EC: ["crv", "x", "y"],
|
|
473
|
+
OKP: ["crv", "x"],
|
|
474
|
+
RSA: ["n", "e"]
|
|
475
|
+
};
|
|
255
476
|
}
|
|
256
477
|
});
|
|
257
478
|
|
|
@@ -262,21 +483,23 @@ function parseDnsTxtRecord(record) {
|
|
|
262
483
|
}
|
|
263
484
|
const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
|
|
264
485
|
const parsed = {};
|
|
486
|
+
const controllers = [];
|
|
265
487
|
for (const entry of entries) {
|
|
266
|
-
const
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (
|
|
272
|
-
|
|
488
|
+
const eqIndex = entry.indexOf("=");
|
|
489
|
+
if (eqIndex === -1) continue;
|
|
490
|
+
const key = entry.slice(0, eqIndex).trim();
|
|
491
|
+
const value = entry.slice(eqIndex + 1).trim();
|
|
492
|
+
if (!key || !value) continue;
|
|
493
|
+
if (key === "controller") {
|
|
494
|
+
controllers.push(value);
|
|
495
|
+
} else {
|
|
496
|
+
parsed[key] = value;
|
|
273
497
|
}
|
|
274
|
-
parsed[key.trim()] = value.trim();
|
|
275
498
|
}
|
|
276
499
|
return {
|
|
277
500
|
version: parsed.v,
|
|
278
|
-
controller:
|
|
279
|
-
|
|
501
|
+
controller: controllers[0],
|
|
502
|
+
controllers
|
|
280
503
|
};
|
|
281
504
|
}
|
|
282
505
|
function buildDnsTxtRecord(controllerDid) {
|
|
@@ -290,6 +513,56 @@ var init_dns_txt_record = __esm({
|
|
|
290
513
|
}
|
|
291
514
|
});
|
|
292
515
|
|
|
516
|
+
// src/identity/controller-id.ts
|
|
517
|
+
function isSameControllerId(a, b) {
|
|
518
|
+
let normalizedA = null;
|
|
519
|
+
let normalizedB = null;
|
|
520
|
+
try {
|
|
521
|
+
normalizedA = normalizeDid(a);
|
|
522
|
+
} catch {
|
|
523
|
+
}
|
|
524
|
+
try {
|
|
525
|
+
normalizedB = normalizeDid(b);
|
|
526
|
+
} catch {
|
|
527
|
+
}
|
|
528
|
+
if (normalizedA && normalizedB && normalizedA === normalizedB) {
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
const evmA = extractControllerEvmAddress(a);
|
|
532
|
+
const evmB = extractControllerEvmAddress(b);
|
|
533
|
+
if (evmA && evmB && evmA.toLowerCase() === evmB.toLowerCase()) {
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
const methodA = extractDidMethod(a);
|
|
537
|
+
const methodB = extractDidMethod(b);
|
|
538
|
+
if (methodA === "jwk" && methodB === "jwk") {
|
|
539
|
+
try {
|
|
540
|
+
const jwkA = didJwkToJwk(a);
|
|
541
|
+
const jwkB = didJwkToJwk(b);
|
|
542
|
+
return publicJwkEquals(jwkA, jwkB);
|
|
543
|
+
} catch {
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
function extractControllerEvmAddress(controllerDid) {
|
|
550
|
+
try {
|
|
551
|
+
const method = extractDidMethod(controllerDid);
|
|
552
|
+
if (method === "pkh" && controllerDid.includes("eip155")) {
|
|
553
|
+
return extractAddressFromDid(controllerDid);
|
|
554
|
+
}
|
|
555
|
+
} catch {
|
|
556
|
+
}
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
var init_controller_id = __esm({
|
|
560
|
+
"src/identity/controller-id.ts"() {
|
|
561
|
+
init_did();
|
|
562
|
+
init_jwk();
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
|
|
293
566
|
// src/reputation/proof/dns-txt-shared.ts
|
|
294
567
|
var dns_txt_shared_exports = {};
|
|
295
568
|
__export(dns_txt_shared_exports, {
|
|
@@ -302,7 +575,6 @@ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options
|
|
|
302
575
|
if (!options.resolveTxt) {
|
|
303
576
|
throw new OmaTrustError("NETWORK_ERROR", "No DNS TXT resolver was provided", { domain });
|
|
304
577
|
}
|
|
305
|
-
const expected = normalizeDid(expectedControllerDid);
|
|
306
578
|
const prefix = options.recordPrefix ?? "_controllers";
|
|
307
579
|
const host = `${prefix}.${domain.toLowerCase().replace(/\.$/, "")}`;
|
|
308
580
|
let records;
|
|
@@ -314,15 +586,18 @@ async function verifyDnsTxtControllerDid(domain, expectedControllerDid, options
|
|
|
314
586
|
for (const recordParts of records) {
|
|
315
587
|
const record = recordParts.join("");
|
|
316
588
|
const parsed = parseDnsTxtRecord(record);
|
|
317
|
-
if (parsed.version
|
|
318
|
-
|
|
589
|
+
if (parsed.version !== "1") continue;
|
|
590
|
+
for (const recordController of parsed.controllers) {
|
|
591
|
+
if (isSameControllerId(recordController, expectedControllerDid)) {
|
|
592
|
+
return { valid: true, record };
|
|
593
|
+
}
|
|
319
594
|
}
|
|
320
595
|
}
|
|
321
|
-
return { valid: false, reason: "
|
|
596
|
+
return { valid: false, reason: "Controller DID not found in DNS TXT records" };
|
|
322
597
|
}
|
|
323
598
|
var init_dns_txt_shared = __esm({
|
|
324
599
|
"src/reputation/proof/dns-txt-shared.ts"() {
|
|
325
|
-
|
|
600
|
+
init_controller_id();
|
|
326
601
|
init_errors();
|
|
327
602
|
init_dns_txt_record();
|
|
328
603
|
}
|
|
@@ -793,19 +1068,32 @@ async function submitDelegatedAttestation(params) {
|
|
|
793
1068
|
payload = {};
|
|
794
1069
|
}
|
|
795
1070
|
if (!response.ok) {
|
|
796
|
-
|
|
797
|
-
|
|
1071
|
+
const relayError = {
|
|
1072
|
+
httpStatus: response.status,
|
|
1073
|
+
code: payload.code,
|
|
1074
|
+
error: payload.error ?? payload.message,
|
|
798
1075
|
payload
|
|
799
|
-
}
|
|
1076
|
+
};
|
|
1077
|
+
throw new OmaTrustError("RELAY_ERROR", `Relay submission failed (HTTP ${response.status})`, relayError);
|
|
800
1078
|
}
|
|
801
1079
|
const uid = payload.uid ?? ZERO_UID;
|
|
802
1080
|
const txHash = payload.txHash;
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1081
|
+
const hasConfirmationSignals = payload.status === "confirmed" || payload.success === true || typeof payload.blockNumber === "number";
|
|
1082
|
+
const status = hasConfirmationSignals ? "confirmed" : "submitted";
|
|
1083
|
+
const relay = {};
|
|
1084
|
+
if (typeof payload.blockNumber === "number") relay.blockNumber = payload.blockNumber;
|
|
1085
|
+
if (typeof payload.chain === "string") relay.chain = payload.chain;
|
|
1086
|
+
if (typeof payload.success === "boolean") relay.success = payload.success;
|
|
1087
|
+
for (const key of Object.keys(payload)) {
|
|
1088
|
+
if (!["uid", "txHash", "status", "blockNumber", "chain", "success"].includes(key)) {
|
|
1089
|
+
relay[key] = payload[key];
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
const result = { uid, txHash, status };
|
|
1093
|
+
if (Object.keys(relay).length > 0) {
|
|
1094
|
+
result.relay = relay;
|
|
1095
|
+
}
|
|
1096
|
+
return result;
|
|
809
1097
|
}
|
|
810
1098
|
|
|
811
1099
|
// src/reputation/query.ts
|
|
@@ -911,7 +1199,7 @@ async function getAttestationsForDid(params) {
|
|
|
911
1199
|
provider,
|
|
912
1200
|
fromBlock,
|
|
913
1201
|
toBlock,
|
|
914
|
-
{ recipient: didToAddress(params.
|
|
1202
|
+
{ recipient: didToAddress(params.subjectDid), schemas: params.schemas }
|
|
915
1203
|
);
|
|
916
1204
|
}
|
|
917
1205
|
async function getAttestationsByAttester(params) {
|
|
@@ -1163,6 +1451,10 @@ function getExplorerAddressUrl(chainId, address) {
|
|
|
1163
1451
|
|
|
1164
1452
|
// src/reputation/proof/did-json.ts
|
|
1165
1453
|
init_did();
|
|
1454
|
+
init_jwk();
|
|
1455
|
+
init_errors();
|
|
1456
|
+
|
|
1457
|
+
// src/shared/did-document.ts
|
|
1166
1458
|
init_errors();
|
|
1167
1459
|
async function fetchDidDocument(domain) {
|
|
1168
1460
|
const normalized = domain.toLowerCase().replace(/\.$/, "");
|
|
@@ -1174,15 +1466,16 @@ async function fetchDidDocument(domain) {
|
|
|
1174
1466
|
throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
|
|
1175
1467
|
}
|
|
1176
1468
|
if (!response.ok) {
|
|
1177
|
-
throw new OmaTrustError("NETWORK_ERROR",
|
|
1469
|
+
throw new OmaTrustError("NETWORK_ERROR", `DID document fetch failed: ${response.status}`, {
|
|
1178
1470
|
domain,
|
|
1179
1471
|
status: response.status
|
|
1180
1472
|
});
|
|
1181
1473
|
}
|
|
1182
|
-
|
|
1183
|
-
return body;
|
|
1474
|
+
return await response.json();
|
|
1184
1475
|
}
|
|
1185
|
-
|
|
1476
|
+
|
|
1477
|
+
// src/reputation/proof/did-json.ts
|
|
1478
|
+
function extractEvmAddressesFromDidDocument(didDocument) {
|
|
1186
1479
|
const methods = didDocument.verificationMethod;
|
|
1187
1480
|
if (!Array.isArray(methods)) {
|
|
1188
1481
|
return [];
|
|
@@ -1206,14 +1499,52 @@ function extractAddressesFromDidDocument(didDocument) {
|
|
|
1206
1499
|
}
|
|
1207
1500
|
return [...addresses];
|
|
1208
1501
|
}
|
|
1502
|
+
function extractJwksFromDidDocument(didDocument) {
|
|
1503
|
+
const methods = didDocument.verificationMethod;
|
|
1504
|
+
if (!Array.isArray(methods)) {
|
|
1505
|
+
return [];
|
|
1506
|
+
}
|
|
1507
|
+
const jwks = [];
|
|
1508
|
+
for (const method of methods) {
|
|
1509
|
+
const publicKeyJwk = method.publicKeyJwk;
|
|
1510
|
+
if (publicKeyJwk && typeof publicKeyJwk === "object" && !Array.isArray(publicKeyJwk)) {
|
|
1511
|
+
jwks.push(publicKeyJwk);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return jwks;
|
|
1515
|
+
}
|
|
1209
1516
|
function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
|
|
1517
|
+
const method = extractDidMethod(expectedControllerDid);
|
|
1518
|
+
if (method === "jwk") {
|
|
1519
|
+
try {
|
|
1520
|
+
const expectedJwk = didJwkToJwk(expectedControllerDid);
|
|
1521
|
+
const documentJwks = extractJwksFromDidDocument(didDocument);
|
|
1522
|
+
for (const docJwk of documentJwks) {
|
|
1523
|
+
try {
|
|
1524
|
+
if (publicJwkEquals(docJwk, expectedJwk)) {
|
|
1525
|
+
return { valid: true };
|
|
1526
|
+
}
|
|
1527
|
+
} catch {
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
return {
|
|
1531
|
+
valid: false,
|
|
1532
|
+
reason: "No matching publicKeyJwk found in DID document verification methods"
|
|
1533
|
+
};
|
|
1534
|
+
} catch {
|
|
1535
|
+
return {
|
|
1536
|
+
valid: false,
|
|
1537
|
+
reason: "Failed to decode did:jwk controller DID"
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1210
1541
|
let expectedAddress;
|
|
1211
1542
|
try {
|
|
1212
1543
|
expectedAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
|
|
1213
1544
|
} catch {
|
|
1214
|
-
return { valid: false, reason: "
|
|
1545
|
+
return { valid: false, reason: "Controller DID does not resolve to an EVM address and is not did:jwk" };
|
|
1215
1546
|
}
|
|
1216
|
-
const addresses =
|
|
1547
|
+
const addresses = extractEvmAddressesFromDidDocument(didDocument);
|
|
1217
1548
|
if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
|
|
1218
1549
|
return { valid: true };
|
|
1219
1550
|
}
|
|
@@ -1267,6 +1598,469 @@ function verifyEip712Signature(typedData, signature) {
|
|
|
1267
1598
|
|
|
1268
1599
|
// src/reputation/verify.ts
|
|
1269
1600
|
init_dns_txt_record();
|
|
1601
|
+
|
|
1602
|
+
// src/reputation/proof/x402-jws.ts
|
|
1603
|
+
init_errors();
|
|
1604
|
+
init_jwk();
|
|
1605
|
+
|
|
1606
|
+
// src/identity/resolve-key.ts
|
|
1607
|
+
init_errors();
|
|
1608
|
+
init_assert();
|
|
1609
|
+
|
|
1610
|
+
// src/identity/did-url.ts
|
|
1611
|
+
init_errors();
|
|
1612
|
+
init_assert();
|
|
1613
|
+
var DID_URL_REGEX = /^did:[a-z0-9]+:.+$/i;
|
|
1614
|
+
function parseDidUrl(input) {
|
|
1615
|
+
assertString(input, "input", "INVALID_DID_URL");
|
|
1616
|
+
const trimmed = input.trim();
|
|
1617
|
+
const hashIndex = trimmed.indexOf("#");
|
|
1618
|
+
let did;
|
|
1619
|
+
let fragment;
|
|
1620
|
+
if (hashIndex === -1) {
|
|
1621
|
+
did = trimmed;
|
|
1622
|
+
fragment = null;
|
|
1623
|
+
} else {
|
|
1624
|
+
did = trimmed.slice(0, hashIndex);
|
|
1625
|
+
const rawFragment = trimmed.slice(hashIndex + 1);
|
|
1626
|
+
if (rawFragment.length === 0) {
|
|
1627
|
+
throw new OmaTrustError(
|
|
1628
|
+
"INVALID_DID_URL",
|
|
1629
|
+
"DID URL has empty fragment (trailing '#' with no identifier)",
|
|
1630
|
+
{ input }
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
fragment = rawFragment;
|
|
1634
|
+
}
|
|
1635
|
+
if (!DID_URL_REGEX.test(did)) {
|
|
1636
|
+
throw new OmaTrustError(
|
|
1637
|
+
"INVALID_DID_URL",
|
|
1638
|
+
"Invalid DID URL: base DID portion is malformed",
|
|
1639
|
+
{ input, did }
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1642
|
+
return {
|
|
1643
|
+
didUrl: trimmed,
|
|
1644
|
+
did,
|
|
1645
|
+
fragment
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
// src/identity/resolve-key.ts
|
|
1650
|
+
init_did();
|
|
1651
|
+
init_jwk();
|
|
1652
|
+
function findVerificationMethod(didDocument, didUrl, fragment) {
|
|
1653
|
+
const methods = didDocument.verificationMethod;
|
|
1654
|
+
if (!Array.isArray(methods)) {
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
for (const method of methods) {
|
|
1658
|
+
if (!method || typeof method !== "object") continue;
|
|
1659
|
+
const id = method.id;
|
|
1660
|
+
if (typeof id !== "string") continue;
|
|
1661
|
+
if (id === didUrl) return method;
|
|
1662
|
+
if (fragment && (id === `#${fragment}` || id.endsWith(`#${fragment}`))) {
|
|
1663
|
+
return method;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
return null;
|
|
1667
|
+
}
|
|
1668
|
+
function extractMethodIdsFromDidDocument(didDocument) {
|
|
1669
|
+
const methods = didDocument.verificationMethod;
|
|
1670
|
+
if (!Array.isArray(methods)) return [];
|
|
1671
|
+
return methods.filter((m) => m && typeof m.id === "string").map((m) => m.id);
|
|
1672
|
+
}
|
|
1673
|
+
async function resolveDidUrlToPublicKey(didUrl, options) {
|
|
1674
|
+
assertString(didUrl, "didUrl", "INVALID_DID_URL");
|
|
1675
|
+
const parsed = parseDidUrl(didUrl);
|
|
1676
|
+
const method = extractDidMethod(parsed.did);
|
|
1677
|
+
if (!method) {
|
|
1678
|
+
throw new OmaTrustError("INVALID_DID_URL", "Cannot extract DID method from DID URL", {
|
|
1679
|
+
didUrl,
|
|
1680
|
+
did: parsed.did
|
|
1681
|
+
});
|
|
1682
|
+
}
|
|
1683
|
+
switch (method) {
|
|
1684
|
+
case "web":
|
|
1685
|
+
return resolveDidUrlKey(parsed.didUrl, parsed.did, parsed.fragment);
|
|
1686
|
+
default:
|
|
1687
|
+
throw new OmaTrustError(
|
|
1688
|
+
"UNSUPPORTED_DID_METHOD",
|
|
1689
|
+
`DID URL key resolution is not supported for method "${method}"`,
|
|
1690
|
+
{ didUrl, method }
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
async function resolveDidUrlKey(didUrl, did, fragment, options) {
|
|
1695
|
+
const domain = getDomainFromDidWeb(did);
|
|
1696
|
+
if (!domain) {
|
|
1697
|
+
throw new OmaTrustError("INVALID_DID_URL", "Cannot extract domain from did:web DID", {
|
|
1698
|
+
didUrl,
|
|
1699
|
+
did
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
const fetchFn = fetchDidDocument;
|
|
1703
|
+
const didDocument = await fetchFn(domain);
|
|
1704
|
+
const method = findVerificationMethod(didDocument, didUrl, fragment);
|
|
1705
|
+
if (!method) {
|
|
1706
|
+
throw new OmaTrustError(
|
|
1707
|
+
"KEY_NOT_FOUND",
|
|
1708
|
+
`No verification method found matching "${fragment ? `#${fragment}` : didUrl}"`,
|
|
1709
|
+
{ didUrl, fragment, availableMethods: extractMethodIdsFromDidDocument(didDocument) }
|
|
1710
|
+
);
|
|
1711
|
+
}
|
|
1712
|
+
const publicKeyJwk = method.publicKeyJwk;
|
|
1713
|
+
if (!publicKeyJwk || typeof publicKeyJwk !== "object") {
|
|
1714
|
+
throw new OmaTrustError(
|
|
1715
|
+
"KEY_NOT_FOUND",
|
|
1716
|
+
`Verification method "${method.id}" does not contain publicKeyJwk`,
|
|
1717
|
+
{ didUrl, verificationMethodId: method.id }
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
const validation = validatePublicJwk(publicKeyJwk);
|
|
1721
|
+
if (!validation.valid) {
|
|
1722
|
+
throw new OmaTrustError(
|
|
1723
|
+
"INVALID_JWK",
|
|
1724
|
+
`publicKeyJwk in verification method "${method.id}" is invalid: ${validation.error}`,
|
|
1725
|
+
{ didUrl, verificationMethodId: method.id }
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
return {
|
|
1729
|
+
didUrl,
|
|
1730
|
+
did,
|
|
1731
|
+
fragment,
|
|
1732
|
+
publicKeyJwk,
|
|
1733
|
+
verificationMethodId: method.id
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// src/reputation/proof/x402-jws.ts
|
|
1738
|
+
var REQUIRED_OFFER_FIELDS = [
|
|
1739
|
+
"version",
|
|
1740
|
+
"resourceUrl",
|
|
1741
|
+
"scheme",
|
|
1742
|
+
"network",
|
|
1743
|
+
"asset",
|
|
1744
|
+
"payTo",
|
|
1745
|
+
"amount"
|
|
1746
|
+
];
|
|
1747
|
+
var REQUIRED_RECEIPT_FIELDS = [
|
|
1748
|
+
"version",
|
|
1749
|
+
"network",
|
|
1750
|
+
"resourceUrl",
|
|
1751
|
+
"payer",
|
|
1752
|
+
"issuedAt"
|
|
1753
|
+
];
|
|
1754
|
+
function validateOfferPayload(payload) {
|
|
1755
|
+
for (const field of REQUIRED_OFFER_FIELDS) {
|
|
1756
|
+
if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
|
|
1757
|
+
return { valid: false, field };
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return { valid: true };
|
|
1761
|
+
}
|
|
1762
|
+
function validateReceiptPayload(payload) {
|
|
1763
|
+
for (const field of REQUIRED_RECEIPT_FIELDS) {
|
|
1764
|
+
if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
|
|
1765
|
+
return { valid: false, field };
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
return { valid: true };
|
|
1769
|
+
}
|
|
1770
|
+
function failure(code, message, partial) {
|
|
1771
|
+
return {
|
|
1772
|
+
valid: false,
|
|
1773
|
+
error: { code, message },
|
|
1774
|
+
...partial
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
async function verifyX402JwsArtifact(artifact, options) {
|
|
1778
|
+
if (!artifact || typeof artifact !== "object") {
|
|
1779
|
+
return failure("INVALID_ARTIFACT", "Artifact must be a non-null object");
|
|
1780
|
+
}
|
|
1781
|
+
if (artifact.format !== "jws") {
|
|
1782
|
+
return failure("INVALID_ARTIFACT", `Expected format "jws", got "${artifact.format}"`);
|
|
1783
|
+
}
|
|
1784
|
+
if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
|
|
1785
|
+
return failure("INVALID_ARTIFACT", "Artifact signature must be a non-empty string");
|
|
1786
|
+
}
|
|
1787
|
+
const compactJws = artifact.signature;
|
|
1788
|
+
const parts = compactJws.split(".");
|
|
1789
|
+
if (parts.length !== 3) {
|
|
1790
|
+
return failure("MALFORMED_JWS", "Invalid compact JWS format: expected 3 dot-separated parts");
|
|
1791
|
+
}
|
|
1792
|
+
let header;
|
|
1793
|
+
try {
|
|
1794
|
+
header = jose.decodeProtectedHeader(compactJws);
|
|
1795
|
+
} catch {
|
|
1796
|
+
return failure("MALFORMED_JWS", "Failed to decode JWS protected header");
|
|
1797
|
+
}
|
|
1798
|
+
if (!header.alg || typeof header.alg !== "string") {
|
|
1799
|
+
return failure("MISSING_ALG", "JWS header must include alg", { header });
|
|
1800
|
+
}
|
|
1801
|
+
const kid = typeof header.kid === "string" ? header.kid : null;
|
|
1802
|
+
const embeddedJwk = header.jwk;
|
|
1803
|
+
if (!kid && !embeddedJwk) {
|
|
1804
|
+
return failure(
|
|
1805
|
+
"MISSING_KEY_MATERIAL",
|
|
1806
|
+
"JWS header must include at least one of kid or jwk",
|
|
1807
|
+
{ header }
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1810
|
+
let publicKeyJwk;
|
|
1811
|
+
let publicKeySource;
|
|
1812
|
+
if (embeddedJwk) {
|
|
1813
|
+
const validation = validatePublicJwk(embeddedJwk);
|
|
1814
|
+
if (!validation.valid) {
|
|
1815
|
+
return failure(
|
|
1816
|
+
"INVALID_EMBEDDED_JWK",
|
|
1817
|
+
`Embedded JWK is invalid: ${validation.error}`,
|
|
1818
|
+
{ header }
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
publicKeyJwk = embeddedJwk;
|
|
1822
|
+
publicKeySource = "embedded-jwk";
|
|
1823
|
+
if (kid) {
|
|
1824
|
+
try {
|
|
1825
|
+
const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
|
|
1826
|
+
if (!publicJwkEquals(publicKeyJwk, resolved.publicKeyJwk)) {
|
|
1827
|
+
return failure(
|
|
1828
|
+
"KEY_CONFLICT",
|
|
1829
|
+
"Embedded jwk conflicts with public key resolved from kid",
|
|
1830
|
+
{ header, kid }
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
} catch (err) {
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
} else {
|
|
1837
|
+
publicKeySource = "kid-resolution";
|
|
1838
|
+
try {
|
|
1839
|
+
const resolved = await resolveDidUrlToPublicKey(kid, options?.resolveOptions);
|
|
1840
|
+
publicKeyJwk = resolved.publicKeyJwk;
|
|
1841
|
+
} catch (err) {
|
|
1842
|
+
const message = err instanceof OmaTrustError ? `Failed to resolve kid "${kid}": ${err.message}` : `Failed to resolve kid "${kid}"`;
|
|
1843
|
+
return failure("KID_RESOLUTION_FAILED", message, { header, kid });
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
let payload;
|
|
1847
|
+
try {
|
|
1848
|
+
const key = await jose.importJWK(publicKeyJwk, header.alg);
|
|
1849
|
+
const result = await jose.compactVerify(compactJws, key);
|
|
1850
|
+
const payloadText = new TextDecoder().decode(result.payload);
|
|
1851
|
+
payload = JSON.parse(payloadText);
|
|
1852
|
+
} catch (err) {
|
|
1853
|
+
const message = err instanceof Error ? err.message : "Signature verification failed";
|
|
1854
|
+
return failure("SIGNATURE_INVALID", message, { header, kid });
|
|
1855
|
+
}
|
|
1856
|
+
const publicKeyDid = jwkToDidJwk(publicKeyJwk);
|
|
1857
|
+
return {
|
|
1858
|
+
valid: true,
|
|
1859
|
+
header,
|
|
1860
|
+
payload,
|
|
1861
|
+
kid,
|
|
1862
|
+
publicKeyJwk,
|
|
1863
|
+
publicKeySource,
|
|
1864
|
+
publicKeyDid
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
async function verifyX402JwsOffer(artifact, options) {
|
|
1868
|
+
const result = await verifyX402JwsArtifact(artifact, options);
|
|
1869
|
+
if (!result.valid) return result;
|
|
1870
|
+
const payloadCheck = validateOfferPayload(result.payload);
|
|
1871
|
+
if (!payloadCheck.valid) {
|
|
1872
|
+
return failure(
|
|
1873
|
+
"INVALID_OFFER_PAYLOAD",
|
|
1874
|
+
`Missing required offer field: ${payloadCheck.field}`,
|
|
1875
|
+
{
|
|
1876
|
+
header: result.header,
|
|
1877
|
+
payload: result.payload,
|
|
1878
|
+
kid: result.kid,
|
|
1879
|
+
publicKeyJwk: result.publicKeyJwk,
|
|
1880
|
+
publicKeySource: result.publicKeySource,
|
|
1881
|
+
publicKeyDid: result.publicKeyDid
|
|
1882
|
+
}
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
return result;
|
|
1886
|
+
}
|
|
1887
|
+
async function verifyX402JwsReceipt(artifact, options) {
|
|
1888
|
+
const result = await verifyX402JwsArtifact(artifact, options);
|
|
1889
|
+
if (!result.valid) return result;
|
|
1890
|
+
const payloadCheck = validateReceiptPayload(result.payload);
|
|
1891
|
+
if (!payloadCheck.valid) {
|
|
1892
|
+
return failure(
|
|
1893
|
+
"INVALID_RECEIPT_PAYLOAD",
|
|
1894
|
+
`Missing required receipt field: ${payloadCheck.field}`,
|
|
1895
|
+
{
|
|
1896
|
+
header: result.header,
|
|
1897
|
+
payload: result.payload,
|
|
1898
|
+
kid: result.kid,
|
|
1899
|
+
publicKeyJwk: result.publicKeyJwk,
|
|
1900
|
+
publicKeySource: result.publicKeySource,
|
|
1901
|
+
publicKeyDid: result.publicKeyDid
|
|
1902
|
+
}
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
return result;
|
|
1906
|
+
}
|
|
1907
|
+
var OFFER_DOMAIN = {
|
|
1908
|
+
name: "x402 offer",
|
|
1909
|
+
version: "1",
|
|
1910
|
+
chainId: 1
|
|
1911
|
+
};
|
|
1912
|
+
var RECEIPT_DOMAIN = {
|
|
1913
|
+
name: "x402 receipt",
|
|
1914
|
+
version: "1",
|
|
1915
|
+
chainId: 1
|
|
1916
|
+
};
|
|
1917
|
+
var OFFER_TYPES = {
|
|
1918
|
+
Offer: [
|
|
1919
|
+
{ name: "version", type: "uint256" },
|
|
1920
|
+
{ name: "resourceUrl", type: "string" },
|
|
1921
|
+
{ name: "scheme", type: "string" },
|
|
1922
|
+
{ name: "network", type: "string" },
|
|
1923
|
+
{ name: "asset", type: "string" },
|
|
1924
|
+
{ name: "payTo", type: "string" },
|
|
1925
|
+
{ name: "amount", type: "string" },
|
|
1926
|
+
{ name: "validUntil", type: "uint256" }
|
|
1927
|
+
]
|
|
1928
|
+
};
|
|
1929
|
+
var RECEIPT_TYPES = {
|
|
1930
|
+
Receipt: [
|
|
1931
|
+
{ name: "version", type: "uint256" },
|
|
1932
|
+
{ name: "network", type: "string" },
|
|
1933
|
+
{ name: "resourceUrl", type: "string" },
|
|
1934
|
+
{ name: "payer", type: "string" },
|
|
1935
|
+
{ name: "issuedAt", type: "uint256" },
|
|
1936
|
+
{ name: "transaction", type: "string" }
|
|
1937
|
+
]
|
|
1938
|
+
};
|
|
1939
|
+
var REQUIRED_OFFER_FIELDS2 = [
|
|
1940
|
+
"version",
|
|
1941
|
+
"resourceUrl",
|
|
1942
|
+
"scheme",
|
|
1943
|
+
"network",
|
|
1944
|
+
"asset",
|
|
1945
|
+
"payTo",
|
|
1946
|
+
"amount"
|
|
1947
|
+
];
|
|
1948
|
+
var REQUIRED_RECEIPT_FIELDS2 = [
|
|
1949
|
+
"version",
|
|
1950
|
+
"network",
|
|
1951
|
+
"resourceUrl",
|
|
1952
|
+
"payer",
|
|
1953
|
+
"issuedAt"
|
|
1954
|
+
];
|
|
1955
|
+
function validateOfferPayload2(payload) {
|
|
1956
|
+
for (const field of REQUIRED_OFFER_FIELDS2) {
|
|
1957
|
+
if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
|
|
1958
|
+
return { valid: false, field };
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
return { valid: true };
|
|
1962
|
+
}
|
|
1963
|
+
function validateReceiptPayload2(payload) {
|
|
1964
|
+
for (const field of REQUIRED_RECEIPT_FIELDS2) {
|
|
1965
|
+
if (!(field in payload) || payload[field] === void 0 || payload[field] === null) {
|
|
1966
|
+
return { valid: false, field };
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
return { valid: true };
|
|
1970
|
+
}
|
|
1971
|
+
function failure2(code, message, partial) {
|
|
1972
|
+
return {
|
|
1973
|
+
valid: false,
|
|
1974
|
+
error: { code, message },
|
|
1975
|
+
...partial
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
function prepareOfferMessage(payload) {
|
|
1979
|
+
return {
|
|
1980
|
+
version: payload.version,
|
|
1981
|
+
resourceUrl: payload.resourceUrl,
|
|
1982
|
+
scheme: payload.scheme,
|
|
1983
|
+
network: payload.network,
|
|
1984
|
+
asset: payload.asset,
|
|
1985
|
+
payTo: payload.payTo,
|
|
1986
|
+
amount: payload.amount,
|
|
1987
|
+
validUntil: payload.validUntil ?? 0
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
function prepareReceiptMessage(payload) {
|
|
1991
|
+
return {
|
|
1992
|
+
version: payload.version,
|
|
1993
|
+
network: payload.network,
|
|
1994
|
+
resourceUrl: payload.resourceUrl,
|
|
1995
|
+
payer: payload.payer,
|
|
1996
|
+
issuedAt: payload.issuedAt,
|
|
1997
|
+
transaction: payload.transaction ?? ""
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
function verifyX402Eip712Artifact(artifact, artifactType) {
|
|
2001
|
+
if (!artifact || typeof artifact !== "object") {
|
|
2002
|
+
return failure2("INVALID_ARTIFACT", "Artifact must be a non-null object");
|
|
2003
|
+
}
|
|
2004
|
+
if (artifact.format !== "eip712") {
|
|
2005
|
+
return failure2("INVALID_ARTIFACT", `Expected format "eip712", got "${artifact.format}"`);
|
|
2006
|
+
}
|
|
2007
|
+
if (!artifact.payload || typeof artifact.payload !== "object" || Array.isArray(artifact.payload)) {
|
|
2008
|
+
return failure2("MISSING_PAYLOAD", "EIP-712 artifact must include a payload object");
|
|
2009
|
+
}
|
|
2010
|
+
if (typeof artifact.signature !== "string" || artifact.signature.length === 0) {
|
|
2011
|
+
return failure2("MISSING_SIGNATURE", "EIP-712 artifact must include a non-empty signature");
|
|
2012
|
+
}
|
|
2013
|
+
const payload = artifact.payload;
|
|
2014
|
+
if (artifactType === "offer") {
|
|
2015
|
+
const check = validateOfferPayload2(payload);
|
|
2016
|
+
if (!check.valid) {
|
|
2017
|
+
return failure2(
|
|
2018
|
+
"INVALID_OFFER_PAYLOAD",
|
|
2019
|
+
`Missing required offer field: ${check.field}`,
|
|
2020
|
+
{ payload }
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
} else {
|
|
2024
|
+
const check = validateReceiptPayload2(payload);
|
|
2025
|
+
if (!check.valid) {
|
|
2026
|
+
return failure2(
|
|
2027
|
+
"INVALID_RECEIPT_PAYLOAD",
|
|
2028
|
+
`Missing required receipt field: ${check.field}`,
|
|
2029
|
+
{ payload }
|
|
2030
|
+
);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
const domain = artifactType === "offer" ? OFFER_DOMAIN : RECEIPT_DOMAIN;
|
|
2034
|
+
const types = artifactType === "offer" ? OFFER_TYPES : RECEIPT_TYPES;
|
|
2035
|
+
const message = artifactType === "offer" ? prepareOfferMessage(payload) : prepareReceiptMessage(payload);
|
|
2036
|
+
let signer;
|
|
2037
|
+
try {
|
|
2038
|
+
signer = ethers.verifyTypedData(
|
|
2039
|
+
domain,
|
|
2040
|
+
types,
|
|
2041
|
+
message,
|
|
2042
|
+
artifact.signature
|
|
2043
|
+
);
|
|
2044
|
+
signer = ethers.getAddress(signer);
|
|
2045
|
+
} catch (err) {
|
|
2046
|
+
const message2 = err instanceof Error ? err.message : "EIP-712 signature verification failed";
|
|
2047
|
+
return failure2("SIGNATURE_INVALID", message2, { payload });
|
|
2048
|
+
}
|
|
2049
|
+
return {
|
|
2050
|
+
valid: true,
|
|
2051
|
+
payload,
|
|
2052
|
+
signer,
|
|
2053
|
+
artifactType
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
function verifyX402Eip712Offer(artifact) {
|
|
2057
|
+
return verifyX402Eip712Artifact(artifact, "offer");
|
|
2058
|
+
}
|
|
2059
|
+
function verifyX402Eip712Receipt(artifact) {
|
|
2060
|
+
return verifyX402Eip712Artifact(artifact, "receipt");
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
// src/reputation/verify.ts
|
|
1270
2064
|
function parseChainId(input) {
|
|
1271
2065
|
if (typeof input === "number") {
|
|
1272
2066
|
return input;
|
|
@@ -1323,18 +2117,18 @@ function decodeJwtPayload(compactJws) {
|
|
|
1323
2117
|
return JSON.parse(json);
|
|
1324
2118
|
}
|
|
1325
2119
|
async function verifyProof(params) {
|
|
1326
|
-
const { proof, provider,
|
|
2120
|
+
const { proof, provider, expectedSubjectDid, expectedControllerDid } = params;
|
|
1327
2121
|
try {
|
|
1328
2122
|
switch (proof.proofType) {
|
|
1329
2123
|
case "tx-encoded-value": {
|
|
1330
2124
|
if (!provider) {
|
|
1331
2125
|
return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
|
|
1332
2126
|
}
|
|
1333
|
-
if (!
|
|
2127
|
+
if (!expectedSubjectDid || !expectedControllerDid) {
|
|
1334
2128
|
return {
|
|
1335
2129
|
valid: false,
|
|
1336
2130
|
proofType: proof.proofType,
|
|
1337
|
-
reason: "
|
|
2131
|
+
reason: "expectedSubjectDid and expectedControllerDid are required"
|
|
1338
2132
|
};
|
|
1339
2133
|
}
|
|
1340
2134
|
const proofObject = proof.proofObject;
|
|
@@ -1346,13 +2140,13 @@ async function verifyProof(params) {
|
|
|
1346
2140
|
return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
|
|
1347
2141
|
}
|
|
1348
2142
|
const expectedAmount = calculateTransferAmount(
|
|
1349
|
-
|
|
1350
|
-
|
|
2143
|
+
expectedSubjectDid,
|
|
2144
|
+
expectedControllerDid,
|
|
1351
2145
|
chainId,
|
|
1352
2146
|
getProofPurpose(proof)
|
|
1353
2147
|
);
|
|
1354
|
-
const subjectAddress = ethers.getAddress(extractAddressFromDid(
|
|
1355
|
-
const controllerAddress = ethers.getAddress(extractAddressFromDid(
|
|
2148
|
+
const subjectAddress = ethers.getAddress(extractAddressFromDid(expectedSubjectDid));
|
|
2149
|
+
const controllerAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
|
|
1356
2150
|
if (tx.from && ethers.getAddress(tx.from) !== subjectAddress) {
|
|
1357
2151
|
return { valid: false, proofType: proof.proofType, reason: "Transaction sender mismatch" };
|
|
1358
2152
|
}
|
|
@@ -1426,6 +2220,38 @@ async function verifyProof(params) {
|
|
|
1426
2220
|
if (!proof.proofObject || typeof proof.proofObject !== "object") {
|
|
1427
2221
|
return { valid: false, proofType: proof.proofType, reason: "Invalid x402 proof object" };
|
|
1428
2222
|
}
|
|
2223
|
+
const proofObj = proof.proofObject;
|
|
2224
|
+
if (proofObj.format === "jws" && typeof proofObj.signature === "string") {
|
|
2225
|
+
const artifact = {
|
|
2226
|
+
format: "jws",
|
|
2227
|
+
signature: proofObj.signature
|
|
2228
|
+
};
|
|
2229
|
+
const jwsResult = proof.proofType === "x402-offer" ? await verifyX402JwsOffer(artifact) : await verifyX402JwsReceipt(artifact);
|
|
2230
|
+
if (!jwsResult.valid) {
|
|
2231
|
+
return {
|
|
2232
|
+
valid: false,
|
|
2233
|
+
proofType: proof.proofType,
|
|
2234
|
+
reason: jwsResult.error?.message ?? "JWS verification failed"
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
return { valid: true, proofType: proof.proofType };
|
|
2238
|
+
}
|
|
2239
|
+
if (proofObj.format === "eip712" && typeof proofObj.signature === "string" && proofObj.payload && typeof proofObj.payload === "object") {
|
|
2240
|
+
const artifact = {
|
|
2241
|
+
format: "eip712",
|
|
2242
|
+
payload: proofObj.payload,
|
|
2243
|
+
signature: proofObj.signature
|
|
2244
|
+
};
|
|
2245
|
+
const eip712Result = proof.proofType === "x402-offer" ? verifyX402Eip712Offer(artifact) : verifyX402Eip712Receipt(artifact);
|
|
2246
|
+
if (!eip712Result.valid) {
|
|
2247
|
+
return {
|
|
2248
|
+
valid: false,
|
|
2249
|
+
proofType: proof.proofType,
|
|
2250
|
+
reason: eip712Result.error?.message ?? "EIP-712 verification failed"
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
return { valid: true, proofType: proof.proofType };
|
|
2254
|
+
}
|
|
1429
2255
|
return { valid: true, proofType: proof.proofType };
|
|
1430
2256
|
}
|
|
1431
2257
|
case "evidence-pointer": {
|
|
@@ -1437,9 +2263,9 @@ async function verifyProof(params) {
|
|
|
1437
2263
|
if (!response.ok) {
|
|
1438
2264
|
return { valid: false, proofType: proof.proofType, reason: `Evidence fetch failed (${response.status})` };
|
|
1439
2265
|
}
|
|
1440
|
-
if (object.url.endsWith("/.well-known/did.json") &&
|
|
2266
|
+
if (object.url.endsWith("/.well-known/did.json") && expectedControllerDid) {
|
|
1441
2267
|
const didDoc = await response.json();
|
|
1442
|
-
const didCheck = verifyDidDocumentControllerDid(didDoc,
|
|
2268
|
+
const didCheck = verifyDidDocumentControllerDid(didDoc, expectedControllerDid);
|
|
1443
2269
|
return {
|
|
1444
2270
|
valid: didCheck.valid,
|
|
1445
2271
|
proofType: proof.proofType,
|
|
@@ -1447,10 +2273,10 @@ async function verifyProof(params) {
|
|
|
1447
2273
|
};
|
|
1448
2274
|
}
|
|
1449
2275
|
const body = await response.text();
|
|
1450
|
-
if (
|
|
2276
|
+
if (expectedControllerDid && !body.includes(expectedControllerDid)) {
|
|
1451
2277
|
try {
|
|
1452
2278
|
const parsed = parseDnsTxtRecord(body);
|
|
1453
|
-
if (parsed.
|
|
2279
|
+
if (!parsed.controllers.includes(expectedControllerDid)) {
|
|
1454
2280
|
return {
|
|
1455
2281
|
valid: false,
|
|
1456
2282
|
proofType: proof.proofType,
|
|
@@ -1506,8 +2332,8 @@ async function verifyAttestation(params) {
|
|
|
1506
2332
|
const result = await verifyProof({
|
|
1507
2333
|
proof,
|
|
1508
2334
|
provider: params.provider,
|
|
1509
|
-
|
|
1510
|
-
|
|
2335
|
+
expectedSubjectDid: params.context?.subjectDid,
|
|
2336
|
+
expectedControllerDid: params.context?.controllerDid
|
|
1511
2337
|
});
|
|
1512
2338
|
checks[proof.proofType] = result.valid;
|
|
1513
2339
|
if (!result.valid) {
|
|
@@ -1612,6 +2438,38 @@ async function callControllerWitness(params) {
|
|
|
1612
2438
|
method: "did-json"
|
|
1613
2439
|
};
|
|
1614
2440
|
}
|
|
2441
|
+
var DEFAULT_CONTROLLER_WITNESS_URL = "https://api.omatrust.org/v1/controller-witness";
|
|
2442
|
+
async function requestControllerWitness(params) {
|
|
2443
|
+
const url = params.gatewayUrl ?? DEFAULT_CONTROLLER_WITNESS_URL;
|
|
2444
|
+
const body = {
|
|
2445
|
+
subjectDid: params.subjectDid,
|
|
2446
|
+
controllerDid: params.controllerDid
|
|
2447
|
+
};
|
|
2448
|
+
if (params.chainId !== void 0) {
|
|
2449
|
+
body.chainId = params.chainId;
|
|
2450
|
+
}
|
|
2451
|
+
let response;
|
|
2452
|
+
try {
|
|
2453
|
+
response = await fetch(url, {
|
|
2454
|
+
method: "POST",
|
|
2455
|
+
headers: { "Content-Type": "application/json" },
|
|
2456
|
+
credentials: "include",
|
|
2457
|
+
body: JSON.stringify(body),
|
|
2458
|
+
signal: AbortSignal.timeout(params.timeoutMs ?? 15e3)
|
|
2459
|
+
});
|
|
2460
|
+
} catch (err) {
|
|
2461
|
+
throw new OmaTrustError("NETWORK_ERROR", "Controller witness request failed", { err });
|
|
2462
|
+
}
|
|
2463
|
+
if (!response.ok) {
|
|
2464
|
+
const errorBody = await response.json().catch(() => ({ error: response.statusText }));
|
|
2465
|
+
throw new OmaTrustError(
|
|
2466
|
+
"API_ERROR",
|
|
2467
|
+
errorBody.error ?? `Controller witness failed: ${response.status}`,
|
|
2468
|
+
{ status: response.status, ...errorBody }
|
|
2469
|
+
);
|
|
2470
|
+
}
|
|
2471
|
+
return await response.json();
|
|
2472
|
+
}
|
|
1615
2473
|
|
|
1616
2474
|
// src/reputation/proof/tx-interaction.ts
|
|
1617
2475
|
init_errors();
|
|
@@ -1758,29 +2616,20 @@ async function verifyDnsTxtControllerDid2(domain, expectedControllerDid, options
|
|
|
1758
2616
|
);
|
|
1759
2617
|
}
|
|
1760
2618
|
|
|
1761
|
-
// src/reputation/
|
|
2619
|
+
// src/reputation/subject-ownership.ts
|
|
1762
2620
|
init_did();
|
|
1763
2621
|
init_errors();
|
|
1764
2622
|
init_dns_txt_shared();
|
|
2623
|
+
|
|
2624
|
+
// src/reputation/contract-ownership.ts
|
|
2625
|
+
init_did();
|
|
1765
2626
|
var EIP1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
|
|
1766
2627
|
var OWNERSHIP_PATTERNS = [
|
|
1767
2628
|
{ method: "owner", signature: "function owner() view returns (address)" },
|
|
1768
2629
|
{ method: "admin", signature: "function admin() view returns (address)" },
|
|
1769
2630
|
{ method: "getOwner", signature: "function getOwner() view returns (address)" }
|
|
1770
2631
|
];
|
|
1771
|
-
function
|
|
1772
|
-
const normalized = normalizeDid(input);
|
|
1773
|
-
if (extractDidMethod(normalized) !== "pkh") {
|
|
1774
|
-
throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
|
|
1775
|
-
connectedWalletDid: input
|
|
1776
|
-
});
|
|
1777
|
-
}
|
|
1778
|
-
return normalized;
|
|
1779
|
-
}
|
|
1780
|
-
function normalizeSubjectDid(input) {
|
|
1781
|
-
return normalizeDid(input);
|
|
1782
|
-
}
|
|
1783
|
-
async function readAddressFromContract(provider, contractAddress, signature, method) {
|
|
2632
|
+
async function readOwnerFromContract(provider, contractAddress, signature, method) {
|
|
1784
2633
|
try {
|
|
1785
2634
|
const iface = new ethers.Interface([signature]);
|
|
1786
2635
|
const data = iface.encodeFunctionData(method, []);
|
|
@@ -1793,16 +2642,24 @@ async function readAddressFromContract(provider, contractAddress, signature, met
|
|
|
1793
2642
|
}
|
|
1794
2643
|
return null;
|
|
1795
2644
|
}
|
|
1796
|
-
async function
|
|
2645
|
+
async function discoverContractOwner(provider, contractAddress) {
|
|
2646
|
+
try {
|
|
2647
|
+
const code = await provider.getCode(contractAddress);
|
|
2648
|
+
if (code === "0x" || code === "0x0") {
|
|
2649
|
+
return null;
|
|
2650
|
+
}
|
|
2651
|
+
} catch {
|
|
2652
|
+
return null;
|
|
2653
|
+
}
|
|
1797
2654
|
for (const pattern of OWNERSHIP_PATTERNS) {
|
|
1798
|
-
const address = await
|
|
2655
|
+
const address = await readOwnerFromContract(
|
|
1799
2656
|
provider,
|
|
1800
2657
|
contractAddress,
|
|
1801
2658
|
pattern.signature,
|
|
1802
2659
|
pattern.method
|
|
1803
2660
|
);
|
|
1804
2661
|
if (address) {
|
|
1805
|
-
return
|
|
2662
|
+
return address;
|
|
1806
2663
|
}
|
|
1807
2664
|
}
|
|
1808
2665
|
try {
|
|
@@ -1810,13 +2667,31 @@ async function discoverControllingWallet(provider, contractAddress, chainId) {
|
|
|
1810
2667
|
if (adminValue && adminValue !== "0x" && adminValue !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
1811
2668
|
const adminAddress = ethers.getAddress(`0x${adminValue.slice(-40)}`);
|
|
1812
2669
|
if (adminAddress !== ethers.ZeroAddress) {
|
|
1813
|
-
return
|
|
2670
|
+
return adminAddress;
|
|
1814
2671
|
}
|
|
1815
2672
|
}
|
|
1816
2673
|
} catch {
|
|
1817
2674
|
}
|
|
1818
2675
|
return null;
|
|
1819
2676
|
}
|
|
2677
|
+
async function discoverControllingWalletDid(provider, contractAddress, chainId) {
|
|
2678
|
+
const owner = await discoverContractOwner(provider, contractAddress);
|
|
2679
|
+
return owner ? buildEvmDidPkh(chainId, owner) : null;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
// src/reputation/subject-ownership.ts
|
|
2683
|
+
function assertConnectedWalletDid(input) {
|
|
2684
|
+
const normalized = normalizeDid(input);
|
|
2685
|
+
if (extractDidMethod(normalized) !== "pkh") {
|
|
2686
|
+
throw new OmaTrustError("INVALID_INPUT", "connectedWalletDid must be a did:pkh DID", {
|
|
2687
|
+
connectedWalletDid: input
|
|
2688
|
+
});
|
|
2689
|
+
}
|
|
2690
|
+
return normalized;
|
|
2691
|
+
}
|
|
2692
|
+
function normalizeSubjectDid(input) {
|
|
2693
|
+
return normalizeDid(input);
|
|
2694
|
+
}
|
|
1820
2695
|
async function verifyDidWebOwnership(params) {
|
|
1821
2696
|
const subjectDid = normalizeSubjectDid(params.subjectDid);
|
|
1822
2697
|
const connectedWalletDid = assertConnectedWalletDid(params.connectedWalletDid);
|
|
@@ -1914,7 +2789,7 @@ async function verifyDidPkhOwnership(params) {
|
|
|
1914
2789
|
connectedWalletDid
|
|
1915
2790
|
};
|
|
1916
2791
|
}
|
|
1917
|
-
const controllingWalletDid = await
|
|
2792
|
+
const controllingWalletDid = await discoverControllingWalletDid(params.provider, subjectAddress, chainId);
|
|
1918
2793
|
if (params.txHash) {
|
|
1919
2794
|
if (!controllingWalletDid) {
|
|
1920
2795
|
return {
|
|
@@ -1997,7 +2872,7 @@ async function verifyDidPkhOwnership(params) {
|
|
|
1997
2872
|
};
|
|
1998
2873
|
}
|
|
1999
2874
|
for (const pattern of OWNERSHIP_PATTERNS) {
|
|
2000
|
-
const ownerAddress = await
|
|
2875
|
+
const ownerAddress = await readOwnerFromContract(
|
|
2001
2876
|
params.provider,
|
|
2002
2877
|
subjectAddress,
|
|
2003
2878
|
pattern.signature,
|
|
@@ -2091,8 +2966,9 @@ exports.createX402ReceiptProof = createX402ReceiptProof;
|
|
|
2091
2966
|
exports.decodeAttestationData = decodeAttestationData;
|
|
2092
2967
|
exports.deduplicateReviews = deduplicateReviews;
|
|
2093
2968
|
exports.encodeAttestationData = encodeAttestationData;
|
|
2094
|
-
exports.
|
|
2969
|
+
exports.extractEvmAddressesFromDidDocument = extractEvmAddressesFromDidDocument;
|
|
2095
2970
|
exports.extractExpirationTime = extractExpirationTime;
|
|
2971
|
+
exports.extractJwksFromDidDocument = extractJwksFromDidDocument;
|
|
2096
2972
|
exports.fetchDidDocument = fetchDidDocument;
|
|
2097
2973
|
exports.formatSchemaUid = formatSchemaUid;
|
|
2098
2974
|
exports.formatTransferAmount = formatTransferAmount;
|
|
@@ -2113,6 +2989,7 @@ exports.listAttestations = listAttestations;
|
|
|
2113
2989
|
exports.normalizeSchema = normalizeSchema;
|
|
2114
2990
|
exports.parseDnsTxtRecord = parseDnsTxtRecord;
|
|
2115
2991
|
exports.prepareDelegatedAttestation = prepareDelegatedAttestation;
|
|
2992
|
+
exports.requestControllerWitness = requestControllerWitness;
|
|
2116
2993
|
exports.revokeAttestation = revokeAttestation;
|
|
2117
2994
|
exports.schemaToString = schemaToString;
|
|
2118
2995
|
exports.splitSignature = splitSignature;
|