@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
@@ -1,8 +1,8 @@
1
1
  import { type ConfigMapping, booleanConfigHelper, numberConfigHelper } from '@aztec/foundation/config';
2
2
 
3
- export const DEFAULT_INDIVIDUAL_REQUEST_TIMEOUT_MS = 2000;
4
- export const DEFAULT_OVERALL_REQUEST_TIMEOUT_MS = 4000;
5
- export const DEFAULT_REQRESP_DIAL_TIMEOUT_MS = 1000;
3
+ export const DEFAULT_INDIVIDUAL_REQUEST_TIMEOUT_MS = 10_000;
4
+ export const DEFAULT_OVERALL_REQUEST_TIMEOUT_MS = 10_000; // Not currently used
5
+ export const DEFAULT_REQRESP_DIAL_TIMEOUT_MS = 5_000;
6
6
  export const DEFAULT_OPTIMISTIC_NEGOTIATION = false;
7
7
 
8
8
  // For use in tests.
@@ -1,6 +1,5 @@
1
- import { AbortError, TimeoutError } from '@aztec/foundation/error';
1
+ import { AbortError } from '@aztec/foundation/error';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
- import { SerialQueue } from '@aztec/foundation/queue';
4
3
 
5
4
  import type { Libp2p, PeerId, Stream } from '@libp2p/interface';
6
5
 
