@aztec/p2p 0.35.1 → 0.36.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 (37) hide show
  1. package/dest/bootstrap/bootstrap.d.ts +10 -3
  2. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  3. package/dest/bootstrap/bootstrap.js +40 -59
  4. package/dest/client/index.d.ts.map +1 -1
  5. package/dest/client/index.js +16 -4
  6. package/dest/client/p2p_client.d.ts +1 -1
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/config.d.ts +17 -5
  9. package/dest/config.d.ts.map +1 -1
  10. package/dest/config.js +6 -3
  11. package/dest/service/discV5_service.d.ts +34 -0
  12. package/dest/service/discV5_service.d.ts.map +1 -0
  13. package/dest/service/discV5_service.js +95 -0
  14. package/dest/service/dummy_service.d.ts +23 -1
  15. package/dest/service/dummy_service.d.ts.map +1 -1
  16. package/dest/service/dummy_service.js +28 -1
  17. package/dest/service/known_txs.d.ts +1 -1
  18. package/dest/service/known_txs.d.ts.map +1 -1
  19. package/dest/service/libp2p_service.d.ts +12 -5
  20. package/dest/service/libp2p_service.d.ts.map +1 -1
  21. package/dest/service/libp2p_service.js +64 -24
  22. package/dest/service/peer_store.d.ts +18 -0
  23. package/dest/service/peer_store.d.ts.map +1 -0
  24. package/dest/service/peer_store.js +25 -0
  25. package/dest/service/service.d.ts +27 -1
  26. package/dest/service/service.d.ts.map +1 -1
  27. package/package.json +26 -14
  28. package/src/bootstrap/bootstrap.ts +52 -70
  29. package/src/client/index.ts +14 -3
  30. package/src/client/p2p_client.ts +1 -1
  31. package/src/config.ts +29 -8
  32. package/src/service/discV5_service.ts +122 -0
  33. package/src/service/dummy_service.ts +30 -1
  34. package/src/service/known_txs.ts +1 -1
  35. package/src/service/libp2p_service.ts +80 -35
  36. package/src/service/peer_store.ts +36 -0
  37. package/src/service/service.ts +31 -1
