@waku/core 0.0.36-f7c290d.0 → 0.0.36
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 +1008 -594
- package/bundle/lib/message/version_0.js +1 -2
- package/bundle/{version_0-CyeTW0Vr.js → version_0-9DPFjcJG.js} +1570 -6
- package/dist/.tsbuildinfo +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 +4 -3
- package/dist/lib/filter/filter.js +9 -7
- 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 +1 -1
- package/dist/lib/metadata/metadata.js +6 -4
- package/dist/lib/metadata/metadata.js.map +1 -1
- package/dist/lib/store/store.d.ts +4 -3
- package/dist/lib/store/store.js +6 -4
- 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 +125 -1
- package/src/lib/connection_manager/connection_manager.ts +24 -16
- package/src/lib/filter/filter.ts +13 -8
- package/src/lib/light_push/light_push.ts +8 -5
- package/src/lib/metadata/metadata.ts +8 -5
- package/src/lib/store/store.ts +8 -5
- 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;
|
@@ -2012,25 +2009,38 @@ function queuelessPushable() {
|
|
2012
2009
|
function isAsyncIterable$1(thing) {
|
2013
2010
|
return thing[Symbol.asyncIterator] != null;
|
2014
2011
|
}
|
2015
|
-
async function addAllToPushable(sources, output) {
|
2012
|
+
async function addAllToPushable(sources, output, signal) {
|
2016
2013
|
try {
|
2017
2014
|
await Promise.all(sources.map(async (source) => {
|
2018
2015
|
for await (const item of source) {
|
2019
|
-
await output.push(item
|
2016
|
+
await output.push(item, {
|
2017
|
+
signal
|
2018
|
+
});
|
2019
|
+
signal.throwIfAborted();
|
2020
2020
|
}
|
2021
2021
|
}));
|
2022
|
-
await output.end(
|
2022
|
+
await output.end(undefined, {
|
2023
|
+
signal
|
2024
|
+
});
|
2023
2025
|
}
|
2024
2026
|
catch (err) {
|
2025
|
-
await output.end(err
|
2027
|
+
await output.end(err, {
|
2028
|
+
signal
|
2029
|
+
})
|
2026
2030
|
.catch(() => { });
|
2027
2031
|
}
|
2028
2032
|
}
|
2029
2033
|
async function* mergeSources(sources) {
|
2034
|
+
const controller = new AbortController();
|
2030
2035
|
const output = queuelessPushable();
|
2031
|
-
addAllToPushable(sources, output)
|
2036
|
+
addAllToPushable(sources, output, controller.signal)
|
2032
2037
|
.catch(() => { });
|
2033
|
-
|
2038
|
+
try {
|
2039
|
+
yield* output;
|
2040
|
+
}
|
2041
|
+
finally {
|
2042
|
+
controller.abort();
|
2043
|
+
}
|
2034
2044
|
}
|
2035
2045
|
function* mergeSyncSources(syncSources) {
|
2036
2046
|
for (const source of syncSources) {
|
@@ -2136,6 +2146,129 @@ const duplexPipelineFn = (duplex) => {
|
|
2136
2146
|
};
|
2137
2147
|
};
|
2138
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
|
+
|
2139
2272
|
// Unique ID creation requires a high quality random # generator. In the browser we therefore
|
2140
2273
|
// require the crypto API and do not support built-in fallback to lower quality random number
|
2141
2274
|
// generators (like Math.random()).
|
@@ -2303,12 +2436,14 @@ const FilterCodecs = {
|
|
2303
2436
|
SUBSCRIBE: "/vac/waku/filter-subscribe/2.0.0-beta1",
|
2304
2437
|
PUSH: "/vac/waku/filter-push/2.0.0-beta1"
|
2305
2438
|
};
|
2306
|
-
class FilterCore
|
2439
|
+
class FilterCore {
|
2307
2440
|
pubsubTopics;
|
2441
|
+
streamManager;
|
2308
2442
|
static handleIncomingMessage;
|
2443
|
+
multicodec = FilterCodecs.SUBSCRIBE;
|
2309
2444
|
constructor(handleIncomingMessage, pubsubTopics, libp2p) {
|
2310
|
-
super(FilterCodecs.SUBSCRIBE, libp2p.components, pubsubTopics);
|
2311
2445
|
this.pubsubTopics = pubsubTopics;
|
2446
|
+
this.streamManager = new StreamManager(FilterCodecs.SUBSCRIBE, libp2p.components);
|
2312
2447
|
// TODO(weboko): remove when @waku/sdk 0.0.33 is released
|
2313
2448
|
const prevHandler = FilterCore.handleIncomingMessage;
|
2314
2449
|
FilterCore.handleIncomingMessage = !prevHandler
|
@@ -2337,7 +2472,7 @@ class FilterCore extends BaseProtocol {
|
|
2337
2472
|
});
|
2338
2473
|
}
|
2339
2474
|
async subscribe(pubsubTopic, peerId, contentTopics) {
|
2340
|
-
const stream = await this.getStream(peerId);
|
2475
|
+
const stream = await this.streamManager.getStream(peerId);
|
2341
2476
|
const request = FilterSubscribeRpc.createSubscribeRequest(pubsubTopic, contentTopics);
|
2342
2477
|
let res;
|
2343
2478
|
try {
|
@@ -2372,7 +2507,7 @@ class FilterCore extends BaseProtocol {
|
|
2372
2507
|
async unsubscribe(pubsubTopic, peerId, contentTopics) {
|
2373
2508
|
let stream;
|
2374
2509
|
try {
|
2375
|
-
stream = await this.getStream(peerId);
|
2510
|
+
stream = await this.streamManager.getStream(peerId);
|
2376
2511
|
}
|
2377
2512
|
catch (error) {
|
2378
2513
|
log$5.error(`Failed to get a stream for remote peer${peerId.toString()}`, error);
|
@@ -2404,7 +2539,7 @@ class FilterCore extends BaseProtocol {
|
|
2404
2539
|
};
|
2405
2540
|
}
|
2406
2541
|
async unsubscribeAll(pubsubTopic, peerId) {
|
2407
|
-
const stream = await this.getStream(peerId);
|
2542
|
+
const stream = await this.streamManager.getStream(peerId);
|
2408
2543
|
const request = FilterSubscribeRpc.createUnsubscribeAllRequest(pubsubTopic);
|
2409
2544
|
const res = await pipe([request.encode()], encode, stream, decode, async (source) => await all(source));
|
2410
2545
|
if (!res || !res.length) {
|
@@ -2435,7 +2570,7 @@ class FilterCore extends BaseProtocol {
|
|
2435
2570
|
async ping(peerId) {
|
2436
2571
|
let stream;
|
2437
2572
|
try {
|
2438
|
-
stream = await this.getStream(peerId);
|
2573
|
+
stream = await this.streamManager.getStream(peerId);
|
2439
2574
|
}
|
2440
2575
|
catch (error) {
|
2441
2576
|
log$5.error(`Failed to get a stream for remote peer${peerId.toString()}`, error);
|
@@ -2577,11 +2712,13 @@ const LightPushCodec = "/vac/waku/lightpush/2.0.0-beta1";
|
|
2577
2712
|
/**
|
2578
2713
|
* Implements the [Waku v2 Light Push protocol](https://rfc.vac.dev/spec/19/).
|
2579
2714
|
*/
|
2580
|
-
class LightPushCore
|
2715
|
+
class LightPushCore {
|
2581
2716
|
pubsubTopics;
|
2717
|
+
streamManager;
|
2718
|
+
multicodec = LightPushCodec;
|
2582
2719
|
constructor(pubsubTopics, libp2p) {
|
2583
|
-
super(LightPushCodec, libp2p.components, pubsubTopics);
|
2584
2720
|
this.pubsubTopics = pubsubTopics;
|
2721
|
+
this.streamManager = new StreamManager(LightPushCodec, libp2p.components);
|
2585
2722
|
}
|
2586
2723
|
async preparePushMessage(encoder, message) {
|
2587
2724
|
try {
|
@@ -2625,7 +2762,7 @@ class LightPushCore extends BaseProtocol {
|
|
2625
2762
|
}
|
2626
2763
|
let stream;
|
2627
2764
|
try {
|
2628
|
-
stream = await this.getStream(peerId);
|
2765
|
+
stream = await this.streamManager.getStream(peerId);
|
2629
2766
|
}
|
2630
2767
|
catch (error) {
|
2631
2768
|
log$4.error("Failed to get stream", error);
|
@@ -2804,11 +2941,13 @@ class StoreQueryResponse {
|
|
2804
2941
|
|
2805
2942
|
const log$3 = new Logger("store");
|
2806
2943
|
const StoreCodec = "/vac/waku/store-query/3.0.0";
|
2807
|
-
class StoreCore
|
2944
|
+
class StoreCore {
|
2808
2945
|
pubsubTopics;
|
2946
|
+
streamManager;
|
2947
|
+
multicodec = StoreCodec;
|
2809
2948
|
constructor(pubsubTopics, libp2p) {
|
2810
|
-
super(StoreCodec, libp2p.components, pubsubTopics);
|
2811
2949
|
this.pubsubTopics = pubsubTopics;
|
2950
|
+
this.streamManager = new StreamManager(StoreCodec, libp2p.components);
|
2812
2951
|
}
|
2813
2952
|
async *queryPerPage(queryOpts, decoders, peerId) {
|
2814
2953
|
// Only validate decoder content topics for content-filtered queries
|
@@ -2833,7 +2972,7 @@ class StoreCore extends BaseProtocol {
|
|
2833
2972
|
});
|
2834
2973
|
let stream;
|
2835
2974
|
try {
|
2836
|
-
stream = await this.getStream(peerId);
|
2975
|
+
stream = await this.streamManager.getStream(peerId);
|
2837
2976
|
}
|
2838
2977
|
catch (e) {
|
2839
2978
|
log$3.error("Failed to get stream", e);
|
@@ -2905,9 +3044,71 @@ function isPeerId(other) {
|
|
2905
3044
|
return Boolean(other?.[peerIdSymbol]);
|
2906
3045
|
}
|
2907
3046
|
|
3047
|
+
/**
|
3048
|
+
* @packageDocumentation
|
3049
|
+
*
|
3050
|
+
* Adds types to the EventTarget class.
|
3051
|
+
*
|
3052
|
+
* Hopefully this won't be necessary
|
3053
|
+
* forever:
|
3054
|
+
*
|
3055
|
+
* - https://github.com/microsoft/TypeScript/issues/28357
|
3056
|
+
* - https://github.com/microsoft/TypeScript/issues/43477
|
3057
|
+
* - https://github.com/microsoft/TypeScript/issues/299
|
3058
|
+
* - https://www.npmjs.com/package/typed-events
|
3059
|
+
* - https://www.npmjs.com/package/typed-event-emitter
|
3060
|
+
* - https://www.npmjs.com/package/typed-event-target
|
3061
|
+
* - etc
|
3062
|
+
*
|
3063
|
+
* In addition to types, a `safeDispatchEvent` method is available which
|
3064
|
+
* prevents dispatching events that aren't in the event map, and a
|
3065
|
+
* `listenerCount` method which reports the number of listeners that are
|
3066
|
+
* currently registered for a given event.
|
3067
|
+
*
|
3068
|
+
* @example
|
3069
|
+
*
|
3070
|
+
* ```ts
|
3071
|
+
* import { TypedEventEmitter } from 'main-event'
|
3072
|
+
* import type { TypedEventTarget } from 'main-event'
|
3073
|
+
*
|
3074
|
+
* interface EventTypes {
|
3075
|
+
* 'test': CustomEvent<string>
|
3076
|
+
* }
|
3077
|
+
*
|
3078
|
+
* const target = new TypedEventEmitter<EventTypes>()
|
3079
|
+
*
|
3080
|
+
* // it's a regular EventTarget
|
3081
|
+
* console.info(target instanceof EventTarget) // true
|
3082
|
+
*
|
3083
|
+
* // register listeners normally
|
3084
|
+
* target.addEventListener('test', (evt) => {
|
3085
|
+
* // evt is CustomEvent<string>
|
3086
|
+
* })
|
3087
|
+
*
|
3088
|
+
* // @ts-expect-error 'derp' is not in the event map
|
3089
|
+
* target.addEventListener('derp', () => {})
|
3090
|
+
*
|
3091
|
+
* // use normal dispatchEvent method
|
3092
|
+
* target.dispatchEvent(new CustomEvent('test', {
|
3093
|
+
* detail: 'hello'
|
3094
|
+
* }))
|
3095
|
+
*
|
3096
|
+
* // use type safe dispatch method
|
3097
|
+
* target.safeDispatchEvent('test', {
|
3098
|
+
* detail: 'world'
|
3099
|
+
* })
|
3100
|
+
*
|
3101
|
+
* // report listener count
|
3102
|
+
* console.info(target.listenerCount('test')) // 0
|
3103
|
+
*
|
3104
|
+
* // event emitters can be used purely as interfaces too
|
3105
|
+
* function acceptTarget (target: TypedEventTarget<EventTypes>) {
|
3106
|
+
* // ...
|
3107
|
+
* }
|
3108
|
+
* ```
|
3109
|
+
*/
|
2908
3110
|
/**
|
2909
3111
|
* An implementation of a typed event target
|
2910
|
-
* etc
|
2911
3112
|
*/
|
2912
3113
|
class TypedEventEmitter extends EventTarget {
|
2913
3114
|
#listeners = new Map();
|
@@ -2957,6 +3158,26 @@ class TypedEventEmitter extends EventTarget {
|
|
2957
3158
|
}
|
2958
3159
|
}
|
2959
3160
|
|
3161
|
+
/**
|
3162
|
+
* Thrown when an invalid multiaddr is encountered
|
3163
|
+
*/
|
3164
|
+
class InvalidMultiaddrError extends Error {
|
3165
|
+
static name = 'InvalidMultiaddrError';
|
3166
|
+
name = 'InvalidMultiaddrError';
|
3167
|
+
}
|
3168
|
+
class ValidationError extends Error {
|
3169
|
+
static name = 'ValidationError';
|
3170
|
+
name = 'ValidationError';
|
3171
|
+
}
|
3172
|
+
class InvalidParametersError extends Error {
|
3173
|
+
static name = 'InvalidParametersError';
|
3174
|
+
name = 'InvalidParametersError';
|
3175
|
+
}
|
3176
|
+
class UnknownProtocolError extends Error {
|
3177
|
+
static name = 'UnknownProtocolError';
|
3178
|
+
name = 'UnknownProtocolError';
|
3179
|
+
}
|
3180
|
+
|
2960
3181
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
2961
3182
|
class Parser {
|
2962
3183
|
index = 0;
|
@@ -3180,24 +3401,6 @@ function parseIPv6(input) {
|
|
3180
3401
|
}
|
3181
3402
|
return parser.new(input).parseWith(() => parser.readIPv6Addr());
|
3182
3403
|
}
|
3183
|
-
/** Parse `input` into IPv4 or IPv6 bytes. */
|
3184
|
-
function parseIP(input, mapIPv4ToIPv6 = false) {
|
3185
|
-
// strip zone index if it is present
|
3186
|
-
if (input.includes("%")) {
|
3187
|
-
input = input.split("%")[0];
|
3188
|
-
}
|
3189
|
-
if (input.length > MAX_IPV6_LENGTH) {
|
3190
|
-
return undefined;
|
3191
|
-
}
|
3192
|
-
const addr = parser.new(input).parseWith(() => parser.readIPAddr());
|
3193
|
-
if (!addr) {
|
3194
|
-
return undefined;
|
3195
|
-
}
|
3196
|
-
if (mapIPv4ToIPv6 && addr.length === 4) {
|
3197
|
-
return Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, addr[0], addr[1], addr[2], addr[3]]);
|
3198
|
-
}
|
3199
|
-
return addr;
|
3200
|
-
}
|
3201
3404
|
|
3202
3405
|
/** Check if `input` is IPv4. */
|
3203
3406
|
function isIPv4(input) {
|
@@ -3207,347 +3410,69 @@ function isIPv4(input) {
|
|
3207
3410
|
function isIPv6(input) {
|
3208
3411
|
return Boolean(parseIPv6(input));
|
3209
3412
|
}
|
3210
|
-
/** Check if `input` is IPv4 or IPv6. */
|
3211
|
-
function isIP(input) {
|
3212
|
-
return Boolean(parseIP(input));
|
3213
|
-
}
|
3214
3413
|
|
3215
|
-
|
3216
|
-
const
|
3217
|
-
|
3218
|
-
|
3219
|
-
const
|
3220
|
-
|
3221
|
-
|
3222
|
-
|
3223
|
-
|
3224
|
-
|
3225
|
-
|
3226
|
-
|
3227
|
-
|
3228
|
-
|
3229
|
-
|
3230
|
-
|
3231
|
-
|
3232
|
-
|
3233
|
-
|
3234
|
-
|
3235
|
-
|
3236
|
-
|
3237
|
-
|
3238
|
-
|
3239
|
-
|
3240
|
-
|
3241
|
-
|
3242
|
-
|
3243
|
-
|
3244
|
-
|
3245
|
-
|
3246
|
-
|
3247
|
-
|
3248
|
-
|
3249
|
-
|
3250
|
-
|
3251
|
-
|
3252
|
-
|
3253
|
-
|
3254
|
-
|
3255
|
-
|
3256
|
-
argv.push('0');
|
3257
|
-
}
|
3258
|
-
sections.splice.apply(sections, argv);
|
3259
|
-
}
|
3260
|
-
const bytes = new Uint8Array(offset + 16);
|
3261
|
-
for (i = 0; i < sections.length; i++) {
|
3262
|
-
const word = parseInt(sections[i], 16);
|
3263
|
-
bytes[offset++] = (word >> 8) & 0xff;
|
3264
|
-
bytes[offset++] = word & 0xff;
|
3265
|
-
}
|
3266
|
-
return bytes;
|
3267
|
-
}
|
3268
|
-
throw new Error('invalid ip address');
|
3269
|
-
};
|
3270
|
-
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63
|
3271
|
-
const toString = function (buf, offset = 0, length) {
|
3272
|
-
offset = ~~offset;
|
3273
|
-
length = length ?? (buf.length - offset);
|
3274
|
-
const view = new DataView(buf.buffer);
|
3275
|
-
if (length === 4) {
|
3276
|
-
const result = [];
|
3277
|
-
// IPv4
|
3278
|
-
for (let i = 0; i < length; i++) {
|
3279
|
-
result.push(buf[offset + i]);
|
3280
|
-
}
|
3281
|
-
return result.join('.');
|
3282
|
-
}
|
3283
|
-
if (length === 16) {
|
3284
|
-
const result = [];
|
3285
|
-
// IPv6
|
3286
|
-
for (let i = 0; i < length; i += 2) {
|
3287
|
-
result.push(view.getUint16(offset + i).toString(16));
|
3288
|
-
}
|
3289
|
-
return result.join(':')
|
3290
|
-
.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3')
|
3291
|
-
.replace(/:{3,4}/, '::');
|
3292
|
-
}
|
3293
|
-
return '';
|
3294
|
-
};
|
3414
|
+
// the values here come from https://github.com/multiformats/multiaddr/blob/master/protocols.csv
|
3415
|
+
const CODE_IP4 = 4;
|
3416
|
+
const CODE_TCP = 6;
|
3417
|
+
const CODE_UDP = 273;
|
3418
|
+
const CODE_DCCP = 33;
|
3419
|
+
const CODE_IP6 = 41;
|
3420
|
+
const CODE_IP6ZONE = 42;
|
3421
|
+
const CODE_IPCIDR = 43;
|
3422
|
+
const CODE_DNS = 53;
|
3423
|
+
const CODE_DNS4 = 54;
|
3424
|
+
const CODE_DNS6 = 55;
|
3425
|
+
const CODE_DNSADDR = 56;
|
3426
|
+
const CODE_SCTP = 132;
|
3427
|
+
const CODE_UDT = 301;
|
3428
|
+
const CODE_UTP = 302;
|
3429
|
+
const CODE_UNIX = 400;
|
3430
|
+
const CODE_P2P = 421; // also IPFS
|
3431
|
+
const CODE_ONION = 444;
|
3432
|
+
const CODE_ONION3 = 445;
|
3433
|
+
const CODE_GARLIC64 = 446;
|
3434
|
+
const CODE_GARLIC32 = 447;
|
3435
|
+
const CODE_TLS = 448;
|
3436
|
+
const CODE_SNI = 449;
|
3437
|
+
const CODE_NOISE = 454;
|
3438
|
+
const CODE_QUIC = 460;
|
3439
|
+
const CODE_QUIC_V1 = 461;
|
3440
|
+
const CODE_WEBTRANSPORT = 465;
|
3441
|
+
const CODE_CERTHASH = 466;
|
3442
|
+
const CODE_HTTP = 480;
|
3443
|
+
const CODE_HTTP_PATH = 481;
|
3444
|
+
const CODE_HTTPS = 443;
|
3445
|
+
const CODE_WS = 477;
|
3446
|
+
const CODE_WSS = 478;
|
3447
|
+
const CODE_P2P_WEBSOCKET_STAR = 479;
|
3448
|
+
const CODE_P2P_STARDUST = 277;
|
3449
|
+
const CODE_P2P_WEBRTC_STAR = 275;
|
3450
|
+
const CODE_P2P_WEBRTC_DIRECT = 276;
|
3451
|
+
const CODE_WEBRTC_DIRECT = 280;
|
3452
|
+
const CODE_WEBRTC = 281;
|
3453
|
+
const CODE_P2P_CIRCUIT = 290;
|
3454
|
+
const CODE_MEMORY = 777;
|
3295
3455
|
|
3296
|
-
|
3297
|
-
|
3298
|
-
|
3299
|
-
const table = [
|
3300
|
-
[4, 32, 'ip4'],
|
3301
|
-
[6, 16, 'tcp'],
|
3302
|
-
[33, 16, 'dccp'],
|
3303
|
-
[41, 128, 'ip6'],
|
3304
|
-
[42, V, 'ip6zone'],
|
3305
|
-
[43, 8, 'ipcidr'],
|
3306
|
-
[53, V, 'dns', true],
|
3307
|
-
[54, V, 'dns4', true],
|
3308
|
-
[55, V, 'dns6', true],
|
3309
|
-
[56, V, 'dnsaddr', true],
|
3310
|
-
[132, 16, 'sctp'],
|
3311
|
-
[273, 16, 'udp'],
|
3312
|
-
[275, 0, 'p2p-webrtc-star'],
|
3313
|
-
[276, 0, 'p2p-webrtc-direct'],
|
3314
|
-
[277, 0, 'p2p-stardust'],
|
3315
|
-
[280, 0, 'webrtc-direct'],
|
3316
|
-
[281, 0, 'webrtc'],
|
3317
|
-
[290, 0, 'p2p-circuit'],
|
3318
|
-
[301, 0, 'udt'],
|
3319
|
-
[302, 0, 'utp'],
|
3320
|
-
[400, V, 'unix', false, true],
|
3321
|
-
// `ipfs` is added before `p2p` for legacy support.
|
3322
|
-
// All text representations will default to `p2p`, but `ipfs` will
|
3323
|
-
// still be supported
|
3324
|
-
[421, V, 'ipfs'],
|
3325
|
-
// `p2p` is the preferred name for 421, and is now the default
|
3326
|
-
[421, V, 'p2p'],
|
3327
|
-
[443, 0, 'https'],
|
3328
|
-
[444, 96, 'onion'],
|
3329
|
-
[445, 296, 'onion3'],
|
3330
|
-
[446, V, 'garlic64'],
|
3331
|
-
[448, 0, 'tls'],
|
3332
|
-
[449, V, 'sni'],
|
3333
|
-
[460, 0, 'quic'],
|
3334
|
-
[461, 0, 'quic-v1'],
|
3335
|
-
[465, 0, 'webtransport'],
|
3336
|
-
[466, V, 'certhash'],
|
3337
|
-
[477, 0, 'ws'],
|
3338
|
-
[478, 0, 'wss'],
|
3339
|
-
[479, 0, 'p2p-websocket-star'],
|
3340
|
-
[480, 0, 'http'],
|
3341
|
-
[481, V, 'http-path'],
|
3342
|
-
[777, V, 'memory']
|
3343
|
-
];
|
3344
|
-
// populate tables
|
3345
|
-
table.forEach(row => {
|
3346
|
-
const proto = createProtocol(...row);
|
3347
|
-
codes[proto.code] = proto;
|
3348
|
-
names[proto.name] = proto;
|
3349
|
-
});
|
3350
|
-
function createProtocol(code, size, name, resolvable, path) {
|
3351
|
-
return {
|
3352
|
-
code,
|
3353
|
-
size,
|
3354
|
-
name,
|
3355
|
-
resolvable: Boolean(resolvable),
|
3356
|
-
path: Boolean(path)
|
3456
|
+
function bytesToString(base) {
|
3457
|
+
return (buf) => {
|
3458
|
+
return toString(buf, base);
|
3357
3459
|
};
|
3358
3460
|
}
|
3359
|
-
|
3360
|
-
|
3361
|
-
|
3362
|
-
|
3363
|
-
*
|
3364
|
-
* ```js
|
3365
|
-
* import { protocol } from '@multiformats/multiaddr'
|
3366
|
-
*
|
3367
|
-
* console.info(protocol(4))
|
3368
|
-
* // { code: 4, size: 32, name: 'ip4', resolvable: false, path: false }
|
3369
|
-
* ```
|
3370
|
-
*/
|
3371
|
-
function getProtocol(proto) {
|
3372
|
-
if (typeof proto === 'number') {
|
3373
|
-
if (codes[proto] != null) {
|
3374
|
-
return codes[proto];
|
3375
|
-
}
|
3376
|
-
throw new Error(`no protocol with code: ${proto}`);
|
3377
|
-
}
|
3378
|
-
else if (typeof proto === 'string') {
|
3379
|
-
if (names[proto] != null) {
|
3380
|
-
return names[proto];
|
3381
|
-
}
|
3382
|
-
throw new Error(`no protocol with name: ${proto}`);
|
3383
|
-
}
|
3384
|
-
throw new Error(`invalid protocol id type: ${typeof proto}`);
|
3385
|
-
}
|
3386
|
-
|
3387
|
-
getProtocol('ip4');
|
3388
|
-
getProtocol('ip6');
|
3389
|
-
getProtocol('ipcidr');
|
3390
|
-
/**
|
3391
|
-
* Convert [code,Uint8Array] to string
|
3392
|
-
*/
|
3393
|
-
// eslint-disable-next-line complexity
|
3394
|
-
function convertToString(proto, buf) {
|
3395
|
-
const protocol = getProtocol(proto);
|
3396
|
-
switch (protocol.code) {
|
3397
|
-
case 4: // ipv4
|
3398
|
-
case 41: // ipv6
|
3399
|
-
return bytes2ip(buf);
|
3400
|
-
case 42: // ipv6zone
|
3401
|
-
return bytes2str(buf);
|
3402
|
-
case 43: // ipcidr
|
3403
|
-
return toString$1(buf, 'base10');
|
3404
|
-
case 6: // tcp
|
3405
|
-
case 273: // udp
|
3406
|
-
case 33: // dccp
|
3407
|
-
case 132: // sctp
|
3408
|
-
return bytes2port(buf).toString();
|
3409
|
-
case 53: // dns
|
3410
|
-
case 54: // dns4
|
3411
|
-
case 55: // dns6
|
3412
|
-
case 56: // dnsaddr
|
3413
|
-
case 400: // unix
|
3414
|
-
case 449: // sni
|
3415
|
-
case 777: // memory
|
3416
|
-
return bytes2str(buf);
|
3417
|
-
case 421: // ipfs
|
3418
|
-
return bytes2mh(buf);
|
3419
|
-
case 444: // onion
|
3420
|
-
return bytes2onion(buf);
|
3421
|
-
case 445: // onion3
|
3422
|
-
return bytes2onion(buf);
|
3423
|
-
case 466: // certhash
|
3424
|
-
return bytes2mb(buf);
|
3425
|
-
case 481: // http-path
|
3426
|
-
return globalThis.encodeURIComponent(bytes2str(buf));
|
3427
|
-
default:
|
3428
|
-
return toString$1(buf, 'base16'); // no clue. convert to hex
|
3429
|
-
}
|
3430
|
-
}
|
3431
|
-
// eslint-disable-next-line complexity
|
3432
|
-
function convertToBytes(proto, str) {
|
3433
|
-
const protocol = getProtocol(proto);
|
3434
|
-
switch (protocol.code) {
|
3435
|
-
case 4: // ipv4
|
3436
|
-
return ip2bytes(str);
|
3437
|
-
case 41: // ipv6
|
3438
|
-
return ip2bytes(str);
|
3439
|
-
case 42: // ipv6zone
|
3440
|
-
return str2bytes(str);
|
3441
|
-
case 43: // ipcidr
|
3442
|
-
return fromString(str, 'base10');
|
3443
|
-
case 6: // tcp
|
3444
|
-
case 273: // udp
|
3445
|
-
case 33: // dccp
|
3446
|
-
case 132: // sctp
|
3447
|
-
return port2bytes(parseInt(str, 10));
|
3448
|
-
case 53: // dns
|
3449
|
-
case 54: // dns4
|
3450
|
-
case 55: // dns6
|
3451
|
-
case 56: // dnsaddr
|
3452
|
-
case 400: // unix
|
3453
|
-
case 449: // sni
|
3454
|
-
case 777: // memory
|
3455
|
-
return str2bytes(str);
|
3456
|
-
case 421: // ipfs
|
3457
|
-
return mh2bytes(str);
|
3458
|
-
case 444: // onion
|
3459
|
-
return onion2bytes(str);
|
3460
|
-
case 445: // onion3
|
3461
|
-
return onion32bytes(str);
|
3462
|
-
case 466: // certhash
|
3463
|
-
return mb2bytes(str);
|
3464
|
-
case 481: // http-path
|
3465
|
-
return str2bytes(globalThis.decodeURIComponent(str));
|
3466
|
-
default:
|
3467
|
-
return fromString(str, 'base16'); // no clue. convert from hex
|
3468
|
-
}
|
3469
|
-
}
|
3470
|
-
const decoders = Object.values(bases).map((c) => c.decoder);
|
3471
|
-
const anybaseDecoder = (function () {
|
3472
|
-
let acc = decoders[0].or(decoders[1]);
|
3473
|
-
decoders.slice(2).forEach((d) => (acc = acc.or(d)));
|
3474
|
-
return acc;
|
3475
|
-
})();
|
3476
|
-
function ip2bytes(ipString) {
|
3477
|
-
if (!isIP(ipString)) {
|
3478
|
-
throw new Error('invalid ip address');
|
3479
|
-
}
|
3480
|
-
return toBytes(ipString);
|
3461
|
+
function stringToBytes(base) {
|
3462
|
+
return (buf) => {
|
3463
|
+
return fromString(buf, base);
|
3464
|
+
};
|
3481
3465
|
}
|
3482
|
-
function
|
3483
|
-
const
|
3484
|
-
|
3485
|
-
throw new Error('ipBuff is required');
|
3486
|
-
}
|
3487
|
-
if (!isIP(ipString)) {
|
3488
|
-
throw new Error('invalid ip address');
|
3489
|
-
}
|
3490
|
-
return ipString;
|
3466
|
+
function bytes2port(buf) {
|
3467
|
+
const view = new DataView(buf.buffer);
|
3468
|
+
return view.getUint16(buf.byteOffset).toString();
|
3491
3469
|
}
|
3492
3470
|
function port2bytes(port) {
|
3493
3471
|
const buf = new ArrayBuffer(2);
|
3494
3472
|
const view = new DataView(buf);
|
3495
|
-
view.setUint16(0, port);
|
3473
|
+
view.setUint16(0, typeof port === 'string' ? parseInt(port) : port);
|
3496
3474
|
return new Uint8Array(buf);
|
3497
3475
|
}
|
3498
|
-
function bytes2port(buf) {
|
3499
|
-
const view = new DataView(buf.buffer);
|
3500
|
-
return view.getUint16(buf.byteOffset);
|
3501
|
-
}
|
3502
|
-
function str2bytes(str) {
|
3503
|
-
const buf = fromString(str);
|
3504
|
-
const size = Uint8Array.from(encode$2(buf.length));
|
3505
|
-
return concat([size, buf], size.length + buf.length);
|
3506
|
-
}
|
3507
|
-
function bytes2str(buf) {
|
3508
|
-
const size = decode$4(buf);
|
3509
|
-
buf = buf.slice(encodingLength$1(size));
|
3510
|
-
if (buf.length !== size) {
|
3511
|
-
throw new Error('inconsistent lengths');
|
3512
|
-
}
|
3513
|
-
return toString$1(buf);
|
3514
|
-
}
|
3515
|
-
function mh2bytes(hash) {
|
3516
|
-
let mh;
|
3517
|
-
if (hash[0] === 'Q' || hash[0] === '1') {
|
3518
|
-
mh = decode$1(base58btc.decode(`z${hash}`)).bytes;
|
3519
|
-
}
|
3520
|
-
else {
|
3521
|
-
mh = CID.parse(hash).multihash.bytes;
|
3522
|
-
}
|
3523
|
-
// the address is a varint prefixed multihash string representation
|
3524
|
-
const size = Uint8Array.from(encode$2(mh.length));
|
3525
|
-
return concat([size, mh], size.length + mh.length);
|
3526
|
-
}
|
3527
|
-
function mb2bytes(mbstr) {
|
3528
|
-
const mb = anybaseDecoder.decode(mbstr);
|
3529
|
-
const size = Uint8Array.from(encode$2(mb.length));
|
3530
|
-
return concat([size, mb], size.length + mb.length);
|
3531
|
-
}
|
3532
|
-
function bytes2mb(buf) {
|
3533
|
-
const size = decode$4(buf);
|
3534
|
-
const hash = buf.slice(encodingLength$1(size));
|
3535
|
-
if (hash.length !== size) {
|
3536
|
-
throw new Error('inconsistent lengths');
|
3537
|
-
}
|
3538
|
-
return 'u' + toString$1(hash, 'base64url');
|
3539
|
-
}
|
3540
|
-
/**
|
3541
|
-
* Converts bytes to bas58btc string
|
3542
|
-
*/
|
3543
|
-
function bytes2mh(buf) {
|
3544
|
-
const size = decode$4(buf);
|
3545
|
-
const address = buf.slice(encodingLength$1(size));
|
3546
|
-
if (address.length !== size) {
|
3547
|
-
throw new Error('inconsistent lengths');
|
3548
|
-
}
|
3549
|
-
return toString$1(address, 'base58btc');
|
3550
|
-
}
|
3551
3476
|
function onion2bytes(str) {
|
3552
3477
|
const addr = str.split(':');
|
3553
3478
|
if (addr.length !== 2) {
|
@@ -3557,7 +3482,7 @@ function onion2bytes(str) {
|
|
3557
3482
|
throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion address.`);
|
3558
3483
|
}
|
3559
3484
|
// onion addresses do not include the multibase prefix, add it before decoding
|
3560
|
-
const buf =
|
3485
|
+
const buf = fromString(addr[0], 'base32');
|
3561
3486
|
// onion port number
|
3562
3487
|
const port = parseInt(addr[1], 10);
|
3563
3488
|
if (port < 1 || port > 65536) {
|
@@ -3585,167 +3510,558 @@ function onion32bytes(str) {
|
|
3585
3510
|
return concat([buf, portBuf], buf.length + portBuf.length);
|
3586
3511
|
}
|
3587
3512
|
function bytes2onion(buf) {
|
3588
|
-
const addrBytes = buf.
|
3589
|
-
const portBytes = buf.
|
3590
|
-
const addr = toString
|
3513
|
+
const addrBytes = buf.subarray(0, buf.length - 2);
|
3514
|
+
const portBytes = buf.subarray(buf.length - 2);
|
3515
|
+
const addr = toString(addrBytes, 'base32');
|
3591
3516
|
const port = bytes2port(portBytes);
|
3592
3517
|
return `${addr}:${port}`;
|
3593
3518
|
}
|
3519
|
+
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7
|
3520
|
+
// but with buf/offset args removed because we don't use them
|
3521
|
+
const ip4ToBytes = function (ip) {
|
3522
|
+
ip = ip.toString().trim();
|
3523
|
+
const bytes = new Uint8Array(4);
|
3524
|
+
ip.split(/\./g).forEach((byte, index) => {
|
3525
|
+
const value = parseInt(byte, 10);
|
3526
|
+
if (isNaN(value) || value < 0 || value > 0xff) {
|
3527
|
+
throw new InvalidMultiaddrError('Invalid byte value in IP address');
|
3528
|
+
}
|
3529
|
+
bytes[index] = value;
|
3530
|
+
});
|
3531
|
+
return bytes;
|
3532
|
+
};
|
3533
|
+
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7
|
3534
|
+
// but with buf/offset args removed because we don't use them
|
3535
|
+
const ip6ToBytes = function (ip) {
|
3536
|
+
let offset = 0;
|
3537
|
+
ip = ip.toString().trim();
|
3538
|
+
const sections = ip.split(':', 8);
|
3539
|
+
let i;
|
3540
|
+
for (i = 0; i < sections.length; i++) {
|
3541
|
+
const isv4 = isIPv4(sections[i]);
|
3542
|
+
let v4Buffer;
|
3543
|
+
if (isv4) {
|
3544
|
+
v4Buffer = ip4ToBytes(sections[i]);
|
3545
|
+
sections[i] = toString(v4Buffer.subarray(0, 2), 'base16');
|
3546
|
+
}
|
3547
|
+
if (v4Buffer != null && ++i < 8) {
|
3548
|
+
sections.splice(i, 0, toString(v4Buffer.subarray(2, 4), 'base16'));
|
3549
|
+
}
|
3550
|
+
}
|
3551
|
+
if (sections[0] === '') {
|
3552
|
+
while (sections.length < 8) {
|
3553
|
+
sections.unshift('0');
|
3554
|
+
}
|
3555
|
+
}
|
3556
|
+
else if (sections[sections.length - 1] === '') {
|
3557
|
+
while (sections.length < 8) {
|
3558
|
+
sections.push('0');
|
3559
|
+
}
|
3560
|
+
}
|
3561
|
+
else if (sections.length < 8) {
|
3562
|
+
for (i = 0; i < sections.length && sections[i] !== ''; i++) { }
|
3563
|
+
const argv = [i, 1];
|
3564
|
+
for (i = 9 - sections.length; i > 0; i--) {
|
3565
|
+
argv.push('0');
|
3566
|
+
}
|
3567
|
+
sections.splice.apply(sections, argv);
|
3568
|
+
}
|
3569
|
+
const bytes = new Uint8Array(offset + 16);
|
3570
|
+
for (i = 0; i < sections.length; i++) {
|
3571
|
+
if (sections[i] === '') {
|
3572
|
+
sections[i] = '0';
|
3573
|
+
}
|
3574
|
+
const word = parseInt(sections[i], 16);
|
3575
|
+
if (isNaN(word) || word < 0 || word > 0xffff) {
|
3576
|
+
throw new InvalidMultiaddrError('Invalid byte value in IP address');
|
3577
|
+
}
|
3578
|
+
bytes[offset++] = (word >> 8) & 0xff;
|
3579
|
+
bytes[offset++] = word & 0xff;
|
3580
|
+
}
|
3581
|
+
return bytes;
|
3582
|
+
};
|
3583
|
+
// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63
|
3584
|
+
const ip4ToString = function (buf) {
|
3585
|
+
if (buf.byteLength !== 4) {
|
3586
|
+
throw new InvalidMultiaddrError('IPv4 address was incorrect length');
|
3587
|
+
}
|
3588
|
+
const result = [];
|
3589
|
+
for (let i = 0; i < buf.byteLength; i++) {
|
3590
|
+
result.push(buf[i]);
|
3591
|
+
}
|
3592
|
+
return result.join('.');
|
3593
|
+
};
|
3594
|
+
const ip6ToString = function (buf) {
|
3595
|
+
if (buf.byteLength !== 16) {
|
3596
|
+
throw new InvalidMultiaddrError('IPv6 address was incorrect length');
|
3597
|
+
}
|
3598
|
+
const result = [];
|
3599
|
+
for (let i = 0; i < buf.byteLength; i += 2) {
|
3600
|
+
const byte1 = buf[i];
|
3601
|
+
const byte2 = buf[i + 1];
|
3602
|
+
const tuple = `${byte1.toString(16).padStart(2, '0')}${byte2.toString(16).padStart(2, '0')}`;
|
3603
|
+
result.push(tuple);
|
3604
|
+
}
|
3605
|
+
const ip = result.join(':');
|
3606
|
+
try {
|
3607
|
+
const url = new URL(`http://[${ip}]`);
|
3608
|
+
return url.hostname.substring(1, url.hostname.length - 1);
|
3609
|
+
}
|
3610
|
+
catch {
|
3611
|
+
throw new InvalidMultiaddrError(`Invalid IPv6 address "${ip}"`);
|
3612
|
+
}
|
3613
|
+
};
|
3614
|
+
function ip6StringToValue(str) {
|
3615
|
+
try {
|
3616
|
+
const url = new URL(`http://[${str}]`);
|
3617
|
+
return url.hostname.substring(1, url.hostname.length - 1);
|
3618
|
+
}
|
3619
|
+
catch {
|
3620
|
+
throw new InvalidMultiaddrError(`Invalid IPv6 address "${str}"`);
|
3621
|
+
}
|
3622
|
+
}
|
3623
|
+
const decoders = Object.values(bases).map((c) => c.decoder);
|
3624
|
+
const anybaseDecoder = (function () {
|
3625
|
+
let acc = decoders[0].or(decoders[1]);
|
3626
|
+
decoders.slice(2).forEach((d) => (acc = acc.or(d)));
|
3627
|
+
return acc;
|
3628
|
+
})();
|
3629
|
+
function mb2bytes(mbstr) {
|
3630
|
+
return anybaseDecoder.decode(mbstr);
|
3631
|
+
}
|
3632
|
+
function bytes2mb(base) {
|
3633
|
+
return (buf) => {
|
3634
|
+
return base.encoder.encode(buf);
|
3635
|
+
};
|
3636
|
+
}
|
3594
3637
|
|
3595
|
-
function
|
3596
|
-
|
3597
|
-
|
3598
|
-
|
3599
|
-
|
3600
|
-
|
3601
|
-
|
3602
|
-
|
3603
|
-
|
3604
|
-
string: '/',
|
3605
|
-
tuples: [],
|
3606
|
-
stringTuples: [],
|
3607
|
-
path: null
|
3608
|
-
};
|
3638
|
+
function integer(value) {
|
3639
|
+
const int = parseInt(value);
|
3640
|
+
if (int.toString() !== value) {
|
3641
|
+
throw new ValidationError('Value must be an integer');
|
3642
|
+
}
|
3643
|
+
}
|
3644
|
+
function positive(value) {
|
3645
|
+
if (value < 0) {
|
3646
|
+
throw new ValidationError('Value must be a positive integer, or zero');
|
3609
3647
|
}
|
3610
|
-
|
3611
|
-
|
3612
|
-
|
3613
|
-
if (
|
3614
|
-
|
3615
|
-
|
3616
|
-
|
3617
|
-
|
3618
|
-
|
3619
|
-
|
3620
|
-
|
3621
|
-
|
3622
|
-
}
|
3623
|
-
// if it's a path proto, take the rest
|
3624
|
-
if (proto.path === true) {
|
3625
|
-
// should we need to check each path part to see if it's a proto?
|
3626
|
-
// This would allow for other protocols to be added after a unix path,
|
3627
|
-
// however it would have issues if the path had a protocol name in the path
|
3628
|
-
path = cleanPath(parts.slice(p).join('/'));
|
3629
|
-
tuples.push([proto.code, convertToBytes(proto.code, path)]);
|
3630
|
-
stringTuples.push([proto.code, path]);
|
3631
|
-
break;
|
3632
|
-
}
|
3633
|
-
const bytes = convertToBytes(proto.code, parts[p]);
|
3634
|
-
tuples.push([proto.code, bytes]);
|
3635
|
-
stringTuples.push([proto.code, convertToString(proto.code, bytes)]);
|
3636
|
-
}
|
3637
|
-
return {
|
3638
|
-
string: stringTuplesToString(stringTuples),
|
3639
|
-
bytes: tuplesToBytes(tuples),
|
3640
|
-
tuples,
|
3641
|
-
stringTuples,
|
3642
|
-
path
|
3648
|
+
}
|
3649
|
+
function maxValue(max) {
|
3650
|
+
return (value) => {
|
3651
|
+
if (value > max) {
|
3652
|
+
throw new ValidationError(`Value must be smaller than or equal to ${max}`);
|
3653
|
+
}
|
3654
|
+
};
|
3655
|
+
}
|
3656
|
+
function validate$1(...funcs) {
|
3657
|
+
return (value) => {
|
3658
|
+
for (const fn of funcs) {
|
3659
|
+
fn(value);
|
3660
|
+
}
|
3643
3661
|
};
|
3644
3662
|
}
|
3645
|
-
|
3646
|
-
|
3647
|
-
|
3648
|
-
|
3663
|
+
const validatePort = validate$1(integer, positive, maxValue(65_535));
|
3664
|
+
|
3665
|
+
const V = -1;
|
3666
|
+
class Registry {
|
3667
|
+
protocolsByCode = new Map();
|
3668
|
+
protocolsByName = new Map();
|
3669
|
+
getProtocol(key) {
|
3670
|
+
let codec;
|
3671
|
+
if (typeof key === 'string') {
|
3672
|
+
codec = this.protocolsByName.get(key);
|
3673
|
+
}
|
3674
|
+
else {
|
3675
|
+
codec = this.protocolsByCode.get(key);
|
3676
|
+
}
|
3677
|
+
if (codec == null) {
|
3678
|
+
throw new UnknownProtocolError(`Protocol ${key} was unknown`);
|
3679
|
+
}
|
3680
|
+
return codec;
|
3681
|
+
}
|
3682
|
+
addProtocol(codec) {
|
3683
|
+
this.protocolsByCode.set(codec.code, codec);
|
3684
|
+
this.protocolsByName.set(codec.name, codec);
|
3685
|
+
codec.aliases?.forEach(alias => {
|
3686
|
+
this.protocolsByName.set(alias, codec);
|
3687
|
+
});
|
3688
|
+
}
|
3689
|
+
removeProtocol(code) {
|
3690
|
+
const codec = this.protocolsByCode.get(code);
|
3691
|
+
if (codec == null) {
|
3692
|
+
return;
|
3693
|
+
}
|
3694
|
+
this.protocolsByCode.delete(codec.code);
|
3695
|
+
this.protocolsByName.delete(codec.name);
|
3696
|
+
codec.aliases?.forEach(alias => {
|
3697
|
+
this.protocolsByName.delete(alias);
|
3698
|
+
});
|
3699
|
+
}
|
3700
|
+
}
|
3701
|
+
const registry = new Registry();
|
3702
|
+
const codecs = [{
|
3703
|
+
code: CODE_IP4,
|
3704
|
+
name: 'ip4',
|
3705
|
+
size: 32,
|
3706
|
+
valueToBytes: ip4ToBytes,
|
3707
|
+
bytesToValue: ip4ToString,
|
3708
|
+
validate: (value) => {
|
3709
|
+
if (!isIPv4(value)) {
|
3710
|
+
throw new ValidationError(`Invalid IPv4 address "${value}"`);
|
3711
|
+
}
|
3712
|
+
}
|
3713
|
+
}, {
|
3714
|
+
code: CODE_TCP,
|
3715
|
+
name: 'tcp',
|
3716
|
+
size: 16,
|
3717
|
+
valueToBytes: port2bytes,
|
3718
|
+
bytesToValue: bytes2port,
|
3719
|
+
validate: validatePort
|
3720
|
+
}, {
|
3721
|
+
code: CODE_UDP,
|
3722
|
+
name: 'udp',
|
3723
|
+
size: 16,
|
3724
|
+
valueToBytes: port2bytes,
|
3725
|
+
bytesToValue: bytes2port,
|
3726
|
+
validate: validatePort
|
3727
|
+
}, {
|
3728
|
+
code: CODE_DCCP,
|
3729
|
+
name: 'dccp',
|
3730
|
+
size: 16,
|
3731
|
+
valueToBytes: port2bytes,
|
3732
|
+
bytesToValue: bytes2port,
|
3733
|
+
validate: validatePort
|
3734
|
+
}, {
|
3735
|
+
code: CODE_IP6,
|
3736
|
+
name: 'ip6',
|
3737
|
+
size: 128,
|
3738
|
+
valueToBytes: ip6ToBytes,
|
3739
|
+
bytesToValue: ip6ToString,
|
3740
|
+
stringToValue: ip6StringToValue,
|
3741
|
+
validate: (value) => {
|
3742
|
+
if (!isIPv6(value)) {
|
3743
|
+
throw new ValidationError(`Invalid IPv6 address "${value}"`);
|
3744
|
+
}
|
3745
|
+
}
|
3746
|
+
}, {
|
3747
|
+
code: CODE_IP6ZONE,
|
3748
|
+
name: 'ip6zone',
|
3749
|
+
size: V
|
3750
|
+
}, {
|
3751
|
+
code: CODE_IPCIDR,
|
3752
|
+
name: 'ipcidr',
|
3753
|
+
size: 8,
|
3754
|
+
bytesToValue: bytesToString('base10'),
|
3755
|
+
valueToBytes: stringToBytes('base10')
|
3756
|
+
}, {
|
3757
|
+
code: CODE_DNS,
|
3758
|
+
name: 'dns',
|
3759
|
+
size: V,
|
3760
|
+
resolvable: true
|
3761
|
+
}, {
|
3762
|
+
code: CODE_DNS4,
|
3763
|
+
name: 'dns4',
|
3764
|
+
size: V,
|
3765
|
+
resolvable: true
|
3766
|
+
}, {
|
3767
|
+
code: CODE_DNS6,
|
3768
|
+
name: 'dns6',
|
3769
|
+
size: V,
|
3770
|
+
resolvable: true
|
3771
|
+
}, {
|
3772
|
+
code: CODE_DNSADDR,
|
3773
|
+
name: 'dnsaddr',
|
3774
|
+
size: V,
|
3775
|
+
resolvable: true
|
3776
|
+
}, {
|
3777
|
+
code: CODE_SCTP,
|
3778
|
+
name: 'sctp',
|
3779
|
+
size: 16,
|
3780
|
+
valueToBytes: port2bytes,
|
3781
|
+
bytesToValue: bytes2port,
|
3782
|
+
validate: validatePort
|
3783
|
+
}, {
|
3784
|
+
code: CODE_UDT,
|
3785
|
+
name: 'udt'
|
3786
|
+
}, {
|
3787
|
+
code: CODE_UTP,
|
3788
|
+
name: 'utp'
|
3789
|
+
}, {
|
3790
|
+
code: CODE_UNIX,
|
3791
|
+
name: 'unix',
|
3792
|
+
size: V,
|
3793
|
+
path: true,
|
3794
|
+
stringToValue: (str) => decodeURIComponent(str),
|
3795
|
+
valueToString: (val) => encodeURIComponent(val)
|
3796
|
+
}, {
|
3797
|
+
code: CODE_P2P,
|
3798
|
+
name: 'p2p',
|
3799
|
+
aliases: ['ipfs'],
|
3800
|
+
size: V,
|
3801
|
+
bytesToValue: bytesToString('base58btc'),
|
3802
|
+
valueToBytes: (val) => {
|
3803
|
+
if (val.startsWith('Q') || val.startsWith('1')) {
|
3804
|
+
return stringToBytes('base58btc')(val);
|
3805
|
+
}
|
3806
|
+
return CID.parse(val).multihash.bytes;
|
3807
|
+
}
|
3808
|
+
}, {
|
3809
|
+
code: CODE_ONION,
|
3810
|
+
name: 'onion',
|
3811
|
+
size: 96,
|
3812
|
+
bytesToValue: bytes2onion,
|
3813
|
+
valueToBytes: onion2bytes
|
3814
|
+
}, {
|
3815
|
+
code: CODE_ONION3,
|
3816
|
+
name: 'onion3',
|
3817
|
+
size: 296,
|
3818
|
+
bytesToValue: bytes2onion,
|
3819
|
+
valueToBytes: onion32bytes
|
3820
|
+
}, {
|
3821
|
+
code: CODE_GARLIC64,
|
3822
|
+
name: 'garlic64',
|
3823
|
+
size: V
|
3824
|
+
}, {
|
3825
|
+
code: CODE_GARLIC32,
|
3826
|
+
name: 'garlic32',
|
3827
|
+
size: V
|
3828
|
+
}, {
|
3829
|
+
code: CODE_TLS,
|
3830
|
+
name: 'tls'
|
3831
|
+
}, {
|
3832
|
+
code: CODE_SNI,
|
3833
|
+
name: 'sni',
|
3834
|
+
size: V
|
3835
|
+
}, {
|
3836
|
+
code: CODE_NOISE,
|
3837
|
+
name: 'noise'
|
3838
|
+
}, {
|
3839
|
+
code: CODE_QUIC,
|
3840
|
+
name: 'quic'
|
3841
|
+
}, {
|
3842
|
+
code: CODE_QUIC_V1,
|
3843
|
+
name: 'quic-v1'
|
3844
|
+
}, {
|
3845
|
+
code: CODE_WEBTRANSPORT,
|
3846
|
+
name: 'webtransport'
|
3847
|
+
}, {
|
3848
|
+
code: CODE_CERTHASH,
|
3849
|
+
name: 'certhash',
|
3850
|
+
size: V,
|
3851
|
+
bytesToValue: bytes2mb(base64url),
|
3852
|
+
valueToBytes: mb2bytes
|
3853
|
+
}, {
|
3854
|
+
code: CODE_HTTP,
|
3855
|
+
name: 'http'
|
3856
|
+
}, {
|
3857
|
+
code: CODE_HTTP_PATH,
|
3858
|
+
name: 'http-path',
|
3859
|
+
size: V,
|
3860
|
+
stringToValue: (str) => `/${decodeURIComponent(str)}`,
|
3861
|
+
valueToString: (val) => encodeURIComponent(val.substring(1))
|
3862
|
+
}, {
|
3863
|
+
code: CODE_HTTPS,
|
3864
|
+
name: 'https'
|
3865
|
+
}, {
|
3866
|
+
code: CODE_WS,
|
3867
|
+
name: 'ws'
|
3868
|
+
}, {
|
3869
|
+
code: CODE_WSS,
|
3870
|
+
name: 'wss'
|
3871
|
+
}, {
|
3872
|
+
code: CODE_P2P_WEBSOCKET_STAR,
|
3873
|
+
name: 'p2p-websocket-star'
|
3874
|
+
}, {
|
3875
|
+
code: CODE_P2P_STARDUST,
|
3876
|
+
name: 'p2p-stardust'
|
3877
|
+
}, {
|
3878
|
+
code: CODE_P2P_WEBRTC_STAR,
|
3879
|
+
name: 'p2p-webrtc-star'
|
3880
|
+
}, {
|
3881
|
+
code: CODE_P2P_WEBRTC_DIRECT,
|
3882
|
+
name: 'p2p-webrtc-direct'
|
3883
|
+
}, {
|
3884
|
+
code: CODE_WEBRTC_DIRECT,
|
3885
|
+
name: 'webrtc-direct'
|
3886
|
+
}, {
|
3887
|
+
code: CODE_WEBRTC,
|
3888
|
+
name: 'webrtc'
|
3889
|
+
}, {
|
3890
|
+
code: CODE_P2P_CIRCUIT,
|
3891
|
+
name: 'p2p-circuit'
|
3892
|
+
}, {
|
3893
|
+
code: CODE_MEMORY,
|
3894
|
+
name: 'memory',
|
3895
|
+
size: V
|
3896
|
+
}];
|
3897
|
+
codecs.forEach(codec => {
|
3898
|
+
registry.addProtocol(codec);
|
3899
|
+
});
|
3900
|
+
|
3901
|
+
function bytesToComponents(bytes) {
|
3902
|
+
const components = [];
|
3649
3903
|
let i = 0;
|
3650
3904
|
while (i < bytes.length) {
|
3651
3905
|
const code = decode$4(bytes, i);
|
3652
|
-
const
|
3653
|
-
const
|
3654
|
-
const size = sizeForAddr(
|
3655
|
-
|
3656
|
-
|
3657
|
-
|
3658
|
-
|
3659
|
-
|
3660
|
-
|
3661
|
-
|
3662
|
-
|
3663
|
-
|
3664
|
-
|
3665
|
-
|
3666
|
-
|
3667
|
-
|
3668
|
-
|
3669
|
-
|
3670
|
-
|
3671
|
-
|
3672
|
-
|
3673
|
-
|
3674
|
-
// however it would have issues if the path had a protocol name in the path
|
3675
|
-
path = stringAddr;
|
3676
|
-
break;
|
3677
|
-
}
|
3678
|
-
}
|
3679
|
-
return {
|
3680
|
-
bytes: Uint8Array.from(bytes),
|
3681
|
-
string: stringTuplesToString(stringTuples),
|
3682
|
-
tuples,
|
3683
|
-
stringTuples,
|
3684
|
-
path
|
3685
|
-
};
|
3906
|
+
const codec = registry.getProtocol(code);
|
3907
|
+
const codeLength = encodingLength$1(code);
|
3908
|
+
const size = sizeForAddr(codec, bytes, i + codeLength);
|
3909
|
+
let sizeLength = 0;
|
3910
|
+
if (size > 0 && codec.size === V) {
|
3911
|
+
sizeLength = encodingLength$1(size);
|
3912
|
+
}
|
3913
|
+
const componentLength = codeLength + sizeLength + size;
|
3914
|
+
const component = {
|
3915
|
+
code,
|
3916
|
+
name: codec.name,
|
3917
|
+
bytes: bytes.subarray(i, i + componentLength)
|
3918
|
+
};
|
3919
|
+
if (size > 0) {
|
3920
|
+
const valueOffset = i + codeLength + sizeLength;
|
3921
|
+
const valueBytes = bytes.subarray(valueOffset, valueOffset + size);
|
3922
|
+
component.value = codec.bytesToValue?.(valueBytes) ?? toString(valueBytes);
|
3923
|
+
}
|
3924
|
+
components.push(component);
|
3925
|
+
i += componentLength;
|
3926
|
+
}
|
3927
|
+
return components;
|
3686
3928
|
}
|
3687
|
-
|
3688
|
-
|
3689
|
-
|
3690
|
-
|
3691
|
-
|
3692
|
-
|
3693
|
-
|
3694
|
-
|
3695
|
-
|
3696
|
-
|
3929
|
+
function componentsToBytes(components) {
|
3930
|
+
let length = 0;
|
3931
|
+
const bytes = [];
|
3932
|
+
for (const component of components) {
|
3933
|
+
if (component.bytes == null) {
|
3934
|
+
const codec = registry.getProtocol(component.code);
|
3935
|
+
const codecLength = encodingLength$1(component.code);
|
3936
|
+
let valueBytes;
|
3937
|
+
let valueLength = 0;
|
3938
|
+
let valueLengthLength = 0;
|
3939
|
+
if (component.value != null) {
|
3940
|
+
valueBytes = codec.valueToBytes?.(component.value) ?? fromString(component.value);
|
3941
|
+
valueLength = valueBytes.byteLength;
|
3942
|
+
if (codec.size === V) {
|
3943
|
+
valueLengthLength = encodingLength$1(valueLength);
|
3944
|
+
}
|
3945
|
+
}
|
3946
|
+
const bytes = new Uint8Array(codecLength + valueLengthLength + valueLength);
|
3947
|
+
// encode the protocol code
|
3948
|
+
let offset = 0;
|
3949
|
+
encodeUint8Array(component.code, bytes, offset);
|
3950
|
+
offset += codecLength;
|
3951
|
+
// if there is a value
|
3952
|
+
if (valueBytes != null) {
|
3953
|
+
// if the value has variable length, encode the length
|
3954
|
+
if (codec.size === V) {
|
3955
|
+
encodeUint8Array(valueLength, bytes, offset);
|
3956
|
+
offset += valueLengthLength;
|
3957
|
+
}
|
3958
|
+
// finally encode the value
|
3959
|
+
bytes.set(valueBytes, offset);
|
3960
|
+
}
|
3961
|
+
component.bytes = bytes;
|
3697
3962
|
}
|
3698
|
-
|
3699
|
-
|
3700
|
-
|
3963
|
+
bytes.push(component.bytes);
|
3964
|
+
length += component.bytes.byteLength;
|
3965
|
+
}
|
3966
|
+
return concat(bytes, length);
|
3701
3967
|
}
|
3702
|
-
|
3703
|
-
|
3704
|
-
|
3705
|
-
|
3706
|
-
|
3707
|
-
|
3708
|
-
|
3709
|
-
|
3710
|
-
|
3711
|
-
|
3712
|
-
|
3713
|
-
|
3968
|
+
function stringToComponents(string) {
|
3969
|
+
if (string.charAt(0) !== '/') {
|
3970
|
+
throw new InvalidMultiaddrError('String multiaddr must start with "/"');
|
3971
|
+
}
|
3972
|
+
const components = [];
|
3973
|
+
let collecting = 'protocol';
|
3974
|
+
let value = '';
|
3975
|
+
let protocol = '';
|
3976
|
+
for (let i = 1; i < string.length; i++) {
|
3977
|
+
const char = string.charAt(i);
|
3978
|
+
if (char !== '/') {
|
3979
|
+
if (collecting === 'protocol') {
|
3980
|
+
protocol += string.charAt(i);
|
3981
|
+
}
|
3982
|
+
else {
|
3983
|
+
value += string.charAt(i);
|
3984
|
+
}
|
3985
|
+
}
|
3986
|
+
const ended = i === string.length - 1;
|
3987
|
+
if (char === '/' || ended) {
|
3988
|
+
const codec = registry.getProtocol(protocol);
|
3989
|
+
if (collecting === 'protocol') {
|
3990
|
+
if (codec.size == null || codec.size === 0) {
|
3991
|
+
// a protocol without an address, eg. `/tls`
|
3992
|
+
components.push({
|
3993
|
+
code: codec.code,
|
3994
|
+
name: codec.name
|
3995
|
+
});
|
3996
|
+
value = '';
|
3997
|
+
protocol = '';
|
3998
|
+
collecting = 'protocol';
|
3999
|
+
continue;
|
4000
|
+
}
|
4001
|
+
else if (ended) {
|
4002
|
+
throw new InvalidMultiaddrError(`Component ${protocol} was missing value`);
|
4003
|
+
}
|
4004
|
+
// continue collecting value
|
4005
|
+
collecting = 'value';
|
4006
|
+
}
|
4007
|
+
else if (collecting === 'value') {
|
4008
|
+
const component = {
|
4009
|
+
code: codec.code,
|
4010
|
+
name: codec.name
|
4011
|
+
};
|
4012
|
+
if (codec.size != null && codec.size !== 0) {
|
4013
|
+
if (value === '') {
|
4014
|
+
throw new InvalidMultiaddrError(`Component ${protocol} was missing value`);
|
4015
|
+
}
|
4016
|
+
component.value = codec.stringToValue?.(value) ?? value;
|
4017
|
+
}
|
4018
|
+
components.push(component);
|
4019
|
+
value = '';
|
4020
|
+
protocol = '';
|
4021
|
+
collecting = 'protocol';
|
4022
|
+
}
|
4023
|
+
}
|
4024
|
+
}
|
4025
|
+
if (protocol !== '' && value !== '') {
|
4026
|
+
throw new InvalidMultiaddrError('Incomplete multiaddr');
|
4027
|
+
}
|
4028
|
+
return components;
|
4029
|
+
}
|
4030
|
+
function componentsToString(components) {
|
4031
|
+
return `/${components.flatMap(component => {
|
4032
|
+
if (component.value == null) {
|
4033
|
+
return component.name;
|
4034
|
+
}
|
4035
|
+
const codec = registry.getProtocol(component.code);
|
4036
|
+
if (codec == null) {
|
4037
|
+
throw new InvalidMultiaddrError(`Unknown protocol code ${component.code}`);
|
4038
|
+
}
|
4039
|
+
return [
|
4040
|
+
component.name,
|
4041
|
+
codec.valueToString?.(component.value) ?? component.value
|
4042
|
+
];
|
4043
|
+
}).join('/')}`;
|
3714
4044
|
}
|
3715
4045
|
/**
|
3716
4046
|
* For the passed address, return the serialized size
|
3717
4047
|
*/
|
3718
|
-
function sizeForAddr(
|
3719
|
-
if (
|
3720
|
-
return p.size / 8;
|
3721
|
-
}
|
3722
|
-
else if (p.size === 0) {
|
4048
|
+
function sizeForAddr(codec, bytes, offset) {
|
4049
|
+
if (codec.size == null || codec.size === 0) {
|
3723
4050
|
return 0;
|
3724
4051
|
}
|
3725
|
-
|
3726
|
-
|
3727
|
-
return size + encodingLength$1(size);
|
3728
|
-
}
|
3729
|
-
}
|
3730
|
-
function cleanPath(str) {
|
3731
|
-
return '/' + str.trim().split('/').filter((a) => a).join('/');
|
3732
|
-
}
|
3733
|
-
class ParseError extends Error {
|
3734
|
-
static name = 'ParseError';
|
3735
|
-
name = 'ParseError';
|
3736
|
-
constructor(str) {
|
3737
|
-
super(`Error parsing address: ${str}`);
|
4052
|
+
if (codec.size > 0) {
|
4053
|
+
return codec.size / 8;
|
3738
4054
|
}
|
4055
|
+
return decode$4(bytes, offset);
|
3739
4056
|
}
|
3740
4057
|
|
3741
|
-
/* eslint-disable complexity */
|
3742
4058
|
const inspect = Symbol.for('nodejs.util.inspect.custom');
|
3743
|
-
const symbol = Symbol.for('@multiformats/
|
4059
|
+
const symbol = Symbol.for('@multiformats/multiaddr');
|
3744
4060
|
const DNS_CODES = [
|
3745
|
-
|
3746
|
-
|
3747
|
-
|
3748
|
-
|
4061
|
+
CODE_DNS,
|
4062
|
+
CODE_DNS4,
|
4063
|
+
CODE_DNS6,
|
4064
|
+
CODE_DNSADDR
|
3749
4065
|
];
|
3750
4066
|
class NoAvailableResolverError extends Error {
|
3751
4067
|
constructor(message = 'No available resolver') {
|
@@ -3753,44 +4069,56 @@ class NoAvailableResolverError extends Error {
|
|
3753
4069
|
this.name = 'NoAvailableResolverError';
|
3754
4070
|
}
|
3755
4071
|
}
|
4072
|
+
function toComponents(addr) {
|
4073
|
+
if (addr == null) {
|
4074
|
+
addr = '/';
|
4075
|
+
}
|
4076
|
+
if (isMultiaddr(addr)) {
|
4077
|
+
return addr.getComponents();
|
4078
|
+
}
|
4079
|
+
if (addr instanceof Uint8Array) {
|
4080
|
+
return bytesToComponents(addr);
|
4081
|
+
}
|
4082
|
+
if (typeof addr === 'string') {
|
4083
|
+
addr = addr
|
4084
|
+
.replace(/\/(\/)+/, '/')
|
4085
|
+
.replace(/(\/)+$/, '');
|
4086
|
+
if (addr === '') {
|
4087
|
+
addr = '/';
|
4088
|
+
}
|
4089
|
+
return stringToComponents(addr);
|
4090
|
+
}
|
4091
|
+
if (Array.isArray(addr)) {
|
4092
|
+
return addr;
|
4093
|
+
}
|
4094
|
+
throw new InvalidMultiaddrError('Must be a string, Uint8Array, Component[], or another Multiaddr');
|
4095
|
+
}
|
3756
4096
|
/**
|
3757
4097
|
* Creates a {@link Multiaddr} from a {@link MultiaddrInput}
|
3758
4098
|
*/
|
3759
4099
|
class Multiaddr {
|
3760
|
-
bytes;
|
3761
|
-
#string;
|
3762
|
-
#tuples;
|
3763
|
-
#stringTuples;
|
3764
|
-
#path;
|
3765
4100
|
[symbol] = true;
|
3766
|
-
|
3767
|
-
|
3768
|
-
|
3769
|
-
|
3770
|
-
|
3771
|
-
|
3772
|
-
|
3773
|
-
|
3774
|
-
|
3775
|
-
else if (typeof addr === 'string') {
|
3776
|
-
if (addr.length > 0 && addr.charAt(0) !== '/') {
|
3777
|
-
throw new Error(`multiaddr "${addr}" must start with a "/"`);
|
3778
|
-
}
|
3779
|
-
parts = stringToMultiaddrParts(addr);
|
3780
|
-
}
|
3781
|
-
else if (isMultiaddr(addr)) { // Multiaddr
|
3782
|
-
parts = bytesToMultiaddrParts(addr.bytes);
|
4101
|
+
#components;
|
4102
|
+
// cache string representation
|
4103
|
+
#string;
|
4104
|
+
// cache byte representation
|
4105
|
+
#bytes;
|
4106
|
+
constructor(addr = '/', options = {}) {
|
4107
|
+
this.#components = toComponents(addr);
|
4108
|
+
if (options.validate !== false) {
|
4109
|
+
validate(this);
|
3783
4110
|
}
|
3784
|
-
|
3785
|
-
|
4111
|
+
}
|
4112
|
+
get bytes() {
|
4113
|
+
if (this.#bytes == null) {
|
4114
|
+
this.#bytes = componentsToBytes(this.#components);
|
3786
4115
|
}
|
3787
|
-
this
|
3788
|
-
this.#string = parts.string;
|
3789
|
-
this.#tuples = parts.tuples;
|
3790
|
-
this.#stringTuples = parts.stringTuples;
|
3791
|
-
this.#path = parts.path;
|
4116
|
+
return this.#bytes;
|
3792
4117
|
}
|
3793
4118
|
toString() {
|
4119
|
+
if (this.#string == null) {
|
4120
|
+
this.#string = componentsToString(this.#components);
|
4121
|
+
}
|
3794
4122
|
return this.#string;
|
3795
4123
|
}
|
3796
4124
|
toJSON() {
|
@@ -3802,31 +4130,25 @@ class Multiaddr {
|
|
3802
4130
|
let host;
|
3803
4131
|
let port;
|
3804
4132
|
let zone = '';
|
3805
|
-
const
|
3806
|
-
|
3807
|
-
const ip4 = getProtocol('ip4');
|
3808
|
-
const ip6 = getProtocol('ip6');
|
3809
|
-
const dns6 = getProtocol('dns6');
|
3810
|
-
const ip6zone = getProtocol('ip6zone');
|
3811
|
-
for (const [code, value] of this.stringTuples()) {
|
3812
|
-
if (code === ip6zone.code) {
|
4133
|
+
for (const { code, name, value } of this.#components) {
|
4134
|
+
if (code === CODE_IP6ZONE) {
|
3813
4135
|
zone = `%${value ?? ''}`;
|
3814
4136
|
}
|
3815
4137
|
// default to https when protocol & port are omitted from DNS addrs
|
3816
4138
|
if (DNS_CODES.includes(code)) {
|
3817
|
-
transport =
|
4139
|
+
transport = 'tcp';
|
3818
4140
|
port = 443;
|
3819
4141
|
host = `${value ?? ''}${zone}`;
|
3820
|
-
family = code ===
|
4142
|
+
family = code === CODE_DNS6 ? 6 : 4;
|
3821
4143
|
}
|
3822
|
-
if (code ===
|
3823
|
-
transport =
|
4144
|
+
if (code === CODE_TCP || code === CODE_UDP) {
|
4145
|
+
transport = name === 'tcp' ? 'tcp' : 'udp';
|
3824
4146
|
port = parseInt(value ?? '');
|
3825
4147
|
}
|
3826
|
-
if (code ===
|
3827
|
-
transport =
|
4148
|
+
if (code === CODE_IP4 || code === CODE_IP6) {
|
4149
|
+
transport = 'tcp';
|
3828
4150
|
host = `${value ?? ''}${zone}`;
|
3829
|
-
family = code ===
|
4151
|
+
family = code === CODE_IP6 ? 6 : 4;
|
3830
4152
|
}
|
3831
4153
|
}
|
3832
4154
|
if (family == null || transport == null || host == null || port == null) {
|
@@ -3840,25 +4162,44 @@ class Multiaddr {
|
|
3840
4162
|
};
|
3841
4163
|
return opts;
|
3842
4164
|
}
|
4165
|
+
getComponents() {
|
4166
|
+
return [
|
4167
|
+
...this.#components
|
4168
|
+
];
|
4169
|
+
}
|
3843
4170
|
protos() {
|
3844
|
-
return this.#
|
4171
|
+
return this.#components.map(({ code, value }) => {
|
4172
|
+
const codec = registry.getProtocol(code);
|
4173
|
+
return {
|
4174
|
+
code,
|
4175
|
+
size: codec.size ?? 0,
|
4176
|
+
name: codec.name,
|
4177
|
+
resolvable: Boolean(codec.resolvable),
|
4178
|
+
path: Boolean(codec.path)
|
4179
|
+
};
|
4180
|
+
});
|
3845
4181
|
}
|
3846
4182
|
protoCodes() {
|
3847
|
-
return this.#
|
4183
|
+
return this.#components.map(({ code }) => code);
|
3848
4184
|
}
|
3849
4185
|
protoNames() {
|
3850
|
-
return this.#
|
4186
|
+
return this.#components.map(({ name }) => name);
|
3851
4187
|
}
|
3852
4188
|
tuples() {
|
3853
|
-
return this.#
|
4189
|
+
return this.#components.map(({ code, value }) => {
|
3854
4190
|
if (value == null) {
|
3855
4191
|
return [code];
|
3856
4192
|
}
|
3857
|
-
|
4193
|
+
const codec = registry.getProtocol(code);
|
4194
|
+
const output = [code];
|
4195
|
+
if (value != null) {
|
4196
|
+
output.push(codec.valueToBytes?.(value) ?? fromString(value));
|
4197
|
+
}
|
4198
|
+
return output;
|
3858
4199
|
});
|
3859
4200
|
}
|
3860
4201
|
stringTuples() {
|
3861
|
-
return this.#
|
4202
|
+
return this.#components.map(({ code, value }) => {
|
3862
4203
|
if (value == null) {
|
3863
4204
|
return [code];
|
3864
4205
|
}
|
@@ -3866,37 +4207,47 @@ class Multiaddr {
|
|
3866
4207
|
});
|
3867
4208
|
}
|
3868
4209
|
encapsulate(addr) {
|
3869
|
-
|
3870
|
-
return new Multiaddr(
|
4210
|
+
const ma = new Multiaddr(addr);
|
4211
|
+
return new Multiaddr([
|
4212
|
+
...this.#components,
|
4213
|
+
...ma.getComponents()
|
4214
|
+
], {
|
4215
|
+
validate: false
|
4216
|
+
});
|
3871
4217
|
}
|
3872
4218
|
decapsulate(addr) {
|
3873
4219
|
const addrString = addr.toString();
|
3874
4220
|
const s = this.toString();
|
3875
4221
|
const i = s.lastIndexOf(addrString);
|
3876
4222
|
if (i < 0) {
|
3877
|
-
throw new
|
4223
|
+
throw new InvalidParametersError(`Address ${this.toString()} does not contain subaddress: ${addr.toString()}`);
|
3878
4224
|
}
|
3879
|
-
return new Multiaddr(s.slice(0, i)
|
4225
|
+
return new Multiaddr(s.slice(0, i), {
|
4226
|
+
validate: false
|
4227
|
+
});
|
3880
4228
|
}
|
3881
4229
|
decapsulateCode(code) {
|
3882
|
-
|
3883
|
-
for (let i =
|
3884
|
-
if (
|
3885
|
-
|
4230
|
+
let index;
|
4231
|
+
for (let i = this.#components.length - 1; i > -1; i--) {
|
4232
|
+
if (this.#components[i].code === code) {
|
4233
|
+
index = i;
|
4234
|
+
break;
|
3886
4235
|
}
|
3887
4236
|
}
|
3888
|
-
return this
|
4237
|
+
return new Multiaddr(this.#components.slice(0, index), {
|
4238
|
+
validate: false
|
4239
|
+
});
|
3889
4240
|
}
|
3890
4241
|
getPeerId() {
|
3891
4242
|
try {
|
3892
4243
|
let tuples = [];
|
3893
|
-
this.
|
3894
|
-
if (code ===
|
3895
|
-
tuples.push([code,
|
4244
|
+
this.#components.forEach(({ code, value }) => {
|
4245
|
+
if (code === CODE_P2P) {
|
4246
|
+
tuples.push([code, value]);
|
3896
4247
|
}
|
3897
4248
|
// if this is a p2p-circuit address, return the target peer id if present
|
3898
4249
|
// not the peer id of the relay
|
3899
|
-
if (code ===
|
4250
|
+
if (code === CODE_P2P_CIRCUIT) {
|
3900
4251
|
tuples = [];
|
3901
4252
|
}
|
3902
4253
|
});
|
@@ -3907,10 +4258,10 @@ class Multiaddr {
|
|
3907
4258
|
// peer id is base58btc encoded string but not multibase encoded so add the `z`
|
3908
4259
|
// prefix so we can validate that it is correctly encoded
|
3909
4260
|
if (peerIdStr[0] === 'Q' || peerIdStr[0] === '1') {
|
3910
|
-
return toString
|
4261
|
+
return toString(base58btc.decode(`z${peerIdStr}`), 'base58btc');
|
3911
4262
|
}
|
3912
4263
|
// try to parse peer id as CID
|
3913
|
-
return toString
|
4264
|
+
return toString(CID.parse(peerIdStr).multihash.bytes, 'base58btc');
|
3914
4265
|
}
|
3915
4266
|
return null;
|
3916
4267
|
}
|
@@ -3919,7 +4270,14 @@ class Multiaddr {
|
|
3919
4270
|
}
|
3920
4271
|
}
|
3921
4272
|
getPath() {
|
3922
|
-
|
4273
|
+
for (const component of this.#components) {
|
4274
|
+
const codec = registry.getProtocol(component.code);
|
4275
|
+
if (!codec.path) {
|
4276
|
+
continue;
|
4277
|
+
}
|
4278
|
+
return component.value ?? null;
|
4279
|
+
}
|
4280
|
+
return null;
|
3923
4281
|
}
|
3924
4282
|
equals(addr) {
|
3925
4283
|
return equals(this.bytes, addr.bytes);
|
@@ -3948,15 +4306,14 @@ class Multiaddr {
|
|
3948
4306
|
port: options.port
|
3949
4307
|
};
|
3950
4308
|
}
|
3951
|
-
isThinWaistAddress(
|
3952
|
-
|
3953
|
-
if (protos.length !== 2) {
|
4309
|
+
isThinWaistAddress() {
|
4310
|
+
if (this.#components.length !== 2) {
|
3954
4311
|
return false;
|
3955
4312
|
}
|
3956
|
-
if (
|
4313
|
+
if (this.#components[0].code !== CODE_IP4 && this.#components[0].code !== CODE_IP6) {
|
3957
4314
|
return false;
|
3958
4315
|
}
|
3959
|
-
if (
|
4316
|
+
if (this.#components[1].code !== CODE_TCP && this.#components[1].code !== CODE_UDP) {
|
3960
4317
|
return false;
|
3961
4318
|
}
|
3962
4319
|
return true;
|
@@ -3974,9 +4331,23 @@ class Multiaddr {
|
|
3974
4331
|
* ```
|
3975
4332
|
*/
|
3976
4333
|
[inspect]() {
|
3977
|
-
return `Multiaddr(${this
|
4334
|
+
return `Multiaddr(${this.toString()})`;
|
3978
4335
|
}
|
3979
4336
|
}
|
4337
|
+
/**
|
4338
|
+
* Ensures all multiaddr tuples are correct. Throws if any invalid protocols or
|
4339
|
+
* values are encountered.
|
4340
|
+
*/
|
4341
|
+
function validate(addr) {
|
4342
|
+
addr.getComponents()
|
4343
|
+
.forEach(component => {
|
4344
|
+
const codec = registry.getProtocol(component.code);
|
4345
|
+
if (component.value == null) {
|
4346
|
+
return;
|
4347
|
+
}
|
4348
|
+
codec.validate?.(component.value);
|
4349
|
+
});
|
4350
|
+
}
|
3980
4351
|
|
3981
4352
|
/**
|
3982
4353
|
* @packageDocumentation
|
@@ -4071,9 +4442,42 @@ class Multiaddr {
|
|
4071
4442
|
* console.info(resolved)
|
4072
4443
|
* // [Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...'), Multiaddr('/ip4/147.75...')...]
|
4073
4444
|
* ```
|
4445
|
+
*
|
4446
|
+
* @example Adding custom protocols
|
4447
|
+
*
|
4448
|
+
* To add application-specific or experimental protocols, add a protocol codec
|
4449
|
+
* to the protocol registry:
|
4450
|
+
*
|
4451
|
+
* ```ts
|
4452
|
+
* import { registry, V, multiaddr } from '@multiformats/multiaddr'
|
4453
|
+
* import type { ProtocolCodec } from '@multiformats/multiaddr'
|
4454
|
+
*
|
4455
|
+
* const maWithCustomTuple = '/custom-protocol/hello'
|
4456
|
+
*
|
4457
|
+
* // throws UnknownProtocolError
|
4458
|
+
* multiaddr(maWithCustomTuple)
|
4459
|
+
*
|
4460
|
+
* const protocol: ProtocolCodec = {
|
4461
|
+
* code: 2059,
|
4462
|
+
* name: 'custom-protocol',
|
4463
|
+
* size: V
|
4464
|
+
* // V means variable length, can also be 0, a positive integer (e.g. a fixed
|
4465
|
+
* // length or omitted
|
4466
|
+
* }
|
4467
|
+
*
|
4468
|
+
* registry.addProtocol(protocol)
|
4469
|
+
*
|
4470
|
+
* // does not throw UnknownProtocolError
|
4471
|
+
* multiaddr(maWithCustomTuple)
|
4472
|
+
*
|
4473
|
+
* // protocols can also be removed
|
4474
|
+
* registry.removeProtocol(protocol.code)
|
4475
|
+
* ```
|
4074
4476
|
*/
|
4075
4477
|
/**
|
4076
4478
|
* All configured {@link Resolver}s
|
4479
|
+
*
|
4480
|
+
* @deprecated DNS resolving will be removed in a future release
|
4077
4481
|
*/
|
4078
4482
|
const resolvers = new Map();
|
4079
4483
|
/**
|
@@ -4669,9 +5073,9 @@ class ConnectionManager extends TypedEventEmitter {
|
|
4669
5073
|
log$1.warn(`Already connected to peer ${peerId.toString()}. Not dialing.`);
|
4670
5074
|
return false;
|
4671
5075
|
}
|
4672
|
-
const isSameShard = await this.
|
5076
|
+
const isSameShard = await this.isPeerOnSameShard(peerId);
|
4673
5077
|
if (!isSameShard) {
|
4674
|
-
const shardInfo = await this.getPeerShardInfo(peerId
|
5078
|
+
const shardInfo = await this.getPeerShardInfo(peerId);
|
4675
5079
|
log$1.warn(`Discovered peer ${peerId.toString()} with ShardInfo ${shardInfo} is not part of any of the configured pubsub topics (${this.pubsubTopics}).
|
4676
5080
|
Not dialing.`);
|
4677
5081
|
return false;
|
@@ -4727,17 +5131,25 @@ class ConnectionManager extends TypedEventEmitter {
|
|
4727
5131
|
return [];
|
4728
5132
|
}
|
4729
5133
|
}
|
4730
|
-
async
|
4731
|
-
const shardInfo = await this.getPeerShardInfo(peerId
|
4732
|
-
|
4733
|
-
if (!shardInfo)
|
5134
|
+
async isPeerOnSameShard(peerId) {
|
5135
|
+
const shardInfo = await this.getPeerShardInfo(peerId);
|
5136
|
+
if (!shardInfo) {
|
4734
5137
|
return true;
|
5138
|
+
}
|
4735
5139
|
const pubsubTopics = shardInfoToPubsubTopics(shardInfo);
|
4736
5140
|
const isTopicConfigured = pubsubTopics.some((topic) => this.pubsubTopics.includes(topic));
|
4737
5141
|
return isTopicConfigured;
|
4738
5142
|
}
|
4739
|
-
async
|
4740
|
-
const
|
5143
|
+
async isPeerOnPubsubTopic(peerId, pubsubTopic) {
|
5144
|
+
const shardInfo = await this.getPeerShardInfo(peerId);
|
5145
|
+
if (!shardInfo) {
|
5146
|
+
return true;
|
5147
|
+
}
|
5148
|
+
const pubsubTopics = shardInfoToPubsubTopics(shardInfo);
|
5149
|
+
return pubsubTopics.some((t) => t === pubsubTopic);
|
5150
|
+
}
|
5151
|
+
async getPeerShardInfo(peerId) {
|
5152
|
+
const peer = await this.libp2p.peerStore.get(peerId);
|
4741
5153
|
const shardInfoBytes = peer.metadata.get("shardInfo");
|
4742
5154
|
if (!shardInfoBytes)
|
4743
5155
|
return undefined;
|
@@ -4783,13 +5195,15 @@ class ConnectionManager extends TypedEventEmitter {
|
|
4783
5195
|
|
4784
5196
|
const log = new Logger("metadata");
|
4785
5197
|
const MetadataCodec = "/vac/waku/metadata/1.0.0";
|
4786
|
-
class Metadata
|
5198
|
+
class Metadata {
|
4787
5199
|
pubsubTopics;
|
5200
|
+
streamManager;
|
4788
5201
|
libp2pComponents;
|
4789
5202
|
handshakesConfirmed = new Map();
|
5203
|
+
multicodec = MetadataCodec;
|
4790
5204
|
constructor(pubsubTopics, libp2p) {
|
4791
|
-
super(MetadataCodec, libp2p.components, pubsubTopics);
|
4792
5205
|
this.pubsubTopics = pubsubTopics;
|
5206
|
+
this.streamManager = new StreamManager(MetadataCodec, libp2p);
|
4793
5207
|
this.libp2pComponents = libp2p;
|
4794
5208
|
void libp2p.registrar.handle(MetadataCodec, (streamData) => {
|
4795
5209
|
void this.onRequest(streamData);
|
@@ -4809,7 +5223,7 @@ class Metadata extends BaseProtocol {
|
|
4809
5223
|
}
|
4810
5224
|
let stream;
|
4811
5225
|
try {
|
4812
|
-
stream = await this.getStream(peerId);
|
5226
|
+
stream = await this.streamManager.getStream(peerId);
|
4813
5227
|
}
|
4814
5228
|
catch (error) {
|
4815
5229
|
log.error("Failed to get stream", error);
|
@@ -4973,4 +5387,4 @@ function messageHashStr(pubsubTopic, message) {
|
|
4973
5387
|
return hashStr;
|
4974
5388
|
}
|
4975
5389
|
|
4976
|
-
export { ConnectionManager, FilterCodecs, FilterCore, LightPushCodec, LightPushCore, MetadataCodec, StoreCodec, StoreCore, createEncoder, index$3 as message, messageHash, messageHashStr, wakuMetadata, index$2 as waku_filter, index$1 as waku_light_push, index as waku_store };
|
5390
|
+
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 };
|