@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.
Files changed (221) hide show
  1. package/dest/client/factory.d.ts +5 -1
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +29 -12
  4. package/dest/client/interface.d.ts +8 -13
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +18 -22
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +86 -83
  9. package/dest/config.d.ts +30 -1
  10. package/dest/config.d.ts.map +1 -1
  11. package/dest/config.js +34 -1
  12. package/dest/index.d.ts +1 -0
  13. package/dest/index.d.ts.map +1 -1
  14. package/dest/index.js +1 -0
  15. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +13 -1
  16. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  17. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  18. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +117 -10
  19. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +4 -1
  20. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  21. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +22 -1
  22. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +4 -1
  23. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  24. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +21 -1
  25. package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -2
  26. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  27. package/dest/mem_pools/attestation_pool/mocks.js +2 -10
  28. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +8 -3
  29. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  30. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +64 -37
  31. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +8 -3
  32. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  33. package/dest/mem_pools/tx_pool/memory_tx_pool.js +18 -10
  34. package/dest/mem_pools/tx_pool/tx_pool.d.ts +11 -2
  35. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  36. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  37. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +73 -44
  38. package/dest/msg_validators/attestation_validator/attestation_validator.js +1 -1
  39. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  40. package/dest/msg_validators/tx_validator/block_header_validator.js +2 -2
  41. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  42. package/dest/msg_validators/tx_validator/data_validator.js +35 -59
  43. package/dest/msg_validators/tx_validator/double_spend_validator.js +2 -2
  44. package/dest/msg_validators/tx_validator/gas_validator.js +4 -4
  45. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  46. package/dest/msg_validators/tx_validator/metadata_validator.js +21 -21
  47. package/dest/msg_validators/tx_validator/phases_validator.js +3 -3
  48. package/dest/msg_validators/tx_validator/tx_proof_validator.js +3 -3
  49. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  50. package/dest/services/discv5/discV5_service.js +4 -1
  51. package/dest/services/dummy_service.d.ts +13 -3
  52. package/dest/services/dummy_service.d.ts.map +1 -1
  53. package/dest/services/dummy_service.js +26 -3
  54. package/dest/services/index.d.ts +3 -1
  55. package/dest/services/index.d.ts.map +1 -1
  56. package/dest/services/index.js +3 -1
  57. package/dest/services/libp2p/libp2p_service.d.ts +13 -20
  58. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  59. package/dest/services/libp2p/libp2p_service.js +123 -46
  60. package/dest/services/peer-manager/interface.d.ts +8 -1
  61. package/dest/services/peer-manager/interface.d.ts.map +1 -1
  62. package/dest/services/peer-manager/peer_manager.d.ts +70 -3
  63. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  64. package/dest/services/peer-manager/peer_manager.js +369 -39
  65. package/dest/services/reqresp/config.d.ts +3 -3
  66. package/dest/services/reqresp/config.d.ts.map +1 -1
  67. package/dest/services/reqresp/config.js +3 -3
  68. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +3 -4
  69. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  70. package/dest/services/reqresp/connection-sampler/connection_sampler.js +35 -52
  71. package/dest/services/reqresp/index.d.ts +2 -1
  72. package/dest/services/reqresp/index.d.ts.map +1 -1
  73. package/dest/services/reqresp/index.js +2 -1
  74. package/dest/services/reqresp/interface.d.ts +61 -10
  75. package/dest/services/reqresp/interface.d.ts.map +1 -1
  76. package/dest/services/reqresp/interface.js +41 -6
  77. package/dest/services/reqresp/protocols/auth.d.ts +43 -0
  78. package/dest/services/reqresp/protocols/auth.d.ts.map +1 -0
  79. package/dest/services/reqresp/protocols/auth.js +71 -0
  80. package/dest/services/reqresp/protocols/block.d.ts +5 -0
  81. package/dest/services/reqresp/protocols/block.d.ts.map +1 -1
  82. package/dest/services/reqresp/protocols/block.js +28 -5
  83. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +30 -0
  84. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -0
  85. package/dest/services/reqresp/protocols/block_txs/bitvector.js +75 -0
  86. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +11 -0
  87. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -0
  88. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +39 -0
  89. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +49 -0
  90. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -0
  91. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +75 -0
  92. package/dest/services/reqresp/protocols/block_txs/index.d.ts +4 -0
  93. package/dest/services/reqresp/protocols/block_txs/index.d.ts.map +1 -0
  94. package/dest/services/reqresp/protocols/block_txs/index.js +3 -0
  95. package/dest/services/reqresp/protocols/goodbye.js +3 -5
  96. package/dest/services/reqresp/protocols/index.d.ts +2 -0
  97. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  98. package/dest/services/reqresp/protocols/index.js +2 -0
  99. package/dest/services/reqresp/protocols/status.d.ts +2 -0
  100. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  101. package/dest/services/reqresp/protocols/status.js +7 -0
  102. package/dest/services/reqresp/protocols/tx.d.ts +12 -1
  103. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  104. package/dest/services/reqresp/protocols/tx.js +34 -6
  105. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  106. package/dest/services/reqresp/rate-limiter/rate_limits.js +20 -0
  107. package/dest/services/reqresp/reqresp.d.ts +37 -39
  108. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  109. package/dest/services/reqresp/reqresp.js +220 -220
  110. package/dest/services/reqresp/status.d.ts +8 -3
  111. package/dest/services/reqresp/status.d.ts.map +1 -1
  112. package/dest/services/reqresp/status.js +6 -2
  113. package/dest/services/service.d.ts +10 -10
  114. package/dest/services/service.d.ts.map +1 -1
  115. package/dest/services/tx_collection/config.d.ts +25 -0
  116. package/dest/services/tx_collection/config.d.ts.map +1 -0
  117. package/dest/services/tx_collection/config.js +58 -0
  118. package/dest/services/tx_collection/fast_tx_collection.d.ts +56 -0
  119. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -0
  120. package/dest/services/tx_collection/fast_tx_collection.js +295 -0
  121. package/dest/services/tx_collection/index.d.ts +3 -0
  122. package/dest/services/tx_collection/index.d.ts.map +1 -0
  123. package/dest/services/tx_collection/index.js +2 -0
  124. package/dest/services/tx_collection/instrumentation.d.ts +10 -0
  125. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -0
  126. package/dest/services/tx_collection/instrumentation.js +34 -0
  127. package/dest/services/tx_collection/slow_tx_collection.d.ts +54 -0
  128. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -0
  129. package/dest/services/tx_collection/slow_tx_collection.js +176 -0
  130. package/dest/services/tx_collection/tx_collection.d.ts +109 -0
  131. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -0
  132. package/dest/services/tx_collection/tx_collection.js +127 -0
  133. package/dest/services/tx_collection/tx_collection_sink.d.ts +30 -0
  134. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -0
  135. package/dest/services/tx_collection/tx_collection_sink.js +81 -0
  136. package/dest/services/tx_collection/tx_source.d.ts +18 -0
  137. package/dest/services/tx_collection/tx_source.d.ts.map +1 -0
  138. package/dest/services/tx_collection/tx_source.js +31 -0
  139. package/dest/services/tx_provider.d.ts +49 -0
  140. package/dest/services/tx_provider.d.ts.map +1 -0
  141. package/dest/services/tx_provider.js +206 -0
  142. package/dest/services/{tx_collect_instrumentation.d.ts → tx_provider_instrumentation.d.ts} +2 -2
  143. package/dest/services/tx_provider_instrumentation.d.ts.map +1 -0
  144. package/dest/services/{tx_collect_instrumentation.js → tx_provider_instrumentation.js} +5 -5
  145. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  146. package/dest/test-helpers/make-test-p2p-clients.js +4 -3
  147. package/dest/test-helpers/mock-pubsub.d.ts +2 -1
  148. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  149. package/dest/test-helpers/mock-pubsub.js +2 -1
  150. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  151. package/dest/test-helpers/reqresp-nodes.js +8 -4
  152. package/dest/testbench/p2p_client_testbench_worker.js +11 -5
  153. package/dest/util.d.ts +1 -1
  154. package/dest/util.d.ts.map +1 -1
  155. package/package.json +14 -15
  156. package/src/client/factory.ts +87 -12
  157. package/src/client/interface.ts +19 -15
  158. package/src/client/p2p_client.ts +108 -92
  159. package/src/config.ts +52 -1
  160. package/src/index.ts +1 -0
  161. package/src/mem_pools/attestation_pool/attestation_pool.ts +15 -1
  162. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +155 -4
  163. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +28 -1
  164. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +28 -2
  165. package/src/mem_pools/attestation_pool/mocks.ts +1 -3
  166. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +59 -41
  167. package/src/mem_pools/tx_pool/memory_tx_pool.ts +19 -9
  168. package/src/mem_pools/tx_pool/tx_pool.ts +7 -2
  169. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +63 -40
  170. package/src/msg_validators/attestation_validator/attestation_validator.ts +1 -1
  171. package/src/msg_validators/tx_validator/block_header_validator.ts +2 -2
  172. package/src/msg_validators/tx_validator/data_validator.ts +36 -27
  173. package/src/msg_validators/tx_validator/double_spend_validator.ts +2 -2
  174. package/src/msg_validators/tx_validator/gas_validator.ts +4 -4
  175. package/src/msg_validators/tx_validator/metadata_validator.ts +22 -28
  176. package/src/msg_validators/tx_validator/phases_validator.ts +2 -2
  177. package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -2
  178. package/src/services/discv5/discV5_service.ts +4 -1
  179. package/src/services/dummy_service.ts +44 -4
  180. package/src/services/index.ts +3 -1
  181. package/src/services/libp2p/libp2p_service.ts +147 -55
  182. package/src/services/peer-manager/interface.ts +10 -1
  183. package/src/services/peer-manager/peer_manager.ts +441 -41
  184. package/src/services/reqresp/config.ts +3 -3
  185. package/src/services/reqresp/connection-sampler/connection_sampler.ts +38 -63
  186. package/src/services/reqresp/index.ts +2 -0
  187. package/src/services/reqresp/interface.ts +63 -17
  188. package/src/services/reqresp/protocols/auth.ts +83 -0
  189. package/src/services/reqresp/protocols/block.ts +24 -3
  190. package/src/services/reqresp/protocols/block_txs/bitvector.ts +90 -0
  191. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +53 -0
  192. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +79 -0
  193. package/src/services/reqresp/protocols/block_txs/index.ts +3 -0
  194. package/src/services/reqresp/protocols/goodbye.ts +3 -3
  195. package/src/services/reqresp/protocols/index.ts +2 -0
  196. package/src/services/reqresp/protocols/status.ts +20 -0
  197. package/src/services/reqresp/protocols/tx.ts +35 -6
  198. package/src/services/reqresp/rate-limiter/rate_limits.ts +20 -0
  199. package/src/services/reqresp/reqresp.ts +294 -264
  200. package/src/services/reqresp/status.ts +9 -3
  201. package/src/services/service.ts +23 -14
  202. package/src/services/tx_collection/config.ts +84 -0
  203. package/src/services/tx_collection/fast_tx_collection.ts +338 -0
  204. package/src/services/tx_collection/index.ts +2 -0
  205. package/src/services/tx_collection/instrumentation.ts +43 -0
  206. package/src/services/tx_collection/slow_tx_collection.ts +232 -0
  207. package/src/services/tx_collection/tx_collection.ts +214 -0
  208. package/src/services/tx_collection/tx_collection_sink.ts +98 -0
  209. package/src/services/tx_collection/tx_source.ts +37 -0
  210. package/src/services/tx_provider.ts +215 -0
  211. package/src/services/{tx_collect_instrumentation.ts → tx_provider_instrumentation.ts} +5 -5
  212. package/src/test-helpers/make-test-p2p-clients.ts +4 -2
  213. package/src/test-helpers/mock-pubsub.ts +1 -0
  214. package/src/test-helpers/reqresp-nodes.ts +7 -1
  215. package/src/testbench/p2p_client_testbench_worker.ts +9 -2
  216. package/src/util.ts +1 -1
  217. package/dest/services/tx_collect_instrumentation.d.ts.map +0 -1
  218. package/dest/services/tx_collector.d.ts +0 -23
  219. package/dest/services/tx_collector.d.ts.map +0 -1
  220. package/dest/services/tx_collector.js +0 -95
  221. 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.on(PeerEvent.DISCOVERED, this.handlers.handleDiscoveredPeer);
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 = Date.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
- if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
130
- this.logger.verbose(`Connected to bootstrap peer ${peerId.toString()}`);
131
- } else {
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
- if (!this.config.p2pDisableStatusHandshake) {
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
- if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
144
- this.logger.verbose(`Disconnected from bootstrap peer ${peerId.toString()}`);
145
- } else {
146
- this.logger.verbose(`Disconnected from transaction peer ${peerId.toString()}`);
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
- void this.disconnectPeer(peerId);
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 peersToConnect = this.config.maxPeerCount - healthyConnections.length - this.trustedPeers.size;
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: this.trustedPeers.size,
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
- Date.now() - peerData.addedUnixMs > MAX_CACHED_PEER_AGE_MS) {
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
- if (connections.length > this.config.maxPeerCount - this.trustedPeers.size) {
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 - this.trustedPeers.size);
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
- await this.disconnectPeer(peer);
541
+ this.markPeerForDisconnect(peer);
384
542
  }
385
543
  }
386
- async disconnectPeer(peer) {
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.debug(`Failed to disconnect peer ${peer.toString()}`, {
391
- error: inspect(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 (Date.now() < timedOutPeer.timeoutUntilMs) {
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: Date.now()
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: Date.now() + FAILED_PEER_BAN_TIME_MS
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 syncSummary = (await this.worldStateSynchronizer.status()).syncSummary;
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 { status, data } = await this.reqresp.sendRequestToPeer(peerId, ReqRespSubProtocol.STATUS, ourStatus.toBuffer());
529
- const logData = {
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`, logData);
539
- await this.disconnectPeer(peerId);
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
- await this.disconnectPeer(peerId);
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
- await this.disconnectPeer(peerId);
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 = 2000;
3
- export declare const DEFAULT_OVERALL_REQUEST_TIMEOUT_MS = 4000;
4
- export declare const DEFAULT_REQRESP_DIAL_TIMEOUT_MS = 1000;
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,OAAO,CAAC;AAC1D,eAAO,MAAM,kCAAkC,OAAO,CAAC;AACvD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AACpD,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"}
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"}