@aztec/p2p 0.35.1 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/bootstrap/bootstrap.d.ts +10 -3
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +40 -59
- package/dest/client/index.d.ts.map +1 -1
- package/dest/client/index.js +16 -4
- package/dest/client/p2p_client.d.ts +1 -1
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/config.d.ts +17 -5
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +6 -3
- package/dest/service/discV5_service.d.ts +34 -0
- package/dest/service/discV5_service.d.ts.map +1 -0
- package/dest/service/discV5_service.js +95 -0
- package/dest/service/dummy_service.d.ts +23 -1
- package/dest/service/dummy_service.d.ts.map +1 -1
- package/dest/service/dummy_service.js +28 -1
- package/dest/service/known_txs.d.ts +1 -1
- package/dest/service/known_txs.d.ts.map +1 -1
- package/dest/service/libp2p_service.d.ts +12 -5
- package/dest/service/libp2p_service.d.ts.map +1 -1
- package/dest/service/libp2p_service.js +64 -24
- package/dest/service/peer_store.d.ts +18 -0
- package/dest/service/peer_store.d.ts.map +1 -0
- package/dest/service/peer_store.js +25 -0
- package/dest/service/service.d.ts +27 -1
- package/dest/service/service.d.ts.map +1 -1
- package/package.json +26 -14
- package/src/bootstrap/bootstrap.ts +52 -70
- package/src/client/index.ts +14 -3
- package/src/client/p2p_client.ts +1 -1
- package/src/config.ts +29 -8
- package/src/service/discV5_service.ts +122 -0
- package/src/service/dummy_service.ts +30 -1
- package/src/service/known_txs.ts +1 -1
- package/src/service/libp2p_service.ts +80 -35
- package/src/service/peer_store.ts +36 -0
- 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
|
|
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
|
+
}
|
package/src/service/known_txs.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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
|
+
}
|
package/src/service/service.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
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
|
+
}
|