@le-space/aleph-bootstrap 0.6.24 → 0.6.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/index.d.ts +3 -11
- package/index.js +103 -73
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,20 +27,21 @@ The package defaults to the shared relay-bootstrap namespace:
|
|
|
27
27
|
|
|
28
28
|
- channel: `simple-todo`
|
|
29
29
|
- ref: `simple-todo-bootstrap`
|
|
30
|
-
- post type: `relay-bootstrap`
|
|
30
|
+
- post type: `relay-bootstrap-v2`
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
The channel and ref are overrideable per app or environment. The post type is
|
|
33
|
+
fixed to `relay-bootstrap-v2`.
|
|
33
34
|
|
|
34
35
|
## Discovery Trust Modes
|
|
35
36
|
|
|
36
|
-
The package accepts
|
|
37
|
+
The package accepts two signing modes for v2 records:
|
|
37
38
|
|
|
38
|
-
-
|
|
39
|
+
- wallet-signed bootstrap posts
|
|
39
40
|
- dual-key-attested bootstrap posts
|
|
40
41
|
|
|
41
42
|
By default, discovery will:
|
|
42
43
|
|
|
43
|
-
- accept
|
|
44
|
+
- accept wallet-signed v2 posts
|
|
44
45
|
- verify dual-key records when they are present
|
|
45
46
|
- ignore malformed or invalid dual-key records
|
|
46
47
|
|
package/index.d.ts
CHANGED
|
@@ -3,8 +3,7 @@ import { bootstrap } from '@libp2p/bootstrap';
|
|
|
3
3
|
declare const DEFAULT_ALEPH_API_HOST = "https://api.aleph.im";
|
|
4
4
|
declare const DEFAULT_ALEPH_BOOTSTRAP_CHANNEL = "simple-todo";
|
|
5
5
|
declare const DEFAULT_ALEPH_BOOTSTRAP_REF = "simple-todo-bootstrap";
|
|
6
|
-
declare const DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap";
|
|
7
|
-
declare const DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE = "relay-bootstrap-v2";
|
|
6
|
+
declare const DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap-v2";
|
|
8
7
|
declare const DEFAULT_BOOTSTRAP_MAX_AGE_MS: number;
|
|
9
8
|
declare const DEFAULT_BOOTSTRAP_PAGINATION = 50;
|
|
10
9
|
declare const DEFAULT_BOOTSTRAP_MAX_PAGES = 5;
|
|
@@ -80,8 +79,6 @@ interface DiscoverAlephBootstrapOptions {
|
|
|
80
79
|
apiHost?: string;
|
|
81
80
|
channel?: string;
|
|
82
81
|
ref?: string;
|
|
83
|
-
postType?: string;
|
|
84
|
-
postTypes?: string[];
|
|
85
82
|
page?: number;
|
|
86
83
|
pagination?: number;
|
|
87
84
|
maxPages?: number;
|
|
@@ -104,19 +101,17 @@ interface CreateRelayBootstrapPostOptions {
|
|
|
104
101
|
registrationId?: string;
|
|
105
102
|
ref?: string;
|
|
106
103
|
channel?: string;
|
|
107
|
-
postType?: string;
|
|
108
104
|
profile?: string;
|
|
109
105
|
version?: string;
|
|
110
106
|
ownerAddress?: string;
|
|
111
107
|
publisherAddress?: string;
|
|
112
108
|
authorization?: RelayBootstrapAuthorizationRecord;
|
|
113
109
|
relayProof?: RelayBootstrapProofRecord;
|
|
114
|
-
compact?: boolean;
|
|
115
110
|
compactMultiaddrLimit?: number;
|
|
116
111
|
now?: number;
|
|
117
112
|
hasher: (payload: string) => Promise<string> | string;
|
|
118
113
|
}
|
|
119
|
-
type RelayBootstrapTrustMode = "
|
|
114
|
+
type RelayBootstrapTrustMode = "wallet-signed" | "dual-key-attested";
|
|
120
115
|
declare function relayBootstrapMultiaddrsHash(addrs: readonly string[]): string;
|
|
121
116
|
declare function selectCompactRelayBootstrapMultiaddrs(addrs: readonly string[], limit?: number): string[];
|
|
122
117
|
declare function dedupeMultiaddrs(addrs: readonly string[]): string[];
|
|
@@ -129,14 +124,12 @@ declare function buildRelayBootstrapPostContent(args: {
|
|
|
129
124
|
browserMultiaddrs?: string[];
|
|
130
125
|
registrationId?: string;
|
|
131
126
|
ref?: string;
|
|
132
|
-
postType?: string;
|
|
133
127
|
profile?: string;
|
|
134
128
|
version?: string;
|
|
135
129
|
ownerAddress?: string;
|
|
136
130
|
publisherAddress?: string;
|
|
137
131
|
authorization?: RelayBootstrapAuthorizationRecord;
|
|
138
132
|
relayProof?: RelayBootstrapProofRecord;
|
|
139
|
-
compact?: boolean;
|
|
140
133
|
compactMultiaddrLimit?: number;
|
|
141
134
|
now?: number;
|
|
142
135
|
}): RelayBootstrapPostContent;
|
|
@@ -172,7 +165,6 @@ declare function signRelayBootstrapProof(args: {
|
|
|
172
165
|
profile?: string;
|
|
173
166
|
version?: string;
|
|
174
167
|
updatedAt?: number;
|
|
175
|
-
compact?: boolean;
|
|
176
168
|
compactMultiaddrLimit?: number;
|
|
177
169
|
signer: RelayBootstrapProofSigner;
|
|
178
170
|
}): Promise<RelayBootstrapProofRecord>;
|
|
@@ -196,4 +188,4 @@ declare function createLibp2pAlephBootstrap(options?: DiscoverAlephBootstrapOpti
|
|
|
196
188
|
tagName?: string;
|
|
197
189
|
}): Promise<ReturnType<typeof bootstrap>>;
|
|
198
190
|
|
|
199
|
-
export { type CreateRelayBootstrapPostOptions, DEFAULT_ALEPH_API_HOST, DEFAULT_ALEPH_BOOTSTRAP_CHANNEL,
|
|
191
|
+
export { type CreateRelayBootstrapPostOptions, DEFAULT_ALEPH_API_HOST, DEFAULT_ALEPH_BOOTSTRAP_CHANNEL, DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE, DEFAULT_ALEPH_BOOTSTRAP_REF, DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT, DEFAULT_BOOTSTRAP_MAX_AGE_MS, DEFAULT_BOOTSTRAP_MAX_PAGES, DEFAULT_BOOTSTRAP_PAGINATION, type DiscoverAlephBootstrapOptions, type FilterPublicMultiaddrsOptions, RELAY_BOOTSTRAP_SIGNATURE_SCHEME, type RelayBootstrapAuthorizationPayload, type RelayBootstrapAuthorizationRecord, type RelayBootstrapContent, type RelayBootstrapPostContent, type RelayBootstrapPostRecord, type RelayBootstrapProofPayload, type RelayBootstrapProofRecord, type RelayBootstrapProofSigner, type RelayBootstrapTrustMode, type RelayBootstrapVerificationResult, buildRelayBootstrapPostContent, createLibp2pAlephBootstrap, createRelayBootstrapPost, dedupeMultiaddrs, discoverAlephBootstrapMultiaddrs, fetchAlephBootstrapPosts, filterPublicMultiaddrs, isPublicMultiaddr, relayBootstrapMultiaddrsHash, relayBootstrapTrustMode, selectCompactRelayBootstrapMultiaddrs, selectCurrentRelayBootstrapPosts, signRelayBootstrapAuthorization, signRelayBootstrapProof, verifyRelayBootstrapAuthorization, verifyRelayBootstrapDualKeyContent, verifyRelayBootstrapProof };
|
package/index.js
CHANGED
|
@@ -4,8 +4,7 @@ import { keccak256, recoverMessageAddress, stringToHex } from "viem";
|
|
|
4
4
|
var DEFAULT_ALEPH_API_HOST = "https://api.aleph.im";
|
|
5
5
|
var DEFAULT_ALEPH_BOOTSTRAP_CHANNEL = "simple-todo";
|
|
6
6
|
var DEFAULT_ALEPH_BOOTSTRAP_REF = "simple-todo-bootstrap";
|
|
7
|
-
var DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap";
|
|
8
|
-
var DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE = "relay-bootstrap-v2";
|
|
7
|
+
var DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap-v2";
|
|
9
8
|
var DEFAULT_BOOTSTRAP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
10
9
|
var DEFAULT_BOOTSTRAP_PAGINATION = 50;
|
|
11
10
|
var DEFAULT_BOOTSTRAP_MAX_PAGES = 5;
|
|
@@ -40,9 +39,6 @@ function serializeRelayProofPayload(payload) {
|
|
|
40
39
|
function relayBootstrapMultiaddrsHash(addrs) {
|
|
41
40
|
return keccak256(stringToHex(JSON.stringify(dedupeMultiaddrs(addrs))));
|
|
42
41
|
}
|
|
43
|
-
function compactPostType(postType) {
|
|
44
|
-
return (postType ?? DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE) === DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
|
|
45
|
-
}
|
|
46
42
|
function browserMultiaddrPreference(addr) {
|
|
47
43
|
const normalized = addr.toLowerCase();
|
|
48
44
|
if (normalized.includes("/tcp/443/tls/ws/")) return 0;
|
|
@@ -56,7 +52,8 @@ function browserMultiaddrTransport(addr) {
|
|
|
56
52
|
const normalized = addr.toLowerCase();
|
|
57
53
|
if (normalized.includes("/webtransport/")) return "webtransport";
|
|
58
54
|
if (normalized.includes("/webrtc-direct/")) return "webrtc";
|
|
59
|
-
if (normalized.includes("/ws") || normalized.includes("/wss"))
|
|
55
|
+
if (normalized.includes("/ws") || normalized.includes("/wss"))
|
|
56
|
+
return "websocket";
|
|
60
57
|
return "other";
|
|
61
58
|
}
|
|
62
59
|
function selectCompactRelayBootstrapMultiaddrs(addrs, limit = DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT) {
|
|
@@ -139,7 +136,10 @@ function hasPeerId(addr) {
|
|
|
139
136
|
}
|
|
140
137
|
function isBrowserDialableMultiaddr(addr) {
|
|
141
138
|
const normalized = addr.toLowerCase();
|
|
142
|
-
|
|
139
|
+
if (normalized.includes("/webtransport") || normalized.includes("/webrtc-direct")) {
|
|
140
|
+
return normalized.includes("/certhash/");
|
|
141
|
+
}
|
|
142
|
+
return normalized.includes("/ws") || normalized.includes("/wss");
|
|
143
143
|
}
|
|
144
144
|
function dedupeMultiaddrs(addrs) {
|
|
145
145
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -185,8 +185,12 @@ function normalizeRelayBootstrapContent(value) {
|
|
|
185
185
|
const peerId = asTrimmedString(content.peerId);
|
|
186
186
|
const updatedAt = asNumber(content.updatedAt);
|
|
187
187
|
if (!peerId || updatedAt == null) return null;
|
|
188
|
-
const multiaddrs = Array.isArray(content.multiaddrs) ? content.multiaddrs.filter(
|
|
189
|
-
|
|
188
|
+
const multiaddrs = Array.isArray(content.multiaddrs) ? content.multiaddrs.filter(
|
|
189
|
+
(entry) => typeof entry === "string"
|
|
190
|
+
) : [];
|
|
191
|
+
const browserMultiaddrs = Array.isArray(content.browserMultiaddrs) ? content.browserMultiaddrs.filter(
|
|
192
|
+
(entry) => typeof entry === "string"
|
|
193
|
+
) : void 0;
|
|
190
194
|
return {
|
|
191
195
|
peerId,
|
|
192
196
|
multiaddrs: dedupeMultiaddrs(multiaddrs),
|
|
@@ -239,8 +243,12 @@ function normalizeRelayBootstrapProofPayload(value) {
|
|
|
239
243
|
const peerId = asTrimmedString(payload.peerId);
|
|
240
244
|
const updatedAt = asNumber(payload.updatedAt);
|
|
241
245
|
if (!peerId || updatedAt == null) return null;
|
|
242
|
-
const multiaddrs = Array.isArray(payload.multiaddrs) ? payload.multiaddrs.filter(
|
|
243
|
-
|
|
246
|
+
const multiaddrs = Array.isArray(payload.multiaddrs) ? payload.multiaddrs.filter(
|
|
247
|
+
(entry) => typeof entry === "string"
|
|
248
|
+
) : [];
|
|
249
|
+
const browserMultiaddrs = Array.isArray(payload.browserMultiaddrs) ? payload.browserMultiaddrs.filter(
|
|
250
|
+
(entry) => typeof entry === "string"
|
|
251
|
+
) : void 0;
|
|
244
252
|
return {
|
|
245
253
|
peerId,
|
|
246
254
|
multiaddrs: multiaddrs.length > 0 ? dedupeMultiaddrs(multiaddrs) : void 0,
|
|
@@ -278,8 +286,6 @@ function normalizeRelayBootstrapPostRecord(value) {
|
|
|
278
286
|
function buildRelayBootstrapPostContent(args) {
|
|
279
287
|
const now = args.now ?? Date.now() / 1e3;
|
|
280
288
|
const updatedAt = args.now ?? Date.now();
|
|
281
|
-
const postType = args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE;
|
|
282
|
-
const compact = args.compact ?? compactPostType(postType);
|
|
283
289
|
const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
|
|
284
290
|
const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
|
|
285
291
|
browserDialableOnly: true
|
|
@@ -289,13 +295,13 @@ function buildRelayBootstrapPostContent(args) {
|
|
|
289
295
|
args.compactMultiaddrLimit
|
|
290
296
|
);
|
|
291
297
|
return {
|
|
292
|
-
type:
|
|
298
|
+
type: DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
|
|
293
299
|
address: args.sender,
|
|
294
300
|
...args.ref ? { ref: args.ref } : {},
|
|
295
301
|
content: {
|
|
296
302
|
peerId: args.peerId,
|
|
297
|
-
multiaddrs:
|
|
298
|
-
browserMultiaddrs:
|
|
303
|
+
multiaddrs: compactMultiaddrs,
|
|
304
|
+
browserMultiaddrs: void 0,
|
|
299
305
|
registrationId: args.registrationId,
|
|
300
306
|
profile: args.profile,
|
|
301
307
|
version: args.version,
|
|
@@ -318,14 +324,12 @@ async function createRelayBootstrapPost(args) {
|
|
|
318
324
|
browserMultiaddrs: args.browserMultiaddrs,
|
|
319
325
|
registrationId: args.registrationId,
|
|
320
326
|
ref: args.ref ?? DEFAULT_ALEPH_BOOTSTRAP_REF,
|
|
321
|
-
postType: args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
|
|
322
327
|
profile: args.profile,
|
|
323
328
|
version: args.version,
|
|
324
329
|
ownerAddress: args.ownerAddress,
|
|
325
330
|
publisherAddress: args.publisherAddress,
|
|
326
331
|
authorization: args.authorization,
|
|
327
332
|
relayProof: args.relayProof,
|
|
328
|
-
compact: args.compact,
|
|
329
333
|
compactMultiaddrLimit: args.compactMultiaddrLimit,
|
|
330
334
|
now: nowMillis
|
|
331
335
|
});
|
|
@@ -347,7 +351,7 @@ function relayBootstrapTrustMode(content) {
|
|
|
347
351
|
if (content?.authorization && content?.relayProof && content.authorization.payload.publisherAddress && content.relayProof.payload.peerId) {
|
|
348
352
|
return "dual-key-attested";
|
|
349
353
|
}
|
|
350
|
-
return "
|
|
354
|
+
return "wallet-signed";
|
|
351
355
|
}
|
|
352
356
|
async function signRelayBootstrapAuthorization(args) {
|
|
353
357
|
const payload = {
|
|
@@ -378,12 +382,11 @@ async function signRelayBootstrapProof(args) {
|
|
|
378
382
|
fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
|
|
379
383
|
args.compactMultiaddrLimit
|
|
380
384
|
);
|
|
381
|
-
const compact = args.compact ?? false;
|
|
382
385
|
const payload = {
|
|
383
386
|
peerId: args.peerId,
|
|
384
|
-
multiaddrs:
|
|
385
|
-
browserMultiaddrs:
|
|
386
|
-
multiaddrsHash:
|
|
387
|
+
multiaddrs: void 0,
|
|
388
|
+
browserMultiaddrs: void 0,
|
|
389
|
+
multiaddrsHash: relayBootstrapMultiaddrsHash(compactMultiaddrs),
|
|
387
390
|
registrationId: args.registrationId,
|
|
388
391
|
profile: args.profile,
|
|
389
392
|
version: args.version,
|
|
@@ -404,7 +407,9 @@ async function verifyRelayBootstrapAuthorization(authorization, options = {}) {
|
|
|
404
407
|
return { ok: false, errors };
|
|
405
408
|
}
|
|
406
409
|
if (authorization.scheme !== RELAY_BOOTSTRAP_SIGNATURE_SCHEME) {
|
|
407
|
-
errors.push(
|
|
410
|
+
errors.push(
|
|
411
|
+
`Unsupported owner authorization scheme: ${authorization.scheme}`
|
|
412
|
+
);
|
|
408
413
|
return { ok: false, errors };
|
|
409
414
|
}
|
|
410
415
|
const payload = authorization.payload;
|
|
@@ -415,7 +420,9 @@ async function verifyRelayBootstrapAuthorization(authorization, options = {}) {
|
|
|
415
420
|
authorization.signature
|
|
416
421
|
);
|
|
417
422
|
if (normalizeAddress(recovered) !== normalizeAddress(payload.ownerAddress)) {
|
|
418
|
-
errors.push(
|
|
423
|
+
errors.push(
|
|
424
|
+
"Owner authorization signature does not recover the owner address."
|
|
425
|
+
);
|
|
419
426
|
}
|
|
420
427
|
} catch (error) {
|
|
421
428
|
errors.push(
|
|
@@ -441,9 +448,14 @@ async function verifyRelayBootstrapProof(proof, options = {}) {
|
|
|
441
448
|
const payload = proof.payload;
|
|
442
449
|
const serialized = serializeRelayProofPayload(payload);
|
|
443
450
|
try {
|
|
444
|
-
const recovered = await recoverAddressForSignature(
|
|
451
|
+
const recovered = await recoverAddressForSignature(
|
|
452
|
+
serialized,
|
|
453
|
+
proof.signature
|
|
454
|
+
);
|
|
445
455
|
if (options.expectedPublisherAddress && normalizeAddress(recovered) !== normalizeAddress(options.expectedPublisherAddress)) {
|
|
446
|
-
errors.push(
|
|
456
|
+
errors.push(
|
|
457
|
+
"Relay proof signature does not recover the expected publisher address."
|
|
458
|
+
);
|
|
447
459
|
}
|
|
448
460
|
} catch (error) {
|
|
449
461
|
errors.push(
|
|
@@ -474,7 +486,9 @@ async function verifyRelayBootstrapDualKeyContent(content, options = {}) {
|
|
|
474
486
|
errors.push(...proof.errors);
|
|
475
487
|
if (content.ownerAddress && content.authorization) {
|
|
476
488
|
if (normalizeAddress(content.ownerAddress) !== normalizeAddress(content.authorization.payload.ownerAddress)) {
|
|
477
|
-
errors.push(
|
|
489
|
+
errors.push(
|
|
490
|
+
"Content ownerAddress does not match the owner authorization payload."
|
|
491
|
+
);
|
|
478
492
|
}
|
|
479
493
|
}
|
|
480
494
|
if (expectedPublisherAddress && content.authorization) {
|
|
@@ -485,12 +499,16 @@ async function verifyRelayBootstrapDualKeyContent(content, options = {}) {
|
|
|
485
499
|
}
|
|
486
500
|
}
|
|
487
501
|
if (content.authorization && content.authorization.payload.peerId !== content.peerId) {
|
|
488
|
-
errors.push(
|
|
502
|
+
errors.push(
|
|
503
|
+
"Owner authorization peer ID does not match the bootstrap content peer ID."
|
|
504
|
+
);
|
|
489
505
|
}
|
|
490
506
|
const proofPayload = content.relayProof?.payload;
|
|
491
507
|
if (proofPayload) {
|
|
492
508
|
if (proofPayload.registrationId !== content.registrationId) {
|
|
493
|
-
errors.push(
|
|
509
|
+
errors.push(
|
|
510
|
+
"Relay proof registrationId does not match the bootstrap content."
|
|
511
|
+
);
|
|
494
512
|
}
|
|
495
513
|
if (proofPayload.profile !== content.profile) {
|
|
496
514
|
errors.push("Relay proof profile does not match the bootstrap content.");
|
|
@@ -499,11 +517,15 @@ async function verifyRelayBootstrapDualKeyContent(content, options = {}) {
|
|
|
499
517
|
errors.push("Relay proof version does not match the bootstrap content.");
|
|
500
518
|
}
|
|
501
519
|
if (proofPayload.updatedAt !== content.updatedAt) {
|
|
502
|
-
errors.push(
|
|
520
|
+
errors.push(
|
|
521
|
+
"Relay proof updatedAt does not match the bootstrap content."
|
|
522
|
+
);
|
|
503
523
|
}
|
|
504
524
|
if (proofPayload.multiaddrsHash) {
|
|
505
525
|
if (proofPayload.multiaddrsHash !== relayBootstrapMultiaddrsHash(content.multiaddrs)) {
|
|
506
|
-
errors.push(
|
|
526
|
+
errors.push(
|
|
527
|
+
"Relay proof multiaddrs hash does not match the bootstrap content."
|
|
528
|
+
);
|
|
507
529
|
}
|
|
508
530
|
} else if (JSON.stringify(dedupeMultiaddrs(proofPayload.multiaddrs ?? [])) !== JSON.stringify(dedupeMultiaddrs(content.multiaddrs))) {
|
|
509
531
|
errors.push("Relay proof multiaddrs do not match the bootstrap content.");
|
|
@@ -515,8 +537,12 @@ async function verifyRelayBootstrapDualKeyContent(content, options = {}) {
|
|
|
515
537
|
);
|
|
516
538
|
}
|
|
517
539
|
} else if (proofPayload.browserMultiaddrs || content.browserMultiaddrs) {
|
|
518
|
-
if (JSON.stringify(
|
|
519
|
-
|
|
540
|
+
if (JSON.stringify(
|
|
541
|
+
dedupeMultiaddrs(proofPayload.browserMultiaddrs ?? [])
|
|
542
|
+
) !== JSON.stringify(dedupeMultiaddrs(content.browserMultiaddrs ?? []))) {
|
|
543
|
+
errors.push(
|
|
544
|
+
"Relay proof browserMultiaddrs do not match the bootstrap content."
|
|
545
|
+
);
|
|
520
546
|
}
|
|
521
547
|
}
|
|
522
548
|
}
|
|
@@ -538,10 +564,7 @@ async function fetchAlephBootstrapPosts(options = {}) {
|
|
|
538
564
|
options.channel ?? DEFAULT_ALEPH_BOOTSTRAP_CHANNEL
|
|
539
565
|
);
|
|
540
566
|
url.searchParams.set("refs", options.ref ?? DEFAULT_ALEPH_BOOTSTRAP_REF);
|
|
541
|
-
url.searchParams.set(
|
|
542
|
-
"types",
|
|
543
|
-
options.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE
|
|
544
|
-
);
|
|
567
|
+
url.searchParams.set("types", DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE);
|
|
545
568
|
url.searchParams.set(
|
|
546
569
|
"pagination",
|
|
547
570
|
String(options.pagination ?? DEFAULT_BOOTSTRAP_PAGINATION)
|
|
@@ -555,7 +578,21 @@ async function fetchAlephBootstrapPosts(options = {}) {
|
|
|
555
578
|
throw new Error(`Aleph bootstrap post lookup failed: ${response.status}`);
|
|
556
579
|
}
|
|
557
580
|
const payload = await response.json();
|
|
558
|
-
|
|
581
|
+
const posts = (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null).sort((left, right) => compareRelayBootstrapPostRecency(right, left));
|
|
582
|
+
posts.forEach(assertSupportedRelayBootstrapPost);
|
|
583
|
+
return posts;
|
|
584
|
+
}
|
|
585
|
+
function assertSupportedRelayBootstrapPost(post) {
|
|
586
|
+
if (post.type === "relay-bootstrap") {
|
|
587
|
+
throw new Error(
|
|
588
|
+
"Legacy relay-bootstrap record encountered. Only relay-bootstrap-v2 is supported."
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
if (post.type && post.type !== DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE) {
|
|
592
|
+
throw new Error(
|
|
593
|
+
`Unsupported relay bootstrap post type: ${post.type}. Expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}.`
|
|
594
|
+
);
|
|
595
|
+
}
|
|
559
596
|
}
|
|
560
597
|
function compareRelayBootstrapPostRecency(left, right) {
|
|
561
598
|
const leftUpdatedAt = left.content?.updatedAt ?? 0;
|
|
@@ -577,6 +614,7 @@ function selectCurrentRelayBootstrapPosts(posts, options = {}) {
|
|
|
577
614
|
const now = options.now ?? Date.now();
|
|
578
615
|
const selected = /* @__PURE__ */ new Map();
|
|
579
616
|
for (const post of posts) {
|
|
617
|
+
assertSupportedRelayBootstrapPost(post);
|
|
580
618
|
const content = post.content;
|
|
581
619
|
if (!content) continue;
|
|
582
620
|
if (now - content.updatedAt > maxAgeMs) continue;
|
|
@@ -599,7 +637,7 @@ async function filterTrustedRelayBootstrapPosts(posts, options = {}) {
|
|
|
599
637
|
const content = post.content;
|
|
600
638
|
if (!content) continue;
|
|
601
639
|
const trustMode = relayBootstrapTrustMode(content);
|
|
602
|
-
if (trustMode === "
|
|
640
|
+
if (trustMode === "wallet-signed") {
|
|
603
641
|
if (!requireDualKeyAttestation) {
|
|
604
642
|
trusted.push(post);
|
|
605
643
|
}
|
|
@@ -621,38 +659,31 @@ async function discoverAlephBootstrapMultiaddrs(options = {}) {
|
|
|
621
659
|
const pagination = options.pagination ?? DEFAULT_BOOTSTRAP_PAGINATION;
|
|
622
660
|
const startPage = options.page ?? 1;
|
|
623
661
|
const maxPages = Math.max(1, options.maxPages ?? DEFAULT_BOOTSTRAP_MAX_PAGES);
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
);
|
|
650
|
-
if (addrs.length > 0) {
|
|
651
|
-
return addrs;
|
|
652
|
-
}
|
|
653
|
-
if (pagePosts.length < pagination) {
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
662
|
+
const collectedPosts = [];
|
|
663
|
+
for (let offset = 0; offset < maxPages; offset += 1) {
|
|
664
|
+
const page = startPage + offset;
|
|
665
|
+
const pagePosts = await fetchAlephBootstrapPosts({
|
|
666
|
+
...options,
|
|
667
|
+
page,
|
|
668
|
+
pagination
|
|
669
|
+
});
|
|
670
|
+
collectedPosts.push(...pagePosts);
|
|
671
|
+
const selectedPosts = selectCurrentRelayBootstrapPosts(collectedPosts, {
|
|
672
|
+
maxAgeMs: options.maxAgeMs
|
|
673
|
+
});
|
|
674
|
+
const trustedPosts = await filterTrustedRelayBootstrapPosts(selectedPosts, {
|
|
675
|
+
requireDualKeyAttestation: options.requireDualKeyAttestation,
|
|
676
|
+
verifyDualKeyAttestation: options.verifyDualKeyAttestation
|
|
677
|
+
});
|
|
678
|
+
const addrs = relayBootstrapPostsToMultiaddrs(
|
|
679
|
+
trustedPosts,
|
|
680
|
+
browserDialableOnly
|
|
681
|
+
);
|
|
682
|
+
if (addrs.length > 0) {
|
|
683
|
+
return addrs;
|
|
684
|
+
}
|
|
685
|
+
if (pagePosts.length < pagination) {
|
|
686
|
+
break;
|
|
656
687
|
}
|
|
657
688
|
}
|
|
658
689
|
return [];
|
|
@@ -682,7 +713,6 @@ async function createLibp2pAlephBootstrap(options = {}) {
|
|
|
682
713
|
export {
|
|
683
714
|
DEFAULT_ALEPH_API_HOST,
|
|
684
715
|
DEFAULT_ALEPH_BOOTSTRAP_CHANNEL,
|
|
685
|
-
DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE,
|
|
686
716
|
DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
|
|
687
717
|
DEFAULT_ALEPH_BOOTSTRAP_REF,
|
|
688
718
|
DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT,
|