@@ -21,13 +20,14 @@ export class RandomSampler {
21
20
  */
22
21
  export class ConnectionSampler {
23
22
  private cleanupInterval: NodeJS.Timeout;
24
- private dialAttempts: AbortController[] = [];
25
23
 
26
- private readonly activeConnectionsCount: Map<PeerId, number> = new Map();
27
- private readonly streams: Set<Stream> = new Set();
24
+ // Map from stringified peer id to number of active connections
25
+ protected readonly activeConnectionsCount: Map<string, number> = new Map();
28
26
 
29
- // Serial queue to ensure that we only dial one peer at a time
30
- private dialQueue: SerialQueue = new SerialQueue();
27
+ // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
28
+ protected readonly streams: Set<Stream> = new Set();
29
+
30
+ private abortOnStop: AbortController = new AbortController();
31
31
 
32
32
  constructor(
33
33
  private readonly libp2p: Libp2p,
@@ -39,8 +39,6 @@ export class ConnectionSampler {
39
39
  () => void this.cleanupStaleConnections(),
40
40
  this.opts.cleanupIntervalMs ?? 60_000,
41
41
  );
42
-
43
- this.dialQueue.start();
44
42
  }
45
43
 
46
44
  /**
@@ -48,14 +46,9 @@ export class ConnectionSampler {
48
46
  */
49
47
  async stop() {
50
48
  this.logger.info('Stopping connection sampler');
49
+ this.abortOnStop.abort(new AbortError('Connection sampler stopped'));
51
50
  clearInterval(this.cleanupInterval);
52
51
 
53
- for (const attempt of this.dialAttempts) {
54
- attempt.abort(new AbortError('Connection sampler stopped'));
55
- }
56
- this.dialAttempts = [];
57
- await this.dialQueue.end();
58
-
59
52
  // Close all active streams
60
53
  const closePromises = Array.from(this.streams.values()).map(stream => this.close(stream));
61
54
  await Promise.all(closePromises);
@@ -103,7 +96,7 @@ export class ConnectionSampler {
103
96
  for (let attempts = 0; attempts < MAX_SAMPLE_ATTEMPTS && peers.length > 0; attempts++) {
104
97
  const randomIndex = this.sampler.random(peers.length);
105
98
  const peer = peers[randomIndex];
106
- const hasActiveConnections = (this.activeConnectionsCount.get(peer) ?? 0) > 0;
99
+ const hasActiveConnections = (this.activeConnectionsCount.get(peer.toString()) ?? 0) > 0;
107
100
  const isExcluded = excluding?.get(peer.toString()) ?? false;
108
101
 
109
102
  // Remove this peer from consideration
@@ -147,6 +140,7 @@ export class ConnectionSampler {
147
140
  numberToSample = Math.min(numberToSample, peers.length);
148
141
 
149
142
  const batch: PeerId[] = [];
143
+ // eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
150
144
  const withActiveConnections: Set<PeerId> = new Set();
151
145
  for (let i = 0; i < numberToSample; i++) {
152
146
  const { peer, sampledPeers } = this.getPeerFromList(peers, excluding);
@@ -189,54 +183,33 @@ export class ConnectionSampler {
189
183
  * @returns The stream
190
184
  */
191
185
  async dialProtocol(peerId: PeerId, protocol: string, timeout?: number): Promise<Stream> {
192
- // Dialling at the same time can cause race conditions where two different streams
193
- // end up with the same id, hence a serial queue
194
- this.logger.debug(`Dial queue length: ${this.dialQueue.length()}`);
195
-
196
- const abortController = new AbortController();
197
- this.dialAttempts.push(abortController);
198
- let timeoutHandle: NodeJS.Timeout | undefined;
199
- if (timeout) {
200
- timeoutHandle = setTimeout(() => abortController.abort(new TimeoutError('Dial protocol timeout')), timeout);
201
- }
202
-
203
- try {
204
- const stream = await this.dialQueue.put(() =>
205
- this.libp2p.dialProtocol(peerId, protocol, {
206
- signal: abortController.signal,
207
- negotiateFully: !this.opts.p2pOptimisticNegotiation,
208
- }),
209
- );
210
- stream.metadata.peerId = peerId;
211
- this.streams.add(stream);
212
-
213
- const updatedActiveConnectionsCount = (this.activeConnectionsCount.get(peerId) ?? 0) + 1;
214
- this.activeConnectionsCount.set(peerId, updatedActiveConnectionsCount);
215
-
216
- this.logger.trace('Dialed protocol', {
217
- streamId: stream.id,
218
- protocol,
219
- peerId: peerId.toString(),
220
- activeConnectionsCount: updatedActiveConnectionsCount,
221
- });
222
- return stream;
223
- } finally {
224
- if (timeoutHandle) {
225
- clearTimeout(timeoutHandle);
226
- }
227
-
228
- const idx = this.dialAttempts.indexOf(abortController);
229
- if (idx > -1) {
230
- this.dialAttempts.splice(idx, 1);
231
- }
232
- }
186
+ const stream = await this.libp2p.dialProtocol(peerId, protocol, {
187
+ signal: AbortSignal.any(
188
+ timeout ? [this.abortOnStop.signal, AbortSignal.timeout(timeout!)] : [this.abortOnStop.signal],
189
+ ),
190
+ negotiateFully: !this.opts.p2pOptimisticNegotiation,
191
+ });
192
+ stream.metadata.peerId = peerId;
193
+ this.streams.add(stream);
194
+
195
+ const peerIdString = peerId.toString();
196
+ const updatedActiveConnectionsCount = (this.activeConnectionsCount.get(peerIdString) ?? 0) + 1;
197
+ this.activeConnectionsCount.set(peerIdString, updatedActiveConnectionsCount);
198
+
199
+ this.logger.trace('Dialed protocol', {
200
+ streamId: stream.id,
201
+ protocol,
202
+ peerId: peerIdString,
203
+ activeConnectionsCount: updatedActiveConnectionsCount,
204
+ });
205
+ return stream;
233
206
  }
234
207
 
235
208
  /**
236
209
  * Closes a stream and updates the active connections count
237
210
  */
238
211
  async close(stream: Stream): Promise<void> {
239
- let peerId = undefined;
212
+ let peerId: PeerId | undefined = undefined;
240
213
 
241
214
  try {
242
215
  peerId = stream.metadata.peerId;
@@ -245,13 +218,13 @@ export class ConnectionSampler {
245
218
  if (!peerId) {
246
219
  this.logger.warn(`Stream ${stream.id} does not have a peerId set`);
247
220
  } else {
248
- updatedActiveConnectionsCount = (this.activeConnectionsCount.get(peerId) ?? 1) - 1;
249
- this.activeConnectionsCount.set(peerId, updatedActiveConnectionsCount);
221
+ updatedActiveConnectionsCount = (this.activeConnectionsCount.get(peerId.toString()) ?? 1) - 1;
222
+ this.activeConnectionsCount.set(peerId.toString(), updatedActiveConnectionsCount);
250
223
  }
251
224
 
252
225
  this.logger.trace('Closing connection', {
253
226
  streamId: stream.id,
254
- peerId: peerId.toString(),
227
+ peerId: peerId?.toString(),
255
228
  protocol: stream.protocol,
256
229
  activeConnectionsCount: updatedActiveConnectionsCount,
257
230
  });
@@ -263,6 +236,8 @@ export class ConnectionSampler {
263
236
  await stream.close();
264
237
  } catch (error) {
265
238
  this.logger.error(`Failed to close connection to peer ${peerId ?? 'unknown'} with stream id ${stream.id}`, error);
239
+ // graceful close failed, abort the stream
240
+ stream.abort(new AbortError('Failed to close stream gracefully'));
266
241
  } finally {
267
242
  this.streams.delete(stream);
268
243
  }
@@ -277,10 +252,10 @@ export class ConnectionSampler {
277
252
  for (const stream of this.streams.values()) {
278
253
  try {
279
254
  // Check if we have lost track of accounting
280
- const peerId = stream.metadata.peerId;
255
+ const peerId: PeerId = stream.metadata.peerId;
281
256
  if (!peerId) {
282
257
  this.logger.warn(`Stream ${stream.id} does not have a peerId set`);
283
- } else if (this.activeConnectionsCount.get(peerId) === 0) {
258
+ } else if (this.activeConnectionsCount.get(peerId.toString()) === 0) {
284
259
  await this.close(stream);
285
260
  this.logger.debug('Cleaned up stale connection', { streamId: stream.id, peerId: peerId.toString() });
286
261
  }
@@ -2,3 +2,5 @@
2
2
  * Request Response protocol allows nodes to ask their peers for data
3
3
  * that they missed via the traditional gossip protocol.
4
4
  */
5
+ export * from './protocols/index.js';
6
+ export * from './interface.js';
@@ -1,10 +1,12 @@
1
1
  import { Fr } from '@aztec/foundation/fields';
2
2
  import { L2Block } from '@aztec/stdlib/block';
3
- import { Tx, TxHash } from '@aztec/stdlib/tx';
3
+ import { TxArray, TxHashArray } from '@aztec/stdlib/tx';
4
4
 
5
5
  import type { PeerId } from '@libp2p/interface';
6
6
 
7
7
  import type { P2PReqRespConfig } from './config.js';
8
+ import { AuthRequest, AuthResponse } from './protocols/auth.js';
9
+ import { BlockTxsRequest, BlockTxsResponse } from './protocols/block_txs/block_txs_reqresp.js';
8
10
  import { StatusMessage } from './protocols/status.js';
9
11
  import type { ReqRespStatus } from './status.js';
10
12
 
@@ -16,6 +18,8 @@ export const STATUS_PROTOCOL = '/aztec/req/status/0.1.0';
16
18
  export const GOODBYE_PROTOCOL = '/aztec/req/goodbye/0.1.0';
17
19
  export const TX_REQ_PROTOCOL = '/aztec/req/tx/0.1.0';
18
20
  export const BLOCK_REQ_PROTOCOL = '/aztec/req/block/0.1.0';
21
+ export const AUTH_PROTOCOL = '/aztec/req/auth/0.1.0';
22
+ export const BLOCK_TXS_REQ_PROTOCOL = '/aztec/req/block_txs/0.1.0';
19
23
 
20
24
  export enum ReqRespSubProtocol {
21
25
  PING = PING_PROTOCOL,
@@ -23,6 +27,8 @@ export enum ReqRespSubProtocol {
23
27
  GOODBYE = GOODBYE_PROTOCOL,
24
28
  TX = TX_REQ_PROTOCOL,
25
29
  BLOCK = BLOCK_REQ_PROTOCOL,
30
+ AUTH = AUTH_PROTOCOL,
31
+ BLOCK_TXS = BLOCK_TXS_REQ_PROTOCOL,
26
32
  }
27
33
 
28
34
  /**
@@ -38,12 +44,12 @@ export type ReqRespSubProtocolRateLimits = Record<ReqRespSubProtocol, ProtocolRa
38
44
 
39
45
  /**
40
46
  * The response from the ReqResp protocol
41
- * Consists of a status (Error code) and data
47
+ * Consists of a status
48
+ * And, optionally, a data buffer (in case status is SUCCESS)
42
49
  */
43
- export interface ReqRespResponse {
44
- status: ReqRespStatus;
45
- data: Buffer;
46
- }
50
+ export type ReqRespResponse =
51
+ | { status: ReqRespStatus.SUCCESS; data: Buffer }
52
+ | { status: Exclude<ReqRespStatus, ReqRespStatus.SUCCESS> };
47
53
 
48
54
  /**
49
55
  * A rate limit quota
@@ -93,21 +99,34 @@ export const DEFAULT_SUB_PROTOCOL_VALIDATORS: ReqRespSubProtocolValidators = {
93
99
  [ReqRespSubProtocol.TX]: noopValidator,
94
100
  [ReqRespSubProtocol.GOODBYE]: noopValidator,
95
101
  [ReqRespSubProtocol.BLOCK]: noopValidator,
102
+ [ReqRespSubProtocol.AUTH]: noopValidator,
103
+ [ReqRespSubProtocol.BLOCK_TXS]: noopValidator,
96
104
  };
97
105
 
106
+ /*
107
+ * Helper class to sub-protocol validation error*/
108
+ export class ValidationError extends Error {
109
+ constructor(message: string) {
110
+ super(message);
111
+ }
112
+ }
113
+
98
114
  /**
99
115
  * Sub protocol map determines the request and response types for each
100
116
  * Req Resp protocol
101
117
  */
102
118
  export type SubProtocolMap = {
103
- [S in ReqRespSubProtocol]: RequestResponsePair<any, any>;
119
+ [S in ReqRespSubProtocol]: RequestResponsePair<
120
+ InstanceType<(typeof subProtocolMap)[S]['request']>,
121
+ InstanceType<(typeof subProtocolMap)[S]['response']>
122
+ >;
104
123
  };
105
124
 
106
125
  /**
107
126
  * Default handler for unimplemented sub protocols, this SHOULD be overwritten
108
127
  * by the service, but is provided as a fallback
109
128
  */
110
- const defaultHandler = (_msg: any): Promise<Buffer> => {
129
+ export const defaultHandler = (_msg: any): Promise<Buffer> => {
111
130
  return Promise.resolve(Buffer.from('unimplemented'));
112
131
  };
113
132
 
@@ -120,6 +139,8 @@ export const DEFAULT_SUB_PROTOCOL_HANDLERS: ReqRespSubProtocolHandlers = {
120
139
  [ReqRespSubProtocol.TX]: defaultHandler,
121
140
  [ReqRespSubProtocol.GOODBYE]: defaultHandler,
122
141
  [ReqRespSubProtocol.BLOCK]: defaultHandler,
142
+ [ReqRespSubProtocol.AUTH]: defaultHandler,
143
+ [ReqRespSubProtocol.BLOCK_TXS]: defaultHandler,
123
144
  };
124
145
 
125
146
  /**
@@ -140,6 +161,22 @@ interface RequestResponsePair<Req extends { toBuffer(): Buffer }, Res> {
140
161
  };
141
162
  }
142
163
 
164
+ /*
165
+ * Small helper function which parses buffer into specific response type
166
+ * It is needed to make TypeScript happy, as it cannot infer the type from the buffer
167
+ *
168
+ * @param proto - The sub protocol to parse the response for
169
+ * @param buffer - The buffer to parse
170
+ *
171
+ * @returns - The parsed response object
172
+ * */
173
+ export function responseFromBuffer<P extends ReqRespSubProtocol>(
174
+ proto: P,
175
+ buffer: Buffer,
176
+ ): InstanceType<(typeof subProtocolMap)[P]['response']> {
177
+ return subProtocolMap[proto].response.fromBuffer(buffer) as InstanceType<(typeof subProtocolMap)[P]['response']>;
178
+ }
179
+
143
180
  /**
144
181
  * RequestableBuffer is a wrapper around a buffer that allows it to be
145
182
  * used in generic request response protocols
@@ -163,7 +200,7 @@ export class RequestableBuffer {
163
200
  * This defines the request and response types for each sub protocol, used primarily
164
201
  * as a type rather than an object
165
202
  */
166
- export const subProtocolMap: SubProtocolMap = {
203
+ export const subProtocolMap = {
167
204
  [ReqRespSubProtocol.PING]: {
168
205
  request: RequestableBuffer,
169
206
  response: RequestableBuffer,
@@ -173,8 +210,8 @@ export const subProtocolMap: SubProtocolMap = {
173
210
  response: StatusMessage,
174
211
  },
175
212
  [ReqRespSubProtocol.TX]: {
176
- request: TxHash,
177
- response: Tx,
213
+ request: TxHashArray,
214
+ response: TxArray,
178
215
  },
179
216
  [ReqRespSubProtocol.GOODBYE]: {
180
217
  request: RequestableBuffer,
@@ -184,18 +221,27 @@ export const subProtocolMap: SubProtocolMap = {
184
221
  request: Fr, // block number
185
222
  response: L2Block,
186
223
  },
224
+ [ReqRespSubProtocol.AUTH]: {
225
+ request: AuthRequest,
226
+ response: AuthResponse,
227
+ },
228
+ [ReqRespSubProtocol.BLOCK_TXS]: {
229
+ request: BlockTxsRequest,
230
+ response: BlockTxsResponse,
231
+ },
187
232
  };
188
233
 
189
234
  export interface ReqRespInterface {
190
235
  start(
191
- subProtocolHandlers: ReqRespSubProtocolHandlers,
236
+ subProtocolHandlers: Partial<ReqRespSubProtocolHandlers>,
192
237
  subProtocolValidators: ReqRespSubProtocolValidators,
193
238
  ): Promise<void>;
239
+ addSubProtocol(
240
+ subProtocol: ReqRespSubProtocol,
241
+ handler: ReqRespSubProtocolHandler,
242
+ validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
243
+ ): Promise<void>;
194
244
  stop(): Promise<void>;
195
- sendRequest<SubProtocol extends ReqRespSubProtocol>(
196
- subProtocol: SubProtocol,
197
- request: InstanceType<SubProtocolMap[SubProtocol]['request']>,
198
- ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined>;
199
245
  sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
200
246
  subProtocol: SubProtocol,
201
247
  requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
@@ -203,7 +249,7 @@ export interface ReqRespInterface {
203
249
  timeoutMs?: number,
204
250
  maxPeers?: number,
205
251
  maxRetryAttempts?: number,
206
- ): Promise<(InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined)[]>;
252
+ ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]>;
207
253
  sendRequestToPeer(
208
254
  peerId: PeerId,
209
255
  subProtocol: ReqRespSubProtocol,
@@ -0,0 +1,83 @@
1
+ import { Buffer32 } from '@aztec/foundation/buffer';
2
+ import { keccak256 } from '@aztec/foundation/crypto';
3
+ import { Signature } from '@aztec/foundation/eth-signature';
4
+ import { Fr } from '@aztec/foundation/fields';
5
+ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
6
+
7
+ import { StatusMessage } from './status.js';
8
+
9
+ export const VALIDATOR_AUTH_DOMAIN_SEPARATOR = 'Aztec Validator Challenge:';
10
+ export const VALIDATOR_AUTH_FULL_CHALLENGE_ENCODED_LENGTH =
11
+ VALIDATOR_AUTH_DOMAIN_SEPARATOR.length + Fr.random().toString().length;
12
+
13
+ /*
14
+ * P2P Auth Message
15
+ * Superset of the StatusMessage, used to establish a handshake between peers and authenticate them.
16
+ */
17
+ export class AuthRequest {
18
+ constructor(
19
+ readonly status: StatusMessage,
20
+ readonly challenge: Fr,
21
+ ) {}
22
+
23
+ /**
24
+ * Deserializes the AuthRequest object from a Buffer.
25
+ * @param buffer - Buffer or BufferReader object to deserialize.
26
+ * @returns An instance of StatusMessage.
27
+ */
28
+ static fromBuffer(buffer: Buffer | BufferReader): AuthRequest {
29
+ const reader = BufferReader.asReader(buffer);
30
+ return new AuthRequest(
31
+ StatusMessage.fromBuffer(reader), // Deserialize StatusMessage
32
+ Fr.fromBuffer(reader), // challenge
33
+ );
34
+ }
35
+
36
+ /**
37
+ * Serializes the AuthRequest object into a Buffer.
38
+ * @returns Buffer representation of the StatusMessage object.
39
+ */
40
+ toBuffer() {
41
+ return serializeToBuffer([this.status, this.challenge]);
42
+ }
43
+
44
+ getPayloadToSign(): Buffer32 {
45
+ const fullChallenge = VALIDATOR_AUTH_DOMAIN_SEPARATOR + this.challenge.toString();
46
+ return Buffer32.fromBuffer(keccak256(Buffer.from(fullChallenge, 'utf-8')));
47
+ }
48
+
49
+ static random(): AuthRequest {
50
+ return new AuthRequest(StatusMessage.random(), Fr.random());
51
+ }
52
+ }
53
+
54
+ export class AuthResponse {
55
+ constructor(
56
+ readonly status: StatusMessage,
57
+ readonly signature: Signature,
58
+ ) {}
59
+ /**
60
+ * Deserializes the AuthResponse object from a Buffer.
61
+ * @param buffer - Buffer or BufferReader object to deserialize.
62
+ * @returns An instance of StatusMessage.
63
+ */
64
+ static fromBuffer(buffer: Buffer | BufferReader): AuthResponse {
65
+ const reader = BufferReader.asReader(buffer);
66
+ return new AuthResponse(
67
+ StatusMessage.fromBuffer(reader), // Deserialize StatusMessage
68
+ Signature.fromBuffer(reader), // response
69
+ );
70
+ }
71
+
72
+ /**
73
+ * Serializes the AuthRequest object into a Buffer.
74
+ * @returns Buffer representation of the StatusMessage object.
75
+ */
76
+ toBuffer() {
77
+ return serializeToBuffer([this.status, this.signature]);
78
+ }
79
+
80
+ static random(): AuthResponse {
81
+ return new AuthResponse(StatusMessage.random(), Signature.random());
82
+ }
83
+ }
@@ -4,12 +4,33 @@ import type { L2BlockSource } from '@aztec/stdlib/block';
4
4
  import type { PeerId } from '@libp2p/interface';
5
5
 
6
6
  import type { ReqRespSubProtocolHandler } from '../interface.js';
7
+ import { ReqRespStatus, ReqRespStatusError } from '../status.js';
7
8
 
9
+ /**
10
+ * Handler for L2 Block requests
11
+ * @param l2BlockSource - source for L2 blocks
12
+ * @returns the Block request handler
13
+ * */
8
14
  export function reqRespBlockHandler(l2BlockSource: L2BlockSource): ReqRespSubProtocolHandler {
15
+ /**
16
+ * @param peerId - the peer ID of the requester
17
+ * @param msg - the block request message, which is expected to contain valid block number as a Buffer
18
+ * @returns a Buffer containing the requested block data, or an empty Buffer if the block is not found
19
+ * @throws ReqRespStatusError if the input msg is not a valid block number
20
+ * */
9
21
  return async (_peerId: PeerId, msg: Buffer) => {
10
- const blockNumber = Fr.fromBuffer(msg);
22
+ let blockNumber: Fr;
23
+ try {
24
+ blockNumber = Fr.fromBuffer(msg);
25
+ } catch (err: any) {
26
+ throw new ReqRespStatusError(ReqRespStatus.BADLY_FORMED_REQUEST, { cause: err });
27
+ }
11
28
 
12
- const foundBlock = await l2BlockSource.getBlock(Number(blockNumber));
13
- return foundBlock ? foundBlock.toBuffer() : Buffer.alloc(0);
29
+ try {
30
+ const foundBlock = await l2BlockSource.getBlock(Number(blockNumber));
31
+ return foundBlock ? foundBlock.toBuffer() : Buffer.alloc(0);
32
+ } catch (err: any) {
33
+ throw new ReqRespStatusError(ReqRespStatus.INTERNAL_ERROR, { cause: err });
34
+ }
14
35
  };
15
36
  }
@@ -0,0 +1,90 @@
1
+ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
2
+
3
+ /**
4
+ * BitVector helper class for representing and serializing bit vectors
5
+ */
6
+ export class BitVector {
7
+ private buffer: Buffer;
8
+ private length: number;
9
+
10
+ constructor(buffer: Buffer, length: number) {
11
+ this.buffer = buffer;
12
+ this.length = length;
13
+ }
14
+
15
+ /*
16
+ * Creates new BitVector instance
17
+ * @param length - Length of the bit vector
18
+ * @param indices - Array of indices to set to 1 in the bit vector
19
+ *
20
+ * @returns A new BitVector instance with specified length and set indices
21
+ * */
22
+ static init(length: number, indices: number[]): BitVector {
23
+ if (indices.length > length) {
24
+ throw new Error('Indices length exceeds specified length');
25
+ }
26
+
27
+ const buffer = Buffer.alloc(BitVector.byteLength(length));
28
+
29
+ indices.forEach(idx => {
30
+ const invalidIndex = idx < 0 || idx >= length;
31
+ if (invalidIndex) {
32
+ throw new Error(`Index ${idx} is out of bounds for BitVector of length ${length}`);
33
+ }
34
+
35
+ const byteIndex = Math.floor(idx / 8);
36
+ const bitIndex = idx % 8;
37
+ buffer[byteIndex] |= 1 << bitIndex;
38
+ });
39
+
40
+ return new BitVector(buffer, length);
41
+ }
42
+
43
+ getLength(): number {
44
+ return this.length;
45
+ }
46
+
47
+ /*
48
+ * Checks if element at index is set to true
49
+ *
50
+ * @param index - Index of the bit to check
51
+ *
52
+ * @returns True if the bit at index is set, false otherwise
53
+ * */
54
+ isSet(index: number): boolean {
55
+ return index >= 0 && index < this.length && !!(this.buffer[Math.floor(index / 8)] & (1 << index % 8));
56
+ }
57
+
58
+ /**
59
+ * Returns all indices which are set to true
60
+ * */
61
+ getTrueIndices(): number[] {
62
+ return Array.from({ length: this.length }, (_, i) => i).filter(i => this.isSet(i));
63
+ }
64
+
65
+ /**
66
+ * Serializes the BitVector object into a Buffer
67
+ *
68
+ * @returns Buffer representation of the BitVector object
69
+ * */
70
+ toBuffer(): Buffer {
71
+ return serializeToBuffer([this.length, this.buffer]);
72
+ }
73
+
74
+ /**
75
+ * Deserializes buffer into new BitVector
76
+ *
77
+ * @returns A new BitVector instance
78
+ * */
79
+ static fromBuffer(buffer: Buffer | BufferReader): BitVector {
80
+ const reader = BufferReader.asReader(buffer);
81
+ const length = reader.readNumber();
82
+
83
+ const bitBuffer = reader.readBytes(BitVector.byteLength(length));
84
+ return new BitVector(bitBuffer, length);
85
+ }
86
+
87
+ static byteLength(length: number) {
88
+ return Math.ceil(length / 8);
89
+ }
90
+ }
@@ -0,0 +1,53 @@
1
+ import { TxArray } from '@aztec/stdlib/tx';
2
+
3
+ import type { PeerId } from '@libp2p/interface';
4
+
5
+ import type { AttestationPool } from '../../../../mem_pools/attestation_pool/attestation_pool.js';
6
+ import type { TxPool } from '../../../../mem_pools/index.js';
7
+ import type { ReqRespSubProtocolHandler } from '../../interface.js';
8
+ import { ReqRespStatus, ReqRespStatusError } from '../../status.js';
9
+ import { BitVector } from './bitvector.js';
10
+ import { BlockTxsRequest, BlockTxsResponse } from './block_txs_reqresp.js';
11
+
12
+ /**
13
+ * Handler for block txs requests
14
+ * @param attestationPool - the attestation pool to check for block proposals
15
+ * @param mempools - the mempools containing the tx pool
16
+ * @returns the BlockTxs request handler
17
+ */
18
+ export function reqRespBlockTxsHandler(attestationPool: AttestationPool, txPool: TxPool): ReqRespSubProtocolHandler {
19
+ /**
20
+ * Handler for block txs requests
21
+ * @param msg - the block txs request message
22
+ * @returns the block txs response message
23
+ * @throws if msg is not a valid block txs request
24
+ */
25
+ return async (_peerId: PeerId, msg: Buffer) => {
26
+ let request: BlockTxsRequest;
27
+ try {
28
+ request = BlockTxsRequest.fromBuffer(msg);
29
+ } catch (err: any) {
30
+ throw new ReqRespStatusError(ReqRespStatus.BADLY_FORMED_REQUEST, { cause: err });
31
+ }
32
+
33
+ const blockProposal = await attestationPool.getBlockProposal(request.blockHash.toString());
34
+
35
+ if (!blockProposal) {
36
+ throw new ReqRespStatusError(ReqRespStatus.NOT_FOUND);
37
+ }
38
+
39
+ const txsAvailableInPool = await txPool.hasTxs(blockProposal.txHashes);
40
+ //Map txs in the pool to their indices in the block proposal
41
+ const availableIndices = txsAvailableInPool.map((hasTx, idx) => (hasTx ? idx : -1)).filter(idx => idx !== -1);
42
+ const responseBitVector = BitVector.init(blockProposal.txHashes.length, availableIndices);
43
+
44
+ const requestedIndices = new Set(request.txIndices.getTrueIndices());
45
+ const requestedTxsHashes = blockProposal.txHashes.filter((_, idx) => requestedIndices.has(idx));
46
+
47
+ const responseTxs = (await txPool.getTxsByHash(requestedTxsHashes)).filter(tx => !!tx);
48
+
49
+ const response = new BlockTxsResponse(request.blockHash, new TxArray(...responseTxs), responseBitVector);
50
+
51
+ return response.toBuffer();
52
+ };
53
+ }