@aztec/p2p 0.74.0 → 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2
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.js +41 -29
- package/dest/client/factory.js +8 -10
- package/dest/client/index.js +0 -1
- package/dest/client/p2p_client.js +513 -507
- package/dest/config.js +44 -39
- package/dest/errors/reqresp.error.js +6 -10
- package/dest/index.js +0 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +6 -2
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +64 -32
- package/dest/mem_pools/attestation_pool/index.js +0 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +22 -19
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +22 -26
- package/dest/mem_pools/attestation_pool/mocks.js +10 -6
- package/dest/mem_pools/epoch_proof_quote_pool/epoch_proof_quote_pool.js +1 -2
- package/dest/mem_pools/epoch_proof_quote_pool/index.js +0 -1
- package/dest/mem_pools/epoch_proof_quote_pool/memory_epoch_proof_quote_pool.js +5 -4
- package/dest/mem_pools/epoch_proof_quote_pool/test_utils.js +2 -3
- package/dest/mem_pools/index.js +1 -2
- package/dest/mem_pools/instrumentation.js +37 -42
- package/dest/mem_pools/interface.js +3 -2
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +127 -134
- package/dest/mem_pools/tx_pool/index.js +0 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +45 -43
- package/dest/mem_pools/tx_pool/priority.js +1 -3
- package/dest/mem_pools/tx_pool/tx_pool.js +3 -2
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +107 -37
- package/dest/mocks/index.js +47 -38
- package/dest/msg_validators/attestation_validator/attestation_validator.js +3 -3
- package/dest/msg_validators/attestation_validator/index.js +0 -1
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +2 -2
- package/dest/msg_validators/block_proposal_validator/index.js +0 -1
- package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.js +2 -2
- package/dest/msg_validators/epoch_proof_quote_validator/index.js +0 -1
- package/dest/msg_validators/index.js +0 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +9 -11
- package/dest/msg_validators/tx_validator/block_header_validator.js +17 -12
- package/dest/msg_validators/tx_validator/data_validator.js +41 -32
- package/dest/msg_validators/tx_validator/double_spend_validator.js +22 -14
- package/dest/msg_validators/tx_validator/index.js +0 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +29 -26
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +17 -12
- package/dest/services/data_store.js +57 -57
- package/dest/services/discv5/discV5_service.js +31 -23
- package/dest/services/dummy_service.js +40 -58
- package/dest/services/encoding.js +10 -9
- package/dest/services/index.js +0 -1
- package/dest/services/libp2p/libp2p_service.js +709 -695
- package/dest/services/peer-manager/metrics.js +14 -7
- package/dest/services/peer-manager/peer_manager.js +340 -342
- package/dest/services/peer-manager/peer_scoring.js +20 -18
- package/dest/services/reqresp/config.js +4 -5
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +35 -28
- package/dest/services/reqresp/connection-sampler/connection_sampler.js +60 -59
- package/dest/services/reqresp/index.js +1 -3
- package/dest/services/reqresp/interface.js +25 -30
- package/dest/services/reqresp/metrics.js +23 -10
- package/dest/services/reqresp/protocols/block.js +1 -2
- package/dest/services/reqresp/protocols/goodbye.js +35 -40
- package/dest/services/reqresp/protocols/index.js +1 -3
- package/dest/services/reqresp/protocols/ping.js +1 -3
- package/dest/services/reqresp/protocols/status.js +1 -3
- package/dest/services/reqresp/protocols/tx.js +5 -8
- package/dest/services/reqresp/rate-limiter/index.js +0 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.js +42 -36
- package/dest/services/reqresp/rate-limiter/rate_limits.js +16 -17
- package/dest/services/reqresp/reqresp.js +461 -395
- package/dest/services/reqresp/status.js +51 -0
- package/dest/services/service.js +3 -4
- package/dest/services/types.js +16 -23
- package/dest/util.js +23 -34
- package/package.json +8 -8
- package/src/client/p2p_client.ts +159 -125
- package/src/mem_pools/index.ts +3 -3
- package/src/mem_pools/instrumentation.ts +3 -2
- package/src/services/reqresp/interface.ts +11 -0
- package/src/services/reqresp/rate-limiter/rate_limiter.ts +3 -1
- package/src/services/reqresp/rate-limiter/rate_limits.ts +2 -2
- package/src/services/reqresp/reqresp.ts +111 -24
- package/src/services/reqresp/status.ts +59 -0
- package/dest/bootstrap/bootstrap.d.ts +0 -38
- package/dest/bootstrap/bootstrap.d.ts.map +0 -1
- package/dest/client/factory.d.ts +0 -19
- package/dest/client/factory.d.ts.map +0 -1
- package/dest/client/index.d.ts +0 -3
- package/dest/client/index.d.ts.map +0 -1
- package/dest/client/p2p_client.d.ts +0 -321
- package/dest/client/p2p_client.d.ts.map +0 -1
- package/dest/config.d.ts +0 -171
- package/dest/config.d.ts.map +0 -1
- package/dest/errors/reqresp.error.d.ts +0 -28
- package/dest/errors/reqresp.error.d.ts.map +0 -1
- package/dest/index.d.ts +0 -9
- package/dest/index.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +0 -57
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +0 -3
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/index.d.ts +0 -3
- package/dest/mem_pools/attestation_pool/index.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -22
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -17
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/mocks.d.ts +0 -18
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +0 -1
- package/dest/mem_pools/epoch_proof_quote_pool/epoch_proof_quote_pool.d.ts +0 -7
- package/dest/mem_pools/epoch_proof_quote_pool/epoch_proof_quote_pool.d.ts.map +0 -1
- package/dest/mem_pools/epoch_proof_quote_pool/index.d.ts +0 -4
- package/dest/mem_pools/epoch_proof_quote_pool/index.d.ts.map +0 -1
- package/dest/mem_pools/epoch_proof_quote_pool/memory_epoch_proof_quote_pool.d.ts +0 -12
- package/dest/mem_pools/epoch_proof_quote_pool/memory_epoch_proof_quote_pool.d.ts.map +0 -1
- package/dest/mem_pools/epoch_proof_quote_pool/test_utils.d.ts +0 -8
- package/dest/mem_pools/epoch_proof_quote_pool/test_utils.d.ts.map +0 -1
- package/dest/mem_pools/index.d.ts +0 -5
- package/dest/mem_pools/index.d.ts.map +0 -1
- package/dest/mem_pools/instrumentation.d.ts +0 -31
- package/dest/mem_pools/instrumentation.d.ts.map +0 -1
- package/dest/mem_pools/interface.d.ts +0 -13
- package/dest/mem_pools/interface.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -66
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/index.d.ts +0 -4
- package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +0 -56
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/priority.d.ts +0 -8
- package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -66
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
- package/dest/mocks/index.d.ts +0 -54
- package/dest/mocks/index.d.ts.map +0 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +0 -8
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +0 -1
- package/dest/msg_validators/attestation_validator/index.d.ts +0 -2
- package/dest/msg_validators/attestation_validator/index.d.ts.map +0 -1
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +0 -8
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +0 -1
- package/dest/msg_validators/block_proposal_validator/index.d.ts +0 -2
- package/dest/msg_validators/block_proposal_validator/index.d.ts.map +0 -1
- package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.d.ts +0 -8
- package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.d.ts.map +0 -1
- package/dest/msg_validators/epoch_proof_quote_validator/index.d.ts +0 -2
- package/dest/msg_validators/epoch_proof_quote_validator/index.d.ts.map +0 -1
- package/dest/msg_validators/index.d.ts +0 -4
- package/dest/msg_validators/index.d.ts.map +0 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +0 -7
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +0 -1
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts +0 -11
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +0 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts +0 -6
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +0 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +0 -12
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +0 -1
- package/dest/msg_validators/tx_validator/index.d.ts +0 -7
- package/dest/msg_validators/tx_validator/index.d.ts.map +0 -1
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts +0 -10
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +0 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +0 -8
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +0 -1
- package/dest/services/data_store.d.ts +0 -27
- package/dest/services/data_store.d.ts.map +0 -1
- package/dest/services/discv5/discV5_service.d.ts +0 -36
- package/dest/services/discv5/discV5_service.d.ts.map +0 -1
- package/dest/services/dummy_service.d.ts +0 -82
- package/dest/services/dummy_service.d.ts.map +0 -1
- package/dest/services/encoding.d.ts +0 -31
- package/dest/services/encoding.d.ts.map +0 -1
- package/dest/services/index.d.ts +0 -3
- package/dest/services/index.d.ts.map +0 -1
- package/dest/services/libp2p/libp2p_service.d.ts +0 -225
- package/dest/services/libp2p/libp2p_service.d.ts.map +0 -1
- package/dest/services/peer-manager/metrics.d.ts +0 -12
- package/dest/services/peer-manager/metrics.d.ts.map +0 -1
- package/dest/services/peer-manager/peer_manager.d.ts +0 -76
- package/dest/services/peer-manager/peer_manager.d.ts.map +0 -1
- package/dest/services/peer-manager/peer_scoring.d.ts +0 -28
- package/dest/services/peer-manager/peer_scoring.d.ts.map +0 -1
- package/dest/services/reqresp/config.d.ts +0 -16
- package/dest/services/reqresp/config.d.ts.map +0 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -45
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +0 -61
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +0 -1
- package/dest/services/reqresp/index.d.ts +0 -6
- package/dest/services/reqresp/index.d.ts.map +0 -1
- package/dest/services/reqresp/interface.d.ts +0 -107
- package/dest/services/reqresp/interface.d.ts.map +0 -1
- package/dest/services/reqresp/metrics.d.ts +0 -15
- package/dest/services/reqresp/metrics.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/block.d.ts +0 -4
- package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/goodbye.d.ts +0 -51
- package/dest/services/reqresp/protocols/goodbye.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/index.d.ts +0 -9
- package/dest/services/reqresp/protocols/index.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/ping.d.ts +0 -9
- package/dest/services/reqresp/protocols/ping.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/status.d.ts +0 -9
- package/dest/services/reqresp/protocols/status.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/tx.d.ts +0 -13
- package/dest/services/reqresp/protocols/tx.d.ts.map +0 -1
- package/dest/services/reqresp/rate-limiter/index.d.ts +0 -2
- package/dest/services/reqresp/rate-limiter/index.d.ts.map +0 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +0 -102
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +0 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +0 -3
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +0 -1
- package/dest/services/reqresp/reqresp.d.ts +0 -161
- package/dest/services/reqresp/reqresp.d.ts.map +0 -1
- package/dest/services/service.d.ts +0 -85
- package/dest/services/service.d.ts.map +0 -1
- package/dest/services/types.d.ts +0 -38
- package/dest/services/types.d.ts.map +0 -1
- package/dest/util.d.ts +0 -53
- package/dest/util.d.ts.map +0 -1
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
2
7
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
8
|
import { trackSpan } from '@aztec/telemetry-client';
|
|
4
9
|
import { inspect } from 'util';
|
|
@@ -11,348 +16,345 @@ const MAX_DIAL_ATTEMPTS = 3;
|
|
|
11
16
|
const MAX_CACHED_PEERS = 100;
|
|
12
17
|
const MAX_CACHED_PEER_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
13
18
|
const FAILED_PEER_BAN_TIME_MS = 5 * 60 * 1000; // 5 minutes timeout after failing MAX_DIAL_ATTEMPTS
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
handleConnectedPeerEvent(e) {
|
|
72
|
-
const peerId = e.detail;
|
|
73
|
-
if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
|
|
74
|
-
this.logger.verbose(`Connected to bootstrap peer ${peerId.toString()}`);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
this.logger.verbose(`Connected to transaction peer ${peerId.toString()}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Simply logs the type of disconnected peer.
|
|
82
|
-
* @param e - The disconnected peer event.
|
|
83
|
-
*/
|
|
84
|
-
handleDisconnectedPeerEvent(e) {
|
|
85
|
-
const peerId = e.detail;
|
|
86
|
-
if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
|
|
87
|
-
this.logger.verbose(`Disconnected from bootstrap peer ${peerId.toString()}`);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
this.logger.verbose(`Disconnected from transaction peer ${peerId.toString()}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Handles a goodbye received from a peer.
|
|
95
|
-
*
|
|
96
|
-
* Used as the reqresp handler when a peer sends us goodbye message.
|
|
97
|
-
* @param peerId - The peer ID.
|
|
98
|
-
* @param reason - The reason for the goodbye.
|
|
99
|
-
*/
|
|
100
|
-
goodbyeReceived(peerId, reason) {
|
|
101
|
-
this.logger.debug(`Goodbye received from peer ${peerId.toString()} with reason ${prettyGoodbyeReason(reason)}`);
|
|
102
|
-
this.metrics.recordGoodbyeReceived(reason);
|
|
103
|
-
void this.disconnectPeer(peerId);
|
|
19
|
+
export class PeerManager {
|
|
20
|
+
libP2PNode;
|
|
21
|
+
peerDiscoveryService;
|
|
22
|
+
config;
|
|
23
|
+
logger;
|
|
24
|
+
peerScoring;
|
|
25
|
+
reqresp;
|
|
26
|
+
cachedPeers;
|
|
27
|
+
heartbeatCounter;
|
|
28
|
+
displayPeerCountsPeerHeartbeat;
|
|
29
|
+
timedOutPeers;
|
|
30
|
+
metrics;
|
|
31
|
+
discoveredPeerHandler;
|
|
32
|
+
constructor(libP2PNode, peerDiscoveryService, config, telemetryClient, logger = createLogger('p2p:peer-manager'), peerScoring, reqresp){
|
|
33
|
+
this.libP2PNode = libP2PNode;
|
|
34
|
+
this.peerDiscoveryService = peerDiscoveryService;
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.logger = logger;
|
|
37
|
+
this.peerScoring = peerScoring;
|
|
38
|
+
this.reqresp = reqresp;
|
|
39
|
+
this.cachedPeers = new Map();
|
|
40
|
+
this.heartbeatCounter = 0;
|
|
41
|
+
this.displayPeerCountsPeerHeartbeat = 0;
|
|
42
|
+
this.timedOutPeers = new Map();
|
|
43
|
+
this.metrics = new PeerManagerMetrics(telemetryClient, 'PeerManager');
|
|
44
|
+
// Handle new established connections
|
|
45
|
+
this.libP2PNode.addEventListener(PeerEvent.CONNECTED, this.handleConnectedPeerEvent.bind(this));
|
|
46
|
+
// Handle lost connections
|
|
47
|
+
this.libP2PNode.addEventListener(PeerEvent.DISCONNECTED, this.handleDisconnectedPeerEvent.bind(this));
|
|
48
|
+
// Handle Discovered peers
|
|
49
|
+
this.discoveredPeerHandler = (enr)=>this.handleDiscoveredPeer(enr).catch((e)=>this.logger.error('Error handling discovered peer', e));
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
51
|
+
this.peerDiscoveryService.on(PeerEvent.DISCOVERED, this.discoveredPeerHandler);
|
|
52
|
+
// Display peer counts every 60 seconds
|
|
53
|
+
this.displayPeerCountsPeerHeartbeat = Math.floor(60_000 / this.config.peerCheckIntervalMS);
|
|
54
|
+
}
|
|
55
|
+
get tracer() {
|
|
56
|
+
return this.metrics.tracer;
|
|
57
|
+
}
|
|
58
|
+
heartbeat() {
|
|
59
|
+
this.heartbeatCounter++;
|
|
60
|
+
this.peerScoring.decayAllScores();
|
|
61
|
+
this.cleanupExpiredTimeouts();
|
|
62
|
+
this.discover();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Cleans up expired timeouts.
|
|
66
|
+
*
|
|
67
|
+
* When peers fail to dial after a number of retries, they are temporarily timed out.
|
|
68
|
+
* This function removes any peers that have been in the timed out state for too long.
|
|
69
|
+
* To give them a chance to reconnect.
|
|
70
|
+
*/ cleanupExpiredTimeouts() {
|
|
71
|
+
// Clean up expired timeouts
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
for (const [peerId, timedOutPeer] of this.timedOutPeers.entries()){
|
|
74
|
+
if (now >= timedOutPeer.timeoutUntilMs) {
|
|
75
|
+
this.timedOutPeers.delete(peerId);
|
|
104
76
|
}
|
|
105
|
-
|
|
106
|
-
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Simply logs the type of connected peer.
|
|
81
|
+
* @param e - The connected peer event.
|
|
82
|
+
*/ handleConnectedPeerEvent(e) {
|
|
83
|
+
const peerId = e.detail;
|
|
84
|
+
if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
|
|
85
|
+
this.logger.verbose(`Connected to bootstrap peer ${peerId.toString()}`);
|
|
86
|
+
} else {
|
|
87
|
+
this.logger.verbose(`Connected to transaction peer ${peerId.toString()}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Simply logs the type of disconnected peer.
|
|
92
|
+
* @param e - The disconnected peer event.
|
|
93
|
+
*/ handleDisconnectedPeerEvent(e) {
|
|
94
|
+
const peerId = e.detail;
|
|
95
|
+
if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
|
|
96
|
+
this.logger.verbose(`Disconnected from bootstrap peer ${peerId.toString()}`);
|
|
97
|
+
} else {
|
|
98
|
+
this.logger.verbose(`Disconnected from transaction peer ${peerId.toString()}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Handles a goodbye received from a peer.
|
|
103
|
+
*
|
|
104
|
+
* Used as the reqresp handler when a peer sends us goodbye message.
|
|
105
|
+
* @param peerId - The peer ID.
|
|
106
|
+
* @param reason - The reason for the goodbye.
|
|
107
|
+
*/ goodbyeReceived(peerId, reason) {
|
|
108
|
+
this.logger.debug(`Goodbye received from peer ${peerId.toString()} with reason ${prettyGoodbyeReason(reason)}`);
|
|
109
|
+
this.metrics.recordGoodbyeReceived(reason);
|
|
110
|
+
void this.disconnectPeer(peerId);
|
|
111
|
+
}
|
|
112
|
+
penalizePeer(peerId, penalty) {
|
|
113
|
+
this.peerScoring.penalizePeer(peerId, penalty);
|
|
114
|
+
}
|
|
115
|
+
getPeerScore(peerId) {
|
|
116
|
+
return this.peerScoring.getScore(peerId);
|
|
117
|
+
}
|
|
118
|
+
getPeers(includePending = false) {
|
|
119
|
+
const connected = this.libP2PNode.getPeers().map((peer)=>({
|
|
120
|
+
id: peer.toString(),
|
|
121
|
+
score: this.getPeerScore(peer.toString()),
|
|
122
|
+
status: 'connected'
|
|
123
|
+
}));
|
|
124
|
+
if (!includePending) {
|
|
125
|
+
return connected;
|
|
126
|
+
}
|
|
127
|
+
const dialQueue = this.libP2PNode.getDialQueue().filter((peer)=>!!peer.peerId).map((peer)=>({
|
|
128
|
+
id: peer.peerId.toString(),
|
|
129
|
+
status: 'dialing',
|
|
130
|
+
dialStatus: peer.status,
|
|
131
|
+
addresses: peer.multiaddrs.map((m)=>m.toString())
|
|
132
|
+
}));
|
|
133
|
+
const cachedPeers = Array.from(this.cachedPeers.values()).filter((peer)=>!dialQueue.some((dialPeer)=>dialPeer.id && peer.peerId.toString() === dialPeer.id.toString())).filter((peer)=>!connected.some((connPeer)=>connPeer.id.toString() === peer.peerId.toString())).map((peer)=>({
|
|
134
|
+
status: 'cached',
|
|
135
|
+
id: peer.peerId.toString(),
|
|
136
|
+
addresses: [
|
|
137
|
+
peer.multiaddrTcp.toString()
|
|
138
|
+
],
|
|
139
|
+
dialAttempts: peer.dialAttempts,
|
|
140
|
+
enr: peer.enr.encodeTxt()
|
|
141
|
+
}));
|
|
142
|
+
return [
|
|
143
|
+
...connected,
|
|
144
|
+
...dialQueue,
|
|
145
|
+
...cachedPeers
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Discovers peers.
|
|
150
|
+
*/ discover() {
|
|
151
|
+
const connections = this.libP2PNode.getConnections();
|
|
152
|
+
const healthyConnections = this.pruneUnhealthyPeers(connections);
|
|
153
|
+
// Calculate how many connections we're looking to make
|
|
154
|
+
const peersToConnect = this.config.maxPeerCount - healthyConnections.length;
|
|
155
|
+
const logLevel = this.heartbeatCounter % this.displayPeerCountsPeerHeartbeat === 0 ? 'info' : 'debug';
|
|
156
|
+
this.logger[logLevel](`Connected to ${connections.length} peers`, {
|
|
157
|
+
connections: connections.length,
|
|
158
|
+
maxPeerCount: this.config.maxPeerCount,
|
|
159
|
+
cachedPeers: this.cachedPeers.size,
|
|
160
|
+
...this.peerScoring.getStats()
|
|
161
|
+
});
|
|
162
|
+
// Exit if no peers to connect
|
|
163
|
+
if (peersToConnect <= 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const cachedPeersToDial = [];
|
|
167
|
+
const pendingDials = new Set(this.libP2PNode.getDialQueue().map((pendingDial)=>pendingDial.peerId?.toString()).filter(Boolean));
|
|
168
|
+
for (const [id, peerData] of this.cachedPeers.entries()){
|
|
169
|
+
// if already dialling or connected to, remove from cache
|
|
170
|
+
if (pendingDials.has(id) || healthyConnections.some((conn)=>conn.remotePeer.equals(peerData.peerId)) || // if peer has been in cache for the max cache age, remove from cache
|
|
171
|
+
Date.now() - peerData.addedUnixMs > MAX_CACHED_PEER_AGE_MS) {
|
|
172
|
+
this.cachedPeers.delete(id);
|
|
173
|
+
} else {
|
|
174
|
+
// cachedPeersToDial.set(id, enr);
|
|
175
|
+
cachedPeersToDial.push(peerData);
|
|
107
176
|
}
|
|
108
|
-
|
|
109
|
-
|
|
177
|
+
}
|
|
178
|
+
// reverse to dial older entries first
|
|
179
|
+
cachedPeersToDial.reverse();
|
|
180
|
+
for (const peer of cachedPeersToDial){
|
|
181
|
+
// We remove from the cache before, as dialling will add it back if it fails
|
|
182
|
+
this.cachedPeers.delete(peer.peerId.toString());
|
|
183
|
+
void this.dialPeer(peer);
|
|
184
|
+
}
|
|
185
|
+
// if we need more peers, start randomNodesQuery
|
|
186
|
+
if (peersToConnect > 0) {
|
|
187
|
+
this.logger.trace(`Running random nodes query to connect to ${peersToConnect} peers`);
|
|
188
|
+
void this.peerDiscoveryService.runRandomNodesQuery();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
pruneUnhealthyPeers(connections) {
|
|
192
|
+
const connectedHealthyPeers = [];
|
|
193
|
+
for (const peer of connections){
|
|
194
|
+
const score = this.peerScoring.getScoreState(peer.remotePeer.toString());
|
|
195
|
+
switch(score){
|
|
196
|
+
case PeerScoreState.Banned:
|
|
197
|
+
void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.BANNED);
|
|
198
|
+
break;
|
|
199
|
+
case PeerScoreState.Disconnect:
|
|
200
|
+
void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.DISCONNECTED);
|
|
201
|
+
break;
|
|
202
|
+
case PeerScoreState.Healthy:
|
|
203
|
+
connectedHealthyPeers.push(peer);
|
|
110
204
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
205
|
+
}
|
|
206
|
+
return connectedHealthyPeers;
|
|
207
|
+
}
|
|
208
|
+
async goodbyeAndDisconnectPeer(peer, reason) {
|
|
209
|
+
this.logger.debug(`Disconnecting peer ${peer.toString()} with reason ${prettyGoodbyeReason(reason)}`);
|
|
210
|
+
this.metrics.recordGoodbyeSent(reason);
|
|
211
|
+
try {
|
|
212
|
+
await this.reqresp.sendRequestToPeer(peer, ReqRespSubProtocol.GOODBYE, Buffer.from([
|
|
213
|
+
reason
|
|
214
|
+
]));
|
|
215
|
+
} catch (error) {
|
|
216
|
+
this.logger.debug(`Failed to send goodbye to peer ${peer.toString()}: ${error}`);
|
|
217
|
+
} finally{
|
|
218
|
+
await this.disconnectPeer(peer);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async disconnectPeer(peer) {
|
|
222
|
+
try {
|
|
223
|
+
await this.libP2PNode.hangUp(peer);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
this.logger.debug(`Failed to disconnect peer ${peer.toString()}`, {
|
|
226
|
+
error: inspect(error)
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Handles a discovered peer.
|
|
232
|
+
* @param enr - The discovered peer's ENR.
|
|
233
|
+
*/ async handleDiscoveredPeer(enr) {
|
|
234
|
+
// Check that the peer has not already been banned
|
|
235
|
+
const peerId = await enr.peerId();
|
|
236
|
+
const peerIdString = peerId.toString();
|
|
237
|
+
// Check if peer is temporarily timed out
|
|
238
|
+
const timedOutPeer = this.timedOutPeers.get(peerIdString);
|
|
239
|
+
if (timedOutPeer) {
|
|
240
|
+
if (Date.now() < timedOutPeer.timeoutUntilMs) {
|
|
241
|
+
this.logger.trace(`Skipping timed out peer ${peerId}`);
|
|
242
|
+
return;
|
|
138
243
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
244
|
+
// Timeout period expired, remove from timed out peers
|
|
245
|
+
this.timedOutPeers.delete(peerIdString);
|
|
246
|
+
}
|
|
247
|
+
if (this.peerScoring.getScoreState(peerIdString) != PeerScoreState.Healthy) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const [multiaddrTcp] = await Promise.all([
|
|
251
|
+
enr.getFullMultiaddr('tcp')
|
|
252
|
+
]);
|
|
253
|
+
this.logger.trace(`Handling discovered peer ${peerId} at ${multiaddrTcp?.toString() ?? 'undefined address'}`);
|
|
254
|
+
// stop if no tcp addr in multiaddr
|
|
255
|
+
if (!multiaddrTcp) {
|
|
256
|
+
this.logger.debug(`No TCP address in discovered node's multiaddr ${enr.encodeTxt()}`);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
// check if peer is already connected
|
|
260
|
+
const connections = this.libP2PNode.getConnections();
|
|
261
|
+
if (connections.some((conn)=>conn.remotePeer.equals(peerId))) {
|
|
262
|
+
this.logger.trace(`Already connected to peer ${peerId}`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
// check if peer is already in cache
|
|
266
|
+
if (this.cachedPeers.has(peerIdString)) {
|
|
267
|
+
this.logger.trace(`Peer already in cache ${peerIdString}`);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
// create cached peer object
|
|
271
|
+
const cachedPeer = {
|
|
272
|
+
peerId,
|
|
273
|
+
enr,
|
|
274
|
+
multiaddrTcp,
|
|
275
|
+
dialAttempts: 0,
|
|
276
|
+
addedUnixMs: Date.now()
|
|
277
|
+
};
|
|
278
|
+
// Determine if we should dial immediately or not
|
|
279
|
+
if (this.shouldDialPeer()) {
|
|
280
|
+
void this.dialPeer(cachedPeer);
|
|
281
|
+
} else {
|
|
282
|
+
this.logger.trace(`Caching peer ${peerIdString}`);
|
|
283
|
+
this.cachedPeers.set(peerIdString, cachedPeer);
|
|
284
|
+
// Prune set of cached peers
|
|
285
|
+
this.pruneCachedPeers();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async dialPeer(peer) {
|
|
289
|
+
const id = peer.peerId.toString();
|
|
290
|
+
// Add to the address book before dialing
|
|
291
|
+
await this.libP2PNode.peerStore.merge(peer.peerId, {
|
|
292
|
+
multiaddrs: [
|
|
293
|
+
peer.multiaddrTcp
|
|
294
|
+
]
|
|
295
|
+
});
|
|
296
|
+
this.logger.trace(`Dialing peer ${id}`);
|
|
297
|
+
try {
|
|
298
|
+
await this.libP2PNode.dial(peer.multiaddrTcp);
|
|
299
|
+
} catch (error) {
|
|
300
|
+
peer.dialAttempts++;
|
|
301
|
+
if (peer.dialAttempts < MAX_DIAL_ATTEMPTS) {
|
|
302
|
+
this.logger.trace(`Failed to dial peer ${id} (attempt ${peer.dialAttempts})`, {
|
|
303
|
+
error: inspect(error)
|
|
304
|
+
});
|
|
305
|
+
this.cachedPeers.set(id, peer);
|
|
306
|
+
} else {
|
|
307
|
+
formatLibp2pDialError(error);
|
|
308
|
+
this.logger.debug(`Failed to dial peer ${id} (dropping)`, {
|
|
309
|
+
error: inspect(error)
|
|
310
|
+
});
|
|
311
|
+
this.cachedPeers.delete(id);
|
|
312
|
+
// Add to timed out peers
|
|
313
|
+
this.timedOutPeers.set(id, {
|
|
314
|
+
peerId: id,
|
|
315
|
+
timeoutUntilMs: Date.now() + FAILED_PEER_BAN_TIME_MS
|
|
153
316
|
});
|
|
154
|
-
// Exit if no peers to connect
|
|
155
|
-
if (peersToConnect <= 0) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const cachedPeersToDial = [];
|
|
159
|
-
const pendingDials = new Set(this.libP2PNode
|
|
160
|
-
.getDialQueue()
|
|
161
|
-
.map(pendingDial => pendingDial.peerId?.toString())
|
|
162
|
-
.filter(Boolean));
|
|
163
|
-
for (const [id, peerData] of this.cachedPeers.entries()) {
|
|
164
|
-
// if already dialling or connected to, remove from cache
|
|
165
|
-
if (pendingDials.has(id) ||
|
|
166
|
-
healthyConnections.some(conn => conn.remotePeer.equals(peerData.peerId)) ||
|
|
167
|
-
// if peer has been in cache for the max cache age, remove from cache
|
|
168
|
-
Date.now() - peerData.addedUnixMs > MAX_CACHED_PEER_AGE_MS) {
|
|
169
|
-
this.cachedPeers.delete(id);
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
// cachedPeersToDial.set(id, enr);
|
|
173
|
-
cachedPeersToDial.push(peerData);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
// reverse to dial older entries first
|
|
177
|
-
cachedPeersToDial.reverse();
|
|
178
|
-
for (const peer of cachedPeersToDial) {
|
|
179
|
-
// We remove from the cache before, as dialling will add it back if it fails
|
|
180
|
-
this.cachedPeers.delete(peer.peerId.toString());
|
|
181
|
-
void this.dialPeer(peer);
|
|
182
|
-
}
|
|
183
|
-
// if we need more peers, start randomNodesQuery
|
|
184
|
-
if (peersToConnect > 0) {
|
|
185
|
-
this.logger.trace(`Running random nodes query to connect to ${peersToConnect} peers`);
|
|
186
|
-
void this.peerDiscoveryService.runRandomNodesQuery();
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
pruneUnhealthyPeers(connections) {
|
|
190
|
-
const connectedHealthyPeers = [];
|
|
191
|
-
for (const peer of connections) {
|
|
192
|
-
const score = this.peerScoring.getScoreState(peer.remotePeer.toString());
|
|
193
|
-
switch (score) {
|
|
194
|
-
case PeerScoreState.Banned:
|
|
195
|
-
void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.BANNED);
|
|
196
|
-
break;
|
|
197
|
-
case PeerScoreState.Disconnect:
|
|
198
|
-
void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.DISCONNECTED);
|
|
199
|
-
break;
|
|
200
|
-
case PeerScoreState.Healthy:
|
|
201
|
-
connectedHealthyPeers.push(peer);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return connectedHealthyPeers;
|
|
205
|
-
}
|
|
206
|
-
async goodbyeAndDisconnectPeer(peer, reason) {
|
|
207
|
-
this.logger.debug(`Disconnecting peer ${peer.toString()} with reason ${prettyGoodbyeReason(reason)}`);
|
|
208
|
-
this.metrics.recordGoodbyeSent(reason);
|
|
209
|
-
try {
|
|
210
|
-
await this.reqresp.sendRequestToPeer(peer, ReqRespSubProtocol.GOODBYE, Buffer.from([reason]));
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
this.logger.debug(`Failed to send goodbye to peer ${peer.toString()}: ${error}`);
|
|
214
|
-
}
|
|
215
|
-
finally {
|
|
216
|
-
await this.disconnectPeer(peer);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
async disconnectPeer(peer) {
|
|
220
|
-
try {
|
|
221
|
-
await this.libP2PNode.hangUp(peer);
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
this.logger.debug(`Failed to disconnect peer ${peer.toString()}`, { error: inspect(error) });
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Handles a discovered peer.
|
|
229
|
-
* @param enr - The discovered peer's ENR.
|
|
230
|
-
*/
|
|
231
|
-
async handleDiscoveredPeer(enr) {
|
|
232
|
-
// Check that the peer has not already been banned
|
|
233
|
-
const peerId = await enr.peerId();
|
|
234
|
-
const peerIdString = peerId.toString();
|
|
235
|
-
// Check if peer is temporarily timed out
|
|
236
|
-
const timedOutPeer = this.timedOutPeers.get(peerIdString);
|
|
237
|
-
if (timedOutPeer) {
|
|
238
|
-
if (Date.now() < timedOutPeer.timeoutUntilMs) {
|
|
239
|
-
this.logger.trace(`Skipping timed out peer ${peerId}`);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
// Timeout period expired, remove from timed out peers
|
|
243
|
-
this.timedOutPeers.delete(peerIdString);
|
|
244
|
-
}
|
|
245
|
-
if (this.peerScoring.getScoreState(peerIdString) != PeerScoreState.Healthy) {
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
const [multiaddrTcp] = await Promise.all([enr.getFullMultiaddr('tcp')]);
|
|
249
|
-
this.logger.trace(`Handling discovered peer ${peerId} at ${multiaddrTcp?.toString() ?? 'undefined address'}`);
|
|
250
|
-
// stop if no tcp addr in multiaddr
|
|
251
|
-
if (!multiaddrTcp) {
|
|
252
|
-
this.logger.debug(`No TCP address in discovered node's multiaddr ${enr.encodeTxt()}`);
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
// check if peer is already connected
|
|
256
|
-
const connections = this.libP2PNode.getConnections();
|
|
257
|
-
if (connections.some((conn) => conn.remotePeer.equals(peerId))) {
|
|
258
|
-
this.logger.trace(`Already connected to peer ${peerId}`);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
// check if peer is already in cache
|
|
262
|
-
if (this.cachedPeers.has(peerIdString)) {
|
|
263
|
-
this.logger.trace(`Peer already in cache ${peerIdString}`);
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
// create cached peer object
|
|
267
|
-
const cachedPeer = {
|
|
268
|
-
peerId,
|
|
269
|
-
enr,
|
|
270
|
-
multiaddrTcp,
|
|
271
|
-
dialAttempts: 0,
|
|
272
|
-
addedUnixMs: Date.now(),
|
|
273
|
-
};
|
|
274
|
-
// Determine if we should dial immediately or not
|
|
275
|
-
if (this.shouldDialPeer()) {
|
|
276
|
-
void this.dialPeer(cachedPeer);
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
this.logger.trace(`Caching peer ${peerIdString}`);
|
|
280
|
-
this.cachedPeers.set(peerIdString, cachedPeer);
|
|
281
|
-
// Prune set of cached peers
|
|
282
|
-
this.pruneCachedPeers();
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
async dialPeer(peer) {
|
|
286
|
-
const id = peer.peerId.toString();
|
|
287
|
-
// Add to the address book before dialing
|
|
288
|
-
await this.libP2PNode.peerStore.merge(peer.peerId, { multiaddrs: [peer.multiaddrTcp] });
|
|
289
|
-
this.logger.trace(`Dialing peer ${id}`);
|
|
290
|
-
try {
|
|
291
|
-
await this.libP2PNode.dial(peer.multiaddrTcp);
|
|
292
|
-
}
|
|
293
|
-
catch (error) {
|
|
294
|
-
peer.dialAttempts++;
|
|
295
|
-
if (peer.dialAttempts < MAX_DIAL_ATTEMPTS) {
|
|
296
|
-
this.logger.trace(`Failed to dial peer ${id} (attempt ${peer.dialAttempts})`, { error: inspect(error) });
|
|
297
|
-
this.cachedPeers.set(id, peer);
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
formatLibp2pDialError(error);
|
|
301
|
-
this.logger.debug(`Failed to dial peer ${id} (dropping)`, { error: inspect(error) });
|
|
302
|
-
this.cachedPeers.delete(id);
|
|
303
|
-
// Add to timed out peers
|
|
304
|
-
this.timedOutPeers.set(id, {
|
|
305
|
-
peerId: id,
|
|
306
|
-
timeoutUntilMs: Date.now() + FAILED_PEER_BAN_TIME_MS,
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
shouldDialPeer() {
|
|
312
|
-
const connections = this.libP2PNode.getConnections().length;
|
|
313
|
-
if (connections >= this.config.maxPeerCount) {
|
|
314
|
-
this.logger.trace(`Not dialing peer due to max peer count of ${this.config.maxPeerCount} reached (${connections} current connections)`);
|
|
315
|
-
return false;
|
|
316
|
-
}
|
|
317
|
-
return true;
|
|
318
|
-
}
|
|
319
|
-
pruneCachedPeers() {
|
|
320
|
-
let peersToDelete = this.cachedPeers.size - MAX_CACHED_PEERS;
|
|
321
|
-
if (peersToDelete <= 0) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
// Remove the oldest peers
|
|
325
|
-
for (const key of this.cachedPeers.keys()) {
|
|
326
|
-
this.cachedPeers.delete(key);
|
|
327
|
-
this.logger.trace(`Pruning peer ${key} from cache`);
|
|
328
|
-
peersToDelete--;
|
|
329
|
-
if (peersToDelete <= 0) {
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
317
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
shouldDialPeer() {
|
|
321
|
+
const connections = this.libP2PNode.getConnections().length;
|
|
322
|
+
if (connections >= this.config.maxPeerCount) {
|
|
323
|
+
this.logger.trace(`Not dialing peer due to max peer count of ${this.config.maxPeerCount} reached (${connections} current connections)`);
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
pruneCachedPeers() {
|
|
329
|
+
let peersToDelete = this.cachedPeers.size - MAX_CACHED_PEERS;
|
|
330
|
+
if (peersToDelete <= 0) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
// Remove the oldest peers
|
|
334
|
+
for (const key of this.cachedPeers.keys()){
|
|
335
|
+
this.cachedPeers.delete(key);
|
|
336
|
+
this.logger.trace(`Pruning peer ${key} from cache`);
|
|
337
|
+
peersToDelete--;
|
|
338
|
+
if (peersToDelete <= 0) {
|
|
339
|
+
break;
|
|
345
340
|
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Stops the peer manager.
|
|
345
|
+
* Removing all event listeners.
|
|
346
|
+
*/ async stop() {
|
|
347
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
348
|
+
this.peerDiscoveryService.off(PeerEvent.DISCOVERED, this.discoveredPeerHandler);
|
|
349
|
+
// Send goodbyes to all peers
|
|
350
|
+
await Promise.all(this.libP2PNode.getPeers().map((peer)=>this.goodbyeAndDisconnectPeer(peer, GoodByeReason.SHUTDOWN)));
|
|
351
|
+
this.libP2PNode.removeEventListener(PeerEvent.CONNECTED, this.handleConnectedPeerEvent);
|
|
352
|
+
this.libP2PNode.removeEventListener(PeerEvent.DISCONNECTED, this.handleDisconnectedPeerEvent);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
_ts_decorate([
|
|
356
|
+
trackSpan('PeerManager.heartbeat')
|
|
357
|
+
], PeerManager.prototype, "heartbeat", null);
|
|
356
358
|
/**
|
|
357
359
|
* copied from github.com/ChainSafe/lodestar
|
|
358
360
|
* libp2p errors with extremely noisy errors here, which are deeply nested taking 30-50 lines.
|
|
@@ -381,15 +383,11 @@ export { PeerManager };
|
|
|
381
383
|
* ```
|
|
382
384
|
*
|
|
383
385
|
* Tracking issue https://github.com/libp2p/js-libp2p/issues/996
|
|
384
|
-
*/
|
|
385
|
-
function formatLibp2pDialError(e) {
|
|
386
|
+
*/ function formatLibp2pDialError(e) {
|
|
386
387
|
const errorMessage = e.message.trim();
|
|
387
388
|
const newlineIndex = errorMessage.indexOf('\n');
|
|
388
389
|
e.message = newlineIndex !== -1 ? errorMessage.slice(0, newlineIndex) : errorMessage;
|
|
389
|
-
if (e.message.includes('The operation was aborted') ||
|
|
390
|
-
e.message.includes('stream ended before 1 bytes became available') ||
|
|
391
|
-
e.message.includes('The operation was aborted')) {
|
|
390
|
+
if (e.message.includes('The operation was aborted') || e.message.includes('stream ended before 1 bytes became available') || e.message.includes('The operation was aborted')) {
|
|
392
391
|
e.stack = undefined;
|
|
393
392
|
}
|
|
394
393
|
}
|
|
395
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVlcl9tYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3NlcnZpY2VzL3BlZXItbWFuYWdlci9wZWVyX21hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUNBLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNyRCxPQUFPLEVBQXdCLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBSzFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFJL0IsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGFBQWEsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBR3JGLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDeEMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ2xELE9BQU8sRUFBRSxjQUFjLEVBQW9CLE1BQU0sbUJBQW1CLENBQUM7QUFFckUsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLENBQUM7QUFDNUIsTUFBTSxnQkFBZ0IsR0FBRyxHQUFHLENBQUM7QUFDN0IsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDLFlBQVk7QUFDMUQsTUFBTSx1QkFBdUIsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDLG9EQUFvRDtJQWV0RixXQUFXOzs7O3NCQUFYLFdBQVc7WUFTdEIsWUFDVSxVQUF3QixFQUN4QixvQkFBMEMsRUFDMUMsTUFBaUIsRUFDekIsZUFBZ0MsRUFDeEIsU0FBUyxZQUFZLENBQUMsa0JBQWtCLENBQUMsRUFDekMsV0FBd0IsRUFDeEIsT0FBZ0I7Z0JBTmhCLGVBQVUsSUFWVCxtREFBVyxFQVVaLFVBQVUsRUFBYztnQkFDeEIseUJBQW9CLEdBQXBCLG9CQUFvQixDQUFzQjtn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
|