package/src/config.ts CHANGED
@@ -8,10 +8,15 @@ export interface P2PConfig {
8
8
  p2pEnabled: boolean;
9
9
 
10
10
  /**
11
- * The frequency in which to check.
11
+ * The frequency in which to check for new L2 blocks.
12
12
  */
13
13
  p2pBlockCheckIntervalMS: number;
14
14
 
15
+ /**
16
+ * The frequency in which to check for new peers.
17
+ */
18
+ p2pPeerCheckIntervalMS: number;
19
+
15
20
  /**
16
21
  * Size of queue of L2 blocks to store.
17
22
  */
@@ -27,6 +32,16 @@ export interface P2PConfig {
27
32
  */
28
33
  tcpListenIp: string;
29
34
 
35
+ /**
36
+ * The udp port on which the P2P service should listen for connections. Used for Discv5 peer discovery.
37
+ */
38
+ udpListenPort: number;
39
+
40
+ /**
41
+ * The udp IP on which the P2P service should listen for connections. Used for Discv5 peer discovery.
42
+ */
43
+ udpListenIp: string;
44
+
30
45
  /**
31
46
  * An optional peer id private key. If blank, will generate a random key.
32
47
  */
@@ -52,11 +67,6 @@ export interface P2PConfig {
52
67
  */
53
68
  announcePort?: number;
54
69
 
55
- /**
56
- * Optional specification to run as a client in the Kademlia routing protocol.
57
- */
58
- clientKADRouting: boolean;
59
-
60
70
  /**
61
71
  * Whether to enable NAT from libp2p (ignored for bootstrap node).
62
72
  */
@@ -71,6 +81,11 @@ export interface P2PConfig {
71
81
  * The maximum number of peers (a peer count above this will cause the node to refuse connection attempts)
72
82
  */
73
83
  maxPeerCount: number;
84
+
85
+ /**
86
+ * Data directory for peer & tx databases.
87
+ */
88
+ dataDirectory?: string;
74
89
  }
75
90
 
76
91
  /**
@@ -81,33 +96,39 @@ export function getP2PConfigEnvVars(): P2PConfig {
81
96
  const {
82
97
  P2P_ENABLED,
83
98
  P2P_BLOCK_CHECK_INTERVAL_MS,
99
+ P2P_PEER_CHECK_INTERVAL_MS,
84
100
  P2P_L2_BLOCK_QUEUE_SIZE,
85
101
  P2P_TCP_LISTEN_PORT,
86
102
  P2P_TCP_LISTEN_IP,
103
+ P2P_UDP_LISTEN_PORT,
104
+ P2P_UDP_LISTEN_IP,
87
105
  PEER_ID_PRIVATE_KEY,
88
106
  BOOTSTRAP_NODES,
89
107
  P2P_ANNOUNCE_HOSTNAME,
90
108
  P2P_ANNOUNCE_PORT,
91
- P2P_KAD_CLIENT,
92
109
  P2P_NAT_ENABLED,
93
110
  P2P_MIN_PEERS,
94
111
  P2P_MAX_PEERS,
112
+ DATA_DIRECTORY,
95
113
  } = process.env;
96
114
  const envVars: P2PConfig = {
97
115
  p2pEnabled: P2P_ENABLED === 'true',
98
116
  p2pBlockCheckIntervalMS: P2P_BLOCK_CHECK_INTERVAL_MS ? +P2P_BLOCK_CHECK_INTERVAL_MS : 100,
117
+ p2pPeerCheckIntervalMS: P2P_PEER_CHECK_INTERVAL_MS ? +P2P_PEER_CHECK_INTERVAL_MS : 1000,
99
118
  p2pL2QueueSize: P2P_L2_BLOCK_QUEUE_SIZE ? +P2P_L2_BLOCK_QUEUE_SIZE : 1000,
100
119
  tcpListenPort: P2P_TCP_LISTEN_PORT ? +P2P_TCP_LISTEN_PORT : 40400,
101
120
  tcpListenIp: P2P_TCP_LISTEN_IP ? P2P_TCP_LISTEN_IP : '0.0.0.0',
121
+ udpListenPort: P2P_UDP_LISTEN_PORT ? +P2P_UDP_LISTEN_PORT : 40400,
122
+ udpListenIp: P2P_UDP_LISTEN_IP ? P2P_UDP_LISTEN_IP : '0.0.0.0',
102
123
  peerIdPrivateKey: PEER_ID_PRIVATE_KEY,
103
124
  bootstrapNodes: BOOTSTRAP_NODES ? BOOTSTRAP_NODES.split(',') : [],
104
125
  transactionProtocol: '',
105
126
  announceHostname: P2P_ANNOUNCE_HOSTNAME,
106
127
  announcePort: P2P_ANNOUNCE_PORT ? +P2P_ANNOUNCE_PORT : undefined,
107
- clientKADRouting: P2P_KAD_CLIENT === 'true',
108
128
  enableNat: P2P_NAT_ENABLED === 'true',
109
129
  minPeerCount: P2P_MIN_PEERS ? +P2P_MIN_PEERS : 10,
110
130
  maxPeerCount: P2P_MAX_PEERS ? +P2P_MAX_PEERS : 100,
131
+ dataDirectory: DATA_DIRECTORY,
111
132
  };
112
133
  return envVars;
113
134
  }
@@ -0,0 +1,122 @@
1
+ import { createDebugLogger } from '@aztec/foundation/log';
2
+ import { RunningPromise } from '@aztec/foundation/running-promise';
3
+
4
+ import { Discv5, type Discv5EventEmitter } from '@chainsafe/discv5';
5
+ import { type ENR, SignableENR } from '@chainsafe/enr';
6
+ import type { PeerId } from '@libp2p/interface';
7
+ import { multiaddr } from '@multiformats/multiaddr';
8
+ import EventEmitter from 'events';
9
+
10
+ import type { P2PConfig } from '../config.js';
11
+ import type { PeerDiscoveryService } from './service.js';
12
+
13
+ export enum PeerDiscoveryState {
14
+ RUNNING = 'running',
15
+ STOPPED = 'stopped',
16
+ }
17
+
18
+ /**
19
+ * Peer discovery service using Discv5.
20
+ */
21
+ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService {
22
+ /** The Discv5 instance */
23
+ private discv5: Discv5;
24
+
25
+ /** This instance's ENR */
26
+ private enr: SignableENR;
27
+
28
+ /** The interval for checking for new peers */
29
+ private discoveryInterval: NodeJS.Timeout | null = null;
30
+
31
+ private runningPromise: RunningPromise;
32
+
33
+ private currentState = PeerDiscoveryState.STOPPED;
34
+
35
+ constructor(private peerId: PeerId, config: P2PConfig, private logger = createDebugLogger('aztec:discv5_service')) {
36
+ super();
37
+ const { announceHostname, tcpListenPort, udpListenIp, udpListenPort, bootstrapNodes } = config;
38
+ // create ENR from PeerId
39
+ this.enr = SignableENR.createFromPeerId(peerId);
40
+
41
+ const multiAddrUdp = multiaddr(`${announceHostname}/udp/${udpListenPort}/p2p/${peerId.toString()}`);
42
+ const multiAddrTcp = multiaddr(`${announceHostname}/tcp/${tcpListenPort}/p2p/${peerId.toString()}`);
43
+
44
+ const listenMultiAddrUdp = multiaddr(`/ip4/${udpListenIp}/udp/${udpListenPort}`);
45
+
46
+ // set location multiaddr in ENR record
47
+ this.enr.setLocationMultiaddr(multiAddrUdp);
48
+ this.enr.setLocationMultiaddr(multiAddrTcp);
49
+
50
+ this.discv5 = Discv5.create({
51
+ enr: this.enr,
52
+ peerId,
53
+ bindAddrs: { ip4: listenMultiAddrUdp },
54
+ config: {
55
+ lookupTimeout: 2000,
56
+ },
57
+ });
58
+
59
+ this.logger.info(`ENR NodeId: ${this.enr.nodeId}`);
60
+ this.logger.info(`ENR UDP: ${multiAddrUdp.toString()}`);
61
+
62
+ (this.discv5 as Discv5EventEmitter).on('discovered', (enr: ENR) => this.onDiscovered(enr));
63
+ (this.discv5 as Discv5EventEmitter).on('enrAdded', async (enr: ENR) => {
64
+ const multiAddrTcp = await enr.getFullMultiaddr('tcp');
65
+ const multiAddrUdp = await enr.getFullMultiaddr('udp');
66
+ this.logger.debug(`ENR multiaddr: ${multiAddrTcp?.toString()}, ${multiAddrUdp?.toString()}`);
67
+ });
68
+
69
+ // Add bootnode ENR if provided
70
+ if (bootstrapNodes?.length) {
71
+ this.logger.info(`Adding bootstrap ENRs: ${bootstrapNodes.join(', ')}`);
72
+ try {
73
+ bootstrapNodes.forEach(enr => {
74
+ this.discv5.addEnr(enr);
75
+ });
76
+ } catch (e) {
77
+ this.logger.error(`Error adding bootnode ENRs: ${e}`);
78
+ }
79
+ }
80
+
81
+ this.runningPromise = new RunningPromise(async () => {
82
+ await this.discv5.findRandomNode();
83
+ }, config.p2pPeerCheckIntervalMS);
84
+ }
85
+
86
+ public async start(): Promise<void> {
87
+ if (this.currentState === PeerDiscoveryState.RUNNING) {
88
+ throw new Error('DiscV5Service already started');
89
+ }
90
+ this.logger.info('Starting DiscV5');
91
+ await this.discv5.start();
92
+ this.logger.info('DiscV5 started');
93
+ this.currentState = PeerDiscoveryState.RUNNING;
94
+ this.runningPromise.start();
95
+ }
96
+
97
+ public getAllPeers(): ENR[] {
98
+ return this.discv5.kadValues();
99
+ }
100
+
101
+ public getEnr(): ENR {
102
+ return this.enr.toENR();
103
+ }
104
+
105
+ public getPeerId(): PeerId {
106
+ return this.peerId;
107
+ }
108
+
109
+ public getStatus(): PeerDiscoveryState {
110
+ return this.currentState;
111
+ }
112
+
113
+ public async stop(): Promise<void> {
114
+ await this.runningPromise.stop();
115
+ await this.discv5.stop();
116
+ this.currentState = PeerDiscoveryState.STOPPED;
117
+ }
118
+
119
+ private onDiscovered(enr: ENR) {
120
+ this.emit('peer:discovered', enr);
121
+ }
122
+ }
@@ -1,6 +1,8 @@
1
1
  import { type Tx, type TxHash } from '@aztec/circuit-types';
2
2
 
3
- import { type P2PService } from './service.js';
3
+ import EventEmitter from 'events';
4
+
5
+ import type { P2PService, PeerDiscoveryService } from './service.js';
4
6
 
5
7
  /**
6
8
  * A dummy implementation of the P2P Service.
@@ -34,3 +36,30 @@ export class DummyP2PService implements P2PService {
34
36
  */
35
37
  public settledTxs(_: TxHash[]) {}
36
38
  }
39
+
40
+ /**
41
+ * A dummy implementation of the Peer Discovery Service.
42
+ */
43
+ export class DummyPeerDiscoveryService extends EventEmitter implements PeerDiscoveryService {
44
+ /**
45
+ * Starts the dummy implementation.
46
+ * @returns A resolved promise.
47
+ */
48
+ public start() {
49
+ return Promise.resolve();
50
+ }
51
+ /**
52
+ * Stops the dummy implementation.
53
+ * @returns A resolved promise.
54
+ */
55
+ public stop() {
56
+ return Promise.resolve();
57
+ }
58
+ /**
59
+ * Called to discover peers in the network.
60
+ * @returns An array of discovered peer addresses.
61
+ */
62
+ public getAllPeers() {
63
+ return [];
64
+ }
65
+ }
@@ -1,4 +1,4 @@
1
- import { type PeerId } from '@libp2p/interface-peer-id';
1
+ import { type PeerId } from '@libp2p/interface';
2
2
 
3
3
  /**
4
4
  * Keeps a record of which Peers have 'seen' which transactions.
@@ -1,25 +1,27 @@
1
1
  import { type Tx, type TxHash } from '@aztec/circuit-types';
2
2
  import { SerialQueue } from '@aztec/foundation/fifo';
3
3
  import { createDebugLogger } from '@aztec/foundation/log';
4
+ import { type AztecKVStore } from '@aztec/kv-store';
4
5
 
6
+ import { ENR } from '@chainsafe/enr';
5
7
  import { noise } from '@chainsafe/libp2p-noise';
6
8
  import { yamux } from '@chainsafe/libp2p-yamux';
7
- import { bootstrap } from '@libp2p/bootstrap';
9
+ import { identify } from '@libp2p/identify';
10
+ import type { IncomingStreamData, PeerId, Stream } from '@libp2p/interface';
8
11
  import type { ServiceMap } from '@libp2p/interface-libp2p';
9
- import { type PeerId } from '@libp2p/interface-peer-id';
10
- import { type IncomingStreamData } from '@libp2p/interface/stream-handler';
11
- import { type DualKadDHT, kadDHT } from '@libp2p/kad-dht';
12
+ import '@libp2p/kad-dht';
12
13
  import { mplex } from '@libp2p/mplex';
14
+ import { peerIdFromString } from '@libp2p/peer-id';
13
15
  import { createFromJSON, createSecp256k1PeerId, exportToProtobuf } from '@libp2p/peer-id-factory';
14
16
  import { tcp } from '@libp2p/tcp';
15
17
  import { pipe } from 'it-pipe';
16
18
  import { type Libp2p, type Libp2pOptions, type ServiceFactoryMap, createLibp2p } from 'libp2p';
17
- import { identifyService } from 'libp2p/identify';
18
19
 
19
20
  import { type P2PConfig } from '../config.js';
20
21
  import { type TxPool } from '../tx_pool/index.js';
21
22
  import { KnownTxLookup } from './known_txs.js';
22
- import { type P2PService } from './service.js';
23
+ import { AztecPeerDb, type AztecPeerStore } from './peer_store.js';
24
+ import type { P2PService, PeerDiscoveryService } from './service.js';
23
25
  import {
24
26
  Messages,
25
27
  createGetTransactionsRequestMessage,
@@ -65,8 +67,11 @@ export class LibP2PService implements P2PService {
65
67
  constructor(
66
68
  private config: P2PConfig,
67
69
  private node: Libp2p,
70
+ private peerDiscoveryService: PeerDiscoveryService,
71
+ private peerStore: AztecPeerStore,
68
72
  private protocolId: string,
69
73
  private txPool: TxPool,
74
+ private bootstrapPeerIds: PeerId[] = [],
70
75
  private logger = createDebugLogger('aztec:libp2p_service'),
71
76
  ) {}
72
77
 
@@ -75,7 +80,7 @@ export class LibP2PService implements P2PService {
75
80
  * @returns An empty promise.
76
81
  */
77
82
  public async start() {
78
- if (this.node.isStarted()) {
83
+ if (this.node.status === 'started') {
79
84
  throw new Error('P2P service already started');
80
85
  }
81
86
  const { enableNat, tcpListenIp, tcpListenPort, announceHostname, announcePort } = this.config;
@@ -87,6 +92,11 @@ export class LibP2PService implements P2PService {
87
92
  this.logger.info(`Enabling NAT in libp2p module`);
88
93
  }
89
94
 
95
+ // handle discovered peers from external discovery service
96
+ this.peerDiscoveryService.on('peer:discovered', async (enr: ENR) => {
97
+ await this.addPeer(enr);
98
+ });
99
+
90
100
  this.node.addEventListener('peer:discovery', evt => {
91
101
  const peerId = evt.detail.id;
92
102
  if (this.isBootstrapPeer(peerId)) {
@@ -109,12 +119,12 @@ export class LibP2PService implements P2PService {
109
119
  });
110
120
 
111
121
  this.jobQueue.start();
122
+ await this.peerDiscoveryService.start();
112
123
  await this.node.start();
113
124
  await this.node.handle(this.protocolId, (incoming: IncomingStreamData) =>
114
125
  this.jobQueue.put(() => Promise.resolve(this.handleProtocolDial(incoming))),
115
126
  );
116
- const dht = this.node.services['kadDHT'] as DualKadDHT;
117
- this.logger.info(`Started P2P client as ${await dht.getMode()} with Peer ID ${this.node.peerId.toString()}`);
127
+ this.logger.info(`Started P2P client with Peer ID ${this.node.peerId.toString()}`);
118
128
  }
119
129
 
120
130
  /**
@@ -135,25 +145,19 @@ export class LibP2PService implements P2PService {
135
145
  * @param txPool - The transaction pool to be accessed by the service.
136
146
  * @returns The new service.
137
147
  */
138
- public static async new(config: P2PConfig, txPool: TxPool) {
139
- const {
140
- tcpListenIp,
141
- tcpListenPort,
142
- announceHostname,
143
- announcePort,
144
- clientKADRouting,
145
- minPeerCount,
146
- maxPeerCount,
147
- peerIdPrivateKey,
148
- } = config;
149
- const peerId = await createLibP2PPeerId(peerIdPrivateKey);
150
-
148
+ public static async new(
149
+ config: P2PConfig,
150
+ peerDiscoveryService: PeerDiscoveryService,
151
+ peerId: PeerId,
152
+ txPool: TxPool,
153
+ store: AztecKVStore,
154
+ ) {
155
+ const { tcpListenIp, tcpListenPort, minPeerCount, maxPeerCount } = config;
151
156
  const opts: Libp2pOptions<ServiceMap> = {
152
157
  start: false,
153
158
  peerId,
154
159
  addresses: {
155
160
  listen: [`/ip4/${tcpListenIp}/tcp/${tcpListenPort}`],
156
- announce: announceHostname ? [`${announceHostname}/tcp/${announcePort ?? tcpListenPort}`] : [],
157
161
  },
158
162
  transports: [tcp()],
159
163
  streamMuxers: [yamux(), mplex()],
@@ -162,21 +166,12 @@ export class LibP2PService implements P2PService {
162
166
  minConnections: minPeerCount,
163
167
  maxConnections: maxPeerCount,
164
168
  },
165
- peerDiscovery: [
166
- bootstrap({
167
- list: config.bootstrapNodes,
168
- }),
169
- ],
170
169
  };
171
170
 
172
171
  const services: ServiceFactoryMap = {
173
- identify: identifyService({
172
+ identify: identify({
174
173
  protocolPrefix: 'aztec',
175
174
  }),
176
- kadDHT: kadDHT({
177
- protocolPrefix: 'aztec',
178
- clientMode: clientKADRouting,
179
- }),
180
175
  };
181
176
 
182
177
  // The autonat service seems quite problematic in that using it seems to cause a lot of attempts
@@ -198,7 +193,19 @@ export class LibP2PService implements P2PService {
198
193
  services,
199
194
  });
200
195
  const protocolId = config.transactionProtocol;
201
- return new LibP2PService(config, node, protocolId, txPool);
196
+
197
+ // Create an LMDB peer store
198
+ const peerDb = new AztecPeerDb(store);
199
+
200
+ // extract bootstrap node peer IDs
201
+ let bootstrapPeerIds: PeerId[] = [];
202
+ if (config.bootstrapNodes.length) {
203
+ bootstrapPeerIds = await Promise.all(
204
+ config.bootstrapNodes.map(bootnodeEnr => ENR.decodeTxt(bootnodeEnr).peerId()),
205
+ );
206
+ }
207
+
208
+ return new LibP2PService(config, node, peerDiscoveryService, peerDb, protocolId, txPool, bootstrapPeerIds);
202
209
  }
203
210
 
204
211
  /**
@@ -217,6 +224,44 @@ export class LibP2PService implements P2PService {
217
224
  this.knownTxLookup.handleSettledTxs(txHashes.map(x => x.toString()));
218
225
  }
219
226
 
227
+ private async addPeer(enr: ENR) {
228
+ const peerMultiAddr = await enr.getFullMultiaddr('tcp');
229
+ if (!peerMultiAddr) {
230
+ // No TCP address, can't connect
231
+ return;
232
+ }
233
+ const peerIdStr = peerMultiAddr.getPeerId();
234
+
235
+ if (!peerIdStr) {
236
+ this.logger.debug(`Peer ID not found in discovered node's multiaddr: ${peerMultiAddr}`);
237
+ return;
238
+ }
239
+
240
+ // check if peer is already known
241
+ const peerId = peerIdFromString(peerIdStr);
242
+ const hasPeer = await this.node.peerStore.has(peerId);
243
+
244
+ // add to peer store if not already known
245
+ if (!hasPeer) {
246
+ this.logger.info(`Discovered peer ${enr.peerId().toString()}. Adding to libp2p peer list`);
247
+ let stream: Stream | undefined;
248
+ try {
249
+ stream = await this.node.dialProtocol(peerMultiAddr, this.protocolId);
250
+
251
+ // dial successful, add to DB as well
252
+ if (!this.peerStore.getPeer(peerIdStr)) {
253
+ await this.peerStore.addPeer(peerIdStr, enr);
254
+ }
255
+ } catch (err) {
256
+ this.logger.error(`Failed to dial peer ${peerIdStr}`, err);
257
+ } finally {
258
+ if (stream) {
259
+ await stream.close();
260
+ }
261
+ }
262
+ }
263
+ }
264
+
220
265
  private async handleProtocolDial(incomingStreamData: IncomingStreamData) {
221
266
  try {
222
267
  const { message, peer } = await this.consumeInboundStream(incomingStreamData);
@@ -392,6 +437,6 @@ export class LibP2PService implements P2PService {
392
437
  }
393
438
 
394
439
  private isBootstrapPeer(peer: PeerId) {
395
- return this.config.bootstrapNodes.findIndex(bootstrap => bootstrap.includes(peer.toString())) != -1;
440
+ return this.bootstrapPeerIds.some(bootstrapPeer => bootstrapPeer.equals(peer));
396
441
  }
397
442
  }
@@ -0,0 +1,36 @@
1
+ import type { AztecKVStore, AztecMap } from '@aztec/kv-store';
2
+
3
+ import type { ENR } from '@chainsafe/enr';
4
+
5
+ export interface AztecPeerStore {
6
+ addPeer(peerId: string, enr: ENR): Promise<void>;
7
+ removePeer(peerId: string): Promise<void>;
8
+ getPeer(peerId: string): ENR | undefined;
9
+ getAllPeers(): IterableIterator<ENR>;
10
+ }
11
+
12
+ export class AztecPeerDb implements AztecPeerStore {
13
+ #peers: AztecMap<string, ENR>;
14
+
15
+ constructor(private db: AztecKVStore) {
16
+ this.#peers = db.openMap('p2p_peers');
17
+ }
18
+
19
+ async addPeer(peerId: string, enr: ENR): Promise<void> {
20
+ void (await this.#peers.set(peerId, enr));
21
+ }
22
+
23
+ async removePeer(peerId: string): Promise<void> {
24
+ void (await this.#peers.delete(peerId));
25
+ }
26
+
27
+ getPeer(peerId: string): ENR | undefined {
28
+ return this.#peers.get(peerId);
29
+ }
30
+
31
+ *getAllPeers(): IterableIterator<ENR> {
32
+ for (const enr of this.#peers.values()) {
33
+ yield enr;
34
+ }
35
+ }
36
+ }
@@ -1,4 +1,7 @@
1
- import { type Tx, type TxHash } from '@aztec/circuit-types';
1
+ import type { Tx, TxHash } from '@aztec/circuit-types';
2
+
3
+ import type { ENR } from '@chainsafe/enr';
4
+ import type EventEmitter from 'events';
2
5
 
3
6
  /**
4
7
  * The interface for a P2P service implementation.
@@ -28,3 +31,30 @@ export interface P2PService {
28
31
  */
29
32
  settledTxs(txHashes: TxHash[]): void;
30
33
  }
34
+
35
+ /**
36
+ * The interface for a peer discovery service implementation.
37
+ */
38
+ export interface PeerDiscoveryService extends EventEmitter {
39
+ /**
40
+ * Starts the service.
41
+ * */
42
+ start(): Promise<void>;
43
+
44
+ /**
45
+ * Stops the service.
46
+ * */
47
+ stop(): Promise<void>;
48
+
49
+ /**
50
+ * Gets all peers.
51
+ * @returns An array of peer ENRs.
52
+ */
53
+ getAllPeers(): ENR[];
54
+
55
+ /**
56
+ * Event emitted when a new peer is discovered.
57
+ */
58
+ on(event: 'peer:discovered', listener: (enr: ENR) => void): this;
59
+ emit(event: 'peer:discovered', enr: ENR): boolean;
60
+ }