@waku/discovery 0.0.2-434be7b.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/bundle/index.js +27179 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/dns/constants.d.ts +9 -0
  4. package/dist/dns/constants.js +13 -0
  5. package/dist/dns/constants.js.map +1 -0
  6. package/dist/dns/dns.d.ts +32 -0
  7. package/dist/dns/dns.js +160 -0
  8. package/dist/dns/dns.js.map +1 -0
  9. package/dist/dns/dns_discovery.d.ts +24 -0
  10. package/dist/dns/dns_discovery.js +95 -0
  11. package/dist/dns/dns_discovery.js.map +1 -0
  12. package/dist/dns/dns_over_https.d.ts +25 -0
  13. package/dist/dns/dns_over_https.js +72 -0
  14. package/dist/dns/dns_over_https.js.map +1 -0
  15. package/dist/dns/enrtree.d.ts +33 -0
  16. package/dist/dns/enrtree.js +76 -0
  17. package/dist/dns/enrtree.js.map +1 -0
  18. package/dist/dns/fetch_nodes.d.ts +13 -0
  19. package/dist/dns/fetch_nodes.js +133 -0
  20. package/dist/dns/fetch_nodes.js.map +1 -0
  21. package/dist/dns/index.d.ts +3 -0
  22. package/dist/dns/index.js +4 -0
  23. package/dist/dns/index.js.map +1 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.js +10 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/local-peer-cache/index.d.ts +24 -0
  28. package/dist/local-peer-cache/index.js +106 -0
  29. package/dist/local-peer-cache/index.js.map +1 -0
  30. package/dist/peer-exchange/index.d.ts +2 -0
  31. package/dist/peer-exchange/index.js +3 -0
  32. package/dist/peer-exchange/index.js.map +1 -0
  33. package/dist/peer-exchange/rpc.d.ts +22 -0
  34. package/dist/peer-exchange/rpc.js +41 -0
  35. package/dist/peer-exchange/rpc.js.map +1 -0
  36. package/dist/peer-exchange/waku_peer_exchange.d.ts +21 -0
  37. package/dist/peer-exchange/waku_peer_exchange.js +80 -0
  38. package/dist/peer-exchange/waku_peer_exchange.js.map +1 -0
  39. package/dist/peer-exchange/waku_peer_exchange_discovery.d.ts +53 -0
  40. package/dist/peer-exchange/waku_peer_exchange_discovery.js +136 -0
  41. package/dist/peer-exchange/waku_peer_exchange_discovery.js.map +1 -0
  42. package/package.json +1 -0
  43. package/src/dns/constants.ts +16 -0
  44. package/src/dns/dns.ts +215 -0
  45. package/src/dns/dns_discovery.ts +144 -0
  46. package/src/dns/dns_over_https.ts +83 -0
  47. package/src/dns/enrtree.ts +123 -0
  48. package/src/dns/fetch_nodes.ts +181 -0
  49. package/src/dns/index.ts +3 -0
  50. package/src/index.ts +21 -0
  51. package/src/local-peer-cache/index.ts +160 -0
  52. package/src/peer-exchange/index.ts +11 -0
  53. package/src/peer-exchange/rpc.ts +44 -0
  54. package/src/peer-exchange/waku_peer_exchange.ts +111 -0
  55. package/src/peer-exchange/waku_peer_exchange_discovery.ts +238 -0
