@waku/core 0.0.15 → 0.0.17
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 +42 -0
- package/bundle/index.js +5836 -4045
- package/bundle/lib/message/version_0.js +1 -1
- package/bundle/{version_0-e9a6cfb0.js → version_0-5a4becbc.js} +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/filter/index.js +25 -6
- package/dist/lib/filter/index.js.map +1 -1
- package/dist/lib/light_push/index.js +1 -1
- package/dist/lib/light_push/index.js.map +1 -1
- package/dist/lib/message/version_0.js +6 -0
- package/dist/lib/message/version_0.js.map +1 -1
- package/dist/lib/relay/index.d.ts +4 -2
- package/dist/lib/relay/index.js +90 -38
- package/dist/lib/relay/index.js.map +1 -1
- package/dist/lib/store/index.js +1 -1
- package/dist/lib/store/index.js.map +1 -1
- package/dist/lib/wait_for_remote_peer.js +1 -1
- package/dist/lib/wait_for_remote_peer.js.map +1 -1
- package/dist/lib/waku.d.ts +3 -3
- package/dist/lib/waku.js +11 -17
- package/dist/lib/waku.js.map +1 -1
- package/package.json +10 -16
- package/src/index.ts +5 -1
- package/src/lib/filter/index.ts +39 -8
- package/src/lib/light_push/index.ts +2 -2
- package/src/lib/message/version_0.ts +10 -2
- package/src/lib/relay/index.ts +136 -47
- package/src/lib/store/index.ts +2 -2
- package/src/lib/wait_for_remote_peer.ts +1 -1
- package/src/lib/waku.ts +18 -21
package/dist/lib/waku.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"waku.js","sourceRoot":"","sources":["../../src/lib/waku.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"waku.js","sourceRoot":"","sources":["../../src/lib/waku.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@waku/core",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.17",
|
4
4
|
"description": "TypeScript implementation of the Waku v2 protocol",
|
5
5
|
"types": "./dist/index.d.ts",
|
6
6
|
"module": "./dist/index.js",
|
@@ -56,17 +56,11 @@
|
|
56
56
|
"build:esm": "tsc",
|
57
57
|
"build:bundle": "rollup --config rollup.config.js",
|
58
58
|
"fix": "run-s fix:*",
|
59
|
-
"fix:prettier": "prettier . --write",
|
60
59
|
"fix:lint": "eslint src *.js --fix",
|
61
60
|
"pretest": "run-s pretest:*",
|
62
|
-
"pretest:1-init-git-submodules": "[ -f '../../nwaku/build/wakunode2' ] || git submodule update --init --recursive",
|
63
|
-
"pretest:2-build-nwaku": "[ -f '../../nwaku/build/wakunode2' ] || run-s nwaku:build",
|
64
|
-
"nwaku:build": "(PROC=$(nproc --all 2>/dev/null || echo 2); cd ../../nwaku; make -j$PROC update; NIMFLAGS=\"-d:chronicles_colors=off -d:chronicles_sinks=textlines -d:chronicles_log_level=TRACE\" make -j$PROC wakunode2)",
|
65
|
-
"nwaku:force-build": "(cd ../../nwaku && rm -rf ./build/ ./vendor) && run-s nwaku:build",
|
66
61
|
"check": "run-s check:*",
|
67
62
|
"check:tsc": "tsc -p tsconfig.dev.json",
|
68
63
|
"check:lint": "eslint src *.js",
|
69
|
-
"check:prettier": "prettier . --list-different",
|
70
64
|
"check:spelling": "cspell \"{README.md,src/**/*.ts}\"",
|
71
65
|
"test": "run-s test:*",
|
72
66
|
"test:node": "TS_NODE_PROJECT=./tsconfig.dev.json mocha",
|
@@ -81,13 +75,14 @@
|
|
81
75
|
},
|
82
76
|
"dependencies": {
|
83
77
|
"@chainsafe/libp2p-gossipsub": "^6.1.0",
|
78
|
+
"@libp2p/interface-pubsub": "^3.0.7",
|
84
79
|
"@noble/hashes": "^1.3.0",
|
85
|
-
"@waku/interfaces": "0.0.
|
80
|
+
"@waku/interfaces": "0.0.12",
|
86
81
|
"@waku/proto": "0.0.4",
|
87
|
-
"@waku/utils": "0.0.
|
82
|
+
"@waku/utils": "0.0.5",
|
88
83
|
"debug": "^4.3.4",
|
89
|
-
"it-all": "^
|
90
|
-
"it-length-prefixed": "^
|
84
|
+
"it-all": "^3.0.1",
|
85
|
+
"it-length-prefixed": "^9.0.1",
|
91
86
|
"it-pipe": "^2.0.5",
|
92
87
|
"p-event": "^5.0.1",
|
93
88
|
"uint8arraylist": "^2.4.3",
|
@@ -98,12 +93,11 @@
|
|
98
93
|
"@libp2p/interface-libp2p": "^1.1.2",
|
99
94
|
"@libp2p/interface-peer-id": "^2.0.1",
|
100
95
|
"@libp2p/interface-peer-store": "^1.2.8",
|
101
|
-
"@libp2p/interface-pubsub": "^3.0.6",
|
102
96
|
"@libp2p/interface-registrar": "^2.0.8",
|
103
97
|
"@multiformats/multiaddr": "^12.0.0",
|
104
98
|
"@rollup/plugin-commonjs": "^24.0.1",
|
105
99
|
"@rollup/plugin-json": "^6.0.0",
|
106
|
-
"@rollup/plugin-node-resolve": "^15.0.
|
100
|
+
"@rollup/plugin-node-resolve": "^15.0.2",
|
107
101
|
"@types/chai": "^4.3.4",
|
108
102
|
"@types/debug": "^4.1.7",
|
109
103
|
"@types/mocha": "^10.0.1",
|
@@ -119,7 +113,7 @@
|
|
119
113
|
"eslint-plugin-functional": "^5.0.4",
|
120
114
|
"eslint-plugin-import": "^2.27.5",
|
121
115
|
"eslint-plugin-prettier": "^4.2.1",
|
122
|
-
"fast-check": "^3.
|
116
|
+
"fast-check": "^3.8.1",
|
123
117
|
"ignore-loader": "^0.1.2",
|
124
118
|
"isomorphic-fetch": "^3.0.0",
|
125
119
|
"karma": "^6.4.1",
|
@@ -128,10 +122,10 @@
|
|
128
122
|
"karma-webpack": "^5.0.0",
|
129
123
|
"mocha": "^10.2.0",
|
130
124
|
"npm-run-all": "^4.1.5",
|
131
|
-
"prettier": "^2.8.
|
125
|
+
"prettier": "^2.8.8",
|
132
126
|
"process": "^0.11.10",
|
133
127
|
"puppeteer": "^19.8.2",
|
134
|
-
"rollup": "^3.
|
128
|
+
"rollup": "^3.21.3",
|
135
129
|
"ts-loader": "^9.4.2",
|
136
130
|
"ts-node": "^10.9.1",
|
137
131
|
"typescript": "^4.9.5"
|
package/src/index.ts
CHANGED
@@ -19,7 +19,11 @@ export * as waku_light_push from "./lib/light_push/index.js";
|
|
19
19
|
export { wakuLightPush, LightPushCodec } from "./lib/light_push/index.js";
|
20
20
|
|
21
21
|
export * as waku_relay from "./lib/relay/index.js";
|
22
|
-
export {
|
22
|
+
export {
|
23
|
+
wakuRelay,
|
24
|
+
RelayCreateOptions,
|
25
|
+
wakuGossipSub,
|
26
|
+
} from "./lib/relay/index.js";
|
23
27
|
|
24
28
|
export * as waku_store from "./lib/store/index.js";
|
25
29
|
export {
|
package/src/lib/filter/index.ts
CHANGED
@@ -2,7 +2,9 @@ import type { Libp2p } from "@libp2p/interface-libp2p";
|
|
2
2
|
import type { Peer } from "@libp2p/interface-peer-store";
|
3
3
|
import type { IncomingStreamData } from "@libp2p/interface-registrar";
|
4
4
|
import type {
|
5
|
+
ActiveSubscriptions,
|
5
6
|
Callback,
|
7
|
+
IAsyncIterator,
|
6
8
|
IDecodedMessage,
|
7
9
|
IDecoder,
|
8
10
|
IFilter,
|
@@ -10,6 +12,7 @@ import type {
|
|
10
12
|
ProtocolOptions,
|
11
13
|
} from "@waku/interfaces";
|
12
14
|
import { WakuMessage as WakuMessageProto } from "@waku/proto";
|
15
|
+
import { toAsyncIterator } from "@waku/utils";
|
13
16
|
import debug from "debug";
|
14
17
|
import all from "it-all";
|
15
18
|
import * as lp from "it-length-prefixed";
|
@@ -58,19 +61,20 @@ class Filter extends BaseProtocol implements IFilter {
|
|
58
61
|
}
|
59
62
|
|
60
63
|
/**
|
61
|
-
* @param decoders
|
64
|
+
* @param decoders Decoder or array of Decoders to use to decode messages, it also specifies the content topics.
|
62
65
|
* @param callback A function that will be called on each message returned by the filter.
|
63
66
|
* @param opts The FilterSubscriptionOpts used to narrow which messages are returned, and which peer to connect to.
|
64
67
|
* @returns Unsubscribe function that can be used to end the subscription.
|
65
68
|
*/
|
66
69
|
async subscribe<T extends IDecodedMessage>(
|
67
|
-
decoders: IDecoder<T>[],
|
70
|
+
decoders: IDecoder<T> | IDecoder<T>[],
|
68
71
|
callback: Callback<T>,
|
69
72
|
opts?: ProtocolOptions
|
70
73
|
): Promise<UnsubscribeFunction> {
|
74
|
+
const decodersArray = Array.isArray(decoders) ? decoders : [decoders];
|
71
75
|
const { pubSubTopic = DefaultPubSubTopic } = this.options;
|
72
76
|
|
73
|
-
const contentTopics = Array.from(groupByContentTopic(
|
77
|
+
const contentTopics = Array.from(groupByContentTopic(decodersArray).keys());
|
74
78
|
|
75
79
|
const contentFilters = contentTopics.map((contentTopic) => ({
|
76
80
|
contentTopic,
|
@@ -90,9 +94,9 @@ class Filter extends BaseProtocol implements IFilter {
|
|
90
94
|
try {
|
91
95
|
const res = await pipe(
|
92
96
|
[request.encode()],
|
93
|
-
lp.encode
|
97
|
+
lp.encode,
|
94
98
|
stream,
|
95
|
-
lp.decode
|
99
|
+
lp.decode,
|
96
100
|
async (source) => await all(source)
|
97
101
|
);
|
98
102
|
|
@@ -109,7 +113,11 @@ class Filter extends BaseProtocol implements IFilter {
|
|
109
113
|
throw e;
|
110
114
|
}
|
111
115
|
|
112
|
-
const subscription: Subscription<T> = {
|
116
|
+
const subscription: Subscription<T> = {
|
117
|
+
callback,
|
118
|
+
decoders: decodersArray,
|
119
|
+
pubSubTopic,
|
120
|
+
};
|
113
121
|
this.subscriptions.set(requestId, subscription);
|
114
122
|
|
115
123
|
return async () => {
|
@@ -118,10 +126,33 @@ class Filter extends BaseProtocol implements IFilter {
|
|
118
126
|
};
|
119
127
|
}
|
120
128
|
|
129
|
+
public toSubscriptionIterator<T extends IDecodedMessage>(
|
130
|
+
decoders: IDecoder<T> | IDecoder<T>[],
|
131
|
+
opts?: ProtocolOptions | undefined
|
132
|
+
): Promise<IAsyncIterator<T>> {
|
133
|
+
return toAsyncIterator(this, decoders, opts);
|
134
|
+
}
|
135
|
+
|
136
|
+
public getActiveSubscriptions(): ActiveSubscriptions {
|
137
|
+
const map: ActiveSubscriptions = new Map();
|
138
|
+
const subscriptions = this.subscriptions as Map<
|
139
|
+
RequestID,
|
140
|
+
Subscription<IDecodedMessage>
|
141
|
+
>;
|
142
|
+
|
143
|
+
for (const item of subscriptions.values()) {
|
144
|
+
const values = map.get(item.pubSubTopic) || [];
|
145
|
+
const nextValues = item.decoders.map((decoder) => decoder.contentTopic);
|
146
|
+
map.set(item.pubSubTopic, [...values, ...nextValues]);
|
147
|
+
}
|
148
|
+
|
149
|
+
return map;
|
150
|
+
}
|
151
|
+
|
121
152
|
private onRequest(streamData: IncomingStreamData): void {
|
122
153
|
log("Receiving message push");
|
123
154
|
try {
|
124
|
-
pipe(streamData.stream, lp.decode
|
155
|
+
pipe(streamData.stream, lp.decode, async (source) => {
|
125
156
|
for await (const bytes of source) {
|
126
157
|
const res = FilterRpc.decode(bytes.slice());
|
127
158
|
if (res.requestId && res.push?.messages?.length) {
|
@@ -203,7 +234,7 @@ class Filter extends BaseProtocol implements IFilter {
|
|
203
234
|
|
204
235
|
const stream = await this.newStream(peer);
|
205
236
|
try {
|
206
|
-
await pipe([unsubscribeRequest.encode()], lp.encode
|
237
|
+
await pipe([unsubscribeRequest.encode()], lp.encode, stream.sink);
|
207
238
|
} catch (e) {
|
208
239
|
log("Error unsubscribing", e);
|
209
240
|
throw e;
|
@@ -57,9 +57,9 @@ class LightPush extends BaseProtocol implements ILightPush {
|
|
57
57
|
const query = PushRpc.createRequest(protoMessage, pubSubTopic);
|
58
58
|
const res = await pipe(
|
59
59
|
[query.encode()],
|
60
|
-
lp.encode
|
60
|
+
lp.encode,
|
61
61
|
stream,
|
62
|
-
lp.decode
|
62
|
+
lp.decode,
|
63
63
|
async (source) => await all(source)
|
64
64
|
);
|
65
65
|
try {
|
@@ -71,7 +71,11 @@ export class Encoder implements IEncoder {
|
|
71
71
|
public contentTopic: string,
|
72
72
|
public ephemeral: boolean = false,
|
73
73
|
public metaSetter?: IMetaSetter
|
74
|
-
) {
|
74
|
+
) {
|
75
|
+
if (!contentTopic || contentTopic === "") {
|
76
|
+
throw new Error("Content topic must be specified");
|
77
|
+
}
|
78
|
+
}
|
75
79
|
|
76
80
|
async toWire(message: IMessage): Promise<Uint8Array> {
|
77
81
|
return proto.WakuMessage.encode(await this.toProtoObj(message));
|
@@ -117,7 +121,11 @@ export function createEncoder({
|
|
117
121
|
}
|
118
122
|
|
119
123
|
export class Decoder implements IDecoder<DecodedMessage> {
|
120
|
-
constructor(public contentTopic: string) {
|
124
|
+
constructor(public contentTopic: string) {
|
125
|
+
if (!contentTopic || contentTopic === "") {
|
126
|
+
throw new Error("Content topic must be specified");
|
127
|
+
}
|
128
|
+
}
|
121
129
|
|
122
130
|
fromWireToProtoObj(bytes: Uint8Array): Promise<IProtoMessage | undefined> {
|
123
131
|
const protoMessage = proto.WakuMessage.decode(bytes);
|
package/src/lib/relay/index.ts
CHANGED
@@ -6,22 +6,28 @@ import {
|
|
6
6
|
} from "@chainsafe/libp2p-gossipsub";
|
7
7
|
import type { PeerIdStr, TopicStr } from "@chainsafe/libp2p-gossipsub/types";
|
8
8
|
import { SignaturePolicy } from "@chainsafe/libp2p-gossipsub/types";
|
9
|
+
import type { Libp2p } from "@libp2p/interface-libp2p";
|
10
|
+
import type { PubSub } from "@libp2p/interface-pubsub";
|
11
|
+
import { sha256 } from "@noble/hashes/sha256";
|
9
12
|
import type {
|
10
13
|
ActiveSubscriptions,
|
11
14
|
Callback,
|
15
|
+
IAsyncIterator,
|
12
16
|
IDecodedMessage,
|
13
17
|
IDecoder,
|
14
18
|
IEncoder,
|
15
19
|
IMessage,
|
16
20
|
IRelay,
|
17
21
|
ProtocolCreateOptions,
|
22
|
+
ProtocolOptions,
|
18
23
|
SendResult,
|
19
24
|
} from "@waku/interfaces";
|
25
|
+
import { toAsyncIterator } from "@waku/utils";
|
20
26
|
import debug from "debug";
|
21
27
|
|
22
28
|
import { DefaultPubSubTopic } from "../constants.js";
|
29
|
+
import { groupByContentTopic } from "../group_by.js";
|
23
30
|
import { TopicOnlyDecoder } from "../message/topic_only_message.js";
|
24
|
-
import { pushOrInitMapSet } from "../push_or_init_map.js";
|
25
31
|
|
26
32
|
import * as constants from "./constants.js";
|
27
33
|
import { messageValidator } from "./message_validator.js";
|
@@ -38,14 +44,14 @@ export type ContentTopic = string;
|
|
38
44
|
|
39
45
|
/**
|
40
46
|
* Implements the [Waku v2 Relay protocol](https://rfc.vac.dev/spec/11/).
|
41
|
-
*
|
42
|
-
*
|
43
|
-
* @implements {require('libp2p-interfaces/src/pubsub')}
|
47
|
+
* Throws if libp2p.pubsub does not support Waku Relay
|
44
48
|
*/
|
45
|
-
class Relay
|
49
|
+
class Relay implements IRelay {
|
46
50
|
private readonly pubSubTopic: string;
|
47
|
-
defaultDecoder: IDecoder<IDecodedMessage>;
|
51
|
+
private defaultDecoder: IDecoder<IDecodedMessage>;
|
52
|
+
|
48
53
|
public static multicodec: string = constants.RelayCodecs[0];
|
54
|
+
public readonly gossipSub: GossipSub;
|
49
55
|
|
50
56
|
/**
|
51
57
|
* observers called when receiving new message.
|
@@ -53,21 +59,20 @@ class Relay extends GossipSub implements IRelay {
|
|
53
59
|
*/
|
54
60
|
private observers: Map<ContentTopic, Set<unknown>>;
|
55
61
|
|
56
|
-
constructor(
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
globalSignaturePolicy: SignaturePolicy.StrictNoSign,
|
63
|
-
fallbackToFloodsub: false,
|
64
|
-
});
|
65
|
-
|
66
|
-
super(components, options);
|
67
|
-
this.multicodecs = constants.RelayCodecs;
|
62
|
+
constructor(libp2p: Libp2p, options?: Partial<RelayCreateOptions>) {
|
63
|
+
if (!this.isRelayPubSub(libp2p.pubsub)) {
|
64
|
+
throw Error(
|
65
|
+
`Failed to initialize Relay. libp2p.pubsub does not support ${Relay.multicodec}`
|
66
|
+
);
|
67
|
+
}
|
68
68
|
|
69
|
+
this.gossipSub = libp2p.pubsub as GossipSub;
|
69
70
|
this.pubSubTopic = options?.pubSubTopic ?? DefaultPubSubTopic;
|
70
71
|
|
72
|
+
if (this.gossipSub.isStarted()) {
|
73
|
+
this.gossipSubSubscribe(this.pubSubTopic);
|
74
|
+
}
|
75
|
+
|
71
76
|
this.observers = new Map();
|
72
77
|
|
73
78
|
// TODO: User might want to decide what decoder should be used (e.g. for RLN)
|
@@ -82,8 +87,12 @@ class Relay extends GossipSub implements IRelay {
|
|
82
87
|
* @returns {void}
|
83
88
|
*/
|
84
89
|
public async start(): Promise<void> {
|
85
|
-
|
86
|
-
|
90
|
+
if (this.gossipSub.isStarted()) {
|
91
|
+
throw Error("GossipSub already started.");
|
92
|
+
}
|
93
|
+
|
94
|
+
await this.gossipSub.start();
|
95
|
+
this.gossipSubSubscribe(this.pubSubTopic);
|
87
96
|
}
|
88
97
|
|
89
98
|
/**
|
@@ -96,7 +105,7 @@ class Relay extends GossipSub implements IRelay {
|
|
96
105
|
return { recipients: [] };
|
97
106
|
}
|
98
107
|
|
99
|
-
return this.publish(this.pubSubTopic, msg);
|
108
|
+
return this.gossipSub.publish(this.pubSubTopic, msg);
|
100
109
|
}
|
101
110
|
|
102
111
|
/**
|
@@ -104,32 +113,59 @@ class Relay extends GossipSub implements IRelay {
|
|
104
113
|
*
|
105
114
|
* @returns Function to delete the observer
|
106
115
|
*/
|
107
|
-
|
108
|
-
|
116
|
+
public subscribe<T extends IDecodedMessage>(
|
117
|
+
decoders: IDecoder<T> | IDecoder<T>[],
|
109
118
|
callback: Callback<T>
|
110
119
|
): () => void {
|
111
|
-
const
|
112
|
-
|
113
|
-
callback
|
114
|
-
};
|
115
|
-
const contentTopic = decoder.contentTopic;
|
120
|
+
const contentTopicToObservers = Array.isArray(decoders)
|
121
|
+
? toObservers(decoders, callback)
|
122
|
+
: toObservers([decoders], callback);
|
116
123
|
|
117
|
-
|
124
|
+
for (const contentTopic of contentTopicToObservers.keys()) {
|
125
|
+
const currObservers = this.observers.get(contentTopic) || new Set();
|
126
|
+
const newObservers =
|
127
|
+
contentTopicToObservers.get(contentTopic) || new Set();
|
128
|
+
|
129
|
+
this.observers.set(contentTopic, union(currObservers, newObservers));
|
130
|
+
}
|
118
131
|
|
119
132
|
return () => {
|
120
|
-
const
|
121
|
-
|
122
|
-
|
133
|
+
for (const contentTopic of contentTopicToObservers.keys()) {
|
134
|
+
const currentObservers = this.observers.get(contentTopic) || new Set();
|
135
|
+
const observersToRemove =
|
136
|
+
contentTopicToObservers.get(contentTopic) || new Set();
|
137
|
+
|
138
|
+
const nextObservers = leftMinusJoin(
|
139
|
+
currentObservers,
|
140
|
+
observersToRemove
|
141
|
+
);
|
142
|
+
|
143
|
+
if (nextObservers.size) {
|
144
|
+
this.observers.set(contentTopic, nextObservers);
|
145
|
+
} else {
|
146
|
+
this.observers.delete(contentTopic);
|
147
|
+
}
|
123
148
|
}
|
124
149
|
};
|
125
150
|
}
|
126
151
|
|
152
|
+
public toSubscriptionIterator<T extends IDecodedMessage>(
|
153
|
+
decoders: IDecoder<T> | IDecoder<T>[],
|
154
|
+
opts?: ProtocolOptions | undefined
|
155
|
+
): Promise<IAsyncIterator<T>> {
|
156
|
+
return toAsyncIterator(this, decoders, opts);
|
157
|
+
}
|
158
|
+
|
127
159
|
public getActiveSubscriptions(): ActiveSubscriptions {
|
128
160
|
const map = new Map();
|
129
161
|
map.set(this.pubSubTopic, this.observers.keys());
|
130
162
|
return map;
|
131
163
|
}
|
132
164
|
|
165
|
+
public getMeshPeers(topic?: TopicStr): PeerIdStr[] {
|
166
|
+
return this.gossipSub.getMeshPeers(topic ?? this.pubSubTopic);
|
167
|
+
}
|
168
|
+
|
133
169
|
private async processIncomingMessage<T extends IDecodedMessage>(
|
134
170
|
pubSubTopic: string,
|
135
171
|
bytes: Uint8Array
|
@@ -168,8 +204,8 @@ class Relay extends GossipSub implements IRelay {
|
|
168
204
|
*
|
169
205
|
* @override
|
170
206
|
*/
|
171
|
-
|
172
|
-
this.addEventListener(
|
207
|
+
private gossipSubSubscribe(pubSubTopic: string): void {
|
208
|
+
this.gossipSub.addEventListener(
|
173
209
|
"gossipsub:message",
|
174
210
|
async (event: CustomEvent<GossipsubMessage>) => {
|
175
211
|
if (event.detail.msg.topic !== pubSubTopic) return;
|
@@ -182,24 +218,77 @@ class Relay extends GossipSub implements IRelay {
|
|
182
218
|
}
|
183
219
|
);
|
184
220
|
|
185
|
-
this.topicValidators.set(pubSubTopic, messageValidator);
|
186
|
-
|
221
|
+
this.gossipSub.topicValidators.set(pubSubTopic, messageValidator);
|
222
|
+
this.gossipSub.subscribe(pubSubTopic);
|
187
223
|
}
|
188
224
|
|
189
|
-
|
190
|
-
|
191
|
-
this.topicValidators.delete(pubSubTopic);
|
192
|
-
}
|
193
|
-
|
194
|
-
getMeshPeers(topic?: TopicStr): PeerIdStr[] {
|
195
|
-
return super.getMeshPeers(topic ?? this.pubSubTopic);
|
225
|
+
private isRelayPubSub(pubsub: PubSub): boolean {
|
226
|
+
return pubsub?.multicodecs?.includes(Relay.multicodec) || false;
|
196
227
|
}
|
197
228
|
}
|
198
229
|
|
199
|
-
Relay.multicodec = constants.RelayCodecs[constants.RelayCodecs.length - 1];
|
200
|
-
|
201
230
|
export function wakuRelay(
|
231
|
+
init: Partial<ProtocolCreateOptions> = {}
|
232
|
+
): (libp2p: Libp2p) => IRelay {
|
233
|
+
return (libp2p: Libp2p) => new Relay(libp2p, init);
|
234
|
+
}
|
235
|
+
|
236
|
+
export function wakuGossipSub(
|
202
237
|
init: Partial<RelayCreateOptions> = {}
|
203
|
-
): (components: GossipSubComponents) =>
|
204
|
-
return (components: GossipSubComponents) =>
|
238
|
+
): (components: GossipSubComponents) => GossipSub {
|
239
|
+
return (components: GossipSubComponents) => {
|
240
|
+
init = {
|
241
|
+
...init,
|
242
|
+
msgIdFn: ({ data }) => sha256(data),
|
243
|
+
// Ensure that no signature is included nor expected in the messages.
|
244
|
+
globalSignaturePolicy: SignaturePolicy.StrictNoSign,
|
245
|
+
fallbackToFloodsub: false,
|
246
|
+
};
|
247
|
+
const pubsub = new GossipSub(components, init);
|
248
|
+
pubsub.multicodecs = constants.RelayCodecs;
|
249
|
+
return pubsub;
|
250
|
+
};
|
251
|
+
}
|
252
|
+
|
253
|
+
function toObservers<T extends IDecodedMessage>(
|
254
|
+
decoders: IDecoder<T>[],
|
255
|
+
callback: Callback<T>
|
256
|
+
): Map<ContentTopic, Set<Observer<T>>> {
|
257
|
+
const contentTopicToDecoders = Array.from(
|
258
|
+
groupByContentTopic(decoders).entries()
|
259
|
+
);
|
260
|
+
|
261
|
+
const contentTopicToObserversEntries = contentTopicToDecoders.map(
|
262
|
+
([contentTopic, decoders]) =>
|
263
|
+
[
|
264
|
+
contentTopic,
|
265
|
+
new Set(
|
266
|
+
decoders.map(
|
267
|
+
(decoder) =>
|
268
|
+
({
|
269
|
+
decoder,
|
270
|
+
callback,
|
271
|
+
} as Observer<T>)
|
272
|
+
)
|
273
|
+
),
|
274
|
+
] as [ContentTopic, Set<Observer<T>>]
|
275
|
+
);
|
276
|
+
|
277
|
+
return new Map(contentTopicToObserversEntries);
|
278
|
+
}
|
279
|
+
|
280
|
+
function union(left: Set<unknown>, right: Set<unknown>): Set<unknown> {
|
281
|
+
for (const val of right.values()) {
|
282
|
+
left.add(val);
|
283
|
+
}
|
284
|
+
return left;
|
285
|
+
}
|
286
|
+
|
287
|
+
function leftMinusJoin(left: Set<unknown>, right: Set<unknown>): Set<unknown> {
|
288
|
+
for (const val of right.values()) {
|
289
|
+
if (left.has(val)) {
|
290
|
+
left.delete(val);
|
291
|
+
}
|
292
|
+
}
|
293
|
+
return left;
|
205
294
|
}
|
package/src/lib/store/index.ts
CHANGED
@@ -105,7 +105,7 @@ async function waitForGossipSubPeerInMesh(waku: IRelay): Promise<void> {
|
|
105
105
|
let peers = waku.getMeshPeers();
|
106
106
|
|
107
107
|
while (peers.length == 0) {
|
108
|
-
await pEvent(waku, "gossipsub:heartbeat");
|
108
|
+
await pEvent(waku.gossipSub, "gossipsub:heartbeat");
|
109
109
|
peers = waku.getMeshPeers();
|
110
110
|
}
|
111
111
|
}
|
package/src/lib/waku.ts
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
import type { Stream } from "@libp2p/interface-connection";
|
2
2
|
import type { Libp2p } from "@libp2p/interface-libp2p";
|
3
3
|
import type { PeerId } from "@libp2p/interface-peer-id";
|
4
|
-
import
|
5
|
-
import type { Multiaddr } from "@multiformats/multiaddr";
|
4
|
+
import { isPeerId } from "@libp2p/interface-peer-id";
|
5
|
+
import type { Multiaddr, MultiaddrInput } from "@multiformats/multiaddr";
|
6
|
+
import { multiaddr } from "@multiformats/multiaddr";
|
6
7
|
import type {
|
7
8
|
IFilter,
|
8
9
|
ILightPush,
|
@@ -14,7 +15,6 @@ import { Protocols } from "@waku/interfaces";
|
|
14
15
|
import debug from "debug";
|
15
16
|
|
16
17
|
import { ConnectionManager } from "./connection_manager.js";
|
17
|
-
import * as relayConstants from "./relay/constants.js";
|
18
18
|
|
19
19
|
export const DefaultPingKeepAliveValueSecs = 0;
|
20
20
|
export const DefaultRelayKeepAliveValueSecs = 5 * 60;
|
@@ -57,7 +57,8 @@ export class WakuNode implements Waku {
|
|
57
57
|
libp2p: Libp2p,
|
58
58
|
store?: (libp2p: Libp2p) => IStore,
|
59
59
|
lightPush?: (libp2p: Libp2p) => ILightPush,
|
60
|
-
filter?: (libp2p: Libp2p) => IFilter
|
60
|
+
filter?: (libp2p: Libp2p) => IFilter,
|
61
|
+
relay?: (libp2p: Libp2p) => IRelay
|
61
62
|
) {
|
62
63
|
this.libp2p = libp2p;
|
63
64
|
|
@@ -71,8 +72,8 @@ export class WakuNode implements Waku {
|
|
71
72
|
this.lightPush = lightPush(libp2p);
|
72
73
|
}
|
73
74
|
|
74
|
-
if (
|
75
|
-
this.relay = libp2p
|
75
|
+
if (relay) {
|
76
|
+
this.relay = relay(libp2p);
|
76
77
|
}
|
77
78
|
|
78
79
|
const pingKeepAlive =
|
@@ -105,10 +106,11 @@ export class WakuNode implements Waku {
|
|
105
106
|
* @param protocols Waku protocols we expect from the peer; Defaults to mounted protocols
|
106
107
|
*/
|
107
108
|
async dial(
|
108
|
-
peer: PeerId |
|
109
|
+
peer: PeerId | MultiaddrInput,
|
109
110
|
protocols?: Protocols[]
|
110
111
|
): Promise<Stream> {
|
111
112
|
const _protocols = protocols ?? [];
|
113
|
+
const peerId = mapToPeerIdOrMultiaddr(peer);
|
112
114
|
|
113
115
|
if (typeof protocols === "undefined") {
|
114
116
|
this.relay && _protocols.push(Protocols.Relay);
|
@@ -120,7 +122,9 @@ export class WakuNode implements Waku {
|
|
120
122
|
const codecs: string[] = [];
|
121
123
|
if (_protocols.includes(Protocols.Relay)) {
|
122
124
|
if (this.relay) {
|
123
|
-
this.relay.multicodecs.forEach((codec) =>
|
125
|
+
this.relay.gossipSub.multicodecs.forEach((codec: string) =>
|
126
|
+
codecs.push(codec)
|
127
|
+
);
|
124
128
|
} else {
|
125
129
|
log(
|
126
130
|
"Relay codec not included in dial codec: protocol not mounted locally"
|
@@ -155,9 +159,9 @@ export class WakuNode implements Waku {
|
|
155
159
|
}
|
156
160
|
}
|
157
161
|
|
158
|
-
log(`Dialing to ${
|
162
|
+
log(`Dialing to ${peerId.toString()} with protocols ${_protocols}`);
|
159
163
|
|
160
|
-
return this.libp2p.dialProtocol(
|
164
|
+
return this.libp2p.dialProtocol(peerId, codecs);
|
161
165
|
}
|
162
166
|
|
163
167
|
async start(): Promise<void> {
|
@@ -189,15 +193,8 @@ export class WakuNode implements Waku {
|
|
189
193
|
}
|
190
194
|
}
|
191
195
|
|
192
|
-
function
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
relayConstants.RelayCodecs[relayConstants.RelayCodecs.length - 1]
|
197
|
-
);
|
198
|
-
// Exception is expected if `libp2p` was not instantiated with pubsub
|
199
|
-
// eslint-disable-next-line no-empty
|
200
|
-
} catch (e) {}
|
201
|
-
}
|
202
|
-
return false;
|
196
|
+
function mapToPeerIdOrMultiaddr(
|
197
|
+
peerId: PeerId | MultiaddrInput
|
198
|
+
): PeerId | Multiaddr {
|
199
|
+
return isPeerId(peerId) ? peerId : multiaddr(peerId);
|
203
200
|
}
|