@aztec/p2p 0.68.0 → 0.68.2
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 +5 -3
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +17 -15
- package/dest/services/discv5/discV5_service.d.ts +1 -7
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +13 -18
- package/dest/services/libp2p/libp2p_service.d.ts +1 -0
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +15 -8
- package/dest/services/peer-scoring/peer_scoring.d.ts +6 -0
- package/dest/services/peer-scoring/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-scoring/peer_scoring.js +21 -1
- package/dest/services/peer_manager.d.ts +30 -4
- package/dest/services/peer_manager.d.ts.map +1 -1
- package/dest/services/peer_manager.js +132 -44
- package/dest/services/reqresp/reqresp.js +4 -4
- package/dest/services/types.d.ts +38 -0
- package/dest/services/types.d.ts.map +1 -0
- package/dest/services/types.js +43 -0
- package/package.json +9 -9
- package/src/bootstrap/bootstrap.ts +25 -20
- package/src/services/discv5/discV5_service.ts +16 -20
- package/src/services/libp2p/libp2p_service.ts +23 -8
- package/src/services/peer-scoring/peer_scoring.ts +21 -0
- package/src/services/peer_manager.ts +152 -43
- package/src/services/reqresp/reqresp.ts +3 -3
- package/src/services/types.ts +44 -0
|
@@ -11,26 +11,16 @@ import EventEmitter from 'events';
|
|
|
11
11
|
import type { P2PConfig } from '../../config.js';
|
|
12
12
|
import { convertToMultiaddr } from '../../util.js';
|
|
13
13
|
import { type PeerDiscoveryService, PeerDiscoveryState } from '../service.js';
|
|
14
|
-
|
|
15
|
-
export const AZTEC_ENR_KEY = 'aztec_network';
|
|
14
|
+
import { AZTEC_ENR_KEY, AZTEC_NET, Discv5Event, PeerEvent } from '../types.js';
|
|
16
15
|
|
|
17
16
|
const delayBeforeStart = 2000; // 2sec
|
|
18
17
|
|
|
19
|
-
export enum AztecENR {
|
|
20
|
-
devnet = 0x01,
|
|
21
|
-
testnet = 0x02,
|
|
22
|
-
mainnet = 0x03,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// TODO: Make this an env var
|
|
26
|
-
export const AZTEC_NET = AztecENR.devnet;
|
|
27
|
-
|
|
28
18
|
/**
|
|
29
19
|
* Peer discovery service using Discv5.
|
|
30
20
|
*/
|
|
31
21
|
export class DiscV5Service extends EventEmitter implements PeerDiscoveryService {
|
|
32
22
|
/** The Discv5 instance */
|
|
33
|
-
private discv5: Discv5;
|
|
23
|
+
private discv5: Discv5 & Discv5EventEmitter;
|
|
34
24
|
|
|
35
25
|
/** This instance's ENR */
|
|
36
26
|
private enr: SignableENR;
|
|
@@ -88,13 +78,8 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
|
|
|
88
78
|
metricsRegistry,
|
|
89
79
|
});
|
|
90
80
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const multiAddrTcp = await enr.getFullMultiaddr('tcp');
|
|
94
|
-
const multiAddrUdp = await enr.getFullMultiaddr('udp');
|
|
95
|
-
this.logger.debug(`Added ENR ${enr.encodeTxt()}`, { multiAddrTcp, multiAddrUdp, nodeId: enr.nodeId });
|
|
96
|
-
this.onDiscovered(enr);
|
|
97
|
-
});
|
|
81
|
+
this.discv5.on(Discv5Event.DISCOVERED, this.onDiscovered.bind(this));
|
|
82
|
+
this.discv5.on(Discv5Event.ENR_ADDED, this.onEnrAdded.bind(this));
|
|
98
83
|
}
|
|
99
84
|
|
|
100
85
|
public async start(): Promise<void> {
|
|
@@ -168,10 +153,21 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
|
|
|
168
153
|
}
|
|
169
154
|
|
|
170
155
|
public async stop(): Promise<void> {
|
|
156
|
+
await this.discv5.off(Discv5Event.DISCOVERED, this.onDiscovered);
|
|
157
|
+
await this.discv5.off(Discv5Event.ENR_ADDED, this.onEnrAdded);
|
|
158
|
+
|
|
171
159
|
await this.discv5.stop();
|
|
160
|
+
|
|
172
161
|
this.currentState = PeerDiscoveryState.STOPPED;
|
|
173
162
|
}
|
|
174
163
|
|
|
164
|
+
private async onEnrAdded(enr: ENR) {
|
|
165
|
+
const multiAddrTcp = await enr.getFullMultiaddr('tcp');
|
|
166
|
+
const multiAddrUdp = await enr.getFullMultiaddr('udp');
|
|
167
|
+
this.logger.debug(`Added ENR ${enr.encodeTxt()}`, { multiAddrTcp, multiAddrUdp, nodeId: enr.nodeId });
|
|
168
|
+
this.onDiscovered(enr);
|
|
169
|
+
}
|
|
170
|
+
|
|
175
171
|
private onDiscovered(enr: ENR) {
|
|
176
172
|
// check the peer is an aztec peer
|
|
177
173
|
const value = enr.kvs.get(AZTEC_ENR_KEY);
|
|
@@ -179,7 +175,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
|
|
|
179
175
|
const network = value[0];
|
|
180
176
|
// check if the peer is on the same network
|
|
181
177
|
if (network === AZTEC_NET) {
|
|
182
|
-
this.emit(
|
|
178
|
+
this.emit(PeerEvent.DISCOVERED, enr);
|
|
183
179
|
}
|
|
184
180
|
}
|
|
185
181
|
}
|
|
@@ -26,7 +26,12 @@ import type { AztecKVStore } from '@aztec/kv-store';
|
|
|
26
26
|
import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
|
|
27
27
|
|
|
28
28
|
import { type ENR } from '@chainsafe/enr';
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
type GossipSub,
|
|
31
|
+
type GossipSubComponents,
|
|
32
|
+
type GossipsubMessage,
|
|
33
|
+
gossipsub,
|
|
34
|
+
} from '@chainsafe/libp2p-gossipsub';
|
|
30
35
|
import { createPeerScoreParams, createTopicScoreParams } from '@chainsafe/libp2p-gossipsub/score';
|
|
31
36
|
import { noise } from '@chainsafe/libp2p-noise';
|
|
32
37
|
import { yamux } from '@chainsafe/libp2p-yamux';
|
|
@@ -64,6 +69,7 @@ import {
|
|
|
64
69
|
} from '../reqresp/interface.js';
|
|
65
70
|
import { ReqResp } from '../reqresp/reqresp.js';
|
|
66
71
|
import type { P2PService, PeerDiscoveryService } from '../service.js';
|
|
72
|
+
import { GossipSubEvent } from '../types.js';
|
|
67
73
|
|
|
68
74
|
interface MessageValidator {
|
|
69
75
|
validator: {
|
|
@@ -119,7 +125,7 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
119
125
|
) {
|
|
120
126
|
super(telemetry, 'LibP2PService');
|
|
121
127
|
|
|
122
|
-
this.peerManager = new PeerManager(node, peerDiscoveryService, config,
|
|
128
|
+
this.peerManager = new PeerManager(node, peerDiscoveryService, config, telemetry, logger);
|
|
123
129
|
this.node.services.pubsub.score.params.appSpecificScore = (peerId: string) => {
|
|
124
130
|
return this.peerManager.getPeerScore(peerId);
|
|
125
131
|
};
|
|
@@ -179,12 +185,7 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
179
185
|
}
|
|
180
186
|
|
|
181
187
|
// add GossipSub listener
|
|
182
|
-
this.node.services.pubsub.addEventListener(
|
|
183
|
-
const { msg } = e.detail;
|
|
184
|
-
this.logger.trace(`Received PUBSUB message.`);
|
|
185
|
-
|
|
186
|
-
await this.jobQueue.put(() => this.handleNewGossipMessage(msg));
|
|
187
|
-
});
|
|
188
|
+
this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
|
|
188
189
|
|
|
189
190
|
// Start running promise for peer discovery
|
|
190
191
|
this.discoveryRunningPromise = new RunningPromise(
|
|
@@ -212,6 +213,13 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
212
213
|
* @returns An empty promise.
|
|
213
214
|
*/
|
|
214
215
|
public async stop() {
|
|
216
|
+
// Remove gossip sub listener
|
|
217
|
+
this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
|
|
218
|
+
|
|
219
|
+
// Stop peer manager
|
|
220
|
+
this.logger.debug('Stopping peer manager...');
|
|
221
|
+
this.peerManager.stop();
|
|
222
|
+
|
|
215
223
|
this.logger.debug('Stopping job queue...');
|
|
216
224
|
await this.jobQueue.end();
|
|
217
225
|
this.logger.debug('Stopping running promise...');
|
|
@@ -365,6 +373,13 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
365
373
|
return this.peerManager.getPeers(includePending);
|
|
366
374
|
}
|
|
367
375
|
|
|
376
|
+
private async handleGossipSubEvent(e: CustomEvent<GossipsubMessage>) {
|
|
377
|
+
const { msg } = e.detail;
|
|
378
|
+
this.logger.trace(`Received PUBSUB message.`);
|
|
379
|
+
|
|
380
|
+
await this.jobQueue.put(() => this.handleNewGossipMessage(msg));
|
|
381
|
+
}
|
|
382
|
+
|
|
368
383
|
/**
|
|
369
384
|
* Send Request via the ReqResp service
|
|
370
385
|
* The subprotocol defined will determine the request and response types
|
|
@@ -9,6 +9,16 @@ const DefaultPeerPenalties = {
|
|
|
9
9
|
[PeerErrorSeverity.HighToleranceError]: 2,
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
export enum PeerScoreState {
|
|
13
|
+
Banned,
|
|
14
|
+
Disconnect,
|
|
15
|
+
Healthy,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// TODO: move into config / constants
|
|
19
|
+
const MIN_SCORE_BEFORE_BAN = -100;
|
|
20
|
+
const MIN_SCORE_BEFORE_DISCONNECT = -50;
|
|
21
|
+
|
|
12
22
|
export class PeerScoring {
|
|
13
23
|
private scores: Map<string, number> = new Map();
|
|
14
24
|
private lastUpdateTime: Map<string, number> = new Map();
|
|
@@ -65,6 +75,17 @@ export class PeerScoring {
|
|
|
65
75
|
return this.scores.get(peerId) || 0;
|
|
66
76
|
}
|
|
67
77
|
|
|
78
|
+
getScoreState(peerId: string) {
|
|
79
|
+
// TODO: permanently store banned peers???
|
|
80
|
+
const score = this.getScore(peerId);
|
|
81
|
+
if (score < MIN_SCORE_BEFORE_BAN) {
|
|
82
|
+
return PeerScoreState.Banned;
|
|
83
|
+
} else if (score < MIN_SCORE_BEFORE_DISCONNECT) {
|
|
84
|
+
return PeerScoreState.Disconnect;
|
|
85
|
+
}
|
|
86
|
+
return PeerScoreState.Healthy;
|
|
87
|
+
}
|
|
88
|
+
|
|
68
89
|
getStats(): { medianScore: number } {
|
|
69
90
|
return { medianScore: median(Array.from(this.scores.values())) ?? 0 };
|
|
70
91
|
}
|
|
@@ -1,71 +1,116 @@
|
|
|
1
1
|
import { type PeerErrorSeverity, type PeerInfo } from '@aztec/circuit-types';
|
|
2
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
|
-
import { type
|
|
3
|
+
import { type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
|
|
4
4
|
|
|
5
5
|
import { type ENR } from '@chainsafe/enr';
|
|
6
|
-
import { type PeerId } from '@libp2p/interface';
|
|
6
|
+
import { type Connection, type PeerId } from '@libp2p/interface';
|
|
7
7
|
import { type Multiaddr } from '@multiformats/multiaddr';
|
|
8
8
|
import { inspect } from 'util';
|
|
9
9
|
|
|
10
10
|
import { type P2PConfig } from '../config.js';
|
|
11
11
|
import { type PubSubLibp2p } from '../util.js';
|
|
12
|
-
import { PeerScoring } from './peer-scoring/peer_scoring.js';
|
|
12
|
+
import { PeerScoreState, PeerScoring } from './peer-scoring/peer_scoring.js';
|
|
13
13
|
import { type PeerDiscoveryService } from './service.js';
|
|
14
|
+
import { PeerEvent } from './types.js';
|
|
14
15
|
|
|
15
16
|
const MAX_DIAL_ATTEMPTS = 3;
|
|
16
17
|
const MAX_CACHED_PEERS = 100;
|
|
18
|
+
const MAX_CACHED_PEER_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
19
|
+
const FAILED_PEER_BAN_TIME_MS = 5 * 60 * 1000; // 5 minutes timeout after failing MAX_DIAL_ATTEMPTS
|
|
17
20
|
|
|
18
21
|
type CachedPeer = {
|
|
19
22
|
peerId: PeerId;
|
|
20
23
|
enr: ENR;
|
|
21
24
|
multiaddrTcp: Multiaddr;
|
|
22
25
|
dialAttempts: number;
|
|
26
|
+
addedUnixMs: number;
|
|
23
27
|
};
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
type TimedOutPeer = {
|
|
30
|
+
peerId: string;
|
|
31
|
+
timeoutUntilMs: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export class PeerManager extends WithTracer {
|
|
26
35
|
private cachedPeers: Map<string, CachedPeer> = new Map();
|
|
27
36
|
private peerScoring: PeerScoring;
|
|
28
37
|
private heartbeatCounter: number = 0;
|
|
38
|
+
private displayPeerCountsPeerHeartbeat: number = 0;
|
|
39
|
+
private timedOutPeers: Map<string, TimedOutPeer> = new Map();
|
|
29
40
|
|
|
30
41
|
constructor(
|
|
31
42
|
private libP2PNode: PubSubLibp2p,
|
|
32
43
|
private peerDiscoveryService: PeerDiscoveryService,
|
|
33
44
|
private config: P2PConfig,
|
|
34
|
-
|
|
45
|
+
telemetryClient: TelemetryClient,
|
|
35
46
|
private logger = createLogger('p2p:peer-manager'),
|
|
36
47
|
) {
|
|
48
|
+
super(telemetryClient, 'PeerManager');
|
|
49
|
+
|
|
37
50
|
this.peerScoring = new PeerScoring(config);
|
|
38
51
|
// Handle new established connections
|
|
39
|
-
this.libP2PNode.addEventListener(
|
|
40
|
-
const peerId = evt.detail;
|
|
41
|
-
if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
|
|
42
|
-
this.logger.verbose(`Connected to bootstrap peer ${peerId.toString()}`);
|
|
43
|
-
} else {
|
|
44
|
-
this.logger.verbose(`Connected to transaction peer ${peerId.toString()}`);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
52
|
+
this.libP2PNode.addEventListener(PeerEvent.CONNECTED, this.handleConnectedPeerEvent.bind(this));
|
|
48
53
|
// Handle lost connections
|
|
49
|
-
this.libP2PNode.addEventListener(
|
|
50
|
-
const peerId = evt.detail;
|
|
51
|
-
if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
|
|
52
|
-
this.logger.verbose(`Disconnected from bootstrap peer ${peerId.toString()}`);
|
|
53
|
-
} else {
|
|
54
|
-
this.logger.verbose(`Disconnected from transaction peer ${peerId.toString()}`);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
54
|
+
this.libP2PNode.addEventListener(PeerEvent.DISCONNECTED, this.handleDisconnectedPeerEvent.bind(this));
|
|
57
55
|
|
|
58
56
|
// Handle Discovered peers
|
|
59
|
-
this.peerDiscoveryService.on(
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
this.peerDiscoveryService.on(PeerEvent.DISCOVERED, this.handleDiscoveredPeer.bind(this));
|
|
58
|
+
|
|
59
|
+
// Display peer counts every 60 seconds
|
|
60
|
+
this.displayPeerCountsPeerHeartbeat = Math.floor(60_000 / this.config.peerCheckIntervalMS);
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
@trackSpan('PeerManager.heartbeat')
|
|
65
64
|
public heartbeat() {
|
|
66
65
|
this.heartbeatCounter++;
|
|
67
|
-
this.discover();
|
|
68
66
|
this.peerScoring.decayAllScores();
|
|
67
|
+
|
|
68
|
+
this.cleanupExpiredTimeouts();
|
|
69
|
+
|
|
70
|
+
this.discover();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Cleans up expired timeouts.
|
|
75
|
+
*
|
|
76
|
+
* When peers fail to dial after a number of retries, they are temporarily timed out.
|
|
77
|
+
* This function removes any peers that have been in the timed out state for too long.
|
|
78
|
+
* To give them a chance to reconnect.
|
|
79
|
+
*/
|
|
80
|
+
private cleanupExpiredTimeouts() {
|
|
81
|
+
// Clean up expired timeouts
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
for (const [peerId, timedOutPeer] of this.timedOutPeers.entries()) {
|
|
84
|
+
if (now >= timedOutPeer.timeoutUntilMs) {
|
|
85
|
+
this.timedOutPeers.delete(peerId);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Simply logs the type of connected peer.
|
|
92
|
+
* @param e - The connected peer event.
|
|
93
|
+
*/
|
|
94
|
+
private handleConnectedPeerEvent(e: CustomEvent<PeerId>) {
|
|
95
|
+
const peerId = e.detail;
|
|
96
|
+
if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
|
|
97
|
+
this.logger.verbose(`Connected to bootstrap peer ${peerId.toString()}`);
|
|
98
|
+
} else {
|
|
99
|
+
this.logger.verbose(`Connected to transaction peer ${peerId.toString()}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Simply logs the type of disconnected peer.
|
|
105
|
+
* @param e - The disconnected peer event.
|
|
106
|
+
*/
|
|
107
|
+
private handleDisconnectedPeerEvent(e: CustomEvent<PeerId>) {
|
|
108
|
+
const peerId = e.detail;
|
|
109
|
+
if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
|
|
110
|
+
this.logger.verbose(`Disconnected from bootstrap peer ${peerId.toString()}`);
|
|
111
|
+
} else {
|
|
112
|
+
this.logger.verbose(`Disconnected from transaction peer ${peerId.toString()}`);
|
|
113
|
+
}
|
|
69
114
|
}
|
|
70
115
|
|
|
71
116
|
public penalizePeer(peerId: PeerId, penalty: PeerErrorSeverity) {
|
|
@@ -116,13 +161,14 @@ export class PeerManager implements Traceable {
|
|
|
116
161
|
* Discovers peers.
|
|
117
162
|
*/
|
|
118
163
|
private discover() {
|
|
119
|
-
// Get current connections
|
|
120
164
|
const connections = this.libP2PNode.getConnections();
|
|
121
165
|
|
|
166
|
+
const healthyConnections = this.pruneUnhealthyPeers(connections);
|
|
167
|
+
|
|
122
168
|
// Calculate how many connections we're looking to make
|
|
123
|
-
const peersToConnect = this.config.maxPeerCount -
|
|
169
|
+
const peersToConnect = this.config.maxPeerCount - healthyConnections.length;
|
|
124
170
|
|
|
125
|
-
const logLevel = this.heartbeatCounter %
|
|
171
|
+
const logLevel = this.heartbeatCounter % this.displayPeerCountsPeerHeartbeat === 0 ? 'info' : 'debug';
|
|
126
172
|
this.logger[logLevel](`Connected to ${connections.length} peers`, {
|
|
127
173
|
connections: connections.length,
|
|
128
174
|
maxPeerCount: this.config.maxPeerCount,
|
|
@@ -146,7 +192,12 @@ export class PeerManager implements Traceable {
|
|
|
146
192
|
|
|
147
193
|
for (const [id, peerData] of this.cachedPeers.entries()) {
|
|
148
194
|
// if already dialling or connected to, remove from cache
|
|
149
|
-
if (
|
|
195
|
+
if (
|
|
196
|
+
pendingDials.has(id) ||
|
|
197
|
+
healthyConnections.some(conn => conn.remotePeer.equals(peerData.peerId)) ||
|
|
198
|
+
// if peer has been in cache for the max cache age, remove from cache
|
|
199
|
+
Date.now() - peerData.addedUnixMs > MAX_CACHED_PEER_AGE_MS
|
|
200
|
+
) {
|
|
150
201
|
this.cachedPeers.delete(id);
|
|
151
202
|
} else {
|
|
152
203
|
// cachedPeersToDial.set(id, enr);
|
|
@@ -158,6 +209,7 @@ export class PeerManager implements Traceable {
|
|
|
158
209
|
cachedPeersToDial.reverse();
|
|
159
210
|
|
|
160
211
|
for (const peer of cachedPeersToDial) {
|
|
212
|
+
// We remove from the cache before, as dialling will add it back if it fails
|
|
161
213
|
this.cachedPeers.delete(peer.peerId.toString());
|
|
162
214
|
void this.dialPeer(peer);
|
|
163
215
|
}
|
|
@@ -169,35 +221,74 @@ export class PeerManager implements Traceable {
|
|
|
169
221
|
}
|
|
170
222
|
}
|
|
171
223
|
|
|
224
|
+
private pruneUnhealthyPeers(connections: Connection[]): Connection[] {
|
|
225
|
+
const connectedHealthyPeers: Connection[] = [];
|
|
226
|
+
|
|
227
|
+
for (const peer of connections) {
|
|
228
|
+
const score = this.peerScoring.getScoreState(peer.remotePeer.toString());
|
|
229
|
+
switch (score) {
|
|
230
|
+
// TODO: add goodbye and give reasons
|
|
231
|
+
case PeerScoreState.Banned:
|
|
232
|
+
case PeerScoreState.Disconnect:
|
|
233
|
+
void this.disconnectPeer(peer.remotePeer);
|
|
234
|
+
break;
|
|
235
|
+
case PeerScoreState.Healthy:
|
|
236
|
+
connectedHealthyPeers.push(peer);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return connectedHealthyPeers;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// TODO: send a goodbye with a reason to the peer
|
|
244
|
+
private async disconnectPeer(peer: PeerId) {
|
|
245
|
+
this.logger.debug(`Disconnecting peer ${peer.toString()}`);
|
|
246
|
+
await this.libP2PNode.hangUp(peer);
|
|
247
|
+
}
|
|
248
|
+
|
|
172
249
|
/**
|
|
173
250
|
* Handles a discovered peer.
|
|
174
251
|
* @param enr - The discovered peer's ENR.
|
|
175
252
|
*/
|
|
176
253
|
private async handleDiscoveredPeer(enr: ENR) {
|
|
177
|
-
//
|
|
254
|
+
// Check that the peer has not already been banned
|
|
255
|
+
const peerId = await enr.peerId();
|
|
256
|
+
const peerIdString = peerId.toString();
|
|
257
|
+
|
|
258
|
+
// Check if peer is temporarily timed out
|
|
259
|
+
const timedOutPeer = this.timedOutPeers.get(peerIdString);
|
|
260
|
+
if (timedOutPeer) {
|
|
261
|
+
if (Date.now() < timedOutPeer.timeoutUntilMs) {
|
|
262
|
+
this.logger.trace(`Skipping timed out peer ${peerId}`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
// Timeout period expired, remove from timed out peers
|
|
266
|
+
this.timedOutPeers.delete(peerIdString);
|
|
267
|
+
}
|
|
178
268
|
|
|
179
|
-
|
|
180
|
-
|
|
269
|
+
if (this.peerScoring.getScoreState(peerIdString) != PeerScoreState.Healthy) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
181
272
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
);
|
|
273
|
+
const [multiaddrTcp] = await Promise.all([enr.getFullMultiaddr('tcp')]);
|
|
274
|
+
|
|
275
|
+
this.logger.trace(`Handling discovered peer ${peerId} at ${multiaddrTcp?.toString() ?? 'undefined address'}`);
|
|
185
276
|
|
|
186
|
-
//
|
|
277
|
+
// stop if no tcp addr in multiaddr
|
|
187
278
|
if (!multiaddrTcp) {
|
|
188
279
|
this.logger.debug(`No TCP address in discovered node's multiaddr ${enr.encodeTxt()}`);
|
|
189
280
|
return;
|
|
190
281
|
}
|
|
282
|
+
// check if peer is already connected
|
|
191
283
|
const connections = this.libP2PNode.getConnections();
|
|
192
284
|
if (connections.some(conn => conn.remotePeer.equals(peerId))) {
|
|
193
|
-
this.logger.trace(`Already connected to peer ${peerId
|
|
285
|
+
this.logger.trace(`Already connected to peer ${peerId}`);
|
|
194
286
|
return;
|
|
195
287
|
}
|
|
196
288
|
|
|
197
289
|
// check if peer is already in cache
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
this.logger.trace(`Peer already in cache ${id}`);
|
|
290
|
+
if (this.cachedPeers.has(peerIdString)) {
|
|
291
|
+
this.logger.trace(`Peer already in cache ${peerIdString}`);
|
|
201
292
|
return;
|
|
202
293
|
}
|
|
203
294
|
|
|
@@ -207,14 +298,15 @@ export class PeerManager implements Traceable {
|
|
|
207
298
|
enr,
|
|
208
299
|
multiaddrTcp,
|
|
209
300
|
dialAttempts: 0,
|
|
301
|
+
addedUnixMs: Date.now(),
|
|
210
302
|
};
|
|
211
303
|
|
|
212
304
|
// Determine if we should dial immediately or not
|
|
213
305
|
if (this.shouldDialPeer()) {
|
|
214
306
|
void this.dialPeer(cachedPeer);
|
|
215
307
|
} else {
|
|
216
|
-
this.logger.trace(`Caching peer ${
|
|
217
|
-
this.cachedPeers.set(
|
|
308
|
+
this.logger.trace(`Caching peer ${peerIdString}`);
|
|
309
|
+
this.cachedPeers.set(peerIdString, cachedPeer);
|
|
218
310
|
// Prune set of cached peers
|
|
219
311
|
this.pruneCachedPeers();
|
|
220
312
|
}
|
|
@@ -222,6 +314,8 @@ export class PeerManager implements Traceable {
|
|
|
222
314
|
|
|
223
315
|
private async dialPeer(peer: CachedPeer) {
|
|
224
316
|
const id = peer.peerId.toString();
|
|
317
|
+
|
|
318
|
+
// Add to the address book before dialing
|
|
225
319
|
await this.libP2PNode.peerStore.merge(peer.peerId, { multiaddrs: [peer.multiaddrTcp] });
|
|
226
320
|
|
|
227
321
|
this.logger.trace(`Dialing peer ${id}`);
|
|
@@ -236,6 +330,11 @@ export class PeerManager implements Traceable {
|
|
|
236
330
|
formatLibp2pDialError(error as Error);
|
|
237
331
|
this.logger.debug(`Failed to dial peer ${id} (dropping)`, { error: inspect(error) });
|
|
238
332
|
this.cachedPeers.delete(id);
|
|
333
|
+
// Add to timed out peers
|
|
334
|
+
this.timedOutPeers.set(id, {
|
|
335
|
+
peerId: id,
|
|
336
|
+
timeoutUntilMs: Date.now() + FAILED_PEER_BAN_TIME_MS,
|
|
337
|
+
});
|
|
239
338
|
}
|
|
240
339
|
}
|
|
241
340
|
}
|
|
@@ -267,6 +366,16 @@ export class PeerManager implements Traceable {
|
|
|
267
366
|
}
|
|
268
367
|
}
|
|
269
368
|
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Stops the peer manager.
|
|
372
|
+
* Removing all event listeners.
|
|
373
|
+
*/
|
|
374
|
+
public stop() {
|
|
375
|
+
this.libP2PNode.removeEventListener(PeerEvent.CONNECTED, this.handleConnectedPeerEvent);
|
|
376
|
+
this.libP2PNode.removeEventListener(PeerEvent.DISCONNECTED, this.handleDisconnectedPeerEvent);
|
|
377
|
+
this.peerDiscoveryService.off(PeerEvent.DISCOVERED, this.handleDiscoveredPeer);
|
|
378
|
+
}
|
|
270
379
|
}
|
|
271
380
|
|
|
272
381
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @attribution: lodestar impl for inspiration
|
|
2
2
|
import { PeerErrorSeverity } from '@aztec/circuit-types';
|
|
3
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
|
-
import {
|
|
4
|
+
import { executeTimeout } from '@aztec/foundation/timer';
|
|
5
5
|
|
|
6
6
|
import { type IncomingStreamData, type PeerId, type Stream } from '@libp2p/interface';
|
|
7
7
|
import { pipe } from 'it-pipe';
|
|
@@ -159,7 +159,7 @@ export class ReqResp {
|
|
|
159
159
|
};
|
|
160
160
|
|
|
161
161
|
try {
|
|
162
|
-
return await
|
|
162
|
+
return await executeTimeout<InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined>(
|
|
163
163
|
requestFunction,
|
|
164
164
|
this.overallRequestTimeoutMs,
|
|
165
165
|
() => new CollectiveReqRespTimeoutError(),
|
|
@@ -205,7 +205,7 @@ export class ReqResp {
|
|
|
205
205
|
this.logger.trace(`Stream opened with ${peerId.toString()} for ${subProtocol}`);
|
|
206
206
|
|
|
207
207
|
// Open the stream with a timeout
|
|
208
|
-
const result = await
|
|
208
|
+
const result = await executeTimeout<Buffer>(
|
|
209
209
|
(): Promise<Buffer> => pipe([payload], stream!, this.readMessage.bind(this)),
|
|
210
210
|
this.individualRequestTimeoutMs,
|
|
211
211
|
() => new IndividualReqRespTimeoutError(),
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/***************************************************
|
|
2
|
+
* Events
|
|
3
|
+
***************************************************/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Events emitted from the libp2p node.
|
|
7
|
+
*/
|
|
8
|
+
export enum PeerEvent {
|
|
9
|
+
DISCOVERED = 'peer:discovered',
|
|
10
|
+
CONNECTED = 'peer:connect',
|
|
11
|
+
DISCONNECTED = 'peer:disconnect',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Events emitted from the Discv5 service.
|
|
16
|
+
*/
|
|
17
|
+
export enum Discv5Event {
|
|
18
|
+
DISCOVERED = 'discovered',
|
|
19
|
+
ENR_ADDED = 'enrAdded',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Events emitted from the GossipSub protocol.
|
|
24
|
+
*/
|
|
25
|
+
export enum GossipSubEvent {
|
|
26
|
+
MESSAGE = 'gossipsub:message',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/***************************************************
|
|
30
|
+
* Types
|
|
31
|
+
***************************************************/
|
|
32
|
+
/**
|
|
33
|
+
* Aztec network specific types
|
|
34
|
+
*/
|
|
35
|
+
export const AZTEC_ENR_KEY = 'aztec_network';
|
|
36
|
+
|
|
37
|
+
export enum AztecENR {
|
|
38
|
+
devnet = 0x01,
|
|
39
|
+
testnet = 0x02,
|
|
40
|
+
mainnet = 0x03,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// TODO: Make this an env var
|
|
44
|
+
export const AZTEC_NET = AztecENR.devnet;
|