@@ -0,0 +1,238 @@
1
+ import { CustomEvent, TypedEventEmitter } from "@libp2p/interface";
2
+ import { peerDiscoverySymbol as symbol } from "@libp2p/interface";
3
+ import type {
4
+ IdentifyResult,
5
+ PeerDiscovery,
6
+ PeerDiscoveryEvents,
7
+ PeerId,
8
+ PeerInfo
9
+ } from "@libp2p/interface";
10
+ import {
11
+ Libp2pComponents,
12
+ PeerExchangeResult,
13
+ PubsubTopic,
14
+ Tags
15
+ } from "@waku/interfaces";
16
+ import { encodeRelayShard, Logger } from "@waku/utils";
17
+
18
+ import { PeerExchangeCodec, WakuPeerExchange } from "./waku_peer_exchange.js";
19
+
20
+ const log = new Logger("peer-exchange-discovery");
21
+
22
+ const DEFAULT_PEER_EXCHANGE_REQUEST_NODES = 10;
23
+ const DEFAULT_PEER_EXCHANGE_QUERY_INTERVAL_MS = 10 * 1000;
24
+ const DEFAULT_MAX_RETRIES = 3;
25
+
26
+ export interface Options {
27
+ /**
28
+ * Tag a bootstrap peer with this name before "discovering" it (default: 'bootstrap')
29
+ */
30
+ tagName?: string;
31
+
32
+ /**
33
+ * The bootstrap peer tag will have this value (default: 50)
34
+ */
35
+ tagValue?: number;
36
+
37
+ /**
38
+ * Cause the bootstrap peer tag to be removed after this number of ms (default: 2 minutes)
39
+ */
40
+ tagTTL?: number;
41
+ /**
42
+ * The interval between queries to a peer (default: 10 seconds)
43
+ * The interval will increase by a factor of an incrementing number (starting at 1)
44
+ * until it reaches the maximum attempts before backoff
45
+ */
46
+ queryInterval?: number;
47
+ /**
48
+ * The number of attempts before the queries to a peer are aborted (default: 3)
49
+ */
50
+ maxRetries?: number;
51
+ }
52
+
53
+ export const DEFAULT_PEER_EXCHANGE_TAG_NAME = Tags.PEER_EXCHANGE;
54
+ const DEFAULT_PEER_EXCHANGE_TAG_VALUE = 50;
55
+ const DEFAULT_PEER_EXCHANGE_TAG_TTL = 100_000_000;
56
+
57
+ export class PeerExchangeDiscovery
58
+ extends TypedEventEmitter<PeerDiscoveryEvents>
59
+ implements PeerDiscovery
60
+ {
61
+ private readonly components: Libp2pComponents;
62
+ private readonly peerExchange: WakuPeerExchange;
63
+ private readonly options: Options;
64
+ private isStarted: boolean;
65
+ private queryingPeers: Set<string> = new Set();
66
+ private queryAttempts: Map<string, number> = new Map();
67
+
68
+ private readonly handleDiscoveredPeer = (
69
+ event: CustomEvent<IdentifyResult>
70
+ ): void => {
71
+ const { protocols, peerId } = event.detail;
72
+
73
+ if (
74
+ !protocols.includes(PeerExchangeCodec) ||
75
+ this.queryingPeers.has(peerId.toString())
76
+ )
77
+ return;
78
+
79
+ this.queryingPeers.add(peerId.toString());
80
+ this.startRecurringQueries(peerId).catch((error) =>
81
+ log.error(`Error querying peer ${error}`)
82
+ );
83
+ };
84
+
85
+ constructor(
86
+ components: Libp2pComponents,
87
+ pubsubTopics: PubsubTopic[],
88
+ options: Options = {}
89
+ ) {
90
+ super();
91
+ this.components = components;
92
+ this.peerExchange = new WakuPeerExchange(components, pubsubTopics);
93
+ this.options = options;
94
+ this.isStarted = false;
95
+ }
96
+
97
+ /**
98
+ * Start emitting events
99
+ */
100
+ start(): void {
101
+ if (this.isStarted) {
102
+ return;
103
+ }
104
+
105
+ log.info("Starting peer exchange node discovery, discovering peers");
106
+
107
+ // might be better to use "peer:identify" or "peer:update"
108
+ this.components.events.addEventListener(
109
+ "peer:identify",
110
+ this.handleDiscoveredPeer
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Remove event listener
116
+ */
117
+ stop(): void {
118
+ if (!this.isStarted) return;
119
+ log.info("Stopping peer exchange node discovery");
120
+ this.isStarted = false;
121
+ this.queryingPeers.clear();
122
+ this.components.events.removeEventListener(
123
+ "peer:identify",
124
+ this.handleDiscoveredPeer
125
+ );
126
+ }
127
+
128
+ get [symbol](): true {
129
+ return true;
130
+ }
131
+
132
+ get [Symbol.toStringTag](): string {
133
+ return "@waku/peer-exchange";
134
+ }
135
+
136
+ private readonly startRecurringQueries = async (
137
+ peerId: PeerId
138
+ ): Promise<void> => {
139
+ const peerIdStr = peerId.toString();
140
+ const {
141
+ queryInterval = DEFAULT_PEER_EXCHANGE_QUERY_INTERVAL_MS,
142
+ maxRetries = DEFAULT_MAX_RETRIES
143
+ } = this.options;
144
+
145
+ log.info(
146
+ `Querying peer: ${peerIdStr} (attempt ${
147
+ this.queryAttempts.get(peerIdStr) ?? 1
148
+ })`
149
+ );
150
+
151
+ await this.query(peerId);
152
+
153
+ const currentAttempt = this.queryAttempts.get(peerIdStr) ?? 1;
154
+
155
+ if (currentAttempt > maxRetries) {
156
+ this.abortQueriesForPeer(peerIdStr);
157
+ return;
158
+ }
159
+
160
+ setTimeout(() => {
161
+ this.queryAttempts.set(peerIdStr, currentAttempt + 1);
162
+ this.startRecurringQueries(peerId).catch((error) => {
163
+ log.error(`Error in startRecurringQueries: ${error}`);
164
+ });
165
+ }, queryInterval * currentAttempt);
166
+ };
167
+
168
+ private async query(peerId: PeerId): Promise<PeerExchangeResult> {
169
+ const { error, peerInfos } = await this.peerExchange.query({
170
+ numPeers: DEFAULT_PEER_EXCHANGE_REQUEST_NODES,
171
+ peerId
172
+ });
173
+
174
+ if (error) {
175
+ log.error("Peer exchange query failed", error);
176
+ return { error, peerInfos: null };
177
+ }
178
+
179
+ for (const _peerInfo of peerInfos) {
180
+ const { ENR } = _peerInfo;
181
+ if (!ENR) {
182
+ log.warn("No ENR in peerInfo object, skipping");
183
+ continue;
184
+ }
185
+
186
+ const { peerId, peerInfo, shardInfo } = ENR;
187
+ if (!peerId || !peerInfo) {
188
+ continue;
189
+ }
190
+
191
+ const hasPeer = await this.components.peerStore.has(peerId);
192
+ if (hasPeer) {
193
+ continue;
194
+ }
195
+
196
+ // update the tags for the peer
197
+ await this.components.peerStore.save(peerId, {
198
+ tags: {
199
+ [DEFAULT_PEER_EXCHANGE_TAG_NAME]: {
200
+ value: this.options.tagValue ?? DEFAULT_PEER_EXCHANGE_TAG_VALUE,
201
+ ttl: this.options.tagTTL ?? DEFAULT_PEER_EXCHANGE_TAG_TTL
202
+ }
203
+ },
204
+ ...(shardInfo && {
205
+ metadata: {
206
+ shardInfo: encodeRelayShard(shardInfo)
207
+ }
208
+ })
209
+ });
210
+
211
+ log.info(`Discovered peer: ${peerId.toString()}`);
212
+
213
+ this.dispatchEvent(
214
+ new CustomEvent<PeerInfo>("peer", {
215
+ detail: {
216
+ id: peerId,
217
+ multiaddrs: peerInfo.multiaddrs
218
+ }
219
+ })
220
+ );
221
+ }
222
+
223
+ return { error: null, peerInfos };
224
+ }
225
+
226
+ private abortQueriesForPeer(peerIdStr: string): void {
227
+ log.info(`Aborting queries for peer: ${peerIdStr}`);
228
+ this.queryingPeers.delete(peerIdStr);
229
+ this.queryAttempts.delete(peerIdStr);
230
+ }
231
+ }
232
+
233
+ export function wakuPeerExchangeDiscovery(
234
+ pubsubTopics: PubsubTopic[]
235
+ ): (components: Libp2pComponents) => PeerExchangeDiscovery {
236
+ return (components: Libp2pComponents) =>
237
+ new PeerExchangeDiscovery(components, pubsubTopics);
238
+ }