@aztec/p2p 0.0.1-commit.ef17749e1 → 0.0.1-commit.f103f88

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 (283) hide show
  1. package/README.md +129 -3
  2. package/dest/client/factory.d.ts +1 -1
  3. package/dest/client/factory.d.ts.map +1 -1
  4. package/dest/client/factory.js +23 -10
  5. package/dest/client/interface.d.ts +9 -2
  6. package/dest/client/interface.d.ts.map +1 -1
  7. package/dest/client/p2p_client.d.ts +3 -2
  8. package/dest/client/p2p_client.d.ts.map +1 -1
  9. package/dest/client/p2p_client.js +46 -14
  10. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +17 -6
  11. package/dest/config.d.ts +111 -95
  12. package/dest/config.d.ts.map +1 -1
  13. package/dest/config.js +27 -2
  14. package/dest/errors/p2p-service.error.d.ts +9 -0
  15. package/dest/errors/p2p-service.error.d.ts.map +1 -0
  16. package/dest/errors/p2p-service.error.js +10 -0
  17. package/dest/index.d.ts +1 -2
  18. package/dest/index.d.ts.map +1 -1
  19. package/dest/index.js +0 -1
  20. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +7 -5
  21. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  22. package/dest/mem_pools/attestation_pool/attestation_pool.js +11 -8
  23. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +6 -6
  24. package/dest/mem_pools/index.d.ts +1 -2
  25. package/dest/mem_pools/index.d.ts.map +1 -1
  26. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
  27. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
  28. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +2 -1
  29. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
  30. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +7 -5
  31. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  32. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +9 -2
  33. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  34. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +13 -3
  35. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +4 -2
  36. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  37. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +3 -0
  38. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +1 -1
  39. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  40. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +19 -5
  41. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +5 -2
  42. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  43. package/dest/msg_validators/attestation_validator/attestation_validator.js +20 -11
  44. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +4 -2
  45. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  46. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +2 -2
  47. package/dest/msg_validators/clock_tolerance.d.ts +12 -1
  48. package/dest/msg_validators/clock_tolerance.d.ts.map +1 -1
  49. package/dest/msg_validators/clock_tolerance.js +54 -3
  50. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +2 -1
  51. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  52. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +2 -1
  53. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  54. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +3 -1
  55. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  56. package/dest/msg_validators/proposal_validator/proposal_validator.js +19 -11
  57. package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
  58. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
  59. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
  60. package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
  61. package/dest/msg_validators/tx_validator/data_validator.d.ts +1 -1
  62. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  63. package/dest/msg_validators/tx_validator/data_validator.js +35 -2
  64. package/dest/msg_validators/tx_validator/factory.d.ts +23 -4
  65. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  66. package/dest/msg_validators/tx_validator/factory.js +36 -10
  67. package/dest/msg_validators/tx_validator/gas_validator.d.ts +13 -4
  68. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  69. package/dest/msg_validators/tx_validator/gas_validator.js +49 -17
  70. package/dest/msg_validators/tx_validator/phases_validator.d.ts +21 -1
  71. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  72. package/dest/msg_validators/tx_validator/phases_validator.js +28 -1
  73. package/dest/services/data_store.d.ts +1 -1
  74. package/dest/services/data_store.d.ts.map +1 -1
  75. package/dest/services/data_store.js +5 -5
  76. package/dest/services/dummy_service.d.ts +6 -3
  77. package/dest/services/dummy_service.d.ts.map +1 -1
  78. package/dest/services/dummy_service.js +6 -1
  79. package/dest/services/encoding.d.ts +5 -1
  80. package/dest/services/encoding.d.ts.map +1 -1
  81. package/dest/services/encoding.js +7 -1
  82. package/dest/services/gossipsub/topic_score_params.d.ts +13 -2
  83. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
  84. package/dest/services/gossipsub/topic_score_params.js +21 -4
  85. package/dest/services/libp2p/libp2p_service.d.ts +15 -25
  86. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  87. package/dest/services/libp2p/libp2p_service.js +151 -118
  88. package/dest/services/peer-manager/peer_manager.d.ts +6 -2
  89. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  90. package/dest/services/peer-manager/peer_manager.js +37 -10
  91. package/dest/services/peer-manager/peer_scoring.d.ts +7 -2
  92. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  93. package/dest/services/peer-manager/peer_scoring.js +32 -10
  94. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +11 -8
  95. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  96. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +69 -65
  97. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +3 -2
  98. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
  99. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +5 -4
  100. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
  101. package/dest/services/reqresp/batch-tx-requester/missing_txs.js +13 -7
  102. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +3 -1
  103. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
  104. package/dest/services/reqresp/batch-tx-requester/peer_collection.js +3 -0
  105. package/dest/services/reqresp/config.d.ts +3 -3
  106. package/dest/services/reqresp/config.d.ts.map +1 -1
  107. package/dest/services/reqresp/interface.d.ts +14 -9
  108. package/dest/services/reqresp/interface.d.ts.map +1 -1
  109. package/dest/services/reqresp/interface.js +10 -11
  110. package/dest/services/reqresp/metrics.d.ts +1 -1
  111. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  112. package/dest/services/reqresp/metrics.js +0 -1
  113. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  114. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  115. package/dest/services/reqresp/protocols/index.js +0 -1
  116. package/dest/services/reqresp/protocols/tx.d.ts +1 -1
  117. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  118. package/dest/services/reqresp/protocols/tx.js +1 -3
  119. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
  120. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  121. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
  122. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  123. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  124. package/dest/services/reqresp/rate-limiter/rate_limits.js +0 -10
  125. package/dest/services/reqresp/reqresp.d.ts +4 -2
  126. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  127. package/dest/services/reqresp/reqresp.js +28 -11
  128. package/dest/services/service.d.ts +5 -2
  129. package/dest/services/service.d.ts.map +1 -1
  130. package/dest/services/tx_collection/fast_tx_collection.d.ts +1 -4
  131. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  132. package/dest/services/tx_collection/fast_tx_collection.js +57 -73
  133. package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
  134. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
  135. package/dest/services/tx_collection/file_store_tx_source.js +39 -29
  136. package/dest/services/tx_collection/proposal_tx_collector.d.ts +6 -7
  137. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
  138. package/dest/services/tx_collection/proposal_tx_collector.js +4 -4
  139. package/dest/services/tx_collection/request_tracker.d.ts +53 -0
  140. package/dest/services/tx_collection/request_tracker.d.ts.map +1 -0
  141. package/dest/services/tx_collection/request_tracker.js +84 -0
  142. package/dest/services/tx_collection/slow_tx_collection.js +1 -1
  143. package/dest/services/tx_collection/tx_collection.d.ts +3 -6
  144. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  145. package/dest/services/tx_collection/tx_source.d.ts +6 -5
  146. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  147. package/dest/services/tx_collection/tx_source.js +9 -7
  148. package/dest/test-helpers/mock-pubsub.d.ts +11 -3
  149. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  150. package/dest/test-helpers/mock-pubsub.js +35 -10
  151. package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
  152. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  153. package/dest/test-helpers/reqresp-nodes.js +1 -2
  154. package/dest/test-helpers/testbench-utils.d.ts +1 -1
  155. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  156. package/dest/test-helpers/testbench-utils.js +23 -3
  157. package/dest/testbench/p2p_client_testbench_worker.js +66 -15
  158. package/dest/testbench/worker_client_manager.d.ts +8 -1
  159. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  160. package/dest/testbench/worker_client_manager.js +49 -1
  161. package/package.json +14 -14
  162. package/src/client/factory.ts +41 -13
  163. package/src/client/interface.ts +9 -1
  164. package/src/client/p2p_client.ts +50 -17
  165. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +18 -8
  166. package/src/config.ts +48 -3
  167. package/src/errors/p2p-service.error.ts +11 -0
  168. package/src/index.ts +0 -1
  169. package/src/mem_pools/attestation_pool/attestation_pool.ts +12 -8
  170. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +6 -6
  171. package/src/mem_pools/index.ts +0 -3
  172. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +2 -1
  173. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +3 -3
  174. package/src/mem_pools/tx_pool_v2/interfaces.ts +6 -4
  175. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +19 -3
  176. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +13 -1
  177. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +20 -5
  178. package/src/msg_validators/attestation_validator/README.md +49 -0
  179. package/src/msg_validators/attestation_validator/attestation_validator.ts +21 -9
  180. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +4 -1
  181. package/src/msg_validators/clock_tolerance.ts +72 -3
  182. package/src/msg_validators/proposal_validator/README.md +123 -0
  183. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +4 -1
  184. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +4 -1
  185. package/src/msg_validators/proposal_validator/proposal_validator.ts +17 -10
  186. package/src/msg_validators/tx_validator/README.md +5 -1
  187. package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
  188. package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
  189. package/src/msg_validators/tx_validator/data_validator.ts +42 -1
  190. package/src/msg_validators/tx_validator/factory.ts +43 -3
  191. package/src/msg_validators/tx_validator/gas_validator.ts +65 -16
  192. package/src/msg_validators/tx_validator/phases_validator.ts +31 -1
  193. package/src/services/data_store.ts +5 -13
  194. package/src/services/dummy_service.ts +8 -2
  195. package/src/services/encoding.ts +9 -1
  196. package/src/services/gossipsub/topic_score_params.ts +36 -4
  197. package/src/services/libp2p/libp2p_service.ts +150 -134
  198. package/src/services/peer-manager/peer_manager.ts +43 -10
  199. package/src/services/peer-manager/peer_scoring.ts +27 -5
  200. package/src/services/reqresp/README.md +229 -0
  201. package/src/services/reqresp/batch-tx-requester/README.md +46 -7
  202. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +64 -69
  203. package/src/services/reqresp/batch-tx-requester/interface.ts +2 -1
  204. package/src/services/reqresp/batch-tx-requester/missing_txs.ts +13 -6
  205. package/src/services/reqresp/batch-tx-requester/peer_collection.ts +5 -0
  206. package/src/services/reqresp/config.ts +2 -2
  207. package/src/services/reqresp/interface.ts +21 -11
  208. package/src/services/reqresp/metrics.ts +0 -1
  209. package/src/services/reqresp/protocols/index.ts +0 -1
  210. package/src/services/reqresp/protocols/tx.ts +1 -3
  211. package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
  212. package/src/services/reqresp/rate-limiter/rate_limits.ts +0 -10
  213. package/src/services/reqresp/reqresp.ts +37 -12
  214. package/src/services/service.ts +6 -1
  215. package/src/services/tx_collection/fast_tx_collection.ts +57 -83
  216. package/src/services/tx_collection/file_store_tx_source.ts +43 -31
  217. package/src/services/tx_collection/proposal_tx_collector.ts +8 -13
  218. package/src/services/tx_collection/request_tracker.ts +127 -0
  219. package/src/services/tx_collection/slow_tx_collection.ts +1 -1
  220. package/src/services/tx_collection/tx_collection.ts +3 -5
  221. package/src/services/tx_collection/tx_source.ts +8 -7
  222. package/src/test-helpers/mock-pubsub.ts +31 -5
  223. package/src/test-helpers/reqresp-nodes.ts +2 -2
  224. package/src/test-helpers/testbench-utils.ts +30 -3
  225. package/src/testbench/p2p_client_testbench_worker.ts +70 -14
  226. package/src/testbench/worker_client_manager.ts +55 -1
  227. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
  228. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  229. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
  230. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
  231. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
  232. package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
  233. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
  234. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
  235. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
  236. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
  237. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
  238. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -122
  239. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
  240. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
  241. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
  242. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
  243. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
  244. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
  245. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
  246. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
  247. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
  248. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
  249. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
  250. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
  251. package/dest/mem_pools/tx_pool/index.d.ts +0 -3
  252. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  253. package/dest/mem_pools/tx_pool/index.js +0 -2
  254. package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
  255. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  256. package/dest/mem_pools/tx_pool/priority.js +0 -15
  257. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
  258. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  259. package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
  260. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  261. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  262. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -400
  263. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  264. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  265. package/dest/services/reqresp/protocols/block.js +0 -32
  266. package/dest/services/tx_collection/missing_txs_tracker.d.ts +0 -32
  267. package/dest/services/tx_collection/missing_txs_tracker.d.ts.map +0 -1
  268. package/dest/services/tx_collection/missing_txs_tracker.js +0 -27
  269. package/src/mem_pools/tx_pool/README.md +0 -270
  270. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
  271. package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
  272. package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
  273. package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -162
  274. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
  275. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
  276. package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
  277. package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
  278. package/src/mem_pools/tx_pool/index.ts +0 -2
  279. package/src/mem_pools/tx_pool/priority.ts +0 -20
  280. package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
  281. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -319
  282. package/src/services/reqresp/protocols/block.ts +0 -37
  283. package/src/services/tx_collection/missing_txs_tracker.ts +0 -52
