@waku/core 0.0.36-f911bf8.0 → 0.0.37-7a9850d.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/CHANGELOG.md +39 -0
- package/bundle/index.js +1147 -613
- package/bundle/lib/message/version_0.js +1 -2
- package/bundle/{version_0-CiYGrPc2.js → version_0-9DPFjcJG.js} +1586 -10
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/connection_manager/connection_manager.d.ts +2 -1
- package/dist/lib/connection_manager/connection_manager.js +16 -8
- package/dist/lib/connection_manager/connection_manager.js.map +1 -1
- package/dist/lib/filter/filter.d.ts +8 -5
- package/dist/lib/filter/filter.js +33 -10
- package/dist/lib/filter/filter.js.map +1 -1
- package/dist/lib/light_push/light_push.d.ts +4 -3
- package/dist/lib/light_push/light_push.js +6 -4
- package/dist/lib/light_push/light_push.js.map +1 -1
- package/dist/lib/message/version_0.d.ts +3 -4
- package/dist/lib/message/version_0.js +1 -4
- package/dist/lib/message/version_0.js.map +1 -1
- package/dist/lib/message_hash/index.d.ts +1 -0
- package/dist/lib/message_hash/index.js +2 -0
- package/dist/lib/message_hash/index.js.map +1 -0
- package/dist/lib/message_hash/message_hash.d.ts +52 -0
- package/dist/lib/message_hash/message_hash.js +84 -0
- package/dist/lib/message_hash/message_hash.js.map +1 -0
- package/dist/lib/metadata/metadata.js +6 -4
- package/dist/lib/metadata/metadata.js.map +1 -1
- package/dist/lib/store/rpc.js +16 -10
- package/dist/lib/store/rpc.js.map +1 -1
- package/dist/lib/store/store.d.ts +5 -5
- package/dist/lib/store/store.js +19 -9
- package/dist/lib/store/store.js.map +1 -1
- package/dist/lib/stream_manager/stream_manager.d.ts +3 -4
- package/dist/lib/stream_manager/stream_manager.js +6 -8
- package/dist/lib/stream_manager/stream_manager.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/lib/connection_manager/connection_manager.ts +24 -16
- package/src/lib/filter/filter.ts +50 -14
- package/src/lib/light_push/light_push.ts +8 -5
- package/src/lib/message/version_0.ts +3 -7
- package/src/lib/message_hash/index.ts +1 -0
- package/src/lib/message_hash/message_hash.ts +106 -0
- package/src/lib/metadata/metadata.ts +8 -5
- package/src/lib/store/rpc.ts +23 -19
- package/src/lib/store/store.ts +22 -11
- package/src/lib/stream_manager/stream_manager.ts +8 -6
- package/bundle/base_protocol-DvQrudwy.js +0 -152
- package/bundle/index-CTo1my9M.js +0 -1543
- package/bundle/lib/base_protocol.js +0 -2
- package/dist/lib/base_protocol.d.ts +0 -18
- package/dist/lib/base_protocol.js +0 -25
- package/dist/lib/base_protocol.js.map +0 -1
- package/src/lib/base_protocol.ts +0 -44
package/bundle/index.js
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
import { v as version_0,
|
2
|
-
export {
|
3
|
-
import { e as equals$2, c as coerce, b as base32, a as base58btc, d as base36, L as Logger, P as ProtocolError, T as Tags, E as EPeersByDiscoveryEvents, f as EConnectionStateEvents } from './index-CTo1my9M.js';
|
4
|
-
import { B as BaseProtocol } from './base_protocol-DvQrudwy.js';
|
5
|
-
export { S as StreamManager } from './base_protocol-DvQrudwy.js';
|
1
|
+
import { e as equals$2, c as coerce, b as base32, a as base58btc, d as base36, v as version_0, f as allocUnsafe, g as alloc, h as encodingLength$1, i as encode$2, j as decode$4, L as Logger, F as FilterSubscribeRequest, k as FilterSubscribeResponse$1, M as MessagePush, P as ProtocolError, l as PushRpc$1, m as PushResponse, S as StoreQueryRequest$1, n as StoreQueryResponse$1, o as bases, t as toString, p as fromString, q as base64url, r as encodeUint8Array, u as utf8ToBytes, s as createEncoder, w as pubsubTopicToSingleShardInfo, x as bytesToUtf8, T as Tags, E as EPeersByDiscoveryEvents, y as shardInfoToPubsubTopics, z as EConnectionStateEvents, W as WakuMetadataRequest, A as pubsubTopicsToShardInfo, B as WakuMetadataResponse, C as concat$1, D as sha256, G as bytesToHex, H as numberToBytes } from './version_0-9DPFjcJG.js';
|
2
|
+
export { I as createDecoder } from './version_0-9DPFjcJG.js';
|
6
3
|
|
7
4
|
/* eslint-disable */
|
8
5
|
var encode_1 = encode$1;
|
@@ -514,6 +511,10 @@ function encodeCID(version, code, multihash) {
|
|
514
511
|
}
|
515
512
|
const cidSymbol = Symbol.for('@ipld/js-cid/CID');
|
516
513
|
|
514
|
+
function isDefined(value) {
|
515
|
+
return Boolean(value);
|
516
|
+
}
|
517
|
+
|
517
518
|
const MB = 1024 ** 2;
|
518
519
|
const SIZE_CAP_IN_MB = 1;
|
519
520
|
/**
|
@@ -2008,25 +2009,38 @@ function queuelessPushable() {
|
|
2008
2009
|
function isAsyncIterable$1(thing) {
|
2009
2010
|
return thing[Symbol.asyncIterator] != null;
|
2010
2011
|
}
|
2011
|
-
async function addAllToPushable(sources, output) {
|
2012
|
+
async function addAllToPushable(sources, output, signal) {
|
2012
2013
|
try {
|
2013
2014
|
await Promise.all(sources.map(async (source) => {
|
2014
2015
|
for await (const item of source) {
|
2015
|
-
await output.push(item
|
2016
|
+
await output.push(item, {
|
2017
|
+
signal
|
2018
|
+
});
|
2019
|
+
signal.throwIfAborted();
|
2016
2020
|
}
|
2017
2021
|
}));
|
2018
|
-
await output.end(
|
2022
|
+
await output.end(undefined, {
|
2023
|
+
signal
|
2024
|
+
});
|
2019
2025
|
}
|
2020
2026
|
catch (err) {
|
2021
|
-
await output.end(err
|
2027
|
+
await output.end(err, {
|
2028
|
+
signal
|
2029
|
+
})
|
2022
2030
|
.catch(() => { });
|
2023
2031
|
}
|
2024
2032
|
}
|
2025
2033
|
async function* mergeSources(sources) {
|
2034
|
+
const controller = new AbortController();
|
2026
2035
|
const output = queuelessPushable();
|
2027
|
-
addAllToPushable(sources, output)
|
2036
|
+
addAllToPushable(sources, output, controller.signal)
|
2028
2037
|
.catch(() => { });
|
2029
|
-
|
2038
|
+
try {
|
2039
|
+
yield* output;
|
2040
|
+
}
|
2041
|
+
finally {
|
2042
|
+
controller.abort();
|
2043
|
+
}
|
2030
2044
|
}
|
2031
2045
|
function* mergeSyncSources(syncSources) {
|
2032
2046
|
for (const source of syncSources) {
|
@@ -2132,6 +2146,129 @@ const duplexPipelineFn = (duplex) => {
|
|
2132
2146
|
};
|
2133
2147
|
};
|
2134
2148
|
|
2149
|
+
function selectOpenConnection(connections) {
|
2150
|
+
return connections
|
2151
|
+
.filter((c) => c.status === "open")
|
2152
|
+
.sort((left, right) => right.timeline.open - left.timeline.open)
|
2153
|
+
.at(0);
|
2154
|
+
}
|
2155
|
+
|
2156
|
+
const STREAM_LOCK_KEY = "consumed";
|
2157
|
+
class StreamManager {
|
2158
|
+
multicodec;
|
2159
|
+
libp2p;
|
2160
|
+
log;
|
2161
|
+
ongoingCreation = new Set();
|
2162
|
+
streamPool = new Map();
|
2163
|
+
constructor(multicodec, libp2p) {
|
2164
|
+
this.multicodec = multicodec;
|
2165
|
+
this.libp2p = libp2p;
|
2166
|
+
this.log = new Logger(`stream-manager:${multicodec}`);
|
2167
|
+
this.libp2p.events.addEventListener("peer:update", this.handlePeerUpdateStreamPool);
|
2168
|
+
}
|
2169
|
+
async getStream(peerId) {
|
2170
|
+
const peerIdStr = peerId.toString();
|
2171
|
+
const scheduledStream = this.streamPool.get(peerIdStr);
|
2172
|
+
if (scheduledStream) {
|
2173
|
+
this.streamPool.delete(peerIdStr);
|
2174
|
+
await scheduledStream;
|
2175
|
+
}
|
2176
|
+
let stream = this.getOpenStreamForCodec(peerId);
|
2177
|
+
if (stream) {
|
2178
|
+
this.log.info(`Found existing stream peerId=${peerIdStr} multicodec=${this.multicodec}`);
|
2179
|
+
this.lockStream(peerIdStr, stream);
|
2180
|
+
return stream;
|
2181
|
+
}
|
2182
|
+
stream = await this.createStream(peerId);
|
2183
|
+
this.lockStream(peerIdStr, stream);
|
2184
|
+
return stream;
|
2185
|
+
}
|
2186
|
+
async createStream(peerId, retries = 0) {
|
2187
|
+
const connections = this.libp2p.connectionManager.getConnections(peerId);
|
2188
|
+
const connection = selectOpenConnection(connections);
|
2189
|
+
if (!connection) {
|
2190
|
+
throw new Error(`Failed to get a connection to the peer peerId=${peerId.toString()} multicodec=${this.multicodec}`);
|
2191
|
+
}
|
2192
|
+
let lastError;
|
2193
|
+
let stream;
|
2194
|
+
for (let i = 0; i < retries + 1; i++) {
|
2195
|
+
try {
|
2196
|
+
this.log.info(`Attempting to create a stream for peerId=${peerId.toString()} multicodec=${this.multicodec}`);
|
2197
|
+
stream = await connection.newStream(this.multicodec);
|
2198
|
+
this.log.info(`Created stream for peerId=${peerId.toString()} multicodec=${this.multicodec}`);
|
2199
|
+
break;
|
2200
|
+
}
|
2201
|
+
catch (error) {
|
2202
|
+
lastError = error;
|
2203
|
+
}
|
2204
|
+
}
|
2205
|
+
if (!stream) {
|
2206
|
+
throw new Error(`Failed to create a new stream for ${peerId.toString()} -- ` + lastError);
|
2207
|
+
}
|
2208
|
+
return stream;
|
2209
|
+
}
|
2210
|
+
async createStreamWithLock(peer) {
|
2211
|
+
const peerId = peer.id.toString();
|
2212
|
+
if (this.ongoingCreation.has(peerId)) {
|
2213
|
+
this.log.info(`Skipping creation of a stream due to lock for peerId=${peerId} multicodec=${this.multicodec}`);
|
2214
|
+
return;
|
2215
|
+
}
|
2216
|
+
try {
|
2217
|
+
this.ongoingCreation.add(peerId);
|
2218
|
+
await this.createStream(peer.id);
|
2219
|
+
}
|
2220
|
+
catch (error) {
|
2221
|
+
this.log.error(`Failed to createStreamWithLock:`, error);
|
2222
|
+
}
|
2223
|
+
finally {
|
2224
|
+
this.ongoingCreation.delete(peerId);
|
2225
|
+
}
|
2226
|
+
return;
|
2227
|
+
}
|
2228
|
+
handlePeerUpdateStreamPool = (evt) => {
|
2229
|
+
const { peer } = evt.detail;
|
2230
|
+
if (!peer.protocols.includes(this.multicodec)) {
|
2231
|
+
return;
|
2232
|
+
}
|
2233
|
+
const stream = this.getOpenStreamForCodec(peer.id);
|
2234
|
+
if (stream) {
|
2235
|
+
return;
|
2236
|
+
}
|
2237
|
+
this.scheduleNewStream(peer);
|
2238
|
+
};
|
2239
|
+
scheduleNewStream(peer) {
|
2240
|
+
this.log.info(`Scheduling creation of a stream for peerId=${peer.id.toString()} multicodec=${this.multicodec}`);
|
2241
|
+
// abandon previous attempt
|
2242
|
+
if (this.streamPool.has(peer.id.toString())) {
|
2243
|
+
this.streamPool.delete(peer.id.toString());
|
2244
|
+
}
|
2245
|
+
this.streamPool.set(peer.id.toString(), this.createStreamWithLock(peer));
|
2246
|
+
}
|
2247
|
+
getOpenStreamForCodec(peerId) {
|
2248
|
+
const connections = this.libp2p.connectionManager.getConnections(peerId);
|
2249
|
+
const connection = selectOpenConnection(connections);
|
2250
|
+
if (!connection) {
|
2251
|
+
return;
|
2252
|
+
}
|
2253
|
+
const stream = connection.streams.find((s) => s.protocol === this.multicodec);
|
2254
|
+
if (!stream) {
|
2255
|
+
return;
|
2256
|
+
}
|
2257
|
+
const isStreamUnusable = ["done", "closed", "closing"].includes(stream.writeStatus || "");
|
2258
|
+
if (isStreamUnusable || this.isStreamLocked(stream)) {
|
2259
|
+
return;
|
2260
|
+
}
|
2261
|
+
return stream;
|
2262
|
+
}
|
2263
|
+
lockStream(peerId, stream) {
|
2264
|
+
this.log.info(`Locking stream for peerId:${peerId}\tstreamId:${stream.id}`);
|
2265
|
+
stream.metadata[STREAM_LOCK_KEY] = true;
|
2266
|
+
}
|
2267
|
+
isStreamLocked(stream) {
|
2268
|
+
return !!stream.metadata[STREAM_LOCK_KEY];
|
2269
|
+
}
|
2270
|
+
}
|
2271
|
+
|
2135
2272
|
// Unique ID creation requires a high quality random # generator. In the browser we therefore
|
2136
2273
|
// require the crypto API and do not support built-in fallback to lower quality random number
|
2137
2274
|
// generators (like Math.random()).
|
@@ -2299,13 +2436,33 @@ const FilterCodecs = {
|
|
2299
2436
|
SUBSCRIBE: "/vac/waku/filter-subscribe/2.0.0-beta1",
|
2300
2437
|
PUSH: "/vac/waku/filter-push/2.0.0-beta1"
|
2301
2438
|
};
|
2302
|
-
class FilterCore
|
2303
|
-
handleIncomingMessage;
|
2439
|
+
class FilterCore {
|
2304
2440
|
pubsubTopics;
|
2441
|
+
streamManager;
|
2442
|
+
static handleIncomingMessage;
|
2443
|
+
multicodec = FilterCodecs.SUBSCRIBE;
|
2305
2444
|
constructor(handleIncomingMessage, pubsubTopics, libp2p) {
|
2306
|
-
super(FilterCodecs.SUBSCRIBE, libp2p.components, pubsubTopics);
|
2307
|
-
this.handleIncomingMessage = handleIncomingMessage;
|
2308
2445
|
this.pubsubTopics = pubsubTopics;
|
2446
|
+
this.streamManager = new StreamManager(FilterCodecs.SUBSCRIBE, libp2p.components);
|
2447
|
+
// TODO(weboko): remove when @waku/sdk 0.0.33 is released
|
2448
|
+
const prevHandler = FilterCore.handleIncomingMessage;
|
2449
|
+
FilterCore.handleIncomingMessage = !prevHandler
|
2450
|
+
? handleIncomingMessage
|
2451
|
+
: async (pubsubTopic, message, peerIdStr) => {
|
2452
|
+
try {
|
2453
|
+
await prevHandler(pubsubTopic, message, peerIdStr);
|
2454
|
+
}
|
2455
|
+
catch (e) {
|
2456
|
+
log$5.error("Previous FilterCore incoming message handler failed ", e);
|
2457
|
+
}
|
2458
|
+
try {
|
2459
|
+
await handleIncomingMessage(pubsubTopic, message, peerIdStr);
|
2460
|
+
}
|
2461
|
+
catch (e) {
|
2462
|
+
log$5.error("Present FilterCore incoming message handler failed ", e);
|
2463
|
+
}
|
2464
|
+
return;
|
2465
|
+
};
|
2309
2466
|
libp2p
|
2310
2467
|
.handle(FilterCodecs.PUSH, this.onRequest.bind(this), {
|
2311
2468
|
maxInboundStreams: 100
|
@@ -2315,11 +2472,14 @@ class FilterCore extends BaseProtocol {
|
|
2315
2472
|
});
|
2316
2473
|
}
|
2317
2474
|
async subscribe(pubsubTopic, peerId, contentTopics) {
|
2318
|
-
const stream = await this.getStream(peerId);
|
2475
|
+
const stream = await this.streamManager.getStream(peerId);
|
2319
2476
|
const request = FilterSubscribeRpc.createSubscribeRequest(pubsubTopic, contentTopics);
|
2320
2477
|
let res;
|
2321
2478
|
try {
|
2322
2479
|
res = await pipe([request.encode()], encode, stream, decode, async (source) => await all(source));
|
2480
|
+
if (!res?.length) {
|
2481
|
+
throw Error("Received no response from subscription request.");
|
2482
|
+
}
|
2323
2483
|
}
|
2324
2484
|
catch (error) {
|
2325
2485
|
log$5.error("Failed to send subscribe request", error);
|
@@ -2350,7 +2510,7 @@ class FilterCore extends BaseProtocol {
|
|
2350
2510
|
async unsubscribe(pubsubTopic, peerId, contentTopics) {
|
2351
2511
|
let stream;
|
2352
2512
|
try {
|
2353
|
-
stream = await this.getStream(peerId);
|
2513
|
+
stream = await this.streamManager.getStream(peerId);
|
2354
2514
|
}
|
2355
2515
|
catch (error) {
|
2356
2516
|
log$5.error(`Failed to get a stream for remote peer${peerId.toString()}`, error);
|
@@ -2382,7 +2542,7 @@ class FilterCore extends BaseProtocol {
|
|
2382
2542
|
};
|
2383
2543
|
}
|
2384
2544
|
async unsubscribeAll(pubsubTopic, peerId) {
|
2385
|
-
const stream = await this.getStream(peerId);
|
2545
|
+
const stream = await this.streamManager.getStream(peerId);
|
2386
2546
|
const request = FilterSubscribeRpc.createUnsubscribeAllRequest(pubsubTopic);
|
2387
2547
|
const res = await pipe([request.encode()], encode, stream, decode, async (source) => await all(source));
|
2388
2548
|
if (!res || !res.length) {
|
@@ -2413,7 +2573,7 @@ class FilterCore extends BaseProtocol {
|
|
2413
2573
|
async ping(peerId) {
|
2414
2574
|
let stream;
|
2415
2575
|
try {
|
2416
|
-
stream = await this.getStream(peerId);
|
2576
|
+
stream = await this.streamManager.getStream(peerId);
|
2417
2577
|
}
|
2418
2578
|
catch (error) {
|
2419
2579
|
log$5.error(`Failed to get a stream for remote peer${peerId.toString()}`, error);
|
@@ -2482,7 +2642,7 @@ class FilterCore extends BaseProtocol {
|
|
2482
2642
|
log$5.error("Pubsub topic missing from push message");
|
2483
2643
|
return;
|
2484
2644
|
}
|
2485
|
-
await
|
2645
|
+
await FilterCore.handleIncomingMessage?.(pubsubTopic, wakuMessage, connection.remotePeer.toString());
|
2486
2646
|
}
|
2487
2647
|
}).then(() => {
|
2488
2648
|
log$5.info("Receiving pipe closed.");
|
@@ -2555,11 +2715,13 @@ const LightPushCodec = "/vac/waku/lightpush/2.0.0-beta1";
|
|
2555
2715
|
/**
|
2556
2716
|
* Implements the [Waku v2 Light Push protocol](https://rfc.vac.dev/spec/19/).
|
2557
2717
|
*/
|
2558
|
-
class LightPushCore
|
2718
|
+
class LightPushCore {
|
2559
2719
|
pubsubTopics;
|
2720
|
+
streamManager;
|
2721
|
+
multicodec = LightPushCodec;
|
2560
2722
|
constructor(pubsubTopics, libp2p) {
|
2561
|
-
super(LightPushCodec, libp2p.components, pubsubTopics);
|
2562
2723
|
this.pubsubTopics = pubsubTopics;
|
2724
|
+
this.streamManager = new StreamManager(LightPushCodec, libp2p.components);
|
2563
2725
|
}
|
2564
2726
|
async preparePushMessage(encoder, message) {
|
2565
2727
|
try {
|
@@ -2603,7 +2765,7 @@ class LightPushCore extends BaseProtocol {
|
|
2603
2765
|
}
|
2604
2766
|
let stream;
|
2605
2767
|
try {
|
2606
|
-
stream = await this.getStream(peerId);
|
2768
|
+
stream = await this.streamManager.getStream(peerId);
|
2607
2769
|
}
|
2608
2770
|
catch (error) {
|
2609
2771
|
log$4.error("Failed to get stream", error);
|
@@ -2714,6 +2876,7 @@ class StoreQueryRequest {
|
|
2714
2876
|
static create(params) {
|
2715
2877
|
const request = new StoreQueryRequest({
|
2716
2878
|
...params,
|
2879
|
+
contentTopics: params.contentTopics || [],
|
2717
2880
|
requestId: v4(),
|
2718
2881
|
timeStart: params.timeStart
|
2719
2882
|
? BigInt(params.timeStart.getTime() * ONE_MILLION)
|
@@ -2726,17 +2889,22 @@ class StoreQueryRequest {
|
|
2726
2889
|
? BigInt(params.paginationLimit)
|
2727
2890
|
: undefined
|
2728
2891
|
});
|
2729
|
-
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
2733
|
-
|
2734
|
-
|
2735
|
-
|
2736
|
-
|
2737
|
-
|
2738
|
-
|
2739
|
-
|
2892
|
+
const isHashQuery = params.messageHashes && params.messageHashes.length > 0;
|
2893
|
+
const hasContentTopics = params.contentTopics && params.contentTopics.length > 0;
|
2894
|
+
const hasTimeFilter = params.timeStart || params.timeEnd;
|
2895
|
+
if (isHashQuery) {
|
2896
|
+
if (hasContentTopics || hasTimeFilter) {
|
2897
|
+
throw new Error("Message hash lookup queries cannot include content filter criteria (contentTopics, timeStart, or timeEnd)");
|
2898
|
+
}
|
2899
|
+
}
|
2900
|
+
else {
|
2901
|
+
if ((params.pubsubTopic &&
|
2902
|
+
(!params.contentTopics || params.contentTopics.length === 0)) ||
|
2903
|
+
(!params.pubsubTopic &&
|
2904
|
+
params.contentTopics &&
|
2905
|
+
params.contentTopics.length > 0)) {
|
2906
|
+
throw new Error("Both pubsubTopic and contentTopics must be set together for content-filtered queries");
|
2907
|
+
}
|
2740
2908
|
}
|
2741
2909
|
return request;
|
2742
2910
|
}
|
@@ -2776,15 +2944,19 @@ class StoreQueryResponse {
|
|
2776
2944
|
|
2777
2945
|
const log$3 = new Logger("store");
|
2778
2946
|
const StoreCodec = "/vac/waku/store-query/3.0.0";
|
2779
|
-
class StoreCore
|
2780
|
-
|
2781
|
-
|
2782
|
-
|
2783
|
-
this.
|
2947
|
+
class StoreCore {
|
2948
|
+
streamManager;
|
2949
|
+
multicodec = StoreCodec;
|
2950
|
+
constructor(libp2p) {
|
2951
|
+
this.streamManager = new StreamManager(StoreCodec, libp2p.components);
|
2784
2952
|
}
|
2785
2953
|
async *queryPerPage(queryOpts, decoders, peerId) {
|
2786
|
-
|
2787
|
-
|
2954
|
+
// Only validate decoder content topics for content-filtered queries
|
2955
|
+
const isHashQuery = queryOpts.messageHashes && queryOpts.messageHashes.length > 0;
|
2956
|
+
if (!isHashQuery &&
|
2957
|
+
queryOpts.contentTopics &&
|
2958
|
+
queryOpts.contentTopics.toString() !==
|
2959
|
+
Array.from(decoders.keys()).toString()) {
|
2788
2960
|
throw new Error("Internal error, the decoders should match the query's content topics");
|
2789
2961
|
}
|
2790
2962
|
let currentCursor = queryOpts.paginationCursor;
|
@@ -2793,9 +2965,15 @@ class StoreCore extends BaseProtocol {
|
|
2793
2965
|
...queryOpts,
|
2794
2966
|
paginationCursor: currentCursor
|
2795
2967
|
});
|
2968
|
+
log$3.info("Sending store query request:", {
|
2969
|
+
hasMessageHashes: !!queryOpts.messageHashes?.length,
|
2970
|
+
messageHashCount: queryOpts.messageHashes?.length,
|
2971
|
+
pubsubTopic: queryOpts.pubsubTopic,
|
2972
|
+
contentTopics: queryOpts.contentTopics
|
2973
|
+
});
|
2796
2974
|
let stream;
|
2797
2975
|
try {
|
2798
|
-
stream = await this.getStream(peerId);
|
2976
|
+
stream = await this.streamManager.getStream(peerId);
|
2799
2977
|
}
|
2800
2978
|
catch (e) {
|
2801
2979
|
log$3.error("Failed to get stream", e);
|
@@ -2867,9 +3045,71 @@ function isPeerId(other) {
|
|
2867
3045
|
return Boolean(other?.[peerIdSymbol]);
|
2868
3046
|
}
|
2869
3047
|
|
3048
|
+
/**
|
3049
|
+
* @packageDocumentation
|
3050
|
+
*
|
3051
|
+
* Adds types to the EventTarget class.
|
3052
|
+
*
|
3053
|
+
* Hopefully this won't be necessary
|
3054
|
+
* forever:
|
3055
|
+
*
|
3056
|
+
* - https://github.com/microsoft/TypeScript/issues/28357
|
3057
|
+
* - https://github.com/microsoft/TypeScript/issues/43477
|
3058
|
+
* - https://github.com/microsoft/TypeScript/issues/299
|
3059
|
+
* - https://www.npmjs.com/package/typed-events
|
3060
|
+
* - https://www.npmjs.com/package/typed-event-emitter
|
3061
|
+
* - https://www.npmjs.com/package/typed-event-target
|
3062
|
+
* - etc
|
3063
|
+
*
|
3064
|
+
* In addition to types, a `safeDispatchEvent` method is available which
|
3065
|
+
* prevents dispatching events that aren't in the event map, and a
|
3066
|
+
* `listenerCount` method which reports the number of listeners that are
|
3067
|
+
* currently registered for a given event.
|
3068
|
+
*
|
3069
|
+
* @example
|
3070
|
+
*
|
3071
|
+
* ```ts
|
3072
|
+
* import { TypedEventEmitter } from 'main-event'
|
3073
|
+
* import type { TypedEventTarget } from 'main-event'
|
3074
|
+
*
|
3075
|
+
* interface EventTypes {
|
3076
|
+
* 'test': CustomEvent<string>
|
3077
|
+
* }
|
3078
|
+
*
|
3079
|
+
* const target = new TypedEventEmitter<EventTypes>()
|
3080
|
+
*
|
3081
|
+
* // it's a regular EventTarget
|
3082
|
+
* console.info(target instanceof EventTarget) // true
|
3083
|
+
*
|
3084
|
+
* // register listeners normally
|
3085
|
+
* target.addEventListener('test', (evt) => {
|
3086
|
+
* // evt is CustomEvent<string>
|
3087
|
+
* })
|
3088
|
+
*
|
3089
|
+
* // @ts-expect-error 'derp' is not in the event map
|
3090
|
+
* target.addEventListener('derp', () => {})
|
3091
|
+
*
|
3092
|
+
* // use normal dispatchEvent method
|
3093
|
+
* target.dispatchEvent(new CustomEvent('test', {
|
3094
|
+
* detail: 'hello'
|
3095
|
+
* }))
|
3096
|
+
*
|
3097
|
+
* // use type safe dispatch method
|
3098
|
+
* target.safeDispatchEvent('test', {
|
3099
|
+
* detail: 'world'
|
3100
|
+
* })
|
3101
|
+
*
|
3102
|
+
* // report listener count
|
3103
|
+
* console.info(target.listenerCount('test')) // 0
|
3104
|
+
*
|
3105
|
+
* // event emitters can be used purely as interfaces too
|
3106
|
+
* function acceptTarget (target: TypedEventTarget<EventTypes>) {
|
3107
|
+
* // ...
|
3108
|
+
* }
|
3109
|
+
* ```
|
3110
|
+
*/
|
2870
3111
|
/**
|
2871
3112
|
* An implementation of a typed event target
|
2872
|
-
* etc
|
2873
3113
|
*/
|
2874
3114
|
class TypedEventEmitter extends EventTarget {
|
2875
3115
|
#listeners = new Map();
|
@@ -2919,6 +3159,26 @@ class TypedEventEmitter extends EventTarget {
|
|
2919
3159
|
}
|
2920
3160
|
}
|
2921
3161
|
|
3162
|
+
/**
|
3163
|
+
* Thrown when an invalid multiaddr is encountered
|
3164
|
+
*/
|
3165
|
+
class InvalidMultiaddrError extends Error {
|
3166
|
+
static name = 'InvalidMultiaddrError';
|
3167
|
+
name = 'InvalidMultiaddrError';
|
3168
|
+
}
|
3169
|
+
class ValidationError extends Error {
|
3170
|
+
static name = 'ValidationError';
|
3171
|
+
name = 'ValidationError';
|
3172
|
+
}
|
3173
|
+
class InvalidParametersError extends Error {
|
3174
|
+
static name = 'InvalidParametersError';
|
3175
|
+
name = 'InvalidParametersError';
|
3176
|
+
}
|
3177
|
+
class UnknownProtocolError extends Error {
|
3178
|
+
static name = 'UnknownProtocolError';
|
3179
|
+
name = 'UnknownProtocolError';
|
3180
|
+
}
|
3181
|
+
|
2922
3182
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
2923
3183
|
class Parser {
|
2924
3184
|
index = 0;
|
@@ -3142,24 +3402,6 @@ function parseIPv6(input) {
|
|
3142
3402
|
}
|
3143
3403
|
return parser.new(input).parseWith(() => parser.readIPv6Addr());
|
3144
3404
|
}
|
3145
|
-
/** Parse `input` into IPv4 or IPv6 bytes. */
|
3146
|
-
function parseIP(input, mapIPv4ToIPv6 = false) {
|
3147
|
-
// strip zone index if it is present
|
3148
|
-
if (input.includes("%")) {
|
3149
|
-
input = input.split("%")[0];
|
3150
|
-
}
|
3151
|
-
if (input.length > MAX_IPV6_LENGTH) {
|
3152
|
-
return undefined;
|
3153
|
-
}
|
3154
|
-
const addr = parser.new(input).parseWith(() => parser.readIPAddr());
|
3155
|
-
if (!addr) {
|
3156
|
-
return undefined;
|
3157
|
-
}
|
3158
|
-
if (mapIPv4ToIPv6 && addr.length === 4) {
|
3159
|
-
return Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, addr[0], addr[1], addr[2], addr[3]]);
|
3160
|
-
}
|
3161
|
-
return addr;
|
3162
|
-
}
|
3163
3405
|
|
3164
3406
|
/** Check if `input` is IPv4. */
|
3165
3407
|
function isIPv4(input) {
|
@@ -3169,347 +3411,69 @@ function isIPv4(input) {
|
|
3169
3411
|
function isIPv6(input) {
|
3170
3412
|
return Boolean(parseIPv6(input));
|
3171
3413
|
}
|
3172
|
-
/** Check if `input` is IPv4 or IPv6. */
|
3173
|
-
function isIP(input) {
|
3174
|
-
return Boolean(parseIP(input));
|
3175
|
-
}
|
3176
3414
|
|
3177
|
-
|
3178
|
-
const
|
3179
|
-
|
3180
|
-
|
3181
|
-
const
|
3182
|
-
|
3183
|
-
|
3184
|
-
|
3185
|
-
|
3186
|
-
|
3187
|
-
|
3188
|
-
|
3189
|
-
|
3190
|
-
|
3191
|
-
|
3192
|
-
|
3193
|
-
|
3194
|
-
|
3195
|
-
|
3196
|
-
|
3197
|
-
|
3198
|
-
|
3199
|
-
|
3200
|
-
|
3201
|
-
|
3202
|
-
|
3203
|
-
|
3204
|
-
|
3205
|
-
|
3206
|
-
|
3207
|
-
|
3208
|
-
|
3209
|
-
|
3210
|
-
|
3211
|
-
|
3212
|
-
|
3213
|
-
|
3214
|
-
|
3215
|
-
|
3216
|
-
|
3217
|
-
|
3218
|
-
argv.push('0');
|
3219
|
-
}
|
3220
|
-
sections.splice.apply(sections, argv);
|
3221
|
-
}
|
3222
|
-
const bytes = new Uint8Array(offset + 16);
|
3223
|
-
for (i = 0; i < sections.length; i++) {
|
3224
|
-
const word = parseInt(sections[i], 16);
|
3225
|
-
bytes[offset++] = (word >> 8) & 0xff;
|
3226
|
-
bytes[offset++] = word & 0xff;
|
3227
|
-
}
|
3228
|
-
return bytes;
|
3229
|
-
}
|
3230
|
-
throw new Error('invalid ip address');
|
3231
|
-
};
|
3232
|
-
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63
|
3233
|
-
const toString = function (buf, offset = 0, length) {
|
3234
|
-
offset = ~~offset;
|
3235
|
-
length = length ?? (buf.length - offset);
|
3236
|
-
const view = new DataView(buf.buffer);
|
3237
|
-
if (length === 4) {
|
3238
|
-
const result = [];
|
3239
|
-
// IPv4
|
3240
|
-
for (let i = 0; i < length; i++) {
|
3241
|
-
result.push(buf[offset + i]);
|
3242
|
-
}
|
3243
|
-
return result.join('.');
|
3244
|
-
}
|
3245
|
-
if (length === 16) {
|
3246
|
-
const result = [];
|
3247
|
-
// IPv6
|
3248
|
-
for (let i = 0; i < length; i += 2) {
|
3249
|
-
result.push(view.getUint16(offset + i).toString(16));
|
3250
|
-
}
|
3251
|
-
return result.join(':')
|
3252
|
-
.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3')
|
3253
|
-
.replace(/:{3,4}/, '::');
|
3254
|
-
}
|
3255
|
-
return '';
|
3256
|
-
};
|
3415
|
+
// the values here come from https://github.com/multiformats/multiaddr/blob/master/protocols.csv
|
3416
|
+
const CODE_IP4 = 4;
|
3417
|
+
const CODE_TCP = 6;
|
3418
|
+
const CODE_UDP = 273;
|
3419
|
+
const CODE_DCCP = 33;
|
3420
|
+
const CODE_IP6 = 41;
|
3421
|
+
const CODE_IP6ZONE = 42;
|
3422
|
+
const CODE_IPCIDR = 43;
|
3423
|
+
const CODE_DNS = 53;
|
3424
|
+
const CODE_DNS4 = 54;
|
3425
|
+
const CODE_DNS6 = 55;
|
3426
|
+
const CODE_DNSADDR = 56;
|
3427
|
+
const CODE_SCTP = 132;
|
3428
|
+
const CODE_UDT = 301;
|
3429
|
+
const CODE_UTP = 302;
|
3430
|
+
const CODE_UNIX = 400;
|
3431
|
+
const CODE_P2P = 421; // also IPFS
|
3432
|
+
const CODE_ONION = 444;
|
3433
|
+
const CODE_ONION3 = 445;
|
3434
|
+
const CODE_GARLIC64 = 446;
|
3435
|
+
const CODE_GARLIC32 = 447;
|
3436
|
+
const CODE_TLS = 448;
|
3437
|
+
const CODE_SNI = 449;
|
3438
|
+
const CODE_NOISE = 454;
|
3439
|
+
const CODE_QUIC = 460;
|
3440
|
+
const CODE_QUIC_V1 = 461;
|
3441
|
+
const CODE_WEBTRANSPORT = 465;
|
3442
|
+
const CODE_CERTHASH = 466;
|
3443
|
+
const CODE_HTTP = 480;
|
3444
|
+
const CODE_HTTP_PATH = 481;
|
3445
|
+
const CODE_HTTPS = 443;
|
3446
|
+
const CODE_WS = 477;
|
3447
|
+
const CODE_WSS = 478;
|
3448
|
+
const CODE_P2P_WEBSOCKET_STAR = 479;
|
3449
|
+
const CODE_P2P_STARDUST = 277;
|
3450
|
+
const CODE_P2P_WEBRTC_STAR = 275;
|
3451
|
+
const CODE_P2P_WEBRTC_DIRECT = 276;
|
3452
|
+
const CODE_WEBRTC_DIRECT = 280;
|
3453
|
+
const CODE_WEBRTC = 281;
|
3454
|
+
const CODE_P2P_CIRCUIT = 290;
|
3455
|
+
const CODE_MEMORY = 777;
|
3257
3456
|
|
3258
|
-
|
3259
|
-
|
3260
|
-
|
3261
|
-
const table = [
|
3262
|
-
[4, 32, 'ip4'],
|
3263
|
-
[6, 16, 'tcp'],
|
3264
|
-
[33, 16, 'dccp'],
|
3265
|
-
[41, 128, 'ip6'],
|
3266
|
-
[42, V, 'ip6zone'],
|
3267
|
-
[43, 8, 'ipcidr'],
|
3268
|
-
[53, V, 'dns', true],
|
3269
|
-
[54, V, 'dns4', true],
|
3270
|
-
[55, V, 'dns6', true],
|
3271
|
-
[56, V, 'dnsaddr', true],
|
3272
|
-
[132, 16, 'sctp'],
|
3273
|
-
[273, 16, 'udp'],
|
3274
|
-
[275, 0, 'p2p-webrtc-star'],
|
3275
|
-
[276, 0, 'p2p-webrtc-direct'],
|
3276
|
-
[277, 0, 'p2p-stardust'],
|
3277
|
-
[280, 0, 'webrtc-direct'],
|
3278
|
-
[281, 0, 'webrtc'],
|
3279
|
-
[290, 0, 'p2p-circuit'],
|
3280
|
-
[301, 0, 'udt'],
|
3281
|
-
[302, 0, 'utp'],
|
3282
|
-
[400, V, 'unix', false, true],
|
3283
|
-
// `ipfs` is added before `p2p` for legacy support.
|
3284
|
-
// All text representations will default to `p2p`, but `ipfs` will
|
3285
|
-
// still be supported
|
3286
|
-
[421, V, 'ipfs'],
|
3287
|
-
// `p2p` is the preferred name for 421, and is now the default
|
3288
|
-
[421, V, 'p2p'],
|
3289
|
-
[443, 0, 'https'],
|
3290
|
-
[444, 96, 'onion'],
|
3291
|
-
[445, 296, 'onion3'],
|
3292
|
-
[446, V, 'garlic64'],
|
3293
|
-
[448, 0, 'tls'],
|
3294
|
-
[449, V, 'sni'],
|
3295
|
-
[460, 0, 'quic'],
|
3296
|
-
[461, 0, 'quic-v1'],
|
3297
|
-
[465, 0, 'webtransport'],
|
3298
|
-
[466, V, 'certhash'],
|
3299
|
-
[477, 0, 'ws'],
|
3300
|
-
[478, 0, 'wss'],
|
3301
|
-
[479, 0, 'p2p-websocket-star'],
|
3302
|
-
[480, 0, 'http'],
|
3303
|
-
[481, V, 'http-path'],
|
3304
|
-
[777, V, 'memory']
|
3305
|
-
];
|
3306
|
-
// populate tables
|
3307
|
-
table.forEach(row => {
|
3308
|
-
const proto = createProtocol(...row);
|
3309
|
-
codes[proto.code] = proto;
|
3310
|
-
names[proto.name] = proto;
|
3311
|
-
});
|
3312
|
-
function createProtocol(code, size, name, resolvable, path) {
|
3313
|
-
return {
|
3314
|
-
code,
|
3315
|
-
size,
|
3316
|
-
name,
|
3317
|
-
resolvable: Boolean(resolvable),
|
3318
|
-
path: Boolean(path)
|
3457
|
+
function bytesToString(base) {
|
3458
|
+
return (buf) => {
|
3459
|
+
return toString(buf, base);
|
3319
3460
|
};
|
3320
3461
|
}
|
3321
|
-
|
3322
|
-
|
3323
|
-
|
3324
|
-
|
3325
|
-
*
|
3326
|
-
* ```js
|
3327
|
-
* import { protocol } from '@multiformats/multiaddr'
|
3328
|
-
*
|
3329
|
-
* console.info(protocol(4))
|
3330
|
-
* // { code: 4, size: 32, name: 'ip4', resolvable: false, path: false }
|
3331
|
-
* ```
|
3332
|
-
*/
|
3333
|
-
function getProtocol(proto) {
|
3334
|
-
if (typeof proto === 'number') {
|
3335
|
-
if (codes[proto] != null) {
|
3336
|
-
return codes[proto];
|
3337
|
-
}
|
3338
|
-
throw new Error(`no protocol with code: ${proto}`);
|
3339
|
-
}
|
3340
|
-
else if (typeof proto === 'string') {
|
3341
|
-
if (names[proto] != null) {
|
3342
|
-
return names[proto];
|
3343
|
-
}
|
3344
|
-
throw new Error(`no protocol with name: ${proto}`);
|
3345
|
-
}
|
3346
|
-
throw new Error(`invalid protocol id type: ${typeof proto}`);
|
3347
|
-
}
|
3348
|
-
|
3349
|
-
getProtocol('ip4');
|
3350
|
-
getProtocol('ip6');
|
3351
|
-
getProtocol('ipcidr');
|
3352
|
-
/**
|
3353
|
-
* Convert [code,Uint8Array] to string
|
3354
|
-
*/
|
3355
|
-
// eslint-disable-next-line complexity
|
3356
|
-
function convertToString(proto, buf) {
|
3357
|
-
const protocol = getProtocol(proto);
|
3358
|
-
switch (protocol.code) {
|
3359
|
-
case 4: // ipv4
|
3360
|
-
case 41: // ipv6
|
3361
|
-
return bytes2ip(buf);
|
3362
|
-
case 42: // ipv6zone
|
3363
|
-
return bytes2str(buf);
|
3364
|
-
case 43: // ipcidr
|
3365
|
-
return toString$1(buf, 'base10');
|
3366
|
-
case 6: // tcp
|
3367
|
-
case 273: // udp
|
3368
|
-
case 33: // dccp
|
3369
|
-
case 132: // sctp
|
3370
|
-
return bytes2port(buf).toString();
|
3371
|
-
case 53: // dns
|
3372
|
-
case 54: // dns4
|
3373
|
-
case 55: // dns6
|
3374
|
-
case 56: // dnsaddr
|
3375
|
-
case 400: // unix
|
3376
|
-
case 449: // sni
|
3377
|
-
case 777: // memory
|
3378
|
-
return bytes2str(buf);
|
3379
|
-
case 421: // ipfs
|
3380
|
-
return bytes2mh(buf);
|
3381
|
-
case 444: // onion
|
3382
|
-
return bytes2onion(buf);
|
3383
|
-
case 445: // onion3
|
3384
|
-
return bytes2onion(buf);
|
3385
|
-
case 466: // certhash
|
3386
|
-
return bytes2mb(buf);
|
3387
|
-
case 481: // http-path
|
3388
|
-
return globalThis.encodeURIComponent(bytes2str(buf));
|
3389
|
-
default:
|
3390
|
-
return toString$1(buf, 'base16'); // no clue. convert to hex
|
3391
|
-
}
|
3392
|
-
}
|
3393
|
-
// eslint-disable-next-line complexity
|
3394
|
-
function convertToBytes(proto, str) {
|
3395
|
-
const protocol = getProtocol(proto);
|
3396
|
-
switch (protocol.code) {
|
3397
|
-
case 4: // ipv4
|
3398
|
-
return ip2bytes(str);
|
3399
|
-
case 41: // ipv6
|
3400
|
-
return ip2bytes(str);
|
3401
|
-
case 42: // ipv6zone
|
3402
|
-
return str2bytes(str);
|
3403
|
-
case 43: // ipcidr
|
3404
|
-
return fromString(str, 'base10');
|
3405
|
-
case 6: // tcp
|
3406
|
-
case 273: // udp
|
3407
|
-
case 33: // dccp
|
3408
|
-
case 132: // sctp
|
3409
|
-
return port2bytes(parseInt(str, 10));
|
3410
|
-
case 53: // dns
|
3411
|
-
case 54: // dns4
|
3412
|
-
case 55: // dns6
|
3413
|
-
case 56: // dnsaddr
|
3414
|
-
case 400: // unix
|
3415
|
-
case 449: // sni
|
3416
|
-
case 777: // memory
|
3417
|
-
return str2bytes(str);
|
3418
|
-
case 421: // ipfs
|
3419
|
-
return mh2bytes(str);
|
3420
|
-
case 444: // onion
|
3421
|
-
return onion2bytes(str);
|
3422
|
-
case 445: // onion3
|
3423
|
-
return onion32bytes(str);
|
3424
|
-
case 466: // certhash
|
3425
|
-
return mb2bytes(str);
|
3426
|
-
case 481: // http-path
|
3427
|
-
return str2bytes(globalThis.decodeURIComponent(str));
|
3428
|
-
default:
|
3429
|
-
return fromString(str, 'base16'); // no clue. convert from hex
|
3430
|
-
}
|
3431
|
-
}
|
3432
|
-
const decoders = Object.values(bases).map((c) => c.decoder);
|
3433
|
-
const anybaseDecoder = (function () {
|
3434
|
-
let acc = decoders[0].or(decoders[1]);
|
3435
|
-
decoders.slice(2).forEach((d) => (acc = acc.or(d)));
|
3436
|
-
return acc;
|
3437
|
-
})();
|
3438
|
-
function ip2bytes(ipString) {
|
3439
|
-
if (!isIP(ipString)) {
|
3440
|
-
throw new Error('invalid ip address');
|
3441
|
-
}
|
3442
|
-
return toBytes(ipString);
|
3462
|
+
function stringToBytes(base) {
|
3463
|
+
return (buf) => {
|
3464
|
+
return fromString(buf, base);
|
3465
|
+
};
|
3443
3466
|
}
|
3444
|
-
function
|
3445
|
-
const
|
3446
|
-
|
3447
|
-
throw new Error('ipBuff is required');
|
3448
|
-
}
|
3449
|
-
if (!isIP(ipString)) {
|
3450
|
-
throw new Error('invalid ip address');
|
3451
|
-
}
|
3452
|
-
return ipString;
|
3467
|
+
function bytes2port(buf) {
|
3468
|
+
const view = new DataView(buf.buffer);
|
3469
|
+
return view.getUint16(buf.byteOffset).toString();
|
3453
3470
|
}
|
3454
3471
|
function port2bytes(port) {
|
3455
3472
|
const buf = new ArrayBuffer(2);
|
3456
3473
|
const view = new DataView(buf);
|
3457
|
-
view.setUint16(0, port);
|
3474
|
+
view.setUint16(0, typeof port === 'string' ? parseInt(port) : port);
|
3458
3475
|
return new Uint8Array(buf);
|
3459
3476
|
}
|
3460
|
-
function bytes2port(buf) {
|
3461
|
-
const view = new DataView(buf.buffer);
|
3462
|
-
return view.getUint16(buf.byteOffset);
|
3463
|
-
}
|
3464
|
-
function str2bytes(str) {
|
3465
|
-
const buf = fromString(str);
|
3466
|
-
const size = Uint8Array.from(encode$2(buf.length));
|
3467
|
-
return concat([size, buf], size.length + buf.length);
|
3468
|
-
}
|
3469
|
-
function bytes2str(buf) {
|
3470
|
-
const size = decode$4(buf);
|
3471
|
-
buf = buf.slice(encodingLength$1(size));
|
3472
|
-
if (buf.length !== size) {
|
3473
|
-
throw new Error('inconsistent lengths');
|
3474
|
-
}
|
3475
|
-
return toString$1(buf);
|
3476
|
-
}
|
3477
|
-
function mh2bytes(hash) {
|
3478
|
-
let mh;
|
3479
|
-
if (hash[0] === 'Q' || hash[0] === '1') {
|
3480
|
-
mh = decode$1(base58btc.decode(`z${hash}`)).bytes;
|
3481
|
-
}
|
3482
|
-
else {
|
3483
|
-
mh = CID.parse(hash).multihash.bytes;
|
3484
|
-
}
|
3485
|
-
// the address is a varint prefixed multihash string representation
|
3486
|
-
const size = Uint8Array.from(encode$2(mh.length));
|
3487
|
-
return concat([size, mh], size.length + mh.length);
|
3488
|
-
}
|
3489
|
-
function mb2bytes(mbstr) {
|
3490
|
-
const mb = anybaseDecoder.decode(mbstr);
|
3491
|
-
const size = Uint8Array.from(encode$2(mb.length));
|
3492
|
-
return concat([size, mb], size.length + mb.length);
|
3493
|
-
}
|
3494
|
-
function bytes2mb(buf) {
|
3495
|
-
const size = decode$4(buf);
|
3496
|
-
const hash = buf.slice(encodingLength$1(size));
|
3497
|
-
if (hash.length !== size) {
|
3498
|
-
throw new Error('inconsistent lengths');
|
3499
|
-
}
|
3500
|
-
return 'u' + toString$1(hash, 'base64url');
|
3501
|
-
}
|
3502
|
-
/**
|
3503
|
-
* Converts bytes to bas58btc string
|
3504
|
-
*/
|
3505
|
-
function bytes2mh(buf) {
|
3506
|
-
const size = decode$4(buf);
|
3507
|
-
const address = buf.slice(encodingLength$1(size));
|
3508
|
-
if (address.length !== size) {
|
3509
|
-
throw new Error('inconsistent lengths');
|
3510
|
-
}
|
3511
|
-
return toString$1(address, 'base58btc');
|
3512
|
-
}
|
3513
3477
|
function onion2bytes(str) {
|
3514
3478
|
const addr = str.split(':');
|
3515
3479
|
if (addr.length !== 2) {
|
@@ -3519,7 +3483,7 @@ function onion2bytes(str) {
|
|
3519
3483
|
throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion address.`);
|
3520
3484
|
}
|
3521
3485
|
// onion addresses do not include the multibase prefix, add it before decoding
|
3522
|
-
const buf =
|
3486
|
+
const buf = fromString(addr[0], 'base32');
|
3523
3487
|
// onion port number
|
3524
3488
|
const port = parseInt(addr[1], 10);
|
3525
3489
|
if (port < 1 || port > 65536) {
|
@@ -3547,167 +3511,558 @@ function onion32bytes(str) {
|
|
3547
3511
|
return concat([buf, portBuf], buf.length + portBuf.length);
|
3548
3512
|
}
|
3549
3513
|
function bytes2onion(buf) {
|
3550
|
-
const addrBytes = buf.
|
3551
|
-
const portBytes = buf.
|
3552
|
-
const addr = toString
|
3514
|
+
const addrBytes = buf.subarray(0, buf.length - 2);
|
3515
|
+
const portBytes = buf.subarray(buf.length - 2);
|
3516
|
+
const addr = toString(addrBytes, 'base32');
|
3553
3517
|
const port = bytes2port(portBytes);
|
3554
3518
|
return `${addr}:${port}`;
|
3555
3519
|
}
|
3520
|
+
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7
|
3521
|
+
// but with buf/offset args removed because we don't use them
|
3522
|
+
const ip4ToBytes = function (ip) {
|
3523
|
+
ip = ip.toString().trim();
|
3524
|
+
const bytes = new Uint8Array(4);
|
3525
|
+
ip.split(/\./g).forEach((byte, index) => {
|
3526
|
+
const value = parseInt(byte, 10);
|
3527
|
+
if (isNaN(value) || value < 0 || value > 0xff) {
|
3528
|
+
throw new InvalidMultiaddrError('Invalid byte value in IP address');
|
3529
|
+
}
|
3530
|
+
bytes[index] = value;
|
3531
|
+
});
|
3532
|
+
return bytes;
|
3533
|
+
};
|
3534
|
+
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7
|
3535
|
+
// but with buf/offset args removed because we don't use them
|
3536
|
+
const ip6ToBytes = function (ip) {
|
3537
|
+
let offset = 0;
|
3538
|
+
ip = ip.toString().trim();
|
3539
|
+
const sections = ip.split(':', 8);
|
3540
|
+
let i;
|
3541
|
+
for (i = 0; i < sections.length; i++) {
|
3542
|
+
const isv4 = isIPv4(sections[i]);
|
3543
|
+
let v4Buffer;
|
3544
|
+
if (isv4) {
|
3545
|
+
v4Buffer = ip4ToBytes(sections[i]);
|
3546
|
+
sections[i] = toString(v4Buffer.subarray(0, 2), 'base16');
|
3547
|
+
}
|
3548
|
+
if (v4Buffer != null && ++i < 8) {
|
3549
|
+
sections.splice(i, 0, toString(v4Buffer.subarray(2, 4), 'base16'));
|
3550
|
+
}
|
3551
|
+
}
|
3552
|
+
if (sections[0] === '') {
|
3553
|
+
while (sections.length < 8) {
|
3554
|
+
sections.unshift('0');
|
3555
|
+
}
|
3556
|
+
}
|
3557
|
+
else if (sections[sections.length - 1] === '') {
|
3558
|
+
while (sections.length < 8) {
|
3559
|
+
sections.push('0');
|
3560
|
+
}
|
3561
|
+
}
|
3562
|
+
else if (sections.length < 8) {
|
3563
|
+
for (i = 0; i < sections.length && sections[i] !== ''; i++) { }
|
3564
|
+
const argv = [i, 1];
|
3565
|
+
for (i = 9 - sections.length; i > 0; i--) {
|
3566
|
+
argv.push('0');
|
3567
|
+
}
|
3568
|
+
sections.splice.apply(sections, argv);
|
3569
|
+
}
|
3570
|
+
const bytes = new Uint8Array(offset + 16);
|
3571
|
+
for (i = 0; i < sections.length; i++) {
|
3572
|
+
if (sections[i] === '') {
|
3573
|
+
sections[i] = '0';
|
3574
|
+
}
|
3575
|
+
const word = parseInt(sections[i], 16);
|
3576
|
+
if (isNaN(word) || word < 0 || word > 0xffff) {
|
3577
|
+
throw new InvalidMultiaddrError('Invalid byte value in IP address');
|
3578
|
+
}
|
3579
|
+
bytes[offset++] = (word >> 8) & 0xff;
|
3580
|
+
bytes[offset++] = word & 0xff;
|
3581
|
+
}
|
3582
|
+
return bytes;
|
3583
|
+
};
|
3584
|
+
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63
|
3585
|
+
const ip4ToString = function (buf) {
|
3586
|
+
if (buf.byteLength !== 4) {
|
3587
|
+
throw new InvalidMultiaddrError('IPv4 address was incorrect length');
|
3588
|
+
}
|
3589
|
+
const result = [];
|
3590
|
+
for (let i = 0; i < buf.byteLength; i++) {
|
3591
|
+
result.push(buf[i]);
|
3592
|
+
}
|
3593
|
+
return result.join('.');
|
3594
|
+
};
|
3595
|
+
const ip6ToString = function (buf) {
|
3596
|
+
if (buf.byteLength !== 16) {
|
3597
|
+
throw new InvalidMultiaddrError('IPv6 address was incorrect length');
|
3598
|
+
}
|
3599
|
+
const result = [];
|
3600
|
+
for (let i = 0; i < buf.byteLength; i += 2) {
|
3601
|
+
const byte1 = buf[i];
|
3602
|
+
const byte2 = buf[i + 1];
|
3603
|
+
const tuple = `${byte1.toString(16).padStart(2, '0')}${byte2.toString(16).padStart(2, '0')}`;
|
3604
|
+
result.push(tuple);
|
3605
|
+
}
|
3606
|
+
const ip = result.join(':');
|
3607
|
+
try {
|
3608
|
+
const url = new URL(`http://[${ip}]`);
|
3609
|
+
return url.hostname.substring(1, url.hostname.length - 1);
|
3610
|
+
}
|
3611
|
+
catch {
|
3612
|
+
throw new InvalidMultiaddrError(`Invalid IPv6 address "${ip}"`);
|
3613
|
+
}
|
3614
|
+
};
|
3615
|
+
function ip6StringToValue(str) {
|
3616
|
+
try {
|
3617
|
+
const url = new URL(`http://[${str}]`);
|
3618
|
+
return url.hostname.substring(1, url.hostname.length - 1);
|
3619
|
+
}
|
3620
|
+
catch {
|
3621
|
+
throw new InvalidMultiaddrError(`Invalid IPv6 address "${str}"`);
|
3622
|
+
}
|
3623
|
+
}
|
3624
|
+
const decoders = Object.values(bases).map((c) => c.decoder);
|
3625
|
+
const anybaseDecoder = (function () {
|
3626
|
+
let acc = decoders[0].or(decoders[1]);
|
3627
|
+
decoders.slice(2).forEach((d) => (acc = acc.or(d)));
|
3628
|
+
return acc;
|
3629
|
+
})();
|
3630
|
+
function mb2bytes(mbstr) {
|
3631
|
+
return anybaseDecoder.decode(mbstr);
|
3632
|
+
}
|
3633
|
+
function bytes2mb(base) {
|
3634
|
+
return (buf) => {
|
3635
|
+
return base.encoder.encode(buf);
|
3636
|
+
};
|
3637
|
+
}
|
3556
3638
|
|
3557
|
-
function
|
3558
|
-
|
3559
|
-
|
3560
|
-
|
3561
|
-
let path = null;
|
3562
|
-
const parts = str.split('/').slice(1);
|
3563
|
-
if (parts.length === 1 && parts[0] === '') {
|
3564
|
-
return {
|
3565
|
-
bytes: new Uint8Array(),
|
3566
|
-
string: '/',
|
3567
|
-
tuples: [],
|
3568
|
-
stringTuples: [],
|
3569
|
-
path: null
|
3570
|
-
};
|
3639
|
+
function integer(value) {
|
3640
|
+
const int = parseInt(value);
|
3641
|
+
if (int.toString() !== value) {
|
3642
|
+
throw new ValidationError('Value must be an integer');
|
3571
3643
|
}
|
3572
|
-
|
3573
|
-
|
3574
|
-
|
3575
|
-
|
3576
|
-
|
3577
|
-
|
3578
|
-
|
3579
|
-
|
3580
|
-
|
3581
|
-
|
3582
|
-
|
3583
|
-
|
3584
|
-
|
3585
|
-
|
3586
|
-
|
3587
|
-
|
3588
|
-
|
3589
|
-
|
3590
|
-
path = cleanPath(parts.slice(p).join('/'));
|
3591
|
-
tuples.push([proto.code, convertToBytes(proto.code, path)]);
|
3592
|
-
stringTuples.push([proto.code, path]);
|
3593
|
-
break;
|
3594
|
-
}
|
3595
|
-
const bytes = convertToBytes(proto.code, parts[p]);
|
3596
|
-
tuples.push([proto.code, bytes]);
|
3597
|
-
stringTuples.push([proto.code, convertToString(proto.code, bytes)]);
|
3598
|
-
}
|
3599
|
-
return {
|
3600
|
-
string: stringTuplesToString(stringTuples),
|
3601
|
-
bytes: tuplesToBytes(tuples),
|
3602
|
-
tuples,
|
3603
|
-
stringTuples,
|
3604
|
-
path
|
3644
|
+
}
|
3645
|
+
function positive(value) {
|
3646
|
+
if (value < 0) {
|
3647
|
+
throw new ValidationError('Value must be a positive integer, or zero');
|
3648
|
+
}
|
3649
|
+
}
|
3650
|
+
function maxValue(max) {
|
3651
|
+
return (value) => {
|
3652
|
+
if (value > max) {
|
3653
|
+
throw new ValidationError(`Value must be smaller than or equal to ${max}`);
|
3654
|
+
}
|
3655
|
+
};
|
3656
|
+
}
|
3657
|
+
function validate$1(...funcs) {
|
3658
|
+
return (value) => {
|
3659
|
+
for (const fn of funcs) {
|
3660
|
+
fn(value);
|
3661
|
+
}
|
3605
3662
|
};
|
3606
3663
|
}
|
3607
|
-
|
3608
|
-
|
3609
|
-
|
3610
|
-
|
3664
|
+
const validatePort = validate$1(integer, positive, maxValue(65_535));
|
3665
|
+
|
3666
|
+
const V = -1;
|
3667
|
+
class Registry {
|
3668
|
+
protocolsByCode = new Map();
|
3669
|
+
protocolsByName = new Map();
|
3670
|
+
getProtocol(key) {
|
3671
|
+
let codec;
|
3672
|
+
if (typeof key === 'string') {
|
3673
|
+
codec = this.protocolsByName.get(key);
|
3674
|
+
}
|
3675
|
+
else {
|
3676
|
+
codec = this.protocolsByCode.get(key);
|
3677
|
+
}
|
3678
|
+
if (codec == null) {
|
3679
|
+
throw new UnknownProtocolError(`Protocol ${key} was unknown`);
|
3680
|
+
}
|
3681
|
+
return codec;
|
3682
|
+
}
|
3683
|
+
addProtocol(codec) {
|
3684
|
+
this.protocolsByCode.set(codec.code, codec);
|
3685
|
+
this.protocolsByName.set(codec.name, codec);
|
3686
|
+
codec.aliases?.forEach(alias => {
|
3687
|
+
this.protocolsByName.set(alias, codec);
|
3688
|
+
});
|
3689
|
+
}
|
3690
|
+
removeProtocol(code) {
|
3691
|
+
const codec = this.protocolsByCode.get(code);
|
3692
|
+
if (codec == null) {
|
3693
|
+
return;
|
3694
|
+
}
|
3695
|
+
this.protocolsByCode.delete(codec.code);
|
3696
|
+
this.protocolsByName.delete(codec.name);
|
3697
|
+
codec.aliases?.forEach(alias => {
|
3698
|
+
this.protocolsByName.delete(alias);
|
3699
|
+
});
|
3700
|
+
}
|
3701
|
+
}
|
3702
|
+
const registry = new Registry();
|
3703
|
+
const codecs = [{
|
3704
|
+
code: CODE_IP4,
|
3705
|
+
name: 'ip4',
|
3706
|
+
size: 32,
|
3707
|
+
valueToBytes: ip4ToBytes,
|
3708
|
+
bytesToValue: ip4ToString,
|
3709
|
+
validate: (value) => {
|
3710
|
+
if (!isIPv4(value)) {
|
3711
|
+
throw new ValidationError(`Invalid IPv4 address "${value}"`);
|
3712
|
+
}
|
3713
|
+
}
|
3714
|
+
}, {
|
3715
|
+
code: CODE_TCP,
|
3716
|
+
name: 'tcp',
|
3717
|
+
size: 16,
|
3718
|
+
valueToBytes: port2bytes,
|
3719
|
+
bytesToValue: bytes2port,
|
3720
|
+
validate: validatePort
|
3721
|
+
}, {
|
3722
|
+
code: CODE_UDP,
|
3723
|
+
name: 'udp',
|
3724
|
+
size: 16,
|
3725
|
+
valueToBytes: port2bytes,
|
3726
|
+
bytesToValue: bytes2port,
|
3727
|
+
validate: validatePort
|
3728
|
+
}, {
|
3729
|
+
code: CODE_DCCP,
|
3730
|
+
name: 'dccp',
|
3731
|
+
size: 16,
|
3732
|
+
valueToBytes: port2bytes,
|
3733
|
+
bytesToValue: bytes2port,
|
3734
|
+
validate: validatePort
|
3735
|
+
}, {
|
3736
|
+
code: CODE_IP6,
|
3737
|
+
name: 'ip6',
|
3738
|
+
size: 128,
|
3739
|
+
valueToBytes: ip6ToBytes,
|
3740
|
+
bytesToValue: ip6ToString,
|
3741
|
+
stringToValue: ip6StringToValue,
|
3742
|
+
validate: (value) => {
|
3743
|
+
if (!isIPv6(value)) {
|
3744
|
+
throw new ValidationError(`Invalid IPv6 address "${value}"`);
|
3745
|
+
}
|
3746
|
+
}
|
3747
|
+
}, {
|
3748
|
+
code: CODE_IP6ZONE,
|
3749
|
+
name: 'ip6zone',
|
3750
|
+
size: V
|
3751
|
+
}, {
|
3752
|
+
code: CODE_IPCIDR,
|
3753
|
+
name: 'ipcidr',
|
3754
|
+
size: 8,
|
3755
|
+
bytesToValue: bytesToString('base10'),
|
3756
|
+
valueToBytes: stringToBytes('base10')
|
3757
|
+
}, {
|
3758
|
+
code: CODE_DNS,
|
3759
|
+
name: 'dns',
|
3760
|
+
size: V,
|
3761
|
+
resolvable: true
|
3762
|
+
}, {
|
3763
|
+
code: CODE_DNS4,
|
3764
|
+
name: 'dns4',
|
3765
|
+
size: V,
|
3766
|
+
resolvable: true
|
3767
|
+
}, {
|
3768
|
+
code: CODE_DNS6,
|
3769
|
+
name: 'dns6',
|
3770
|
+
size: V,
|
3771
|
+
resolvable: true
|
3772
|
+
}, {
|
3773
|
+
code: CODE_DNSADDR,
|
3774
|
+
name: 'dnsaddr',
|
3775
|
+
size: V,
|
3776
|
+
resolvable: true
|
3777
|
+
}, {
|
3778
|
+
code: CODE_SCTP,
|
3779
|
+
name: 'sctp',
|
3780
|
+
size: 16,
|
3781
|
+
valueToBytes: port2bytes,
|
3782
|
+
bytesToValue: bytes2port,
|
3783
|
+
validate: validatePort
|
3784
|
+
}, {
|
3785
|
+
code: CODE_UDT,
|
3786
|
+
name: 'udt'
|
3787
|
+
}, {
|
3788
|
+
code: CODE_UTP,
|
3789
|
+
name: 'utp'
|
3790
|
+
}, {
|
3791
|
+
code: CODE_UNIX,
|
3792
|
+
name: 'unix',
|
3793
|
+
size: V,
|
3794
|
+
path: true,
|
3795
|
+
stringToValue: (str) => decodeURIComponent(str),
|
3796
|
+
valueToString: (val) => encodeURIComponent(val)
|
3797
|
+
}, {
|
3798
|
+
code: CODE_P2P,
|
3799
|
+
name: 'p2p',
|
3800
|
+
aliases: ['ipfs'],
|
3801
|
+
size: V,
|
3802
|
+
bytesToValue: bytesToString('base58btc'),
|
3803
|
+
valueToBytes: (val) => {
|
3804
|
+
if (val.startsWith('Q') || val.startsWith('1')) {
|
3805
|
+
return stringToBytes('base58btc')(val);
|
3806
|
+
}
|
3807
|
+
return CID.parse(val).multihash.bytes;
|
3808
|
+
}
|
3809
|
+
}, {
|
3810
|
+
code: CODE_ONION,
|
3811
|
+
name: 'onion',
|
3812
|
+
size: 96,
|
3813
|
+
bytesToValue: bytes2onion,
|
3814
|
+
valueToBytes: onion2bytes
|
3815
|
+
}, {
|
3816
|
+
code: CODE_ONION3,
|
3817
|
+
name: 'onion3',
|
3818
|
+
size: 296,
|
3819
|
+
bytesToValue: bytes2onion,
|
3820
|
+
valueToBytes: onion32bytes
|
3821
|
+
}, {
|
3822
|
+
code: CODE_GARLIC64,
|
3823
|
+
name: 'garlic64',
|
3824
|
+
size: V
|
3825
|
+
}, {
|
3826
|
+
code: CODE_GARLIC32,
|
3827
|
+
name: 'garlic32',
|
3828
|
+
size: V
|
3829
|
+
}, {
|
3830
|
+
code: CODE_TLS,
|
3831
|
+
name: 'tls'
|
3832
|
+
}, {
|
3833
|
+
code: CODE_SNI,
|
3834
|
+
name: 'sni',
|
3835
|
+
size: V
|
3836
|
+
}, {
|
3837
|
+
code: CODE_NOISE,
|
3838
|
+
name: 'noise'
|
3839
|
+
}, {
|
3840
|
+
code: CODE_QUIC,
|
3841
|
+
name: 'quic'
|
3842
|
+
}, {
|
3843
|
+
code: CODE_QUIC_V1,
|
3844
|
+
name: 'quic-v1'
|
3845
|
+
}, {
|
3846
|
+
code: CODE_WEBTRANSPORT,
|
3847
|
+
name: 'webtransport'
|
3848
|
+
}, {
|
3849
|
+
code: CODE_CERTHASH,
|
3850
|
+
name: 'certhash',
|
3851
|
+
size: V,
|
3852
|
+
bytesToValue: bytes2mb(base64url),
|
3853
|
+
valueToBytes: mb2bytes
|
3854
|
+
}, {
|
3855
|
+
code: CODE_HTTP,
|
3856
|
+
name: 'http'
|
3857
|
+
}, {
|
3858
|
+
code: CODE_HTTP_PATH,
|
3859
|
+
name: 'http-path',
|
3860
|
+
size: V,
|
3861
|
+
stringToValue: (str) => `/${decodeURIComponent(str)}`,
|
3862
|
+
valueToString: (val) => encodeURIComponent(val.substring(1))
|
3863
|
+
}, {
|
3864
|
+
code: CODE_HTTPS,
|
3865
|
+
name: 'https'
|
3866
|
+
}, {
|
3867
|
+
code: CODE_WS,
|
3868
|
+
name: 'ws'
|
3869
|
+
}, {
|
3870
|
+
code: CODE_WSS,
|
3871
|
+
name: 'wss'
|
3872
|
+
}, {
|
3873
|
+
code: CODE_P2P_WEBSOCKET_STAR,
|
3874
|
+
name: 'p2p-websocket-star'
|
3875
|
+
}, {
|
3876
|
+
code: CODE_P2P_STARDUST,
|
3877
|
+
name: 'p2p-stardust'
|
3878
|
+
}, {
|
3879
|
+
code: CODE_P2P_WEBRTC_STAR,
|
3880
|
+
name: 'p2p-webrtc-star'
|
3881
|
+
}, {
|
3882
|
+
code: CODE_P2P_WEBRTC_DIRECT,
|
3883
|
+
name: 'p2p-webrtc-direct'
|
3884
|
+
}, {
|
3885
|
+
code: CODE_WEBRTC_DIRECT,
|
3886
|
+
name: 'webrtc-direct'
|
3887
|
+
}, {
|
3888
|
+
code: CODE_WEBRTC,
|
3889
|
+
name: 'webrtc'
|
3890
|
+
}, {
|
3891
|
+
code: CODE_P2P_CIRCUIT,
|
3892
|
+
name: 'p2p-circuit'
|
3893
|
+
}, {
|
3894
|
+
code: CODE_MEMORY,
|
3895
|
+
name: 'memory',
|
3896
|
+
size: V
|
3897
|
+
}];
|
3898
|
+
codecs.forEach(codec => {
|
3899
|
+
registry.addProtocol(codec);
|
3900
|
+
});
|
3901
|
+
|
3902
|
+
function bytesToComponents(bytes) {
|
3903
|
+
const components = [];
|
3611
3904
|
let i = 0;
|
3612
3905
|
while (i < bytes.length) {
|
3613
3906
|
const code = decode$4(bytes, i);
|
3614
|
-
const
|
3615
|
-
const
|
3616
|
-
const size = sizeForAddr(
|
3617
|
-
|
3618
|
-
|
3619
|
-
|
3620
|
-
|
3621
|
-
|
3622
|
-
|
3623
|
-
|
3624
|
-
|
3625
|
-
|
3626
|
-
|
3627
|
-
|
3628
|
-
|
3629
|
-
|
3630
|
-
|
3631
|
-
|
3632
|
-
|
3633
|
-
|
3634
|
-
|
3635
|
-
|
3636
|
-
// however it would have issues if the path had a protocol name in the path
|
3637
|
-
path = stringAddr;
|
3638
|
-
break;
|
3639
|
-
}
|
3640
|
-
}
|
3641
|
-
return {
|
3642
|
-
bytes: Uint8Array.from(bytes),
|
3643
|
-
string: stringTuplesToString(stringTuples),
|
3644
|
-
tuples,
|
3645
|
-
stringTuples,
|
3646
|
-
path
|
3647
|
-
};
|
3907
|
+
const codec = registry.getProtocol(code);
|
3908
|
+
const codeLength = encodingLength$1(code);
|
3909
|
+
const size = sizeForAddr(codec, bytes, i + codeLength);
|
3910
|
+
let sizeLength = 0;
|
3911
|
+
if (size > 0 && codec.size === V) {
|
3912
|
+
sizeLength = encodingLength$1(size);
|
3913
|
+
}
|
3914
|
+
const componentLength = codeLength + sizeLength + size;
|
3915
|
+
const component = {
|
3916
|
+
code,
|
3917
|
+
name: codec.name,
|
3918
|
+
bytes: bytes.subarray(i, i + componentLength)
|
3919
|
+
};
|
3920
|
+
if (size > 0) {
|
3921
|
+
const valueOffset = i + codeLength + sizeLength;
|
3922
|
+
const valueBytes = bytes.subarray(valueOffset, valueOffset + size);
|
3923
|
+
component.value = codec.bytesToValue?.(valueBytes) ?? toString(valueBytes);
|
3924
|
+
}
|
3925
|
+
components.push(component);
|
3926
|
+
i += componentLength;
|
3927
|
+
}
|
3928
|
+
return components;
|
3648
3929
|
}
|
3649
|
-
|
3650
|
-
|
3651
|
-
|
3652
|
-
|
3653
|
-
|
3654
|
-
|
3655
|
-
|
3656
|
-
|
3657
|
-
|
3658
|
-
|
3930
|
+
function componentsToBytes(components) {
|
3931
|
+
let length = 0;
|
3932
|
+
const bytes = [];
|
3933
|
+
for (const component of components) {
|
3934
|
+
if (component.bytes == null) {
|
3935
|
+
const codec = registry.getProtocol(component.code);
|
3936
|
+
const codecLength = encodingLength$1(component.code);
|
3937
|
+
let valueBytes;
|
3938
|
+
let valueLength = 0;
|
3939
|
+
let valueLengthLength = 0;
|
3940
|
+
if (component.value != null) {
|
3941
|
+
valueBytes = codec.valueToBytes?.(component.value) ?? fromString(component.value);
|
3942
|
+
valueLength = valueBytes.byteLength;
|
3943
|
+
if (codec.size === V) {
|
3944
|
+
valueLengthLength = encodingLength$1(valueLength);
|
3945
|
+
}
|
3946
|
+
}
|
3947
|
+
const bytes = new Uint8Array(codecLength + valueLengthLength + valueLength);
|
3948
|
+
// encode the protocol code
|
3949
|
+
let offset = 0;
|
3950
|
+
encodeUint8Array(component.code, bytes, offset);
|
3951
|
+
offset += codecLength;
|
3952
|
+
// if there is a value
|
3953
|
+
if (valueBytes != null) {
|
3954
|
+
// if the value has variable length, encode the length
|
3955
|
+
if (codec.size === V) {
|
3956
|
+
encodeUint8Array(valueLength, bytes, offset);
|
3957
|
+
offset += valueLengthLength;
|
3958
|
+
}
|
3959
|
+
// finally encode the value
|
3960
|
+
bytes.set(valueBytes, offset);
|
3961
|
+
}
|
3962
|
+
component.bytes = bytes;
|
3659
3963
|
}
|
3660
|
-
|
3661
|
-
|
3662
|
-
|
3964
|
+
bytes.push(component.bytes);
|
3965
|
+
length += component.bytes.byteLength;
|
3966
|
+
}
|
3967
|
+
return concat(bytes, length);
|
3663
3968
|
}
|
3664
|
-
|
3665
|
-
|
3666
|
-
|
3667
|
-
|
3668
|
-
|
3669
|
-
|
3670
|
-
|
3671
|
-
|
3672
|
-
|
3673
|
-
|
3674
|
-
|
3675
|
-
|
3969
|
+
function stringToComponents(string) {
|
3970
|
+
if (string.charAt(0) !== '/') {
|
3971
|
+
throw new InvalidMultiaddrError('String multiaddr must start with "/"');
|
3972
|
+
}
|
3973
|
+
const components = [];
|
3974
|
+
let collecting = 'protocol';
|
3975
|
+
let value = '';
|
3976
|
+
let protocol = '';
|
3977
|
+
for (let i = 1; i < string.length; i++) {
|
3978
|
+
const char = string.charAt(i);
|
3979
|
+
if (char !== '/') {
|
3980
|
+
if (collecting === 'protocol') {
|
3981
|
+
protocol += string.charAt(i);
|
3982
|
+
}
|
3983
|
+
else {
|
3984
|
+
value += string.charAt(i);
|
3985
|
+
}
|
3986
|
+
}
|
3987
|
+
const ended = i === string.length - 1;
|
3988
|
+
if (char === '/' || ended) {
|
3989
|
+
const codec = registry.getProtocol(protocol);
|
3990
|
+
if (collecting === 'protocol') {
|
3991
|
+
if (codec.size == null || codec.size === 0) {
|
3992
|
+
// a protocol without an address, eg. `/tls`
|
3993
|
+
components.push({
|
3994
|
+
code: codec.code,
|
3995
|
+
name: codec.name
|
3996
|
+
});
|
3997
|
+
value = '';
|
3998
|
+
protocol = '';
|
3999
|
+
collecting = 'protocol';
|
4000
|
+
continue;
|
4001
|
+
}
|
4002
|
+
else if (ended) {
|
4003
|
+
throw new InvalidMultiaddrError(`Component ${protocol} was missing value`);
|
4004
|
+
}
|
4005
|
+
// continue collecting value
|
4006
|
+
collecting = 'value';
|
4007
|
+
}
|
4008
|
+
else if (collecting === 'value') {
|
4009
|
+
const component = {
|
4010
|
+
code: codec.code,
|
4011
|
+
name: codec.name
|
4012
|
+
};
|
4013
|
+
if (codec.size != null && codec.size !== 0) {
|
4014
|
+
if (value === '') {
|
4015
|
+
throw new InvalidMultiaddrError(`Component ${protocol} was missing value`);
|
4016
|
+
}
|
4017
|
+
component.value = codec.stringToValue?.(value) ?? value;
|
4018
|
+
}
|
4019
|
+
components.push(component);
|
4020
|
+
value = '';
|
4021
|
+
protocol = '';
|
4022
|
+
collecting = 'protocol';
|
4023
|
+
}
|
4024
|
+
}
|
4025
|
+
}
|
4026
|
+
if (protocol !== '' && value !== '') {
|
4027
|
+
throw new InvalidMultiaddrError('Incomplete multiaddr');
|
4028
|
+
}
|
4029
|
+
return components;
|
4030
|
+
}
|
4031
|
+
function componentsToString(components) {
|
4032
|
+
return `/${components.flatMap(component => {
|
4033
|
+
if (component.value == null) {
|
4034
|
+
return component.name;
|
4035
|
+
}
|
4036
|
+
const codec = registry.getProtocol(component.code);
|
4037
|
+
if (codec == null) {
|
4038
|
+
throw new InvalidMultiaddrError(`Unknown protocol code ${component.code}`);
|
4039
|
+
}
|
4040
|
+
return [
|
4041
|
+
component.name,
|
4042
|
+
codec.valueToString?.(component.value) ?? component.value
|
4043
|
+
];
|
4044
|
+
}).join('/')}`;
|
3676
4045
|
}
|
3677
4046
|
/**
|
3678
4047
|
* For the passed address, return the serialized size
|
3679
4048
|
*/
|
3680
|
-
function sizeForAddr(
|
3681
|
-
if (
|
3682
|
-
return p.size / 8;
|
3683
|
-
}
|
3684
|
-
else if (p.size === 0) {
|
4049
|
+
function sizeForAddr(codec, bytes, offset) {
|
4050
|
+
if (codec.size == null || codec.size === 0) {
|
3685
4051
|
return 0;
|
3686
4052
|
}
|
3687
|
-
|
3688
|
-
|
3689
|
-
return size + encodingLength$1(size);
|
3690
|
-
}
|
3691
|
-
}
|
3692
|
-
function cleanPath(str) {
|
3693
|
-
return '/' + str.trim().split('/').filter((a) => a).join('/');
|
3694
|
-
}
|
3695
|
-
class ParseError extends Error {
|
3696
|
-
static name = 'ParseError';
|
3697
|
-
name = 'ParseError';
|
3698
|
-
constructor(str) {
|
3699
|
-
super(`Error parsing address: ${str}`);
|
4053
|
+
if (codec.size > 0) {
|
4054
|
+
return codec.size / 8;
|
3700
4055
|
}
|
4056
|
+
return decode$4(bytes, offset);
|
3701
4057
|
}
|
3702
4058
|
|
3703
|
-
/* eslint-disable complexity */
|
3704
4059
|
const inspect = Symbol.for('nodejs.util.inspect.custom');
|
3705
|
-
const symbol = Symbol.for('@multiformats/
|
4060
|
+
const symbol = Symbol.for('@multiformats/multiaddr');
|
3706
4061
|
const DNS_CODES = [
|
3707
|
-
|
3708
|
-
|
3709
|
-
|
3710
|
-
|
4062
|
+
CODE_DNS,
|
4063
|
+
CODE_DNS4,
|
4064
|
+
CODE_DNS6,
|
4065
|
+
CODE_DNSADDR
|
3711
4066
|
];
|
3712
4067
|
class NoAvailableResolverError extends Error {
|
3713
4068
|
constructor(message = 'No available resolver') {
|
@@ -3715,44 +4070,56 @@ class NoAvailableResolverError extends Error {
|
|
3715
4070
|
this.name = 'NoAvailableResolverError';
|
3716
4071
|
}
|
3717
4072
|
}
|
4073
|
+
function toComponents(addr) {
|
4074
|
+
if (addr == null) {
|
4075
|
+
addr = '/';
|
4076
|
+
}
|
4077
|
+
if (isMultiaddr(addr)) {
|
4078
|
+
return addr.getComponents();
|
4079
|
+
}
|
4080
|
+
if (addr instanceof Uint8Array) {
|
4081
|
+
return bytesToComponents(addr);
|
4082
|
+
}
|
4083
|
+
if (typeof addr === 'string') {
|
4084
|
+
addr = addr
|
4085
|
+
.replace(/\/(\/)+/, '/')
|
4086
|
+
.replace(/(\/)+$/, '');
|
4087
|
+
if (addr === '') {
|
4088
|
+
addr = '/';
|
4089
|
+
}
|
4090
|
+
return stringToComponents(addr);
|
4091
|
+
}
|
4092
|
+
if (Array.isArray(addr)) {
|
4093
|
+
return addr;
|
4094
|
+
}
|
4095
|
+
throw new InvalidMultiaddrError('Must be a string, Uint8Array, Component[], or another Multiaddr');
|
4096
|
+
}
|
3718
4097
|
/**
|
3719
4098
|
* Creates a {@link Multiaddr} from a {@link MultiaddrInput}
|
3720
4099
|
*/
|
3721
4100
|
class Multiaddr {
|
3722
|
-
bytes;
|
3723
|
-
#string;
|
3724
|
-
#tuples;
|
3725
|
-
#stringTuples;
|
3726
|
-
#path;
|
3727
4101
|
[symbol] = true;
|
3728
|
-
|
3729
|
-
|
3730
|
-
|
3731
|
-
|
3732
|
-
|
3733
|
-
|
3734
|
-
|
3735
|
-
|
3736
|
-
|
3737
|
-
else if (typeof addr === 'string') {
|
3738
|
-
if (addr.length > 0 && addr.charAt(0) !== '/') {
|
3739
|
-
throw new Error(`multiaddr "${addr}" must start with a "/"`);
|
3740
|
-
}
|
3741
|
-
parts = stringToMultiaddrParts(addr);
|
3742
|
-
}
|
3743
|
-
else if (isMultiaddr(addr)) { // Multiaddr
|
3744
|
-
parts = bytesToMultiaddrParts(addr.bytes);
|
4102
|
+
#components;
|
4103
|
+
// cache string representation
|
4104
|
+
#string;
|
4105
|
+
// cache byte representation
|
4106
|
+
#bytes;
|
4107
|
+
constructor(addr = '/', options = {}) {
|
4108
|
+
this.#components = toComponents(addr);
|
4109
|
+
if (options.validate !== false) {
|
4110
|
+
validate(this);
|
3745
4111
|
}
|
3746
|
-
|
3747
|
-
|
4112
|
+
}
|
4113
|
+
get bytes() {
|
4114
|
+
if (this.#bytes == null) {
|
4115
|
+
this.#bytes = componentsToBytes(this.#components);
|
3748
4116
|
}
|
3749
|
-
this
|
3750
|
-
this.#string = parts.string;
|
3751
|
-
this.#tuples = parts.tuples;
|
3752
|
-
this.#stringTuples = parts.stringTuples;
|
3753
|
-
this.#path = parts.path;
|
4117
|
+
return this.#bytes;
|
3754
4118
|
}
|
3755
4119
|
toString() {
|
4120
|
+
if (this.#string == null) {
|
4121
|
+
this.#string = componentsToString(this.#components);
|
4122
|
+
}
|
3756
4123
|
return this.#string;
|
3757
4124
|
}
|
3758
4125
|
toJSON() {
|
@@ -3764,31 +4131,25 @@ class Multiaddr {
|
|
3764
4131
|
let host;
|
3765
4132
|
let port;
|
3766
4133
|
let zone = '';
|
3767
|
-
const
|
3768
|
-
|
3769
|
-
const ip4 = getProtocol('ip4');
|
3770
|
-
const ip6 = getProtocol('ip6');
|
3771
|
-
const dns6 = getProtocol('dns6');
|
3772
|
-
const ip6zone = getProtocol('ip6zone');
|
3773
|
-
for (const [code, value] of this.stringTuples()) {
|
3774
|
-
if (code === ip6zone.code) {
|
4134
|
+
for (const { code, name, value } of this.#components) {
|
4135
|
+
if (code === CODE_IP6ZONE) {
|
3775
4136
|
zone = `%${value ?? ''}`;
|
3776
4137
|
}
|
3777
4138
|
// default to https when protocol & port are omitted from DNS addrs
|
3778
4139
|
if (DNS_CODES.includes(code)) {
|
3779
|
-
transport =
|
4140
|
+
transport = 'tcp';
|
3780
4141
|
port = 443;
|
3781
4142
|
host = `${value ?? ''}${zone}`;
|
3782
|
-
family = code ===
|
4143
|
+
family = code === CODE_DNS6 ? 6 : 4;
|
3783
4144
|
}
|
3784
|
-
if (code ===
|
3785
|
-
transport =
|
4145
|
+
if (code === CODE_TCP || code === CODE_UDP) {
|
4146
|
+
transport = name === 'tcp' ? 'tcp' : 'udp';
|
3786
4147
|
port = parseInt(value ?? '');
|
3787
4148
|
}
|
3788
|
-
if (code ===
|
3789
|
-
transport =
|
4149
|
+
if (code === CODE_IP4 || code === CODE_IP6) {
|
4150
|
+
transport = 'tcp';
|
3790
4151
|
host = `${value ?? ''}${zone}`;
|
3791
|
-
family = code ===
|
4152
|
+
family = code === CODE_IP6 ? 6 : 4;
|
3792
4153
|
}
|
3793
4154
|
}
|
3794
4155
|
if (family == null || transport == null || host == null || port == null) {
|
@@ -3802,25 +4163,44 @@ class Multiaddr {
|
|
3802
4163
|
};
|
3803
4164
|
return opts;
|
3804
4165
|
}
|
4166
|
+
getComponents() {
|
4167
|
+
return [
|
4168
|
+
...this.#components
|
4169
|
+
];
|
4170
|
+
}
|
3805
4171
|
protos() {
|
3806
|
-
return this.#
|
4172
|
+
return this.#components.map(({ code, value }) => {
|
4173
|
+
const codec = registry.getProtocol(code);
|
4174
|
+
return {
|
4175
|
+
code,
|
4176
|
+
size: codec.size ?? 0,
|
4177
|
+
name: codec.name,
|
4178
|
+
resolvable: Boolean(codec.resolvable),
|
4179
|
+
path: Boolean(codec.path)
|
4180
|
+
};
|
4181
|
+
});
|
3807
4182
|
}
|
3808
4183
|
protoCodes() {
|
3809
|
-
return this.#
|
4184
|
+
return this.#components.map(({ code }) => code);
|
3810
4185
|
}
|
3811
4186
|
protoNames() {
|
3812
|
-
return this.#
|
4187
|
+
return this.#components.map(({ name }) => name);
|
3813
4188
|
}
|
3814
4189
|
tuples() {
|
3815
|
-
return this.#
|
4190
|
+
return this.#components.map(({ code, value }) => {
|
3816
4191
|
if (value == null) {
|
3817
4192
|
return [code];
|
3818
4193
|
}
|
3819
|
-
|
4194
|
+
const codec = registry.getProtocol(code);
|
4195
|
+
const output = [code];
|
4196
|
+
if (value != null) {
|
4197
|
+
output.push(codec.valueToBytes?.(value) ?? fromString(value));
|
4198
|
+
}
|
4199
|
+
return output;
|
3820
4200
|
});
|
3821
4201
|
}
|
3822
4202
|
stringTuples() {
|
3823
|
-
return this.#
|
4203
|
+
return this.#components.map(({ code, value }) => {
|
3824
4204
|
if (value == null) {
|
3825
4205
|
return [code];
|
3826
4206
|
}
|
@@ -3828,37 +4208,47 @@ class Multiaddr {
|
|
3828
4208
|
});
|
3829
4209
|
}
|
3830
4210
|
encapsulate(addr) {
|
3831
|
-
|
3832
|
-
return new Multiaddr(
|
4211
|
+
const ma = new Multiaddr(addr);
|
4212
|
+
return new Multiaddr([
|
4213
|
+
...this.#components,
|
4214
|
+
...ma.getComponents()
|
4215
|
+
], {
|
4216
|
+
validate: false
|
4217
|
+
});
|
3833
4218
|
}
|
3834
4219
|
decapsulate(addr) {
|
3835
4220
|
const addrString = addr.toString();
|
3836
4221
|
const s = this.toString();
|
3837
4222
|
const i = s.lastIndexOf(addrString);
|
3838
4223
|
if (i < 0) {
|
3839
|
-
throw new
|
4224
|
+
throw new InvalidParametersError(`Address ${this.toString()} does not contain subaddress: ${addr.toString()}`);
|
3840
4225
|
}
|
3841
|
-
return new Multiaddr(s.slice(0, i)
|
4226
|
+
return new Multiaddr(s.slice(0, i), {
|
4227
|
+
validate: false
|
4228
|
+
});
|
3842
4229
|
}
|
3843
4230
|
decapsulateCode(code) {
|
3844
|
-
|
3845
|
-
for (let i =
|
3846
|
-
if (
|
3847
|
-
|
4231
|
+
let index;
|
4232
|
+
for (let i = this.#components.length - 1; i > -1; i--) {
|
4233
|
+
if (this.#components[i].code === code) {
|
4234
|
+
index = i;
|
4235
|
+
break;
|
3848
4236
|
}
|
3849
4237
|
}
|
3850
|
-
return this
|
4238
|
+
return new Multiaddr(this.#components.slice(0, index), {
|
4239
|
+
validate: false
|
4240
|
+
});
|
3851
4241
|
}
|
3852
4242
|
getPeerId() {
|
3853
4243
|
try {
|
3854
4244
|
let tuples = [];
|
3855
|
-
this.
|
3856
|
-
if (code ===
|
3857
|
-
tuples.push([code,
|
4245
|
+
this.#components.forEach(({ code, value }) => {
|
4246
|
+
if (code === CODE_P2P) {
|
4247
|
+
tuples.push([code, value]);
|
3858
4248
|
}
|
3859
4249
|
// if this is a p2p-circuit address, return the target peer id if present
|
3860
4250
|
// not the peer id of the relay
|
3861
|
-
if (code ===
|
4251
|
+
if (code === CODE_P2P_CIRCUIT) {
|
3862
4252
|
tuples = [];
|
3863
4253
|
}
|
3864
4254
|
});
|
@@ -3869,10 +4259,10 @@ class Multiaddr {
|
|
3869
4259
|
// peer id is base58btc encoded string but not multibase encoded so add the `z`
|
3870
4260
|
// prefix so we can validate that it is correctly encoded
|
3871
4261
|
if (peerIdStr[0] === 'Q' || peerIdStr[0] === '1') {
|
3872
|
-
return toString
|
4262
|
+
return toString(base58btc.decode(`z${peerIdStr}`), 'base58btc');
|
3873
4263
|
}
|
3874
4264
|
// try to parse peer id as CID
|
3875
|
-
return toString
|
4265
|
+
return toString(CID.parse(peerIdStr).multihash.bytes, 'base58btc');
|
3876
4266
|
}
|
3877
4267
|
return null;
|
3878
4268
|
}
|
@@ -3881,7 +4271,14 @@ class Multiaddr {
|
|
3881
4271
|
}
|
3882
4272
|
}
|
3883
4273
|
getPath() {
|
3884
|
-
|
4274
|
+
for (const component of this.#components) {
|
4275
|
+
const codec = registry.getProtocol(component.code);
|
4276
|
+
if (!codec.path) {
|
4277
|
+
continue;
|
4278
|
+
}
|
4279
|
+
return component.value ?? null;
|
4280
|
+
}
|
4281
|
+
return null;
|
3885
4282
|
}
|
3886
4283
|
equals(addr) {
|
3887
4284
|
return equals(this.bytes, addr.bytes);
|
@@ -3910,15 +4307,14 @@ class Multiaddr {
|
|
3910
4307
|
port: options.port
|
3911
4308
|
};
|
3912
4309
|
}
|
3913
|
-
isThinWaistAddress(
|
3914
|
-
|
3915
|
-
if (protos.length !== 2) {
|
4310
|
+
isThinWaistAddress() {
|
4311
|
+
if (this.#components.length !== 2) {
|
3916
4312
|
return false;
|
3917
4313
|
}
|
3918
|
-
if (
|
4314
|
+
if (this.#components[0].code !== CODE_IP4 && this.#components[0].code !== CODE_IP6) {
|
3919
4315
|
return false;
|
3920
4316
|
}
|
3921
|
-
if (
|
4317
|
+
if (this.#components[1].code !== CODE_TCP && this.#components[1].code !== CODE_UDP) {
|
3922
4318
|
return false;
|
3923
4319
|
}
|
3924
4320
|
return true;
|
@@ -3936,9 +4332,23 @@ class Multiaddr {
|
|
3936
4332
|
* ```
|
3937
4333
|
*/
|
3938
4334
|
[inspect]() {
|
3939
|
-
return `Multiaddr(${this
|
4335
|
+
return `Multiaddr(${this.toString()})`;
|
3940
4336
|
}
|
3941
4337
|
}
|
4338
|
+
/**
|
4339
|
+
* Ensures all multiaddr tuples are correct. Throws if any invalid protocols or
|
4340
|
+
* values are encountered.
|
4341
|
+
*/
|
4342
|
+
function validate(addr) {
|
4343
|
+
addr.getComponents()
|
4344
|
+
.forEach(component => {
|
4345
|
+
const codec = registry.getProtocol(component.code);
|
4346
|
+
if (component.value == null) {
|
4347
|
+
return;
|
4348
|
+
}
|
4349
|
+
codec.validate?.(component.value);
|
4350
|
+
});
|
4351
|
+
}
|
3942
4352
|
|
3943
4353
|
/**
|
3944
4354
|
* @packageDocumentation
|
@@ -4033,9 +4443,42 @@ class Multiaddr {
|
|
4033
4443
|
* console.info(resolved)
|
4034
4444
|
* // [Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...')...]
|
4035
4445
|
* ```
|
4446
|
+
*
|
4447
|
+
* @example Adding custom protocols
|
4448
|
+
*
|
4449
|
+
* To add application-specific or experimental protocols, add a protocol codec
|
4450
|
+
* to the protocol registry:
|
4451
|
+
*
|
4452
|
+
* ```ts
|
4453
|
+
* import { registry, V, multiaddr } from '@multiformats/multiaddr'
|
4454
|
+
* import type { ProtocolCodec } from '@multiformats/multiaddr'
|
4455
|
+
*
|
4456
|
+
* const maWithCustomTuple = '/custom-protocol/hello'
|
4457
|
+
*
|
4458
|
+
* // throws UnknownProtocolError
|
4459
|
+
* multiaddr(maWithCustomTuple)
|
4460
|
+
*
|
4461
|
+
* const protocol: ProtocolCodec = {
|
4462
|
+
* code: 2059,
|
4463
|
+
* name: 'custom-protocol',
|
4464
|
+
* size: V
|
4465
|
+
* // V means variable length, can also be 0, a positive integer (e.g. a fixed
|
4466
|
+
* // length or omitted
|
4467
|
+
* }
|
4468
|
+
*
|
4469
|
+
* registry.addProtocol(protocol)
|
4470
|
+
*
|
4471
|
+
* // does not throw UnknownProtocolError
|
4472
|
+
* multiaddr(maWithCustomTuple)
|
4473
|
+
*
|
4474
|
+
* // protocols can also be removed
|
4475
|
+
* registry.removeProtocol(protocol.code)
|
4476
|
+
* ```
|
4036
4477
|
*/
|
4037
4478
|
/**
|
4038
4479
|
* All configured {@link Resolver}s
|
4480
|
+
*
|
4481
|
+
* @deprecated DNS resolving will be removed in a future release
|
4039
4482
|
*/
|
4040
4483
|
const resolvers = new Map();
|
4041
4484
|
/**
|
@@ -4631,9 +5074,9 @@ class ConnectionManager extends TypedEventEmitter {
|
|
4631
5074
|
log$1.warn(`Already connected to peer ${peerId.toString()}. Not dialing.`);
|
4632
5075
|
return false;
|
4633
5076
|
}
|
4634
|
-
const isSameShard = await this.
|
5077
|
+
const isSameShard = await this.isPeerOnSameShard(peerId);
|
4635
5078
|
if (!isSameShard) {
|
4636
|
-
const shardInfo = await this.getPeerShardInfo(peerId
|
5079
|
+
const shardInfo = await this.getPeerShardInfo(peerId);
|
4637
5080
|
log$1.warn(`Discovered peer ${peerId.toString()} with ShardInfo ${shardInfo} is not part of any of the configured pubsub topics (${this.pubsubTopics}).
|
4638
5081
|
Not dialing.`);
|
4639
5082
|
return false;
|
@@ -4689,17 +5132,25 @@ class ConnectionManager extends TypedEventEmitter {
|
|
4689
5132
|
return [];
|
4690
5133
|
}
|
4691
5134
|
}
|
4692
|
-
async
|
4693
|
-
const shardInfo = await this.getPeerShardInfo(peerId
|
4694
|
-
|
4695
|
-
if (!shardInfo)
|
5135
|
+
async isPeerOnSameShard(peerId) {
|
5136
|
+
const shardInfo = await this.getPeerShardInfo(peerId);
|
5137
|
+
if (!shardInfo) {
|
4696
5138
|
return true;
|
5139
|
+
}
|
4697
5140
|
const pubsubTopics = shardInfoToPubsubTopics(shardInfo);
|
4698
5141
|
const isTopicConfigured = pubsubTopics.some((topic) => this.pubsubTopics.includes(topic));
|
4699
5142
|
return isTopicConfigured;
|
4700
5143
|
}
|
4701
|
-
async
|
4702
|
-
const
|
5144
|
+
async isPeerOnPubsubTopic(peerId, pubsubTopic) {
|
5145
|
+
const shardInfo = await this.getPeerShardInfo(peerId);
|
5146
|
+
if (!shardInfo) {
|
5147
|
+
return true;
|
5148
|
+
}
|
5149
|
+
const pubsubTopics = shardInfoToPubsubTopics(shardInfo);
|
5150
|
+
return pubsubTopics.some((t) => t === pubsubTopic);
|
5151
|
+
}
|
5152
|
+
async getPeerShardInfo(peerId) {
|
5153
|
+
const peer = await this.libp2p.peerStore.get(peerId);
|
4703
5154
|
const shardInfoBytes = peer.metadata.get("shardInfo");
|
4704
5155
|
if (!shardInfoBytes)
|
4705
5156
|
return undefined;
|
@@ -4745,13 +5196,15 @@ class ConnectionManager extends TypedEventEmitter {
|
|
4745
5196
|
|
4746
5197
|
const log = new Logger("metadata");
|
4747
5198
|
const MetadataCodec = "/vac/waku/metadata/1.0.0";
|
4748
|
-
class Metadata
|
5199
|
+
class Metadata {
|
4749
5200
|
pubsubTopics;
|
5201
|
+
streamManager;
|
4750
5202
|
libp2pComponents;
|
4751
5203
|
handshakesConfirmed = new Map();
|
5204
|
+
multicodec = MetadataCodec;
|
4752
5205
|
constructor(pubsubTopics, libp2p) {
|
4753
|
-
super(MetadataCodec, libp2p.components, pubsubTopics);
|
4754
5206
|
this.pubsubTopics = pubsubTopics;
|
5207
|
+
this.streamManager = new StreamManager(MetadataCodec, libp2p);
|
4755
5208
|
this.libp2pComponents = libp2p;
|
4756
5209
|
void libp2p.registrar.handle(MetadataCodec, (streamData) => {
|
4757
5210
|
void this.onRequest(streamData);
|
@@ -4771,7 +5224,7 @@ class Metadata extends BaseProtocol {
|
|
4771
5224
|
}
|
4772
5225
|
let stream;
|
4773
5226
|
try {
|
4774
|
-
stream = await this.getStream(peerId);
|
5227
|
+
stream = await this.streamManager.getStream(peerId);
|
4775
5228
|
}
|
4776
5229
|
catch (error) {
|
4777
5230
|
log.error("Failed to get stream", error);
|
@@ -4854,4 +5307,85 @@ function wakuMetadata(pubsubTopics) {
|
|
4854
5307
|
return (components) => new Metadata(pubsubTopics, components);
|
4855
5308
|
}
|
4856
5309
|
|
4857
|
-
|
5310
|
+
/**
|
5311
|
+
* Deterministic Message Hashing as defined in
|
5312
|
+
* [14/WAKU2-MESSAGE](https://rfc.vac.dev/spec/14/#deterministic-message-hashing)
|
5313
|
+
*
|
5314
|
+
* Computes a SHA-256 hash of the concatenation of pubsub topic, payload, content topic, meta, and timestamp.
|
5315
|
+
*
|
5316
|
+
* @param pubsubTopic - The pubsub topic string
|
5317
|
+
* @param message - The message to be hashed
|
5318
|
+
* @returns A Uint8Array containing the SHA-256 hash
|
5319
|
+
*
|
5320
|
+
* @example
|
5321
|
+
* ```typescript
|
5322
|
+
* import { messageHash } from "@waku/core";
|
5323
|
+
*
|
5324
|
+
* const pubsubTopic = "/waku/2/default-waku/proto";
|
5325
|
+
* const message = {
|
5326
|
+
* payload: new Uint8Array([1, 2, 3, 4]),
|
5327
|
+
* contentTopic: "/waku/2/default-content/proto",
|
5328
|
+
* meta: new Uint8Array([5, 6, 7, 8]),
|
5329
|
+
* timestamp: new Date()
|
5330
|
+
* };
|
5331
|
+
*
|
5332
|
+
* const hash = messageHash(pubsubTopic, message);
|
5333
|
+
* ```
|
5334
|
+
*/
|
5335
|
+
function messageHash(pubsubTopic, message) {
|
5336
|
+
const pubsubTopicBytes = utf8ToBytes(pubsubTopic);
|
5337
|
+
const contentTopicBytes = utf8ToBytes(message.contentTopic);
|
5338
|
+
const timestampBytes = tryConvertTimestampToBytes(message.timestamp);
|
5339
|
+
const bytes = concat$1([
|
5340
|
+
pubsubTopicBytes,
|
5341
|
+
message.payload,
|
5342
|
+
contentTopicBytes,
|
5343
|
+
message.meta,
|
5344
|
+
timestampBytes
|
5345
|
+
].filter(isDefined));
|
5346
|
+
return sha256(bytes);
|
5347
|
+
}
|
5348
|
+
function tryConvertTimestampToBytes(timestamp) {
|
5349
|
+
if (!timestamp) {
|
5350
|
+
return;
|
5351
|
+
}
|
5352
|
+
let bigIntTimestamp;
|
5353
|
+
if (typeof timestamp === "bigint") {
|
5354
|
+
bigIntTimestamp = timestamp;
|
5355
|
+
}
|
5356
|
+
else {
|
5357
|
+
bigIntTimestamp = BigInt(timestamp.valueOf()) * 1000000n;
|
5358
|
+
}
|
5359
|
+
return numberToBytes(bigIntTimestamp);
|
5360
|
+
}
|
5361
|
+
/**
|
5362
|
+
* Computes a deterministic message hash and returns it as a hexadecimal string.
|
5363
|
+
* This is a convenience wrapper around messageHash that converts the result to a hex string.
|
5364
|
+
*
|
5365
|
+
* @param pubsubTopic - The pubsub topic string
|
5366
|
+
* @param message - The message to be hashed
|
5367
|
+
* @returns A string containing the hex representation of the SHA-256 hash
|
5368
|
+
*
|
5369
|
+
* @example
|
5370
|
+
* ```typescript
|
5371
|
+
* import { messageHashStr } from "@waku/core";
|
5372
|
+
*
|
5373
|
+
* const pubsubTopic = "/waku/2/default-waku/proto";
|
5374
|
+
* const message = {
|
5375
|
+
* payload: new Uint8Array([1, 2, 3, 4]),
|
5376
|
+
* contentTopic: "/waku/2/default-content/proto",
|
5377
|
+
* meta: new Uint8Array([5, 6, 7, 8]),
|
5378
|
+
* timestamp: new Date()
|
5379
|
+
* };
|
5380
|
+
*
|
5381
|
+
* const hashString = messageHashStr(pubsubTopic, message);
|
5382
|
+
* console.log(hashString); // e.g. "a1b2c3d4..."
|
5383
|
+
* ```
|
5384
|
+
*/
|
5385
|
+
function messageHashStr(pubsubTopic, message) {
|
5386
|
+
const hash = messageHash(pubsubTopic, message);
|
5387
|
+
const hashStr = bytesToHex(hash);
|
5388
|
+
return hashStr;
|
5389
|
+
}
|
5390
|
+
|
5391
|
+
export { ConnectionManager, FilterCodecs, FilterCore, LightPushCodec, LightPushCore, MetadataCodec, StoreCodec, StoreCore, StreamManager, createEncoder, index$3 as message, messageHash, messageHashStr, wakuMetadata, index$2 as waku_filter, index$1 as waku_light_push, index as waku_store };
|