@aztec/p2p 1.2.1 → 2.0.0-nightly.20250813
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/client/factory.d.ts +5 -1
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +29 -12
- package/dest/client/interface.d.ts +8 -13
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +18 -22
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +86 -83
- package/dest/config.d.ts +30 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +34 -1
- package/dest/index.d.ts +1 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +13 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +117 -10
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +4 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +22 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +4 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +21 -1
- package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -2
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +2 -10
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +8 -3
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +64 -37
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +8 -3
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +18 -10
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +11 -2
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +73 -44
- package/dest/msg_validators/attestation_validator/attestation_validator.js +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.js +2 -2
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +35 -59
- package/dest/msg_validators/tx_validator/double_spend_validator.js +2 -2
- package/dest/msg_validators/tx_validator/gas_validator.js +4 -4
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +21 -21
- package/dest/msg_validators/tx_validator/phases_validator.js +3 -3
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +3 -3
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +4 -1
- package/dest/services/dummy_service.d.ts +13 -3
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +26 -3
- package/dest/services/index.d.ts +3 -1
- package/dest/services/index.d.ts.map +1 -1
- package/dest/services/index.js +3 -1
- package/dest/services/libp2p/libp2p_service.d.ts +13 -20
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +123 -46
- package/dest/services/peer-manager/interface.d.ts +8 -1
- package/dest/services/peer-manager/interface.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.d.ts +70 -3
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +369 -39
- package/dest/services/reqresp/config.d.ts +3 -3
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/config.js +3 -3
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +3 -4
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
- package/dest/services/reqresp/connection-sampler/connection_sampler.js +35 -52
- package/dest/services/reqresp/index.d.ts +2 -1
- package/dest/services/reqresp/index.d.ts.map +1 -1
- package/dest/services/reqresp/index.js +2 -1
- package/dest/services/reqresp/interface.d.ts +61 -10
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +41 -6
- package/dest/services/reqresp/protocols/auth.d.ts +43 -0
- package/dest/services/reqresp/protocols/auth.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/auth.js +71 -0
- package/dest/services/reqresp/protocols/block.d.ts +5 -0
- package/dest/services/reqresp/protocols/block.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block.js +28 -5
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +30 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.js +75 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +11 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +39 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +49 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +75 -0
- package/dest/services/reqresp/protocols/block_txs/index.d.ts +4 -0
- package/dest/services/reqresp/protocols/block_txs/index.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/index.js +3 -0
- package/dest/services/reqresp/protocols/goodbye.js +3 -5
- package/dest/services/reqresp/protocols/index.d.ts +2 -0
- package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.js +2 -0
- package/dest/services/reqresp/protocols/status.d.ts +2 -0
- package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/status.js +7 -0
- package/dest/services/reqresp/protocols/tx.d.ts +12 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +34 -6
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.js +20 -0
- package/dest/services/reqresp/reqresp.d.ts +37 -39
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +220 -220
- package/dest/services/reqresp/status.d.ts +8 -3
- package/dest/services/reqresp/status.d.ts.map +1 -1
- package/dest/services/reqresp/status.js +6 -2
- package/dest/services/service.d.ts +10 -10
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +25 -0
- package/dest/services/tx_collection/config.d.ts.map +1 -0
- package/dest/services/tx_collection/config.js +58 -0
- package/dest/services/tx_collection/fast_tx_collection.d.ts +56 -0
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/fast_tx_collection.js +295 -0
- package/dest/services/tx_collection/index.d.ts +3 -0
- package/dest/services/tx_collection/index.d.ts.map +1 -0
- package/dest/services/tx_collection/index.js +2 -0
- package/dest/services/tx_collection/instrumentation.d.ts +10 -0
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -0
- package/dest/services/tx_collection/instrumentation.js +34 -0
- package/dest/services/tx_collection/slow_tx_collection.d.ts +54 -0
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/slow_tx_collection.js +176 -0
- package/dest/services/tx_collection/tx_collection.d.ts +109 -0
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_collection.js +127 -0
- package/dest/services/tx_collection/tx_collection_sink.d.ts +30 -0
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_collection_sink.js +81 -0
- package/dest/services/tx_collection/tx_source.d.ts +18 -0
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_source.js +31 -0
- package/dest/services/tx_provider.d.ts +49 -0
- package/dest/services/tx_provider.d.ts.map +1 -0
- package/dest/services/tx_provider.js +206 -0
- package/dest/services/{tx_collect_instrumentation.d.ts → tx_provider_instrumentation.d.ts} +2 -2
- package/dest/services/tx_provider_instrumentation.d.ts.map +1 -0
- package/dest/services/{tx_collect_instrumentation.js → tx_provider_instrumentation.js} +5 -5
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +4 -3
- package/dest/test-helpers/mock-pubsub.d.ts +2 -1
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +2 -1
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +8 -4
- package/dest/testbench/p2p_client_testbench_worker.js +11 -5
- package/dest/util.d.ts +1 -1
- package/dest/util.d.ts.map +1 -1
- package/package.json +14 -15
- package/src/client/factory.ts +87 -12
- package/src/client/interface.ts +19 -15
- package/src/client/p2p_client.ts +108 -92
- package/src/config.ts +52 -1
- package/src/index.ts +1 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +15 -1
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +155 -4
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +28 -1
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +28 -2
- package/src/mem_pools/attestation_pool/mocks.ts +1 -3
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +59 -41
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +19 -9
- package/src/mem_pools/tx_pool/tx_pool.ts +7 -2
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +63 -40
- package/src/msg_validators/attestation_validator/attestation_validator.ts +1 -1
- package/src/msg_validators/tx_validator/block_header_validator.ts +2 -2
- package/src/msg_validators/tx_validator/data_validator.ts +36 -27
- package/src/msg_validators/tx_validator/double_spend_validator.ts +2 -2
- package/src/msg_validators/tx_validator/gas_validator.ts +4 -4
- package/src/msg_validators/tx_validator/metadata_validator.ts +22 -28
- package/src/msg_validators/tx_validator/phases_validator.ts +2 -2
- package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -2
- package/src/services/discv5/discV5_service.ts +4 -1
- package/src/services/dummy_service.ts +44 -4
- package/src/services/index.ts +3 -1
- package/src/services/libp2p/libp2p_service.ts +147 -55
- package/src/services/peer-manager/interface.ts +10 -1
- package/src/services/peer-manager/peer_manager.ts +441 -41
- package/src/services/reqresp/config.ts +3 -3
- package/src/services/reqresp/connection-sampler/connection_sampler.ts +38 -63
- package/src/services/reqresp/index.ts +2 -0
- package/src/services/reqresp/interface.ts +63 -17
- package/src/services/reqresp/protocols/auth.ts +83 -0
- package/src/services/reqresp/protocols/block.ts +24 -3
- package/src/services/reqresp/protocols/block_txs/bitvector.ts +90 -0
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +53 -0
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +79 -0
- package/src/services/reqresp/protocols/block_txs/index.ts +3 -0
- package/src/services/reqresp/protocols/goodbye.ts +3 -3
- package/src/services/reqresp/protocols/index.ts +2 -0
- package/src/services/reqresp/protocols/status.ts +20 -0
- package/src/services/reqresp/protocols/tx.ts +35 -6
- package/src/services/reqresp/rate-limiter/rate_limits.ts +20 -0
- package/src/services/reqresp/reqresp.ts +294 -264
- package/src/services/reqresp/status.ts +9 -3
- package/src/services/service.ts +23 -14
- package/src/services/tx_collection/config.ts +84 -0
- package/src/services/tx_collection/fast_tx_collection.ts +338 -0
- package/src/services/tx_collection/index.ts +2 -0
- package/src/services/tx_collection/instrumentation.ts +43 -0
- package/src/services/tx_collection/slow_tx_collection.ts +232 -0
- package/src/services/tx_collection/tx_collection.ts +214 -0
- package/src/services/tx_collection/tx_collection_sink.ts +98 -0
- package/src/services/tx_collection/tx_source.ts +37 -0
- package/src/services/tx_provider.ts +215 -0
- package/src/services/{tx_collect_instrumentation.ts → tx_provider_instrumentation.ts} +5 -5
- package/src/test-helpers/make-test-p2p-clients.ts +4 -2
- package/src/test-helpers/mock-pubsub.ts +1 -0
- package/src/test-helpers/reqresp-nodes.ts +7 -1
- package/src/testbench/p2p_client_testbench_worker.ts +9 -2
- package/src/util.ts +1 -1
- package/dest/services/tx_collect_instrumentation.d.ts.map +0 -1
- package/dest/services/tx_collector.d.ts +0 -23
- package/dest/services/tx_collector.d.ts.map +0 -1
- package/dest/services/tx_collector.js +0 -95
- package/src/services/tx_collector.ts +0 -134
|
@@ -4,13 +4,18 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
4
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
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
}
|
|
7
|
+
import { makeEthSignDigest, recoverAddress } from '@aztec/foundation/crypto';
|
|
8
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
7
9
|
import { createLogger } from '@aztec/foundation/log';
|
|
8
10
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
11
|
+
import { DateProvider } from '@aztec/foundation/timer';
|
|
9
12
|
import { trackSpan } from '@aztec/telemetry-client';
|
|
10
13
|
import { ENR } from '@chainsafe/enr';
|
|
14
|
+
import { peerIdFromString } from '@libp2p/peer-id';
|
|
11
15
|
import { inspect } from 'util';
|
|
12
16
|
import { PeerEvent } from '../../types/index.js';
|
|
13
17
|
import { ReqRespSubProtocol } from '../reqresp/interface.js';
|
|
18
|
+
import { AuthRequest, AuthResponse } from '../reqresp/protocols/auth.js';
|
|
14
19
|
import { GoodByeReason, prettyGoodbyeReason } from '../reqresp/protocols/goodbye.js';
|
|
15
20
|
import { StatusMessage } from '../reqresp/protocols/status.js';
|
|
16
21
|
import { ReqRespStatus } from '../reqresp/status.js';
|
|
@@ -21,6 +26,7 @@ const MAX_CACHED_PEERS = 100;
|
|
|
21
26
|
const MAX_CACHED_PEER_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
22
27
|
const FAILED_PEER_BAN_TIME_MS = 5 * 60 * 1000; // 5 minutes timeout after failing MAX_DIAL_ATTEMPTS
|
|
23
28
|
const GOODBYE_DIAL_TIMEOUT_MS = 1000;
|
|
29
|
+
const FAILED_AUTH_HANDSHAKE_EXPIRY_MS = 60 * 60 * 1000; // 1 hour
|
|
24
30
|
export class PeerManager {
|
|
25
31
|
libP2PNode;
|
|
26
32
|
peerDiscoveryService;
|
|
@@ -30,6 +36,8 @@ export class PeerManager {
|
|
|
30
36
|
reqresp;
|
|
31
37
|
worldStateSynchronizer;
|
|
32
38
|
protocolVersion;
|
|
39
|
+
epochCache;
|
|
40
|
+
dateProvider;
|
|
33
41
|
cachedPeers;
|
|
34
42
|
heartbeatCounter;
|
|
35
43
|
displayPeerCountsPeerHeartbeat;
|
|
@@ -38,9 +46,16 @@ export class PeerManager {
|
|
|
38
46
|
trustedPeersInitialized;
|
|
39
47
|
privatePeers;
|
|
40
48
|
privatePeersInitialized;
|
|
49
|
+
preferredPeers;
|
|
50
|
+
authenticatedPeerIdToValidatorAddress;
|
|
51
|
+
authenticatedValidatorAddressToPeerId;
|
|
52
|
+
peersToBeDisconnected;
|
|
53
|
+
failedAuthHandshakes;
|
|
54
|
+
validatorAddresses;
|
|
55
|
+
initializedPreferredPeers;
|
|
41
56
|
metrics;
|
|
42
57
|
handlers;
|
|
43
|
-
constructor(libP2PNode, peerDiscoveryService, config, telemetryClient, logger = createLogger('p2p:peer-manager'), peerScoring, reqresp, worldStateSynchronizer, protocolVersion){
|
|
58
|
+
constructor(libP2PNode, peerDiscoveryService, config, telemetryClient, logger = createLogger('p2p:peer-manager'), peerScoring, reqresp, worldStateSynchronizer, protocolVersion, epochCache, dateProvider = new DateProvider()){
|
|
44
59
|
this.libP2PNode = libP2PNode;
|
|
45
60
|
this.peerDiscoveryService = peerDiscoveryService;
|
|
46
61
|
this.config = config;
|
|
@@ -49,6 +64,8 @@ export class PeerManager {
|
|
|
49
64
|
this.reqresp = reqresp;
|
|
50
65
|
this.worldStateSynchronizer = worldStateSynchronizer;
|
|
51
66
|
this.protocolVersion = protocolVersion;
|
|
67
|
+
this.epochCache = epochCache;
|
|
68
|
+
this.dateProvider = dateProvider;
|
|
52
69
|
this.cachedPeers = new Map();
|
|
53
70
|
this.heartbeatCounter = 0;
|
|
54
71
|
this.displayPeerCountsPeerHeartbeat = 0;
|
|
@@ -57,6 +74,16 @@ export class PeerManager {
|
|
|
57
74
|
this.trustedPeersInitialized = false;
|
|
58
75
|
this.privatePeers = new Set();
|
|
59
76
|
this.privatePeersInitialized = false;
|
|
77
|
+
this.preferredPeers = new Set();
|
|
78
|
+
this.authenticatedPeerIdToValidatorAddress = new Map();
|
|
79
|
+
this.authenticatedValidatorAddressToPeerId = new Map();
|
|
80
|
+
this.peersToBeDisconnected = new Set();
|
|
81
|
+
this.failedAuthHandshakes = new Map();
|
|
82
|
+
this.validatorAddresses = [];
|
|
83
|
+
this.initializedPreferredPeers = false;
|
|
84
|
+
if (this.config.p2pDisableStatusHandshake && this.config.p2pAllowOnlyValidators) {
|
|
85
|
+
throw new Error('Status handshake disabled but is required to allow only validators to connect.');
|
|
86
|
+
}
|
|
60
87
|
this.metrics = new PeerManagerMetrics(telemetryClient, 'PeerManager');
|
|
61
88
|
// Handle Discovered peers
|
|
62
89
|
this.handlers = {
|
|
@@ -69,7 +96,7 @@ export class PeerManager {
|
|
|
69
96
|
// Handle lost connections
|
|
70
97
|
this.libP2PNode.addEventListener(PeerEvent.DISCONNECTED, this.handlers.handleDisconnectedPeerEvent);
|
|
71
98
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
72
|
-
this.peerDiscoveryService
|
|
99
|
+
this.peerDiscoveryService?.on(PeerEvent.DISCOVERED, this.handlers.handleDiscoveredPeer);
|
|
73
100
|
// Display peer counts every 60 seconds
|
|
74
101
|
this.displayPeerCountsPeerHeartbeat = Math.floor(60_000 / this.config.peerCheckIntervalMS);
|
|
75
102
|
}
|
|
@@ -96,16 +123,61 @@ export class PeerManager {
|
|
|
96
123
|
this.privatePeersInitialized = true;
|
|
97
124
|
}).catch((e)=>this.logger.error('Error initializing private peers', e));
|
|
98
125
|
}
|
|
126
|
+
if (this.config.preferredPeers) {
|
|
127
|
+
const preferredPeersEnrs = this.config.preferredPeers.map((enr)=>ENR.decodeTxt(enr));
|
|
128
|
+
await Promise.all(preferredPeersEnrs.map((enr)=>enr.peerId())).then((peerIds)=>peerIds.forEach((peerId)=>this.preferredPeers.add(peerId.toString()))).catch((e)=>this.logger.error('Error initializing preferred peers', e));
|
|
129
|
+
}
|
|
99
130
|
}
|
|
100
131
|
get tracer() {
|
|
101
132
|
return this.metrics.tracer;
|
|
102
133
|
}
|
|
103
|
-
heartbeat() {
|
|
134
|
+
async heartbeat() {
|
|
104
135
|
this.heartbeatCounter++;
|
|
105
136
|
this.peerScoring.decayAllScores();
|
|
106
137
|
this.cleanupExpiredTimeouts();
|
|
138
|
+
await this.setupDirectPeersIfValidator();
|
|
139
|
+
await this.updateAuthenticatedPeers();
|
|
140
|
+
await this.processScheduledDisconnects();
|
|
107
141
|
this.discover();
|
|
108
142
|
}
|
|
143
|
+
/*
|
|
144
|
+
* If this node is connecting to preferred peers, make sure it is registered validator */ async setupDirectPeersIfValidator() {
|
|
145
|
+
if (!this.config.preferredPeers) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
// Already initialized preferred peers, don't wastefully repeat the same work
|
|
149
|
+
if (this.initializedPreferredPeers) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const registeredValidators = await this.epochCache.getRegisteredValidators();
|
|
153
|
+
const validatorSet = new Set(registeredValidators.map((v)=>v.toString()));
|
|
154
|
+
const isThisNodePartOfValidatorSet = this.validatorAddresses.some((v)=>validatorSet.has(v.toString()));
|
|
155
|
+
if (!isThisNodePartOfValidatorSet) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const preferredPeersEnrs = this.config.preferredPeers.map((enr)=>ENR.decodeTxt(enr));
|
|
159
|
+
await Promise.all(preferredPeersEnrs.map((enr)=>enr.peerId())).then((peerIds)=>peerIds.forEach((peerId)=>this.preferredPeers.add(peerId.toString()))).catch((e)=>this.logger.error('Error initializing preferred peers', e));
|
|
160
|
+
const directPeers = (await Promise.all(preferredPeersEnrs.map(async (enr)=>{
|
|
161
|
+
const peerId = await enr.peerId();
|
|
162
|
+
const address = enr.getLocationMultiaddr('tcp');
|
|
163
|
+
if (address === undefined) {
|
|
164
|
+
throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
id: peerId,
|
|
168
|
+
addrs: [
|
|
169
|
+
address
|
|
170
|
+
]
|
|
171
|
+
};
|
|
172
|
+
}))).filter((peer)=>peer !== undefined);
|
|
173
|
+
await Promise.all(directPeers.map((peer)=>{
|
|
174
|
+
this.libP2PNode.services.pubsub.direct.add(peer.id.toString());
|
|
175
|
+
return this.libP2PNode.peerStore.merge(peer.id, {
|
|
176
|
+
multiaddrs: peer.addrs
|
|
177
|
+
});
|
|
178
|
+
}));
|
|
179
|
+
this.initializedPreferredPeers = true;
|
|
180
|
+
}
|
|
109
181
|
/**
|
|
110
182
|
* Cleans up expired timeouts.
|
|
111
183
|
*
|
|
@@ -114,7 +186,7 @@ export class PeerManager {
|
|
|
114
186
|
* To give them a chance to reconnect.
|
|
115
187
|
*/ cleanupExpiredTimeouts() {
|
|
116
188
|
// Clean up expired timeouts
|
|
117
|
-
const now =
|
|
189
|
+
const now = this.dateProvider.now();
|
|
118
190
|
for (const [peerId, timedOutPeer] of this.timedOutPeers.entries()){
|
|
119
191
|
if (now >= timedOutPeer.timeoutUntilMs) {
|
|
120
192
|
this.timedOutPeers.delete(peerId);
|
|
@@ -122,30 +194,69 @@ export class PeerManager {
|
|
|
122
194
|
}
|
|
123
195
|
}
|
|
124
196
|
/**
|
|
197
|
+
* Processes scheduled disconnects during heartbeat.
|
|
198
|
+
*
|
|
199
|
+
* This batch processes all peers that have been marked for disconnect.
|
|
200
|
+
* preventing immediate disconnects that could cause libp2p state corruption.
|
|
201
|
+
*/ async processScheduledDisconnects() {
|
|
202
|
+
if (this.peersToBeDisconnected.size === 0) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const peersToDisconnect = Array.from(this.peersToBeDisconnected);
|
|
206
|
+
this.logger.debug(`Processing ${peersToDisconnect.length} scheduled disconnects`);
|
|
207
|
+
try {
|
|
208
|
+
await Promise.all(peersToDisconnect.map(async (peerIdStr)=>{
|
|
209
|
+
if (await this.disconnectPeer(peerIdFromString(peerIdStr))) {
|
|
210
|
+
this.peersToBeDisconnected.delete(peerIdStr);
|
|
211
|
+
}
|
|
212
|
+
}));
|
|
213
|
+
this.logger.verbose(`Disconnected ${peersToDisconnect.length} peers`, {
|
|
214
|
+
peersToDisconnect
|
|
215
|
+
});
|
|
216
|
+
} catch (error) {
|
|
217
|
+
this.logger.error('Error when disconnecting from peers', error);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
125
221
|
* Performs Status Handshake with a connected peer.
|
|
126
222
|
* @param e - The connected peer event.
|
|
127
223
|
*/ handleConnectedPeerEvent(e) {
|
|
128
224
|
const peerId = e.detail;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
this.logger.verbose(`Connected to transaction peer ${peerId.toString()}`);
|
|
225
|
+
this.logger.verbose(`Connected to peer ${peerId.toString()}`);
|
|
226
|
+
if (this.config.p2pDisableStatusHandshake) {
|
|
227
|
+
return;
|
|
133
228
|
}
|
|
134
|
-
|
|
229
|
+
// If we are not configured to only allow validators then perform a status handshake
|
|
230
|
+
if (!this.config.p2pAllowOnlyValidators) {
|
|
135
231
|
void this.exchangeStatusHandshake(peerId);
|
|
232
|
+
return;
|
|
136
233
|
}
|
|
234
|
+
// We are configured to only allow validators, but this doesn't apply to trusted, private peers or preferred peers
|
|
235
|
+
if (this.isProtectedPeer(peerId)) {
|
|
236
|
+
void this.exchangeStatusHandshake(peerId);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
// Initiate auth handshake
|
|
240
|
+
void this.exchangeAuthHandshake(peerId);
|
|
137
241
|
}
|
|
138
242
|
/**
|
|
139
243
|
* Simply logs the type of disconnected peer.
|
|
140
244
|
* @param e - The disconnected peer event.
|
|
141
245
|
*/ handleDisconnectedPeerEvent(e) {
|
|
142
246
|
const peerId = e.detail;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this.logger.
|
|
247
|
+
this.logger.verbose(`Disconnected from peer ${peerId.toString()}`);
|
|
248
|
+
const validatorAddress = this.authenticatedPeerIdToValidatorAddress.get(peerId.toString());
|
|
249
|
+
if (validatorAddress !== undefined) {
|
|
250
|
+
this.logger.info(`Removing authentication for validator ${validatorAddress} at peer id ${peerId.toString()} due to disconnection`);
|
|
251
|
+
this.authenticatedValidatorAddressToPeerId.delete(validatorAddress.toString());
|
|
252
|
+
this.authenticatedPeerIdToValidatorAddress.delete(peerId.toString());
|
|
147
253
|
}
|
|
148
254
|
}
|
|
255
|
+
registerThisValidatorAddresses(address) {
|
|
256
|
+
this.validatorAddresses = [
|
|
257
|
+
...address
|
|
258
|
+
];
|
|
259
|
+
}
|
|
149
260
|
/**
|
|
150
261
|
* Checks if a peer is trusted.
|
|
151
262
|
* @param peerId - The peer ID.
|
|
@@ -190,11 +301,26 @@ export class PeerManager {
|
|
|
190
301
|
return this.privatePeers.has(peerId.toString());
|
|
191
302
|
}
|
|
192
303
|
/**
|
|
304
|
+
* Adds a peer to the preferred peers set.
|
|
305
|
+
* @param peerId - The peer ID to add to preferred peers.
|
|
306
|
+
*/ addPreferredPeer(peerId) {
|
|
307
|
+
const peerIdStr = peerId.toString();
|
|
308
|
+
this.preferredPeers.add(peerIdStr);
|
|
309
|
+
this.logger.verbose(`Added preferred peer ${peerIdStr}`);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Checks if a peer is preferred.
|
|
313
|
+
* @param peerId - The peer ID.
|
|
314
|
+
* @returns True if the peer is preferred, false otherwise.
|
|
315
|
+
*/ isPreferredPeer(peerId) {
|
|
316
|
+
return this.preferredPeers.has(peerId.toString());
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
193
319
|
* Checks if a peer is protected (either trusted or private).
|
|
194
320
|
* @param peerId - The peer ID.
|
|
195
321
|
* @returns True if the peer is protected, false otherwise.
|
|
196
322
|
*/ isProtectedPeer(peerId) {
|
|
197
|
-
return this.isTrustedPeer(peerId) || this.isPrivatePeer(peerId);
|
|
323
|
+
return this.isTrustedPeer(peerId) || this.isPrivatePeer(peerId) || this.isPreferredPeer(peerId);
|
|
198
324
|
}
|
|
199
325
|
/**
|
|
200
326
|
* Handles a goodbye received from a peer.
|
|
@@ -205,7 +331,7 @@ export class PeerManager {
|
|
|
205
331
|
*/ goodbyeReceived(peerId, reason) {
|
|
206
332
|
this.logger.debug(`Goodbye received from peer ${peerId.toString()} with reason ${prettyGoodbyeReason(reason)}`);
|
|
207
333
|
this.metrics.recordGoodbyeReceived(reason);
|
|
208
|
-
|
|
334
|
+
this.markPeerForDisconnect(peerId);
|
|
209
335
|
}
|
|
210
336
|
penalizePeer(peerId, penalty) {
|
|
211
337
|
this.peerScoring.penalizePeer(peerId, penalty);
|
|
@@ -213,6 +339,10 @@ export class PeerManager {
|
|
|
213
339
|
getPeerScore(peerId) {
|
|
214
340
|
return this.peerScoring.getScore(peerId);
|
|
215
341
|
}
|
|
342
|
+
shouldDisableP2PGossip(peerId) {
|
|
343
|
+
const isAuthenticated = this.isAuthenticatedPeer(peerIdFromString(peerId));
|
|
344
|
+
return (this.config.p2pAllowOnlyValidators ?? false) && !isAuthenticated;
|
|
345
|
+
}
|
|
216
346
|
getPeers(includePending = false) {
|
|
217
347
|
const connected = this.libP2PNode.getPeers().map((peer)=>({
|
|
218
348
|
id: peer.toString(),
|
|
@@ -243,17 +373,40 @@ export class PeerManager {
|
|
|
243
373
|
...cachedPeers
|
|
244
374
|
];
|
|
245
375
|
}
|
|
376
|
+
isAuthenticatedPeer(peerId) {
|
|
377
|
+
const peerIdAsString = peerId.toString();
|
|
378
|
+
return this.privatePeers.has(peerIdAsString) || this.trustedPeers.has(peerIdAsString) || this.preferredPeers.has(peerIdAsString) || this.authenticatedPeerIdToValidatorAddress.has(peerIdAsString);
|
|
379
|
+
}
|
|
380
|
+
/*
|
|
381
|
+
* Checks whether peer is allowed to connect
|
|
382
|
+
*
|
|
383
|
+
* @param id: Address of the node or it's peerId
|
|
384
|
+
*
|
|
385
|
+
* @returns: True if node is allowed to connect, otherwise false
|
|
386
|
+
* */ isNodeAllowedToConnect(id) {
|
|
387
|
+
const entry = this.failedAuthHandshakes.get(id.toString());
|
|
388
|
+
if (!entry) {
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
// In case entry is too old, remove it and allow connection
|
|
392
|
+
if (this.dateProvider.now() - entry.lastFailureTimestamp > FAILED_AUTH_HANDSHAKE_EXPIRY_MS) {
|
|
393
|
+
this.failedAuthHandshakes.delete(id.toString());
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
return entry.count <= this.config.p2pMaxFailedAuthAttemptsAllowed;
|
|
397
|
+
}
|
|
246
398
|
/**
|
|
247
399
|
* Discovers peers.
|
|
248
400
|
*/ discover() {
|
|
249
401
|
const connections = this.libP2PNode.getConnections();
|
|
250
402
|
const healthyConnections = this.prioritizePeers(this.pruneUnhealthyPeers(this.getNonProtectedPeers(this.pruneDuplicatePeers(connections))));
|
|
251
403
|
// Calculate how many connections we're looking to make
|
|
252
|
-
const
|
|
404
|
+
const protectedPeerCount = this.getProtectedPeerCount();
|
|
405
|
+
const peersToConnect = this.config.maxPeerCount - healthyConnections.length - protectedPeerCount;
|
|
253
406
|
const logLevel = this.heartbeatCounter % this.displayPeerCountsPeerHeartbeat === 0 ? 'info' : 'debug';
|
|
254
407
|
this.logger[logLevel](`Connected to ${healthyConnections.length + this.trustedPeers.size} peers`, {
|
|
255
408
|
discoveredConnections: healthyConnections.length,
|
|
256
|
-
protectedConnections:
|
|
409
|
+
protectedConnections: protectedPeerCount,
|
|
257
410
|
maxPeerCount: this.config.maxPeerCount,
|
|
258
411
|
cachedPeers: this.cachedPeers.size,
|
|
259
412
|
...this.peerScoring.getStats()
|
|
@@ -265,10 +418,11 @@ export class PeerManager {
|
|
|
265
418
|
}
|
|
266
419
|
const cachedPeersToDial = [];
|
|
267
420
|
const pendingDials = new Set(this.libP2PNode.getDialQueue().map((pendingDial)=>pendingDial.peerId?.toString()).filter(Boolean));
|
|
421
|
+
const now = this.dateProvider.now();
|
|
268
422
|
for (const [id, peerData] of this.cachedPeers.entries()){
|
|
269
423
|
// if already dialling or connected to, remove from cache
|
|
270
424
|
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
|
|
271
|
-
|
|
425
|
+
now - peerData.addedUnixMs > MAX_CACHED_PEER_AGE_MS) {
|
|
272
426
|
this.cachedPeers.delete(id);
|
|
273
427
|
} else {
|
|
274
428
|
// cachedPeersToDial.set(id, enr);
|
|
@@ -291,6 +445,9 @@ export class PeerManager {
|
|
|
291
445
|
getNonProtectedPeers(connections) {
|
|
292
446
|
return connections.filter((conn)=>!this.isProtectedPeer(conn.remotePeer));
|
|
293
447
|
}
|
|
448
|
+
getProtectedPeerCount() {
|
|
449
|
+
return this.trustedPeers.size + this.privatePeers.size + this.preferredPeers.size;
|
|
450
|
+
}
|
|
294
451
|
pruneUnhealthyPeers(connections) {
|
|
295
452
|
const connectedHealthyPeers = [];
|
|
296
453
|
for (const peer of connections){
|
|
@@ -314,7 +471,8 @@ export class PeerManager {
|
|
|
314
471
|
* @param connections - The list of connections to prune low scoring peers above the max peer count from.
|
|
315
472
|
* @returns The pruned list of connections.
|
|
316
473
|
*/ prioritizePeers(connections) {
|
|
317
|
-
|
|
474
|
+
const protectedPeerCount = this.getProtectedPeerCount();
|
|
475
|
+
if (connections.length > this.config.maxPeerCount - protectedPeerCount) {
|
|
318
476
|
// Sort the regular peer scores from highest to lowest
|
|
319
477
|
const prioritizedConnections = connections.sort((connectionA, connectionB)=>{
|
|
320
478
|
const connectionScoreA = this.peerScoring.getScore(connectionA.remotePeer.toString());
|
|
@@ -322,7 +480,7 @@ export class PeerManager {
|
|
|
322
480
|
return connectionScoreB - connectionScoreA;
|
|
323
481
|
});
|
|
324
482
|
// Calculate how many regular peers we can keep
|
|
325
|
-
const peersToKeep = Math.max(0, this.config.maxPeerCount -
|
|
483
|
+
const peersToKeep = Math.max(0, this.config.maxPeerCount - protectedPeerCount);
|
|
326
484
|
// Disconnect from the lowest scoring regular connections that exceed our limit
|
|
327
485
|
for (const conn of prioritizedConnections.slice(peersToKeep)){
|
|
328
486
|
void this.goodbyeAndDisconnectPeer(conn.remotePeer, GoodByeReason.MAX_PEERS);
|
|
@@ -380,16 +538,32 @@ export class PeerManager {
|
|
|
380
538
|
} catch (error) {
|
|
381
539
|
this.logger.debug(`Failed to send goodbye to peer ${peer.toString()}: ${error}`);
|
|
382
540
|
} finally{
|
|
383
|
-
|
|
541
|
+
this.markPeerForDisconnect(peer);
|
|
384
542
|
}
|
|
385
543
|
}
|
|
386
|
-
|
|
544
|
+
/*
|
|
545
|
+
* Marks peer to be disconnected on the next heartbeat
|
|
546
|
+
* */ markPeerForDisconnect(peer) {
|
|
547
|
+
const peerIdStr = peer.toString();
|
|
548
|
+
this.logger.debug(`Scheduling peer ${peerIdStr} for disconnection`);
|
|
549
|
+
this.peersToBeDisconnected.add(peerIdStr);
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Performs the actual disconnection of a peer.
|
|
553
|
+
* This is called during heartbeat processing to avoid immediate disconnections.
|
|
554
|
+
*
|
|
555
|
+
* @returns True if peer was disconnect, otherwise false
|
|
556
|
+
*/ async disconnectPeer(peer) {
|
|
557
|
+
const peerIdStr = peer.toString();
|
|
387
558
|
try {
|
|
388
559
|
await this.libP2PNode.hangUp(peer);
|
|
560
|
+
this.logger.debug(`Successfully disconnected peer ${peerIdStr}`);
|
|
561
|
+
return true;
|
|
389
562
|
} catch (error) {
|
|
390
|
-
this.logger.
|
|
391
|
-
error
|
|
563
|
+
this.logger.warn(`Failed to disconnect peer ${peerIdStr}`, {
|
|
564
|
+
error
|
|
392
565
|
});
|
|
566
|
+
return false;
|
|
393
567
|
}
|
|
394
568
|
}
|
|
395
569
|
/**
|
|
@@ -399,10 +573,15 @@ export class PeerManager {
|
|
|
399
573
|
// Check that the peer has not already been banned
|
|
400
574
|
const peerId = await enr.peerId();
|
|
401
575
|
const peerIdString = peerId.toString();
|
|
576
|
+
// Don't attempt to connect to peers scheduled for disconnection
|
|
577
|
+
if (this.peersToBeDisconnected.has(peerIdString)) {
|
|
578
|
+
this.logger.trace(`Skipping peer scheduled for disconnection ${peerId}`);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
402
581
|
// Check if peer is temporarily timed out
|
|
403
582
|
const timedOutPeer = this.timedOutPeers.get(peerIdString);
|
|
404
583
|
if (timedOutPeer) {
|
|
405
|
-
if (
|
|
584
|
+
if (this.dateProvider.now() < timedOutPeer.timeoutUntilMs) {
|
|
406
585
|
this.logger.trace(`Skipping timed out peer ${peerId}`);
|
|
407
586
|
return;
|
|
408
587
|
}
|
|
@@ -438,7 +617,7 @@ export class PeerManager {
|
|
|
438
617
|
enr,
|
|
439
618
|
multiaddrTcp,
|
|
440
619
|
dialAttempts: 0,
|
|
441
|
-
addedUnixMs:
|
|
620
|
+
addedUnixMs: this.dateProvider.now()
|
|
442
621
|
};
|
|
443
622
|
// Determine if we should dial immediately or not
|
|
444
623
|
if (this.shouldDialPeer()) {
|
|
@@ -477,7 +656,7 @@ export class PeerManager {
|
|
|
477
656
|
// Add to timed out peers
|
|
478
657
|
this.timedOutPeers.set(id, {
|
|
479
658
|
peerId: id,
|
|
480
|
-
timeoutUntilMs:
|
|
659
|
+
timeoutUntilMs: this.dateProvider.now() + FAILED_PEER_BAN_TIME_MS
|
|
481
660
|
});
|
|
482
661
|
}
|
|
483
662
|
}
|
|
@@ -509,6 +688,10 @@ export class PeerManager {
|
|
|
509
688
|
}
|
|
510
689
|
}
|
|
511
690
|
}
|
|
691
|
+
async createStatusMessage() {
|
|
692
|
+
const syncSummary = (await this.worldStateSynchronizer.status()).syncSummary;
|
|
693
|
+
return StatusMessage.fromWorldStateSyncStatus(this.protocolVersion, syncSummary);
|
|
694
|
+
}
|
|
512
695
|
/**
|
|
513
696
|
* Performs status Handshake with the Peer
|
|
514
697
|
* The way the protocol is designed is that each peer will call this method on newly established p2p connection.
|
|
@@ -520,29 +703,33 @@ export class PeerManager {
|
|
|
520
703
|
* @param: peerId The Id of the peer to request the Status from.
|
|
521
704
|
* */ async exchangeStatusHandshake(peerId) {
|
|
522
705
|
try {
|
|
523
|
-
const
|
|
524
|
-
const ourStatus = StatusMessage.fromWorldStateSyncStatus(this.protocolVersion, syncSummary);
|
|
706
|
+
const ourStatus = await this.createStatusMessage();
|
|
525
707
|
//Note: Technically we don't have to send out status to peer as well, but we do.
|
|
526
708
|
//It will be easier to update protocol in the future this way if need be.
|
|
527
709
|
this.logger.trace(`Initiating status handshake with peer ${peerId}`);
|
|
528
|
-
const
|
|
529
|
-
const
|
|
530
|
-
peerId,
|
|
531
|
-
status: ReqRespStatus[status],
|
|
532
|
-
data: data ? bufferToHex(data) : undefined
|
|
533
|
-
};
|
|
710
|
+
const response = await this.reqresp.sendRequestToPeer(peerId, ReqRespSubProtocol.STATUS, ourStatus.toBuffer());
|
|
711
|
+
const { status } = response;
|
|
534
712
|
if (status !== ReqRespStatus.SUCCESS) {
|
|
535
713
|
//TODO: maybe hard ban these peers in the future.
|
|
536
714
|
//We could allow this to happen up to N times, and then hard ban?
|
|
537
715
|
//Hard ban: Disallow connection via e.g. libp2p's Gater
|
|
538
|
-
this.logger.debug(`Disconnecting peer ${peerId} who failed to respond status handshake`,
|
|
539
|
-
|
|
716
|
+
this.logger.debug(`Disconnecting peer ${peerId} who failed to respond status handshake`, {
|
|
717
|
+
peerId,
|
|
718
|
+
status: ReqRespStatus[status]
|
|
719
|
+
});
|
|
720
|
+
this.markPeerForDisconnect(peerId);
|
|
540
721
|
return;
|
|
541
722
|
}
|
|
723
|
+
const { data } = response;
|
|
724
|
+
const logData = {
|
|
725
|
+
peerId,
|
|
726
|
+
status: ReqRespStatus[status],
|
|
727
|
+
data: data ? bufferToHex(data) : undefined
|
|
728
|
+
};
|
|
542
729
|
const peerStatusMessage = StatusMessage.fromBuffer(data);
|
|
543
730
|
if (!ourStatus.validate(peerStatusMessage)) {
|
|
544
731
|
this.logger.debug(`Disconnecting peer ${peerId} due to failed status handshake.`, logData);
|
|
545
|
-
|
|
732
|
+
this.markPeerForDisconnect(peerId);
|
|
546
733
|
return;
|
|
547
734
|
}
|
|
548
735
|
this.logger.debug(`Successfully completed status handshake with peer ${peerId}`, logData);
|
|
@@ -551,10 +738,113 @@ export class PeerManager {
|
|
|
551
738
|
this.logger.debug(`Disconnecting peer ${peerId} due to error during status handshake: ${err.message ?? err}`, {
|
|
552
739
|
peerId
|
|
553
740
|
});
|
|
554
|
-
|
|
741
|
+
this.markPeerForDisconnect(peerId);
|
|
555
742
|
}
|
|
556
743
|
}
|
|
557
744
|
/**
|
|
745
|
+
* Performs auth Handshake with the Peer
|
|
746
|
+
* A superset of the status handshake. Also includes a challenge that needs to be signed by the peer's validator key.
|
|
747
|
+
* @param: peerId The Id of the peer to request the Status from.
|
|
748
|
+
* */ async exchangeAuthHandshake(peerId) {
|
|
749
|
+
const peerIdString = peerId.toString();
|
|
750
|
+
try {
|
|
751
|
+
const ourStatus = await this.createStatusMessage();
|
|
752
|
+
const authRequest = new AuthRequest(ourStatus, Fr.random());
|
|
753
|
+
// Note: Technically we don't have to send our status to peer as well, but we do.
|
|
754
|
+
// It will be easier to update protocol in the future this way if need be.
|
|
755
|
+
// We also need to send the challenge at least, so that the peer can sign it.
|
|
756
|
+
this.logger.debug(`Initiating auth handshake with peer ${peerId}`);
|
|
757
|
+
const response = await this.reqresp.sendRequestToPeer(peerId, ReqRespSubProtocol.AUTH, authRequest.toBuffer());
|
|
758
|
+
const { status } = response;
|
|
759
|
+
if (status !== ReqRespStatus.SUCCESS) {
|
|
760
|
+
this.logger.debug(`Disconnecting peer ${peerId} who failed to respond auth handshake`, {
|
|
761
|
+
peerId,
|
|
762
|
+
status: ReqRespStatus[status]
|
|
763
|
+
});
|
|
764
|
+
this.markAuthHandshakeFailed(peerId);
|
|
765
|
+
this.markPeerForDisconnect(peerId);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
const { data } = response;
|
|
769
|
+
const logData = {
|
|
770
|
+
peerId,
|
|
771
|
+
status: ReqRespStatus[status],
|
|
772
|
+
data: data ? bufferToHex(data) : undefined
|
|
773
|
+
};
|
|
774
|
+
const peerAuthResponse = AuthResponse.fromBuffer(data);
|
|
775
|
+
const peerStatusMessage = peerAuthResponse.status;
|
|
776
|
+
if (!ourStatus.validate(peerStatusMessage)) {
|
|
777
|
+
this.logger.debug(`Disconnecting peer ${peerId} due to failed status handshake as part of auth.`, logData);
|
|
778
|
+
this.markAuthHandshakeFailed(peerId);
|
|
779
|
+
this.markPeerForDisconnect(peerId);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
const hashToRecover = authRequest.getPayloadToSign();
|
|
783
|
+
const ethSignedHash = makeEthSignDigest(hashToRecover);
|
|
784
|
+
const sender = recoverAddress(ethSignedHash, peerAuthResponse.signature);
|
|
785
|
+
const registeredValidators = await this.epochCache.getRegisteredValidators();
|
|
786
|
+
const found = registeredValidators.find((v)=>v.toString() === sender.toString()) !== undefined;
|
|
787
|
+
if (!found) {
|
|
788
|
+
this.logger.debug(`Disconnecting peer ${peerId} due to failed auth handshake, peer is not a registered validator.`, {
|
|
789
|
+
peerId,
|
|
790
|
+
address: sender.toString()
|
|
791
|
+
});
|
|
792
|
+
this.markAuthHandshakeFailed(peerId);
|
|
793
|
+
this.markPeerForDisconnect(peerId);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
// Check to see that this validator address isn't already allocated to a different peer
|
|
797
|
+
const peerForAddress = this.authenticatedValidatorAddressToPeerId.get(sender.toString());
|
|
798
|
+
if (peerForAddress !== undefined && peerForAddress.toString() !== peerIdString) {
|
|
799
|
+
this.logger.debug(`Received auth for validator ${sender.toString()} from peer ${peerIdString}, but this validator is already authenticated to peer ${peerForAddress.toString()}`);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
this.markAuthHandshakeSuccess(peerId);
|
|
803
|
+
this.authenticatedPeerIdToValidatorAddress.set(peerIdString, sender);
|
|
804
|
+
this.authenticatedValidatorAddressToPeerId.set(sender.toString(), peerId);
|
|
805
|
+
this.logger.info(`Successfully completed auth handshake with peer ${peerId}, validator address ${sender.toString()}`, logData);
|
|
806
|
+
} catch (err) {
|
|
807
|
+
//TODO: maybe hard ban these peers in the future
|
|
808
|
+
this.logger.debug(`Disconnecting peer ${peerId} due to error during auth handshake: ${err.message ?? err}`, {
|
|
809
|
+
peerId
|
|
810
|
+
});
|
|
811
|
+
this.markAuthHandshakeFailed(peerId);
|
|
812
|
+
this.markPeerForDisconnect(peerId);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
/*
|
|
816
|
+
* Marks when peer fails auth handshake
|
|
817
|
+
* */ markAuthHandshakeFailed(peerId) {
|
|
818
|
+
const now = this.dateProvider.now();
|
|
819
|
+
const peerIdStr = peerId.toString();
|
|
820
|
+
const existingEntry = this.failedAuthHandshakes.get(peerIdStr);
|
|
821
|
+
this.failedAuthHandshakes.set(peerIdStr, {
|
|
822
|
+
count: (existingEntry?.count || 0) + 1,
|
|
823
|
+
lastFailureTimestamp: now
|
|
824
|
+
});
|
|
825
|
+
const connections = this.libP2PNode.getConnections(peerId);
|
|
826
|
+
connections.forEach((conn)=>{
|
|
827
|
+
// We mark the IP address
|
|
828
|
+
const address = conn.remoteAddr.nodeAddress().address;
|
|
829
|
+
const existingAddressEntry = this.failedAuthHandshakes.get(address);
|
|
830
|
+
this.failedAuthHandshakes.set(address, {
|
|
831
|
+
count: (existingAddressEntry?.count || 0) + 1,
|
|
832
|
+
lastFailureTimestamp: now
|
|
833
|
+
});
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
/*
|
|
837
|
+
* Marks when peer exchanges auth handshake
|
|
838
|
+
* Removes any failed previous attempts
|
|
839
|
+
* */ markAuthHandshakeSuccess(peerId) {
|
|
840
|
+
this.failedAuthHandshakes.delete(peerId.toString());
|
|
841
|
+
const connections = this.libP2PNode.getConnections(peerId);
|
|
842
|
+
connections.forEach((conn)=>{
|
|
843
|
+
const address = conn.remoteAddr.nodeAddress().address;
|
|
844
|
+
this.failedAuthHandshakes.delete(address);
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
558
848
|
* Stops the peer manager.
|
|
559
849
|
* Removing all event listeners.
|
|
560
850
|
*/ async stop() {
|
|
@@ -565,6 +855,46 @@ export class PeerManager {
|
|
|
565
855
|
this.libP2PNode.removeEventListener(PeerEvent.CONNECTED, this.handlers.handleConnectedPeerEvent);
|
|
566
856
|
this.libP2PNode.removeEventListener(PeerEvent.DISCONNECTED, this.handlers.handleDisconnectedPeerEvent);
|
|
567
857
|
}
|
|
858
|
+
shouldTrustWithIdentity(peerId) {
|
|
859
|
+
return this.isProtectedPeer(peerId);
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Performs auth request verification from peer. An auth request is valid if requested by an authorized peer (a peer we trust).
|
|
863
|
+
*
|
|
864
|
+
* @param: _authRequest - Auth request (unused)
|
|
865
|
+
* @param: peerId - The ID of the peer that requested the auth handshake
|
|
866
|
+
*
|
|
867
|
+
* @returns: StatusMessage if peer is trusted
|
|
868
|
+
*
|
|
869
|
+
* @throws: If peer is unauthorized
|
|
870
|
+
* */ async handleAuthRequestFromPeer(_authRequest, peerId) {
|
|
871
|
+
if (!this.shouldTrustWithIdentity(peerId)) {
|
|
872
|
+
this.logger.warn(`Received auth request from untrusted peer ${peerId.toString()}`);
|
|
873
|
+
throw new Error('Unauthorised');
|
|
874
|
+
}
|
|
875
|
+
this.logger.debug(`Received auth request from trusted peer ${peerId.toString()}`);
|
|
876
|
+
return await this.createStatusMessage();
|
|
877
|
+
}
|
|
878
|
+
async updateAuthenticatedPeers() {
|
|
879
|
+
const registeredValidators = await this.epochCache.getRegisteredValidators();
|
|
880
|
+
const validatorSet = new Set(registeredValidators.map((v)=>v.toString()));
|
|
881
|
+
const peersToDelete = new Set();
|
|
882
|
+
const addressesToDelete = new Set();
|
|
883
|
+
for (const [peer, address] of this.authenticatedPeerIdToValidatorAddress.entries()){
|
|
884
|
+
const addressString = address.toString();
|
|
885
|
+
if (!validatorSet.has(addressString)) {
|
|
886
|
+
peersToDelete.add(peer);
|
|
887
|
+
addressesToDelete.add(addressString);
|
|
888
|
+
this.logger.info(`Removing authentication for peer ${peer.toString()} at address ${addressString} due to no longer being a registered validator`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
for (const peer of peersToDelete){
|
|
892
|
+
this.authenticatedPeerIdToValidatorAddress.delete(peer);
|
|
893
|
+
}
|
|
894
|
+
for (const address of addressesToDelete){
|
|
895
|
+
this.authenticatedValidatorAddressToPeerId.delete(address);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
568
898
|
}
|
|
569
899
|
_ts_decorate([
|
|
570
900
|
trackSpan('PeerManager.heartbeat')
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ConfigMapping } from '@aztec/foundation/config';
|
|
2
|
-
export declare const DEFAULT_INDIVIDUAL_REQUEST_TIMEOUT_MS =
|
|
3
|
-
export declare const DEFAULT_OVERALL_REQUEST_TIMEOUT_MS =
|
|
4
|
-
export declare const DEFAULT_REQRESP_DIAL_TIMEOUT_MS =
|
|
2
|
+
export declare const DEFAULT_INDIVIDUAL_REQUEST_TIMEOUT_MS = 10000;
|
|
3
|
+
export declare const DEFAULT_OVERALL_REQUEST_TIMEOUT_MS = 10000;
|
|
4
|
+
export declare const DEFAULT_REQRESP_DIAL_TIMEOUT_MS = 5000;
|
|
5
5
|
export declare const DEFAULT_OPTIMISTIC_NEGOTIATION = false;
|
|
6
6
|
export declare const DEFAULT_P2P_REQRESP_CONFIG: P2PReqRespConfig;
|
|
7
7
|
export interface P2PReqRespConfig {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/services/reqresp/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAA2C,MAAM,0BAA0B,CAAC;AAEvG,eAAO,MAAM,qCAAqC,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/services/reqresp/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAA2C,MAAM,0BAA0B,CAAC;AAEvG,eAAO,MAAM,qCAAqC,QAAS,CAAC;AAC5D,eAAO,MAAM,kCAAkC,QAAS,CAAC;AACzD,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AACrD,eAAO,MAAM,8BAA8B,QAAQ,CAAC;AAGpD,eAAO,MAAM,0BAA0B,EAAE,gBAKxC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,uBAAuB,EAAE,MAAM,CAAC;IAEhC,uEAAuE;IACvE,0BAA0B,EAAE,MAAM,CAAC;IAEnC,kHAAkH;IAClH,wBAAwB,EAAE,OAAO,CAAC;IAElC,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,gBAAgB,EAAE,aAAa,CAsBlF,CAAC"}
|