@@ -0,0 +1,127 @@
1
+ import { type PromiseWithResolvers, promiseWithResolvers } from '@aztec/foundation/promise';
2
+ import type { DateProvider } from '@aztec/foundation/timer';
3
+ import { TxHash } from '@aztec/stdlib/tx';
4
+ import type { Tx } from '@aztec/stdlib/tx';
5
+
6
+ /**
7
+ * Tracks which transactions are still missing and need to be fetched.
8
+ * Manages the request deadline and serves as the sole source of cancellation signal.
9
+ * The request is cancelled when all txs are fetched or the deadline expires.
10
+ */
11
+ export interface IRequestTracker {
12
+ /** Returns the set of transaction hashes that are still missing. */
13
+ get missingTxHashes(): Set<string>;
14
+ /** Size of this.missingTxHashes */
15
+ get numberOfMissingTxs(): number;
16
+ /** Are all requested txs fetched */
17
+ allFetched(): boolean;
18
+ /** Checks that transaction is still missing */
19
+ isMissing(txHash: string): boolean;
20
+ /** Marks a transaction as fetched. Returns true if it was previously missing. */
21
+ markFetched(tx: Tx): boolean;
22
+ /** Get list of collected txs */
23
+ get collectedTxs(): Tx[];
24
+ /** The deadline for this request. */
25
+ get deadline(): Date;
26
+ /** Remaining time in milliseconds until deadline. Returns 0 if already past. */
27
+ get timeoutMs(): number;
28
+ /** Checks whether the request is cancelled (deadline expired or all fetched). May trigger cancellation if deadline has passed. */
29
+ checkCancelled(): boolean;
30
+ /** Resolves when deadline expires or all txs are fetched. */
31
+ get cancellationToken(): Promise<void>;
32
+ /** Externally cancel the request. */
33
+ cancel(): void;
34
+ }
35
+
36
+ export class RequestTracker implements IRequestTracker {
37
+ public readonly collectedTxs: Tx[] = [];
38
+ private done = false;
39
+ private readonly cancellationTokenPromise: PromiseWithResolvers<void>;
40
+ private readonly deadlineTimer: ReturnType<typeof setTimeout> | undefined;
41
+
42
+ private constructor(
43
+ public readonly missingTxHashes: Set<string>,
44
+ public readonly deadline: Date,
45
+ private readonly dateProvider?: DateProvider,
46
+ ) {
47
+ this.cancellationTokenPromise = promiseWithResolvers<void>();
48
+
49
+ if (missingTxHashes.size === 0) {
50
+ this.finish();
51
+ return;
52
+ }
53
+
54
+ const now = this.dateProvider?.now() ?? Date.now();
55
+ const remaining = deadline.getTime() - now;
56
+ if (remaining <= 0) {
57
+ this.finish();
58
+ } else {
59
+ this.deadlineTimer = setTimeout(() => this.finish(), remaining);
60
+ }
61
+ }
62
+
63
+ public static create(hashes: TxHash[] | string[], deadline: Date, dateProvider?: DateProvider) {
64
+ return new RequestTracker(new Set(hashes.map(hash => hash.toString())), deadline, dateProvider);
65
+ }
66
+
67
+ markFetched(tx: Tx): boolean {
68
+ if (this.missingTxHashes.delete(tx.txHash.toString())) {
69
+ this.collectedTxs.push(tx);
70
+ if (this.allFetched()) {
71
+ this.finish();
72
+ }
73
+ return true;
74
+ }
75
+ return false;
76
+ }
77
+
78
+ get numberOfMissingTxs(): number {
79
+ return this.missingTxHashes.size;
80
+ }
81
+
82
+ allFetched(): boolean {
83
+ return this.numberOfMissingTxs === 0;
84
+ }
85
+
86
+ isMissing(txHash: string): boolean {
87
+ return this.missingTxHashes.has(txHash.toString());
88
+ }
89
+
90
+ get timeoutMs(): number {
91
+ const now = this.dateProvider?.now() ?? Date.now();
92
+ return Math.max(0, this.deadline.getTime() - now);
93
+ }
94
+
95
+ checkCancelled(): boolean {
96
+ if (this.done) {
97
+ return true;
98
+ }
99
+ // Synchronous fallback: check deadline even if setTimeout hasn't fired yet.
100
+ // This prevents macrotask starvation in tight async loops from blocking cancellation.
101
+ const now = this.dateProvider?.now() ?? Date.now();
102
+ if (now >= this.deadline.getTime()) {
103
+ this.finish();
104
+ return true;
105
+ }
106
+ return false;
107
+ }
108
+
109
+ get cancellationToken(): Promise<void> {
110
+ return this.cancellationTokenPromise.promise;
111
+ }
112
+
113
+ cancel(): void {
114
+ this.finish();
115
+ }
116
+
117
+ private finish() {
118
+ if (this.done) {
119
+ return;
120
+ }
121
+ this.done = true;
122
+ if (this.deadlineTimer) {
123
+ clearTimeout(this.deadlineTimer);
124
+ }
125
+ this.cancellationTokenPromise.resolve();
126
+ }
127
+ }
@@ -196,7 +196,7 @@ export class SlowTxCollection {
196
196
  // from mined unproven blocks it has seen in the past.
197
197
  const fastRequests = this.fastCollection.getFastCollectionRequests();
198
198
  const fastCollectionTxs: Set<string> = new Set(
199
- fastRequests.values().flatMap(r => Array.from(r.missingTxTracker.missingTxHashes)),
199
+ fastRequests.values().flatMap(r => Array.from(r.requestTracker.missingTxHashes)),
200
200
  );
201
201
 
202
202
  // Return all missing txs that are not in fastCollectionTxs and are ready for reqresp if requested
@@ -1,7 +1,7 @@
1
1
  import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { compactArray } from '@aztec/foundation/collection';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
- import { type PromiseWithResolvers, RunningPromise } from '@aztec/foundation/promise';
4
+ import { RunningPromise } from '@aztec/foundation/promise';
5
5
  import { sleep } from '@aztec/foundation/sleep';
6
6
  import { DateProvider } from '@aztec/foundation/timer';
7
7
  import type { L2Block, L2BlockInfo } from '@aztec/stdlib/block';
@@ -19,7 +19,7 @@ import type { TxCollectionConfig } from './config.js';
19
19
  import { FastTxCollection } from './fast_tx_collection.js';
20
20
  import { FileStoreTxCollection } from './file_store_tx_collection.js';
21
21
  import type { FileStoreTxSource } from './file_store_tx_source.js';
22
- import type { IMissingTxsTracker } from './missing_txs_tracker.js';
22
+ import type { IRequestTracker } from './request_tracker.js';
23
23
  import { SlowTxCollection, getProofDeadlineForSlot } from './slow_tx_collection.js';
24
24
  import { type TxAddContext, TxCollectionSink } from './tx_collection_sink.js';
25
25
  import type { TxSource } from './tx_source.js';
@@ -33,10 +33,8 @@ export type FastCollectionRequestInput =
33
33
  | { type: 'proposal'; blockProposal: BlockProposal; blockNumber: BlockNumber };
34
34
 
35
35
  export type FastCollectionRequest = FastCollectionRequestInput & {
36
- missingTxTracker: IMissingTxsTracker;
37
- deadline: Date;
36
+ requestTracker: IRequestTracker;
38
37
  blockInfo: L2BlockInfo;
39
- promise: PromiseWithResolvers<void>;
40
38
  };
41
39
 
42
40
  /**
@@ -2,7 +2,7 @@ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
2
2
  import { protocolContractsHash } from '@aztec/protocol-contracts';
3
3
  import type { ChainConfig } from '@aztec/stdlib/config';
4
4
  import { type AztecNode, createAztecNodeClient } from '@aztec/stdlib/interfaces/client';
5
- import type { Tx, TxHash } from '@aztec/stdlib/tx';
5
+ import type { Tx, TxHash, TxValidator } from '@aztec/stdlib/tx';
6
6
  import { type ComponentsVersions, getComponentsVersionsFromConfig } from '@aztec/stdlib/versioning';
7
7
  import { makeTracedFetch } from '@aztec/telemetry-client';
8
8
 
@@ -16,12 +16,13 @@ export interface TxSource {
16
16
  export class NodeRpcTxSource implements TxSource {
17
17
  constructor(
18
18
  private readonly client: Pick<AztecNode, 'getTxsByHash'>,
19
+ private readonly txValidator: TxValidator,
19
20
  private readonly info: string,
20
21
  ) {}
21
22
 
22
- public static fromUrl(nodeUrl: string, versions: ComponentsVersions): NodeRpcTxSource {
23
+ public static fromUrl(nodeUrl: string, txValidator: TxValidator, versions: ComponentsVersions): NodeRpcTxSource {
23
24
  const client = createAztecNodeClient(nodeUrl, versions, makeTracedFetch([1, 2, 3], false));
24
- return new NodeRpcTxSource(client, nodeUrl);
25
+ return new NodeRpcTxSource(client, txValidator, nodeUrl);
25
26
  }
26
27
 
27
28
  public getInfo() {
@@ -38,8 +39,8 @@ export class NodeRpcTxSource implements TxSource {
38
39
  const invalidTxHashes: string[] = [];
39
40
  await Promise.all(
40
41
  txs.map(async tx => {
41
- const isValid = await tx.validateTxHash();
42
- if (isValid) {
42
+ const validation = await this.txValidator.validateTx(tx);
43
+ if (validation.result === 'valid') {
43
44
  validTxs.push(tx);
44
45
  } else {
45
46
  invalidTxHashes.push(tx.getTxHash().toString());
@@ -50,7 +51,7 @@ export class NodeRpcTxSource implements TxSource {
50
51
  }
51
52
  }
52
53
 
53
- export function createNodeRpcTxSources(urls: string[], chainConfig: ChainConfig) {
54
+ export function createNodeRpcTxSources(urls: string[], txValidator: TxValidator, chainConfig: ChainConfig) {
54
55
  const versions = getComponentsVersionsFromConfig(chainConfig, protocolContractsHash, getVKTreeRoot());
55
- return urls.map(url => NodeRpcTxSource.fromUrl(url, versions));
56
+ return urls.map(url => NodeRpcTxSource.fromUrl(url, txValidator, versions));
56
57
  }
@@ -1,5 +1,6 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
2
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
+ import { sleep } from '@aztec/foundation/sleep';
3
4
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
4
5
  import type { L2BlockSource } from '@aztec/stdlib/block';
5
6
  import type { ContractDataSource } from '@aztec/stdlib/contract';
@@ -100,6 +101,7 @@ class MockReqResp implements ReqRespInterface {
100
101
  }
101
102
 
102
103
  updateConfig(_config: Partial<P2PReqRespConfig>): void {}
104
+ setShouldRejectPeer(): void {}
103
105
 
104
106
  start(
105
107
  subProtocolHandlers: Partial<ReqRespSubProtocolHandlers>,
@@ -138,6 +140,11 @@ class MockReqResp implements ReqRespInterface {
138
140
  const responses: InstanceType<SubProtocolMap[SubProtocol]['response']>[] = [];
139
141
  const peers = this.network.getReqRespPeers().filter(p => !p.peerId.equals(this.peerId));
140
142
  const targetPeers = pinnedPeer ? peers.filter(p => p.peerId.equals(pinnedPeer)) : peers;
143
+ const delayMs = this.network.getPropagationDelayMs();
144
+
145
+ if (delayMs > 0) {
146
+ await sleep(delayMs);
147
+ }
141
148
 
142
149
  for (const request of requests) {
143
150
  const requestBuffer = request.toBuffer();
@@ -174,7 +181,12 @@ class MockReqResp implements ReqRespInterface {
174
181
  return { status: ReqRespStatus.SUCCESS, data: Buffer.from([]) };
175
182
  }
176
183
  try {
184
+ const delayMs = this.network.getPropagationDelayMs();
185
+ if (delayMs > 0) {
186
+ await sleep(delayMs);
187
+ }
177
188
  const data = await handler(this.peerId, payload);
189
+
178
190
  return { status: ReqRespStatus.SUCCESS, data };
179
191
  } catch {
180
192
  return { status: ReqRespStatus.FAILURE };
@@ -242,10 +254,10 @@ class MockGossipSubService extends TypedEventEmitter<GossipsubEvents> implements
242
254
  score: (_peerId: PeerIdStr) => 0,
243
255
  };
244
256
 
245
- publish(topic: TopicStr, data: Uint8Array, _opts?: PublishOpts): Promise<PublishResult> {
257
+ async publish(topic: TopicStr, data: Uint8Array, _opts?: PublishOpts): Promise<PublishResult> {
246
258
  this.logger.debug(`Publishing message on topic ${topic}`, { topic, sender: this.peerId.toString() });
247
- this.network.publishToPeers(topic, data, this.peerId);
248
- return Promise.resolve({ recipients: this.network.getPeers().filter(peer => !this.peerId.equals(peer)) });
259
+ await this.network.publishToPeers(topic, data, this.peerId);
260
+ return { recipients: this.network.getPeers().filter(peer => !this.peerId.equals(peer)) };
249
261
  }
250
262
 
251
263
  receive(msg: GossipsubMessage) {
@@ -281,7 +293,8 @@ class MockGossipSubService extends TypedEventEmitter<GossipsubEvents> implements
281
293
 
282
294
  /**
283
295
  * Mock gossip sub network used for testing.
284
- * All instances of MockGossipSubService connected to the same network will instantly receive the same messages.
296
+ * All instances of MockGossipSubService connected to the same network receive the same messages,
297
+ * optionally delayed by a configurable propagation time.
285
298
  */
286
299
  export class MockGossipSubNetwork {
287
300
  private peers: MockGossipSubService[] = [];
@@ -290,6 +303,15 @@ export class MockGossipSubNetwork {
290
303
 
291
304
  private logger = createLogger('p2p:test:mock-gossipsub-network');
292
305
 
306
+ constructor(
307
+ /** Artificial propagation delay in milliseconds applied to each message delivery. */
308
+ private propagationDelayMs: number = 0,
309
+ ) {}
310
+
311
+ public getPropagationDelayMs(): number {
312
+ return this.propagationDelayMs;
313
+ }
314
+
293
315
  public getPeers(): PeerId[] {
294
316
  return this.peers.map(peer => peer.peerId);
295
317
  }
@@ -306,7 +328,7 @@ export class MockGossipSubNetwork {
306
328
  return this.reqRespPeers;
307
329
  }
308
330
 
309
- public publishToPeers(topic: TopicStr, data: Uint8Array, sender: PeerId): void {
331
+ public async publishToPeers(topic: TopicStr, data: Uint8Array, sender: PeerId): Promise<void> {
310
332
  const msgId = (this.nextMsgId++).toString();
311
333
  this.logger.debug(`Network is distributing message on topic ${topic}`, {
312
334
  topic,
@@ -315,6 +337,10 @@ export class MockGossipSubNetwork {
315
337
  msgId,
316
338
  });
317
339
 
340
+ if (this.propagationDelayMs > 0) {
341
+ await sleep(this.propagationDelayMs);
342
+ }
343
+
318
344
  const gossipSubMsg: GossipsubMessage = { msgId, msg: { type: 'unsigned', topic, data }, propagationSource: sender };
319
345
  for (const peer of this.peers) {
320
346
  if (peer.subscribedTopics.has(topic)) {
@@ -153,6 +153,8 @@ export async function createTestLibP2PService(
153
153
  epochCache,
154
154
  );
155
155
 
156
+ reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
157
+
156
158
  p2pNode.services.pubsub.score.params.appSpecificWeight = APP_SPECIFIC_WEIGHT;
157
159
  p2pNode.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
158
160
  peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
@@ -187,7 +189,6 @@ export const MOCK_SUB_PROTOCOL_HANDLERS: ReqRespSubProtocolHandlers = {
187
189
  [ReqRespSubProtocol.STATUS]: (_msg: any) => Promise.resolve(Buffer.from('status')),
188
190
  [ReqRespSubProtocol.TX]: (_msg: any) => Promise.resolve(Buffer.from('tx')),
189
191
  [ReqRespSubProtocol.GOODBYE]: (_msg: any) => Promise.resolve(Buffer.from('goodbye')),
190
- [ReqRespSubProtocol.BLOCK]: (_msg: any) => Promise.resolve(Buffer.from('block')),
191
192
  [ReqRespSubProtocol.AUTH]: (_msg: any) => Promise.resolve(Buffer.from('auth')),
192
193
  [ReqRespSubProtocol.BLOCK_TXS]: (_msg: any) => Promise.resolve(Buffer.from('block_txs')),
193
194
  };
@@ -199,7 +200,6 @@ export const MOCK_SUB_PROTOCOL_VALIDATORS: ReqRespSubProtocolValidators = {
199
200
  [ReqRespSubProtocol.STATUS]: noopValidator,
200
201
  [ReqRespSubProtocol.TX]: noopValidator,
201
202
  [ReqRespSubProtocol.GOODBYE]: noopValidator,
202
- [ReqRespSubProtocol.BLOCK]: noopValidator,
203
203
  [ReqRespSubProtocol.AUTH]: noopValidator,
204
204
  [ReqRespSubProtocol.BLOCK_TXS]: noopValidator,
205
205
  };
@@ -273,17 +273,42 @@ export class InMemoryAttestationPool {
273
273
  * Creates a mock EpochCache for testing.
274
274
  */
275
275
  export function createMockEpochCache(): EpochCacheInterface {
276
- return {
276
+ const cache: EpochCacheInterface = {
277
277
  getCommittee: () => Promise.resolve({ committee: [], seed: 1n, epoch: EpochNumber.ZERO, isEscapeHatchOpen: false }),
278
278
  getProposerIndexEncoding: () => '0x' as `0x${string}`,
279
- getEpochAndSlotNow: () => ({ epoch: EpochNumber.ZERO, slot: SlotNumber.ZERO, ts: 0n, nowMs: 0n }),
279
+ getSlotNow: () => SlotNumber.ZERO,
280
+ getTargetSlot: () => SlotNumber.ZERO,
281
+ getEpochNow: () => EpochNumber.ZERO,
282
+ getTargetEpoch: () => EpochNumber.ZERO,
283
+ getEpochAndSlotNow: () => ({
284
+ epoch: EpochNumber.ZERO,
285
+ slot: SlotNumber.ZERO,
286
+ ts: 0n,
287
+ nowMs: 0n,
288
+ }),
289
+ isProposerPipeliningEnabled: () => false,
290
+ pipeliningOffset: () => 0,
280
291
  computeProposerIndex: () => 0n,
281
292
  getCurrentAndNextSlot: () => ({ currentSlot: SlotNumber.ZERO, nextSlot: SlotNumber.ZERO }),
293
+ getTargetAndNextSlot: () => ({ targetSlot: SlotNumber.ZERO, nextSlot: SlotNumber.ZERO }),
282
294
  getProposerAttesterAddressInSlot: () => Promise.resolve(undefined),
283
- getEpochAndSlotInNextL1Slot: () => ({ epoch: EpochNumber.ZERO, slot: SlotNumber.ZERO, ts: 0n, now: 0n }),
295
+ getEpochAndSlotInNextL1Slot: () => ({
296
+ epoch: EpochNumber.ZERO,
297
+ slot: SlotNumber.ZERO,
298
+ ts: 0n,
299
+ nowSeconds: 0n,
300
+ }),
301
+ getTargetEpochAndSlotInNextL1Slot: () => ({
302
+ epoch: EpochNumber.ZERO,
303
+ slot: SlotNumber.ZERO,
304
+ ts: 0n,
305
+ nowSeconds: 0n,
306
+ }),
284
307
  isInCommittee: () => Promise.resolve(false),
285
308
  getRegisteredValidators: () => Promise.resolve([]),
286
309
  filterInCommittee: () => Promise.resolve([]),
310
+ isEscapeHatchOpen: () => Promise.resolve(false),
311
+ isEscapeHatchOpenAtSlot: () => Promise.resolve(false),
287
312
  getL1Constants: () => ({
288
313
  l1StartBlock: 0n,
289
314
  l1GenesisTime: 0n,
@@ -292,8 +317,10 @@ export function createMockEpochCache(): EpochCacheInterface {
292
317
  ethereumSlotDuration: 1,
293
318
  proofSubmissionEpochs: 1,
294
319
  targetCommitteeSize: 48,
320
+ rollupManaLimit: Number.MAX_SAFE_INTEGER,
295
321
  }),
296
322
  };
323
+ return cache;
297
324
  }
298
325
 
299
326
  /**
@@ -40,7 +40,7 @@ import type { IBatchRequestTxValidator } from '../services/reqresp/batch-tx-requ
40
40
  import { RateLimitStatus } from '../services/reqresp/rate-limiter/rate_limiter.js';
41
41
  import type { ReqResp } from '../services/reqresp/reqresp.js';
42
42
  import type { PeerDiscoveryService } from '../services/service.js';
43
- import { MissingTxsTracker } from '../services/tx_collection/missing_txs_tracker.js';
43
+ import { RequestTracker } from '../services/tx_collection/request_tracker.js';
44
44
  import { AlwaysTrueCircuitVerifier } from '../test-helpers/index.js';
45
45
  import {
46
46
  BENCHMARK_CONSTANTS,
@@ -204,6 +204,25 @@ function installUnlimitedRateLimits(client: P2PClient): void {
204
204
  rateLimiter.allow = () => RateLimitStatus.Allowed;
205
205
  }
206
206
 
207
+ /** Resets peer scores to prevent cross-case contamination in benchmarks. */
208
+ function resetPeerScores(client: P2PClient): void {
209
+ const peerManager = (client as any).p2pService.peerManager;
210
+ const peerScoring = peerManager?.peerScoring;
211
+ if (peerScoring?.resetAllScores) {
212
+ peerScoring.resetAllScores();
213
+ }
214
+ }
215
+
216
+ /** Returns the number of connected peers for connectivity checks. */
217
+ function getConnectedPeerCount(client: P2PClient): number {
218
+ const p2pService = (client as any).p2pService;
219
+ const connectionSampler = p2pService?.reqresp?.getConnectionSampler?.();
220
+ if (connectionSampler?.getPeerListSortedByConnectionCountAsc) {
221
+ return connectionSampler.getPeerListSortedByConnectionCountAsc().length;
222
+ }
223
+ return 0;
224
+ }
225
+
207
226
  async function runAggregatorBenchmark(
208
227
  client: P2PClient,
209
228
  blockProposal: BlockProposal,
@@ -273,10 +292,9 @@ async function runAggregatorBenchmark(
273
292
  noopTxValidator,
274
293
  );
275
294
  const fetchedTxs = await collector.collectTxs(
276
- MissingTxsTracker.fromArray(txHashes),
295
+ RequestTracker.create(txHashes, new Date(Date.now() + timeoutMs)),
277
296
  blockProposal,
278
297
  pinnedPeer,
279
- timeoutMs,
280
298
  );
281
299
  const durationMs = timer.ms();
282
300
  return {
@@ -293,10 +311,9 @@ async function runAggregatorBenchmark(
293
311
  BENCHMARK_CONSTANTS.FIXED_MAX_RETRY_ATTEMPTS,
294
312
  );
295
313
  const fetchedTxs = await collector.collectTxs(
296
- MissingTxsTracker.fromArray(txHashes),
314
+ RequestTracker.create(txHashes, new Date(Date.now() + timeoutMs)),
297
315
  blockProposal,
298
316
  pinnedPeer,
299
- timeoutMs,
300
317
  );
301
318
  const durationMs = timer.ms();
302
319
  return {
@@ -323,6 +340,37 @@ let workerConfig: P2PConfig | null = null;
323
340
  let workerLogger: Logger | null = null;
324
341
  let kvStore: Awaited<ReturnType<typeof openTmpStore>> | null = null;
325
342
 
343
+ async function stopWorker() {
344
+ try {
345
+ if (workerClient) {
346
+ await workerClient.stop();
347
+ workerClient = null;
348
+ }
349
+ } catch (e) {
350
+ workerLogger?.error('Error stopping worker client', e);
351
+ }
352
+ try {
353
+ if (kvStore?.close) {
354
+ await kvStore.close();
355
+ kvStore = null;
356
+ }
357
+ } catch (e) {
358
+ workerLogger?.error('Error closing kv store', e);
359
+ }
360
+ }
361
+
362
+ function gracefulExit(code: number = 0) {
363
+ try {
364
+ if (process.connected) {
365
+ process.disconnect();
366
+ }
367
+ } catch {
368
+ // IPC channel already closed
369
+ }
370
+ // Safety fallback if lingering handles prevent the event loop from draining
371
+ setTimeout(() => process.exit(code), 5000).unref();
372
+ }
373
+
326
374
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
327
375
  process.on('message', async msg => {
328
376
  const {
@@ -412,13 +460,8 @@ process.on('message', async msg => {
412
460
  const cmd = msg as any;
413
461
  switch (cmd.type) {
414
462
  case 'STOP':
415
- if (workerClient) {
416
- await workerClient.stop();
417
- }
418
- if (kvStore?.close) {
419
- await kvStore.close();
420
- }
421
- process.exit(0);
463
+ await stopWorker();
464
+ gracefulExit(0);
422
465
  break;
423
466
 
424
467
  case 'SEND_TX':
@@ -428,6 +471,13 @@ process.on('message', async msg => {
428
471
  }
429
472
  break;
430
473
 
474
+ case 'GET_PEER_COUNT':
475
+ process.send!({
476
+ type: 'PEER_COUNT',
477
+ count: workerClient ? getConnectedPeerCount(workerClient) : 0,
478
+ });
479
+ break;
480
+
431
481
  case 'BENCH_REQRESP': {
432
482
  const benchCmd = cmd as BenchReqRespCommand;
433
483
  if (!workerClient || !workerTxPool || !workerAttestationPool || !workerConfig || !workerLogger) {
@@ -444,6 +494,7 @@ process.on('message', async msg => {
444
494
  // Reset state before each benchmark run to avoid cross-run contamination
445
495
  workerTxPool.resetState();
446
496
  workerAttestationPool.resetState();
497
+ resetPeerScores(workerClient);
447
498
 
448
499
  installUnlimitedRateLimits(workerClient);
449
500
 
@@ -495,7 +546,12 @@ process.on('message', async msg => {
495
546
  }
496
547
  }
497
548
  } catch (err: any) {
498
- process.send!({ type: 'ERROR', error: err.message });
499
- process.exit(1);
549
+ try {
550
+ process.send!({ type: 'ERROR', error: err.message });
551
+ } catch {
552
+ // IPC channel may be closed
553
+ }
554
+ await stopWorker();
555
+ gracefulExit(1);
500
556
  }
501
557
  });
@@ -72,7 +72,6 @@ class WorkerClientManager {
72
72
  destroy() {
73
73
  this.cleanup().catch((error: Error) => {
74
74
  this.logger.error('Failed to cleanup worker client manager', error);
75
- process.exit(1);
76
75
  });
77
76
  }
78
77
 
@@ -425,6 +424,61 @@ class WorkerClientManager {
425
424
  this.logger.info('All worker processes cleaned up');
426
425
  }
427
426
 
427
+ /**
428
+ * Checks that the aggregator (client 0) has sufficient peer connections before running a benchmark.
429
+ * This prevents benchmark cases from starting with degraded connectivity after a previous case
430
+ * caused connection failures.
431
+ */
432
+ async waitForConnectivity(minPeers: number, timeoutMs: number = 15_000): Promise<number> {
433
+ const waitInterval = 1000;
434
+ let waited = 0;
435
+
436
+ while (waited < timeoutMs) {
437
+ const count = await this.getPeerCount(0, 5000);
438
+ if (count >= minPeers) {
439
+ this.logger.info(`Connectivity check passed: ${count}/${minPeers} peers connected`);
440
+ return count;
441
+ }
442
+ this.logger.debug(`Waiting for connectivity: ${count}/${minPeers} (waited ${waited}ms)`);
443
+ await sleep(waitInterval);
444
+ waited += waitInterval;
445
+ }
446
+
447
+ const finalCount = await this.getPeerCount(0, 5000);
448
+ this.logger.warn(`Connectivity check: only ${finalCount}/${minPeers} peers after ${timeoutMs}ms`);
449
+ return finalCount;
450
+ }
451
+
452
+ private getPeerCount(clientIndex: number, timeoutMs: number): Promise<number> {
453
+ return new Promise<number>(resolve => {
454
+ let resolved = false;
455
+
456
+ const handler = (msg: any) => {
457
+ if (resolved) {
458
+ return;
459
+ }
460
+ if (msg.type === 'PEER_COUNT') {
461
+ resolved = true;
462
+ clearTimeout(timeout);
463
+ this.processes[clientIndex].off('message', handler);
464
+ resolve(msg.count as number);
465
+ }
466
+ };
467
+
468
+ const timeout = setTimeout(() => {
469
+ if (resolved) {
470
+ return;
471
+ }
472
+ resolved = true;
473
+ this.processes[clientIndex].off('message', handler);
474
+ resolve(0);
475
+ }, timeoutMs);
476
+
477
+ this.processes[clientIndex].on('message', handler);
478
+ this.processes[clientIndex].send({ type: 'GET_PEER_COUNT' });
479
+ });
480
+ }
481
+
428
482
  /**
429
483
  * Run a req/resp benchmark across all worker clients.
430
484
  *