@aztec/p2p 0.0.0-test.1 → 0.0.1-commit.5476d83

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 (385) hide show
  1. package/dest/bootstrap/bootstrap.d.ts +1 -1
  2. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  3. package/dest/bootstrap/bootstrap.js +22 -9
  4. package/dest/client/factory.d.ts +14 -4
  5. package/dest/client/factory.d.ts.map +1 -1
  6. package/dest/client/factory.js +60 -24
  7. package/dest/client/index.d.ts +2 -1
  8. package/dest/client/index.d.ts.map +1 -1
  9. package/dest/client/index.js +1 -0
  10. package/dest/client/interface.d.ts +157 -0
  11. package/dest/client/interface.d.ts.map +1 -0
  12. package/dest/client/interface.js +9 -0
  13. package/dest/client/p2p_client.d.ts +72 -187
  14. package/dest/client/p2p_client.d.ts.map +1 -1
  15. package/dest/client/p2p_client.js +373 -177
  16. package/dest/config.d.ts +151 -125
  17. package/dest/config.d.ts.map +1 -1
  18. package/dest/config.js +183 -34
  19. package/dest/enr/generate-enr.d.ts +11 -3
  20. package/dest/enr/generate-enr.d.ts.map +1 -1
  21. package/dest/enr/generate-enr.js +27 -5
  22. package/dest/enr/index.d.ts +1 -1
  23. package/dest/errors/attestation-pool.error.d.ts +7 -0
  24. package/dest/errors/attestation-pool.error.d.ts.map +1 -0
  25. package/dest/errors/attestation-pool.error.js +12 -0
  26. package/dest/errors/reqresp.error.d.ts +1 -1
  27. package/dest/errors/reqresp.error.d.ts.map +1 -1
  28. package/dest/index.d.ts +4 -1
  29. package/dest/index.d.ts.map +1 -1
  30. package/dest/index.js +2 -0
  31. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +68 -8
  32. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  33. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  34. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  35. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +214 -63
  36. package/dest/mem_pools/attestation_pool/index.d.ts +1 -1
  37. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +21 -6
  38. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  39. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +126 -25
  40. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +19 -6
  41. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  42. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +111 -21
  43. package/dest/mem_pools/attestation_pool/mocks.d.ts +225 -5
  44. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  45. package/dest/mem_pools/attestation_pool/mocks.js +9 -15
  46. package/dest/mem_pools/index.d.ts +1 -1
  47. package/dest/mem_pools/instrumentation.d.ts +10 -12
  48. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  49. package/dest/mem_pools/instrumentation.js +35 -38
  50. package/dest/mem_pools/interface.d.ts +1 -1
  51. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +62 -13
  52. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  53. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +469 -97
  54. package/dest/mem_pools/tx_pool/index.d.ts +1 -1
  55. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +34 -10
  56. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  57. package/dest/mem_pools/tx_pool/memory_tx_pool.js +133 -36
  58. package/dest/mem_pools/tx_pool/priority.d.ts +1 -1
  59. package/dest/mem_pools/tx_pool/priority.js +1 -1
  60. package/dest/mem_pools/tx_pool/tx_pool.d.ts +65 -9
  61. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  62. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
  63. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  64. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +264 -39
  65. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +4 -2
  66. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  67. package/dest/msg_validators/attestation_validator/attestation_validator.js +45 -9
  68. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +20 -0
  69. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -0
  70. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +67 -0
  71. package/dest/msg_validators/attestation_validator/index.d.ts +2 -1
  72. package/dest/msg_validators/attestation_validator/index.d.ts.map +1 -1
  73. package/dest/msg_validators/attestation_validator/index.js +1 -0
  74. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +6 -2
  75. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
  76. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +73 -12
  77. package/dest/msg_validators/block_proposal_validator/index.d.ts +1 -1
  78. package/dest/msg_validators/index.d.ts +1 -1
  79. package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts +10 -0
  80. package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts.map +1 -0
  81. package/dest/msg_validators/msg_seen_validator/msg_seen_validator.js +36 -0
  82. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
  83. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  84. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +3 -0
  85. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -0
  86. package/dest/msg_validators/tx_validator/allowed_public_setup.js +27 -0
  87. package/dest/msg_validators/tx_validator/archive_cache.d.ts +14 -0
  88. package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -0
  89. package/dest/msg_validators/tx_validator/archive_cache.js +22 -0
  90. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +1 -1
  91. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  92. package/dest/msg_validators/tx_validator/block_header_validator.js +4 -4
  93. package/dest/msg_validators/tx_validator/data_validator.d.ts +1 -1
  94. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  95. package/dest/msg_validators/tx_validator/data_validator.js +56 -86
  96. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +1 -3
  97. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  98. package/dest/msg_validators/tx_validator/double_spend_validator.js +21 -27
  99. package/dest/msg_validators/tx_validator/factory.d.ts +15 -0
  100. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -0
  101. package/dest/msg_validators/tx_validator/factory.js +74 -0
  102. package/dest/msg_validators/tx_validator/gas_validator.d.ts +11 -0
  103. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -0
  104. package/dest/msg_validators/tx_validator/gas_validator.js +115 -0
  105. package/dest/msg_validators/tx_validator/index.d.ts +8 -1
  106. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  107. package/dest/msg_validators/tx_validator/index.js +7 -0
  108. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +8 -4
  109. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  110. package/dest/msg_validators/tx_validator/metadata_validator.js +39 -20
  111. package/dest/msg_validators/tx_validator/phases_validator.d.ts +14 -0
  112. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -0
  113. package/dest/msg_validators/tx_validator/phases_validator.js +93 -0
  114. package/dest/msg_validators/tx_validator/test_utils.d.ts +17 -0
  115. package/dest/msg_validators/tx_validator/test_utils.d.ts.map +1 -0
  116. package/dest/msg_validators/tx_validator/test_utils.js +22 -0
  117. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +12 -0
  118. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -0
  119. package/dest/msg_validators/tx_validator/timestamp_validator.js +32 -0
  120. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts +8 -0
  121. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -0
  122. package/dest/msg_validators/tx_validator/tx_permitted_validator.js +24 -0
  123. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +1 -1
  124. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  125. package/dest/msg_validators/tx_validator/tx_proof_validator.js +6 -5
  126. package/dest/services/data_store.d.ts +1 -1
  127. package/dest/services/data_store.d.ts.map +1 -1
  128. package/dest/services/discv5/discV5_service.d.ts +10 -9
  129. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  130. package/dest/services/discv5/discV5_service.js +63 -36
  131. package/dest/services/dummy_service.d.ts +50 -11
  132. package/dest/services/dummy_service.d.ts.map +1 -1
  133. package/dest/services/dummy_service.js +88 -5
  134. package/dest/services/encoding.d.ts +26 -7
  135. package/dest/services/encoding.d.ts.map +1 -1
  136. package/dest/services/encoding.js +73 -5
  137. package/dest/services/gossipsub/scoring.d.ts +1 -1
  138. package/dest/services/index.d.ts +5 -1
  139. package/dest/services/index.d.ts.map +1 -1
  140. package/dest/services/index.js +4 -0
  141. package/dest/services/libp2p/instrumentation.d.ts +20 -0
  142. package/dest/services/libp2p/instrumentation.d.ts.map +1 -0
  143. package/dest/services/libp2p/instrumentation.js +164 -0
  144. package/dest/services/libp2p/libp2p_service.d.ts +78 -89
  145. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  146. package/dest/services/libp2p/libp2p_service.js +698 -246
  147. package/dest/services/peer-manager/interface.d.ts +23 -0
  148. package/dest/services/peer-manager/interface.d.ts.map +1 -0
  149. package/dest/services/peer-manager/interface.js +1 -0
  150. package/dest/services/peer-manager/metrics.d.ts +6 -2
  151. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  152. package/dest/services/peer-manager/metrics.js +22 -2
  153. package/dest/services/peer-manager/peer_manager.d.ts +102 -22
  154. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  155. package/dest/services/peer-manager/peer_manager.js +549 -72
  156. package/dest/services/peer-manager/peer_scoring.d.ts +7 -2
  157. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  158. package/dest/services/peer-manager/peer_scoring.js +40 -2
  159. package/dest/services/reqresp/config.d.ts +11 -9
  160. package/dest/services/reqresp/config.d.ts.map +1 -1
  161. package/dest/services/reqresp/config.js +18 -4
  162. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +2 -2
  163. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
  164. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +10 -6
  165. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +31 -17
  166. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  167. package/dest/services/reqresp/connection-sampler/connection_sampler.js +142 -84
  168. package/dest/services/reqresp/index.d.ts +3 -2
  169. package/dest/services/reqresp/index.d.ts.map +1 -1
  170. package/dest/services/reqresp/index.js +2 -1
  171. package/dest/services/reqresp/interface.d.ts +73 -24
  172. package/dest/services/reqresp/interface.d.ts.map +1 -1
  173. package/dest/services/reqresp/interface.js +45 -26
  174. package/dest/services/reqresp/metrics.d.ts +1 -1
  175. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  176. package/dest/services/reqresp/protocols/auth.d.ts +43 -0
  177. package/dest/services/reqresp/protocols/auth.d.ts.map +1 -0
  178. package/dest/services/reqresp/protocols/auth.js +71 -0
  179. package/dest/services/reqresp/protocols/block.d.ts +6 -1
  180. package/dest/services/reqresp/protocols/block.d.ts.map +1 -1
  181. package/dest/services/reqresp/protocols/block.js +28 -5
  182. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +30 -0
  183. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -0
  184. package/dest/services/reqresp/protocols/block_txs/bitvector.js +75 -0
  185. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +11 -0
  186. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -0
  187. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +39 -0
  188. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +47 -0
  189. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -0
  190. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +75 -0
  191. package/dest/services/reqresp/protocols/block_txs/index.d.ts +4 -0
  192. package/dest/services/reqresp/protocols/block_txs/index.d.ts.map +1 -0
  193. package/dest/services/reqresp/protocols/block_txs/index.js +3 -0
  194. package/dest/services/reqresp/protocols/goodbye.d.ts +3 -5
  195. package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
  196. package/dest/services/reqresp/protocols/goodbye.js +7 -7
  197. package/dest/services/reqresp/protocols/index.d.ts +3 -1
  198. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  199. package/dest/services/reqresp/protocols/index.js +2 -0
  200. package/dest/services/reqresp/protocols/ping.d.ts +1 -3
  201. package/dest/services/reqresp/protocols/ping.d.ts.map +1 -1
  202. package/dest/services/reqresp/protocols/status.d.ts +39 -7
  203. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  204. package/dest/services/reqresp/protocols/status.js +72 -5
  205. package/dest/services/reqresp/protocols/tx.d.ts +13 -2
  206. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  207. package/dest/services/reqresp/protocols/tx.js +34 -6
  208. package/dest/services/reqresp/rate-limiter/index.d.ts +1 -1
  209. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +6 -4
  210. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  211. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -2
  212. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  213. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  214. package/dest/services/reqresp/rate-limiter/rate_limits.js +21 -1
  215. package/dest/services/reqresp/reqresp.d.ts +24 -66
  216. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  217. package/dest/services/reqresp/reqresp.js +298 -207
  218. package/dest/services/reqresp/status.d.ts +10 -4
  219. package/dest/services/reqresp/status.d.ts.map +1 -1
  220. package/dest/services/reqresp/status.js +9 -2
  221. package/dest/services/service.d.ts +23 -19
  222. package/dest/services/service.d.ts.map +1 -1
  223. package/dest/services/tx_collection/config.d.ts +25 -0
  224. package/dest/services/tx_collection/config.d.ts.map +1 -0
  225. package/dest/services/tx_collection/config.js +58 -0
  226. package/dest/services/tx_collection/fast_tx_collection.d.ts +50 -0
  227. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -0
  228. package/dest/services/tx_collection/fast_tx_collection.js +300 -0
  229. package/dest/services/tx_collection/index.d.ts +3 -0
  230. package/dest/services/tx_collection/index.d.ts.map +1 -0
  231. package/dest/services/tx_collection/index.js +2 -0
  232. package/dest/services/tx_collection/instrumentation.d.ts +10 -0
  233. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -0
  234. package/dest/services/tx_collection/instrumentation.js +34 -0
  235. package/dest/services/tx_collection/slow_tx_collection.d.ts +52 -0
  236. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -0
  237. package/dest/services/tx_collection/slow_tx_collection.js +177 -0
  238. package/dest/services/tx_collection/tx_collection.d.ts +109 -0
  239. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -0
  240. package/dest/services/tx_collection/tx_collection.js +128 -0
  241. package/dest/services/tx_collection/tx_collection_sink.d.ts +30 -0
  242. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -0
  243. package/dest/services/tx_collection/tx_collection_sink.js +111 -0
  244. package/dest/services/tx_collection/tx_source.d.ts +18 -0
  245. package/dest/services/tx_collection/tx_source.d.ts.map +1 -0
  246. package/dest/services/tx_collection/tx_source.js +31 -0
  247. package/dest/services/tx_provider.d.ts +49 -0
  248. package/dest/services/tx_provider.d.ts.map +1 -0
  249. package/dest/services/tx_provider.js +210 -0
  250. package/dest/services/tx_provider_instrumentation.d.ts +13 -0
  251. package/dest/services/tx_provider_instrumentation.d.ts.map +1 -0
  252. package/dest/services/tx_provider_instrumentation.js +34 -0
  253. package/dest/test-helpers/generate-peer-id-private-keys.d.ts +1 -1
  254. package/dest/test-helpers/get-ports.d.ts +1 -1
  255. package/dest/test-helpers/get-ports.d.ts.map +1 -1
  256. package/dest/test-helpers/index.d.ts +2 -1
  257. package/dest/test-helpers/index.d.ts.map +1 -1
  258. package/dest/test-helpers/index.js +1 -0
  259. package/dest/test-helpers/make-enrs.d.ts +1 -1
  260. package/dest/test-helpers/make-enrs.d.ts.map +1 -1
  261. package/dest/test-helpers/make-enrs.js +4 -5
  262. package/dest/test-helpers/make-test-p2p-clients.d.ts +33 -5
  263. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  264. package/dest/test-helpers/make-test-p2p-clients.js +86 -16
  265. package/dest/test-helpers/mock-pubsub.d.ts +59 -0
  266. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -0
  267. package/dest/test-helpers/mock-pubsub.js +130 -0
  268. package/dest/test-helpers/mock-tx-helpers.d.ts +12 -0
  269. package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -0
  270. package/dest/test-helpers/mock-tx-helpers.js +19 -0
  271. package/dest/test-helpers/reqresp-nodes.d.ts +15 -11
  272. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  273. package/dest/test-helpers/reqresp-nodes.js +62 -28
  274. package/dest/testbench/p2p_client_testbench_worker.d.ts +1 -1
  275. package/dest/testbench/p2p_client_testbench_worker.js +103 -29
  276. package/dest/testbench/parse_log_file.d.ts +1 -1
  277. package/dest/testbench/parse_log_file.js +4 -4
  278. package/dest/testbench/testbench.d.ts +1 -1
  279. package/dest/testbench/testbench.js +4 -4
  280. package/dest/testbench/worker_client_manager.d.ts +1 -6
  281. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  282. package/dest/testbench/worker_client_manager.js +11 -19
  283. package/dest/types/index.d.ts +4 -2
  284. package/dest/types/index.d.ts.map +1 -1
  285. package/dest/types/index.js +2 -0
  286. package/dest/util.d.ts +24 -16
  287. package/dest/util.d.ts.map +1 -1
  288. package/dest/util.js +75 -69
  289. package/dest/versioning.d.ts +4 -4
  290. package/dest/versioning.d.ts.map +1 -1
  291. package/dest/versioning.js +8 -3
  292. package/package.json +32 -27
  293. package/src/bootstrap/bootstrap.ts +27 -11
  294. package/src/client/factory.ts +136 -45
  295. package/src/client/index.ts +1 -0
  296. package/src/client/interface.ts +198 -0
  297. package/src/client/p2p_client.ts +469 -330
  298. package/src/config.ts +305 -134
  299. package/src/enr/generate-enr.ts +39 -6
  300. package/src/errors/attestation-pool.error.ts +13 -0
  301. package/src/index.ts +4 -0
  302. package/src/mem_pools/attestation_pool/attestation_pool.ts +75 -7
  303. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +264 -65
  304. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +173 -34
  305. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +156 -30
  306. package/src/mem_pools/attestation_pool/mocks.ts +11 -10
  307. package/src/mem_pools/instrumentation.ts +43 -44
  308. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +549 -108
  309. package/src/mem_pools/tx_pool/memory_tx_pool.ts +153 -44
  310. package/src/mem_pools/tx_pool/priority.ts +1 -1
  311. package/src/mem_pools/tx_pool/tx_pool.ts +67 -8
  312. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +217 -34
  313. package/src/msg_validators/attestation_validator/attestation_validator.ts +54 -11
  314. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +91 -0
  315. package/src/msg_validators/attestation_validator/index.ts +1 -0
  316. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +82 -14
  317. package/src/msg_validators/msg_seen_validator/msg_seen_validator.ts +36 -0
  318. package/src/msg_validators/tx_validator/allowed_public_setup.ts +35 -0
  319. package/src/msg_validators/tx_validator/archive_cache.ts +28 -0
  320. package/src/msg_validators/tx_validator/block_header_validator.ts +4 -4
  321. package/src/msg_validators/tx_validator/data_validator.ts +81 -69
  322. package/src/msg_validators/tx_validator/double_spend_validator.ts +19 -17
  323. package/src/msg_validators/tx_validator/factory.ts +109 -0
  324. package/src/msg_validators/tx_validator/gas_validator.ts +134 -0
  325. package/src/msg_validators/tx_validator/index.ts +7 -0
  326. package/src/msg_validators/tx_validator/metadata_validator.ts +58 -21
  327. package/src/msg_validators/tx_validator/phases_validator.ts +116 -0
  328. package/src/msg_validators/tx_validator/test_utils.ts +43 -0
  329. package/src/msg_validators/tx_validator/timestamp_validator.ts +46 -0
  330. package/src/msg_validators/tx_validator/tx_permitted_validator.ts +17 -0
  331. package/src/msg_validators/tx_validator/tx_proof_validator.ts +6 -5
  332. package/src/services/discv5/discV5_service.ts +84 -38
  333. package/src/services/dummy_service.ts +147 -9
  334. package/src/services/encoding.ts +80 -5
  335. package/src/services/index.ts +4 -0
  336. package/src/services/libp2p/instrumentation.ts +167 -0
  337. package/src/services/libp2p/libp2p_service.ts +866 -294
  338. package/src/services/peer-manager/interface.ts +29 -0
  339. package/src/services/peer-manager/metrics.ts +26 -1
  340. package/src/services/peer-manager/peer_manager.ts +654 -78
  341. package/src/services/peer-manager/peer_scoring.ts +46 -3
  342. package/src/services/reqresp/config.ts +26 -9
  343. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +12 -6
  344. package/src/services/reqresp/connection-sampler/connection_sampler.ts +148 -95
  345. package/src/services/reqresp/index.ts +2 -0
  346. package/src/services/reqresp/interface.ts +91 -36
  347. package/src/services/reqresp/metrics.ts +4 -1
  348. package/src/services/reqresp/protocols/auth.ts +83 -0
  349. package/src/services/reqresp/protocols/block.ts +24 -3
  350. package/src/services/reqresp/protocols/block_txs/bitvector.ts +90 -0
  351. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +53 -0
  352. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +79 -0
  353. package/src/services/reqresp/protocols/block_txs/index.ts +3 -0
  354. package/src/services/reqresp/protocols/goodbye.ts +9 -7
  355. package/src/services/reqresp/protocols/index.ts +2 -0
  356. package/src/services/reqresp/protocols/status.ts +117 -5
  357. package/src/services/reqresp/protocols/tx.ts +35 -6
  358. package/src/services/reqresp/rate-limiter/rate_limiter.ts +12 -3
  359. package/src/services/reqresp/rate-limiter/rate_limits.ts +21 -1
  360. package/src/services/reqresp/reqresp.ts +387 -256
  361. package/src/services/reqresp/status.ts +12 -3
  362. package/src/services/service.ts +45 -21
  363. package/src/services/tx_collection/config.ts +84 -0
  364. package/src/services/tx_collection/fast_tx_collection.ts +340 -0
  365. package/src/services/tx_collection/index.ts +2 -0
  366. package/src/services/tx_collection/instrumentation.ts +43 -0
  367. package/src/services/tx_collection/slow_tx_collection.ts +233 -0
  368. package/src/services/tx_collection/tx_collection.ts +215 -0
  369. package/src/services/tx_collection/tx_collection_sink.ts +129 -0
  370. package/src/services/tx_collection/tx_source.ts +37 -0
  371. package/src/services/tx_provider.ts +216 -0
  372. package/src/services/tx_provider_instrumentation.ts +44 -0
  373. package/src/test-helpers/index.ts +1 -0
  374. package/src/test-helpers/make-enrs.ts +4 -5
  375. package/src/test-helpers/make-test-p2p-clients.ts +111 -21
  376. package/src/test-helpers/mock-pubsub.ts +188 -0
  377. package/src/test-helpers/mock-tx-helpers.ts +24 -0
  378. package/src/test-helpers/reqresp-nodes.ts +86 -35
  379. package/src/testbench/p2p_client_testbench_worker.ts +151 -25
  380. package/src/testbench/parse_log_file.ts +4 -4
  381. package/src/testbench/testbench.ts +4 -4
  382. package/src/testbench/worker_client_manager.ts +17 -23
  383. package/src/types/index.ts +2 -0
  384. package/src/util.ts +105 -91
  385. package/src/versioning.ts +11 -4
@@ -1,26 +1,35 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
+ import { SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { randomInt } from '@aztec/foundation/crypto';
2
4
  import { Fr } from '@aztec/foundation/fields';
3
- import { createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
4
- import { SerialQueue } from '@aztec/foundation/queue';
5
+ import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
5
6
  import { RunningPromise } from '@aztec/foundation/running-promise';
7
+ import { Timer } from '@aztec/foundation/timer';
6
8
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
7
- import type { L2BlockSource } from '@aztec/stdlib/block';
9
+ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
10
+ import { protocolContractsHash } from '@aztec/protocol-contracts';
11
+ import type { EthAddress, L2Block, L2BlockSource } from '@aztec/stdlib/block';
12
+ import type { ContractDataSource } from '@aztec/stdlib/contract';
13
+ import { GasFees } from '@aztec/stdlib/gas';
8
14
  import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
9
15
  import {
10
16
  BlockAttestation,
11
17
  BlockProposal,
12
18
  type Gossipable,
13
19
  P2PClientType,
20
+ P2PMessage,
14
21
  PeerErrorSeverity,
15
- TopicTypeMap,
16
- getTopicTypeForClientType,
22
+ TopicType,
23
+ createTopicString,
24
+ getTopicsForClientAndConfig,
17
25
  metricsTopicStrToLabels,
18
26
  } from '@aztec/stdlib/p2p';
19
27
  import { MerkleTreeId } from '@aztec/stdlib/trees';
20
- import { Tx, type TxHash, type TxValidationResult } from '@aztec/stdlib/tx';
28
+ import { Tx, type TxHash, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
29
+ import type { UInt64 } from '@aztec/stdlib/types';
30
+ import { compressComponentVersions } from '@aztec/stdlib/versioning';
21
31
  import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
22
32
 
23
- import type { ENR } from '@chainsafe/enr';
24
33
  import {
25
34
  type GossipSub,
26
35
  type GossipSubComponents,
@@ -33,17 +42,26 @@ import { noise } from '@chainsafe/libp2p-noise';
33
42
  import { yamux } from '@chainsafe/libp2p-yamux';
34
43
  import { bootstrap } from '@libp2p/bootstrap';
35
44
  import { identify } from '@libp2p/identify';
36
- import { type Message, type PeerId, TopicValidatorResult } from '@libp2p/interface';
45
+ import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
37
46
  import type { ConnectionManager } from '@libp2p/interface-internal';
38
- import '@libp2p/kad-dht';
39
47
  import { mplex } from '@libp2p/mplex';
40
48
  import { tcp } from '@libp2p/tcp';
49
+ import { ENR } from '@nethermindeth/enr';
41
50
  import { createLibp2p } from 'libp2p';
42
51
 
43
52
  import type { P2PConfig } from '../../config.js';
53
+ import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
44
54
  import type { MemPools } from '../../mem_pools/interface.js';
45
- import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
46
55
  import {
56
+ AttestationValidator,
57
+ BlockProposalValidator,
58
+ FishermanAttestationValidator,
59
+ } from '../../msg_validators/index.js';
60
+ import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
61
+ import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
62
+ import { type MessageValidator, createTxMessageValidators } from '../../msg_validators/tx_validator/factory.js';
63
+ import {
64
+ AggregateTxValidator,
47
65
  DataTxValidator,
48
66
  DoubleSpendTxValidator,
49
67
  MetadataTxValidator,
@@ -51,23 +69,40 @@ import {
51
69
  } from '../../msg_validators/tx_validator/index.js';
52
70
  import { GossipSubEvent } from '../../types/index.js';
53
71
  import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
72
+ import { getVersions } from '../../versioning.js';
54
73
  import { AztecDatastore } from '../data_store.js';
74
+ import { DiscV5Service } from '../discv5/discV5_service.js';
55
75
  import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
56
76
  import { gossipScoreThresholds } from '../gossipsub/scoring.js';
77
+ import type { PeerManagerInterface } from '../peer-manager/interface.js';
57
78
  import { PeerManager } from '../peer-manager/peer_manager.js';
58
79
  import { PeerScoring } from '../peer-manager/peer_scoring.js';
59
- import { DEFAULT_SUB_PROTOCOL_VALIDATORS, ReqRespSubProtocol, type SubProtocolMap } from '../reqresp/interface.js';
80
+ import type { P2PReqRespConfig } from '../reqresp/config.js';
81
+ import {
82
+ DEFAULT_SUB_PROTOCOL_VALIDATORS,
83
+ type ReqRespInterface,
84
+ ReqRespSubProtocol,
85
+ type ReqRespSubProtocolHandler,
86
+ type ReqRespSubProtocolHandlers,
87
+ type ReqRespSubProtocolValidators,
88
+ type SubProtocolMap,
89
+ ValidationError,
90
+ } from '../reqresp/interface.js';
91
+ import { reqRespBlockTxsHandler } from '../reqresp/protocols/block_txs/block_txs_handler.js';
60
92
  import { reqGoodbyeHandler } from '../reqresp/protocols/goodbye.js';
61
- import { pingHandler, reqRespBlockHandler, reqRespTxHandler, statusHandler } from '../reqresp/protocols/index.js';
93
+ import {
94
+ AuthRequest,
95
+ BlockTxsRequest,
96
+ BlockTxsResponse,
97
+ StatusMessage,
98
+ pingHandler,
99
+ reqRespBlockHandler,
100
+ reqRespStatusHandler,
101
+ reqRespTxHandler,
102
+ } from '../reqresp/protocols/index.js';
62
103
  import { ReqResp } from '../reqresp/reqresp.js';
63
- import type { P2PService, PeerDiscoveryService } from '../service.js';
64
-
65
- interface MessageValidator {
66
- validator: {
67
- validateTx(tx: Tx): Promise<TxValidationResult>;
68
- };
69
- severity: PeerErrorSeverity;
70
- }
104
+ import type { P2PBlockReceivedCallback, P2PService, PeerDiscoveryService } from '../service.js';
105
+ import { P2PInstrumentation } from './instrumentation.js';
71
106
 
72
107
  interface ValidationResult {
73
108
  name: string;
@@ -77,74 +112,98 @@ interface ValidationResult {
77
112
 
78
113
  type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
79
114
 
115
+ // REFACTOR: Unify with the type above
116
+ type ReceivedMessageValidationResult<T> =
117
+ | { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject> }
118
+ | { obj?: undefined; result: TopicValidatorResult.Reject };
119
+
80
120
  /**
81
121
  * Lib P2P implementation of the P2PService interface.
82
122
  */
83
- export class LibP2PService<T extends P2PClientType> extends WithTracer implements P2PService {
84
- private jobQueue: SerialQueue = new SerialQueue();
85
- private peerManager: PeerManager;
123
+ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
86
124
  private discoveryRunningPromise?: RunningPromise;
125
+ private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
87
126
 
88
127
  // Message validators
89
128
  private attestationValidator: AttestationValidator;
90
129
  private blockProposalValidator: BlockProposalValidator;
91
130
 
92
- // Request and response sub service
93
- public reqresp: ReqResp;
131
+ private protocolVersion = '';
132
+ private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
133
+
134
+ private feesCache: { blockNumber: number; gasFees: GasFees } | undefined;
94
135
 
95
136
  /**
96
137
  * Callback for when a block is received from a peer.
97
138
  * @param block - The block received from the peer.
98
139
  * @returns The attestation for the block, if any.
99
140
  */
100
- private blockReceivedCallback: (block: BlockProposal) => Promise<BlockAttestation | undefined>;
141
+ private blockReceivedCallback: P2PBlockReceivedCallback;
142
+
143
+ private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
144
+
145
+ private instrumentation: P2PInstrumentation;
146
+
147
+ protected logger: Logger;
101
148
 
102
149
  constructor(
103
150
  private clientType: T,
104
151
  private config: P2PConfig,
105
- private node: PubSubLibp2p,
152
+ protected node: PubSubLibp2p,
106
153
  private peerDiscoveryService: PeerDiscoveryService,
107
- private mempools: MemPools<T>,
108
- private l2BlockSource: L2BlockSource,
109
- epochCache: EpochCacheInterface,
154
+ private reqresp: ReqRespInterface,
155
+ private peerManager: PeerManagerInterface,
156
+ protected mempools: MemPools<T>,
157
+ private archiver: L2BlockSource & ContractDataSource,
158
+ private epochCache: EpochCacheInterface,
110
159
  private proofVerifier: ClientProtocolCircuitVerifier,
111
160
  private worldStateSynchronizer: WorldStateSynchronizer,
112
161
  telemetry: TelemetryClient,
113
- private logger = createLogger('p2p:libp2p_service'),
162
+ logger: Logger = createLogger('p2p:libp2p_service'),
114
163
  ) {
115
164
  super(telemetry, 'LibP2PService');
116
165
 
117
- const peerScoring = new PeerScoring(config);
118
- this.reqresp = new ReqResp(config, node, peerScoring);
166
+ // Create child logger with fisherman prefix if in fisherman mode
167
+ this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
119
168
 
120
- this.peerManager = new PeerManager(
121
- node,
122
- peerDiscoveryService,
123
- config,
124
- telemetry,
125
- createLogger(`${logger.module}:peer_manager`),
126
- peerScoring,
127
- this.reqresp,
169
+ this.instrumentation = new P2PInstrumentation(telemetry, 'LibP2PService');
170
+
171
+ this.msgIdSeenValidators[TopicType.tx] = new MessageSeenValidator(config.seenMessageCacheSize);
172
+ this.msgIdSeenValidators[TopicType.block_proposal] = new MessageSeenValidator(config.seenMessageCacheSize);
173
+ this.msgIdSeenValidators[TopicType.block_attestation] = new MessageSeenValidator(config.seenMessageCacheSize);
174
+
175
+ const versions = getVersions(config);
176
+ this.protocolVersion = compressComponentVersions(versions);
177
+ logger.info(`Started libp2p service with protocol version ${this.protocolVersion}`);
178
+
179
+ this.topicStrings[TopicType.tx] = createTopicString(TopicType.tx, this.protocolVersion);
180
+ this.topicStrings[TopicType.block_proposal] = createTopicString(TopicType.block_proposal, this.protocolVersion);
181
+ this.topicStrings[TopicType.block_attestation] = createTopicString(
182
+ TopicType.block_attestation,
183
+ this.protocolVersion,
128
184
  );
129
185
 
130
- // Update gossipsub score params
131
- this.node.services.pubsub.score.params.appSpecificScore = (peerId: string) => {
132
- return this.peerManager.getPeerScore(peerId);
133
- };
134
- this.node.services.pubsub.score.params.appSpecificWeight = 10;
186
+ // Use FishermanAttestationValidator in fisherman mode to validate attestation payloads against proposals
187
+ this.attestationValidator = config.fishermanMode
188
+ ? new FishermanAttestationValidator(epochCache, mempools.attestationPool!, telemetry)
189
+ : new AttestationValidator(epochCache);
190
+ this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions });
135
191
 
136
- this.attestationValidator = new AttestationValidator(epochCache);
137
- this.blockProposalValidator = new BlockProposalValidator(epochCache);
192
+ this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
138
193
 
139
- this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation | undefined> => {
194
+ this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation[] | undefined> => {
140
195
  this.logger.debug(
141
- `Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber.toNumber()} from peer.`,
142
- { p2pMessageIdentifier: await block.p2pMessageIdentifier() },
196
+ `Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber} from peer.`,
197
+ { p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier() },
143
198
  );
144
199
  return undefined;
145
200
  };
146
201
  }
147
202
 
203
+ public updateConfig(config: Partial<P2PReqRespConfig>) {
204
+ this.reqresp.updateConfig(config);
205
+ }
206
+
148
207
  /**
149
208
  * Creates an instance of the LibP2P service.
150
209
  * @param config - The configuration to use when creating the service.
@@ -154,69 +213,151 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
154
213
  public static async new<T extends P2PClientType>(
155
214
  clientType: T,
156
215
  config: P2PConfig,
157
- peerDiscoveryService: PeerDiscoveryService,
158
216
  peerId: PeerId,
159
- mempools: MemPools<T>,
160
- l2BlockSource: L2BlockSource,
161
- epochCache: EpochCacheInterface,
162
- proofVerifier: ClientProtocolCircuitVerifier,
163
- worldStateSynchronizer: WorldStateSynchronizer,
164
- store: AztecAsyncKVStore,
165
- telemetry: TelemetryClient,
166
- logger = createLogger('p2p:libp2p_service'),
217
+ deps: {
218
+ mempools: MemPools<T>;
219
+ l2BlockSource: L2BlockSource & ContractDataSource;
220
+ epochCache: EpochCacheInterface;
221
+ proofVerifier: ClientProtocolCircuitVerifier;
222
+ worldStateSynchronizer: WorldStateSynchronizer;
223
+ peerStore: AztecAsyncKVStore;
224
+ telemetry: TelemetryClient;
225
+ logger: Logger;
226
+ packageVersion: string;
227
+ },
167
228
  ) {
168
- const { tcpListenAddress, tcpAnnounceAddress, maxPeerCount } = config;
169
- const bindAddrTcp = convertToMultiaddr(tcpListenAddress, 'tcp');
170
- // We know tcpAnnounceAddress cannot be null here because we set it or throw when setting up the service.
171
- const announceAddrTcp = convertToMultiaddr(tcpAnnounceAddress!, 'tcp');
229
+ const {
230
+ worldStateSynchronizer,
231
+ epochCache,
232
+ l2BlockSource,
233
+ mempools,
234
+ proofVerifier,
235
+ peerStore,
236
+ telemetry,
237
+ logger,
238
+ packageVersion,
239
+ } = deps;
240
+ const { p2pPort, maxPeerCount, listenAddress } = config;
241
+ const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
172
242
 
173
- const datastore = new AztecDatastore(store);
243
+ const datastore = new AztecDatastore(peerStore);
174
244
 
175
245
  const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
176
246
 
177
- // If bootstrap nodes are provided, also provide them to the p2p service
247
+ const peerDiscoveryService = new DiscV5Service(
248
+ peerId,
249
+ config,
250
+ packageVersion,
251
+ telemetry,
252
+ createLogger(`${logger.module}:discv5_service`),
253
+ );
254
+
255
+ // Seed libp2p's bootstrap discovery with private and trusted peers
256
+ const bootstrapNodes = [...config.privatePeers, ...config.trustedPeers];
257
+
178
258
  const peerDiscovery = [];
179
- if (peerDiscoveryService.bootstrapNodes.length > 0) {
180
- peerDiscovery.push(bootstrap({ list: peerDiscoveryService.bootstrapNodes }));
259
+ if (bootstrapNodes.length > 0) {
260
+ peerDiscovery.push(bootstrap({ list: bootstrapNodes }));
181
261
  }
182
262
 
263
+ const versions = getVersions(config);
264
+ const protocolVersion = compressComponentVersions(versions);
265
+
266
+ const txTopic = createTopicString(TopicType.tx, protocolVersion);
267
+ const blockProposalTopic = createTopicString(TopicType.block_proposal, protocolVersion);
268
+ const blockAttestationTopic = createTopicString(TopicType.block_attestation, protocolVersion);
269
+
270
+ const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
271
+ const directPeers = (
272
+ await Promise.all(
273
+ preferredPeersEnrs.map(async enr => {
274
+ const peerId = await enr.peerId();
275
+ const address = enr.getLocationMultiaddr('tcp');
276
+ if (address === undefined) {
277
+ throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
278
+ }
279
+ return {
280
+ id: peerId,
281
+ addrs: [address],
282
+ };
283
+ }),
284
+ )
285
+ ).filter(peer => peer !== undefined);
286
+
287
+ const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
288
+
183
289
  const node = await createLibp2p({
184
290
  start: false,
185
291
  peerId,
186
292
  addresses: {
187
293
  listen: [bindAddrTcp],
188
- announce: [announceAddrTcp],
294
+ announce: announceTcpMultiaddr,
189
295
  },
190
296
  transports: [
191
297
  tcp({
192
- maxConnections: config.maxPeerCount,
298
+ // It's better to have this number a bit higher than our maxPeerCount because it's sets the limit on transport (TCP) layer
299
+ // The connection attempts to the node on TCP layer are not necessarily valid Aztec peers so we want to have a bit of leeway here
300
+ // If we hit the limit, the connection will be temporarily accepted and immediately dropped.
301
+ // Docs: https://nodejs.org/api/net.html#servermaxconnections
302
+ maxConnections: maxPeerCount * 2,
193
303
  // socket option: the maximum length of the queue of pending connections
194
- // https://nodejs.org/dist/latest-v18.x/docs/api/net.html#serverlisten
304
+ // https://nodejs.org/dist/latest-v22.x/docs/api/net.html#serverlisten
195
305
  // it's not safe if we increase this number
196
306
  backlog: 5,
197
307
  closeServerOnMaxConnections: {
198
- closeAbove: maxPeerCount ?? Infinity,
199
- listenBelow: maxPeerCount ?? Infinity,
308
+ // The property `maxConnections` will protect us against the most DDOS attack
309
+ // This property protects us in case of burst of new connections where server is not able to close them quickly enough
310
+ // In case closeAbove is reached, the server stops listening altogether
311
+ // It's important that there is enough difference between closeAbove and listenAbove,
312
+ // otherwise the server.listener will flap between being closed and open potentially degrading perf even more
313
+ closeAbove: maxPeerCount * 3,
314
+ listenBelow: Math.floor(maxPeerCount * 0.9),
200
315
  },
201
316
  }),
202
317
  ],
203
318
  datastore,
204
319
  peerDiscovery,
205
- streamMuxers: [mplex(), yamux()],
320
+ streamMuxers: [yamux(), mplex()],
206
321
  connectionEncryption: [noise()],
207
322
  connectionManager: {
208
- minConnections: 0,
209
-
323
+ minConnections: 0, // Disable libp2p peer dialing, we do it manually
324
+ // We set maxConnections above maxPeerCount because if we hit limit of maxPeerCount
325
+ // libp2p will start aggressively rejecting all new connections, preventing network discovery and crawling.
326
+ maxConnections: maxPeerCount * 2,
210
327
  maxParallelDials: 100,
211
328
  dialTimeout: 30_000,
212
329
  maxPeerAddrsToDial: 5,
213
330
  maxIncomingPendingConnections: 5,
214
331
  },
332
+ connectionGater: {
333
+ denyInboundConnection: (maConn: MultiaddrConnection) => {
334
+ const allowed = peerManager.isNodeAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
335
+ if (allowed) {
336
+ return false;
337
+ }
338
+
339
+ logger.debug(`Connection gater: Denying inbound connection from ${maConn.remoteAddr.toString()}`);
340
+ return true;
341
+ },
342
+ denyInboundEncryptedConnection: (peerId: PeerId, _maConn: MultiaddrConnection) => {
343
+ //NOTE: it is not necessary to check address here because this was already done by
344
+ // denyInboundConnection
345
+ const allowed = peerManager.isNodeAllowedToConnect(peerId);
346
+ if (allowed) {
347
+ return false;
348
+ }
349
+
350
+ logger.debug(`Connection gater: Denying inbound encrypted connection from ${peerId.toString()}`);
351
+ return true;
352
+ },
353
+ },
215
354
  services: {
216
355
  identify: identify({
217
356
  protocolPrefix: 'aztec',
357
+ runOnConnectionOpen: true,
218
358
  }),
219
359
  pubsub: gossipsub({
360
+ directPeers,
220
361
  debugName: 'gossipsub',
221
362
  globalSignaturePolicy: SignaturePolicy.StrictNoSign,
222
363
  allowPublishToZeroTopicPeers: true,
@@ -228,29 +369,30 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
228
369
  heartbeatInterval: config.gossipsubInterval,
229
370
  mcacheLength: config.gossipsubMcacheLength,
230
371
  mcacheGossip: config.gossipsubMcacheGossip,
372
+ seenTTL: config.gossipsubSeenTTL,
231
373
  msgIdFn: getMsgIdFn,
232
374
  msgIdToStrFn: msgIdToStrFn,
233
375
  fastMsgIdFn: fastMsgIdFn,
234
376
  dataTransform: new SnappyTransform(),
235
377
  metricsRegister: otelMetricsAdapter,
236
- metricsTopicStrToLabel: metricsTopicStrToLabels(),
378
+ metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
237
379
  asyncValidation: true,
238
380
  scoreThresholds: gossipScoreThresholds,
239
381
  scoreParams: createPeerScoreParams({
240
382
  // IPColocation factor can be disabled for local testing - default to -5
241
383
  IPColocationFactorWeight: config.debugDisableColocationPenalty ? 0 : -5.0,
242
384
  topics: {
243
- [Tx.p2pTopic]: createTopicScoreParams({
385
+ [txTopic]: createTopicScoreParams({
244
386
  topicWeight: 1,
245
387
  invalidMessageDeliveriesWeight: -20,
246
388
  invalidMessageDeliveriesDecay: 0.5,
247
389
  }),
248
- [BlockAttestation.p2pTopic]: createTopicScoreParams({
390
+ [blockAttestationTopic]: createTopicScoreParams({
249
391
  topicWeight: 1,
250
392
  invalidMessageDeliveriesWeight: -20,
251
393
  invalidMessageDeliveriesDecay: 0.5,
252
394
  }),
253
- [BlockProposal.p2pTopic]: createTopicScoreParams({
395
+ [blockProposalTopic]: createTopicScoreParams({
254
396
  topicWeight: 1,
255
397
  invalidMessageDeliveriesWeight: -20,
256
398
  invalidMessageDeliveriesDecay: 0.5,
@@ -265,11 +407,34 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
265
407
  logger: createLibp2pComponentLogger(logger.module),
266
408
  });
267
409
 
410
+ const peerScoring = new PeerScoring(config, telemetry);
411
+ const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
412
+
413
+ const peerManager = new PeerManager(
414
+ node,
415
+ peerDiscoveryService,
416
+ config,
417
+ telemetry,
418
+ createLogger(`${logger.module}:peer_manager`),
419
+ peerScoring,
420
+ reqresp,
421
+ worldStateSynchronizer,
422
+ protocolVersion,
423
+ epochCache,
424
+ );
425
+
426
+ // Update gossipsub score params
427
+ node.services.pubsub.score.params.appSpecificWeight = 10;
428
+ node.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
429
+ peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
430
+
268
431
  return new LibP2PService(
269
432
  clientType,
270
433
  config,
271
434
  node,
272
435
  peerDiscoveryService,
436
+ reqresp,
437
+ peerManager,
273
438
  mempools,
274
439
  l2BlockSource,
275
440
  epochCache,
@@ -291,41 +456,54 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
291
456
  }
292
457
 
293
458
  // Get listen & announce addresses for logging
294
- const { tcpListenAddress, tcpAnnounceAddress } = this.config;
295
- if (!tcpAnnounceAddress) {
459
+ const { p2pIp, p2pPort } = this.config;
460
+ if (!p2pIp) {
296
461
  throw new Error('Announce address not provided.');
297
462
  }
298
- const announceTcpMultiaddr = convertToMultiaddr(tcpAnnounceAddress, 'tcp');
463
+ const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
299
464
 
300
- // Start job queue, peer discovery service and libp2p node
301
- this.jobQueue.start();
302
- await this.peerDiscoveryService.start();
465
+ await this.peerManager.initializePeers();
466
+ if (!this.config.p2pDiscoveryDisabled) {
467
+ await this.peerDiscoveryService.start();
468
+ }
303
469
  await this.node.start();
304
470
 
305
471
  // Subscribe to standard GossipSub topics by default
306
- for (const topic of getTopicTypeForClientType(this.clientType)) {
307
- this.subscribeToTopic(TopicTypeMap[topic].p2pTopic);
472
+ for (const topic of getTopicsForClientAndConfig(this.clientType, this.config.disableTransactions)) {
473
+ this.subscribeToTopic(this.topicStrings[topic]);
308
474
  }
309
475
 
310
476
  // Create request response protocol handlers
311
477
  const txHandler = reqRespTxHandler(this.mempools);
312
478
  const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
313
- const blockHandler = reqRespBlockHandler(this.l2BlockSource);
479
+ const blockHandler = reqRespBlockHandler(this.archiver);
480
+ const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
314
481
 
315
- const requestResponseHandlers = {
482
+ const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
316
483
  [ReqRespSubProtocol.PING]: pingHandler,
317
- [ReqRespSubProtocol.STATUS]: statusHandler,
318
- [ReqRespSubProtocol.TX]: txHandler.bind(this),
484
+ [ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
319
485
  [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
320
486
  [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
321
487
  };
322
488
 
489
+ // Only handle block transactions request if attestation pool is available to the client
490
+ if (this.mempools.attestationPool && !this.config.disableTransactions) {
491
+ const blockTxsHandler = reqRespBlockTxsHandler(this.mempools.attestationPool, this.mempools.txPool);
492
+ requestResponseHandlers[ReqRespSubProtocol.BLOCK_TXS] = blockTxsHandler.bind(this);
493
+ }
494
+
495
+ if (!this.config.disableTransactions) {
496
+ requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
497
+ }
498
+
323
499
  // add GossipSub listener
324
- this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
500
+ this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
325
501
 
326
- // Start running promise for peer discovery
502
+ // Start running promise for peer discovery and metrics collection
327
503
  this.discoveryRunningPromise = new RunningPromise(
328
- () => this.peerManager.heartbeat(),
504
+ async () => {
505
+ await this.peerManager.heartbeat();
506
+ },
329
507
  this.logger,
330
508
  this.config.peerCheckIntervalMS,
331
509
  );
@@ -334,12 +512,14 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
334
512
  // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
335
513
  const reqrespSubProtocolValidators = {
336
514
  ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
337
- // TODO(#11336): A request validator for blocks
338
- [ReqRespSubProtocol.TX]: this.validateRequestedTx.bind(this),
515
+ [ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
516
+ [ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
517
+ [ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
339
518
  };
340
519
  await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
341
520
  this.logger.info(`Started P2P service`, {
342
- listen: tcpListenAddress,
521
+ listen: this.config.listenAddress,
522
+ port: this.config.p2pPort,
343
523
  announce: announceTcpMultiaddr,
344
524
  peerId: this.node.peerId.toString(),
345
525
  });
@@ -351,14 +531,11 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
351
531
  */
352
532
  public async stop() {
353
533
  // Remove gossip sub listener
354
- this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.handleGossipSubEvent.bind(this));
534
+ this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
355
535
 
356
536
  // Stop peer manager
357
537
  this.logger.debug('Stopping peer manager...');
358
538
  await this.peerManager.stop();
359
-
360
- this.logger.debug('Stopping job queue...');
361
- await this.jobQueue.end();
362
539
  this.logger.debug('Stopping running promise...');
363
540
  await this.discoveryRunningPromise?.stop();
364
541
  this.logger.debug('Stopping peer discovery service...');
@@ -370,6 +547,18 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
370
547
  this.logger.info('LibP2P service stopped');
371
548
  }
372
549
 
550
+ addReqRespSubProtocol(
551
+ subProtocol: ReqRespSubProtocol,
552
+ handler: ReqRespSubProtocolHandler,
553
+ validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
554
+ ): Promise<void> {
555
+ return this.reqresp.addSubProtocol(subProtocol, handler, validator);
556
+ }
557
+
558
+ public registerThisValidatorAddresses(address: EthAddress[]): void {
559
+ this.peerManager.registerThisValidatorAddresses(address);
560
+ }
561
+
373
562
  public getPeers(includePending?: boolean): PeerInfo[] {
374
563
  return this.peerManager.getPeers(includePending);
375
564
  }
@@ -387,23 +576,6 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
387
576
  setImmediate(() => void safeJob());
388
577
  }
389
578
 
390
- /**
391
- * Send Request via the ReqResp service
392
- * The subprotocol defined will determine the request and response types
393
- *
394
- * See the subProtocolMap for the mapping of subprotocols to request/response types in `interface.ts`
395
- *
396
- * @param protocol The request response protocol to use
397
- * @param request The request type to send
398
- * @returns
399
- */
400
- sendRequest<SubProtocol extends ReqRespSubProtocol>(
401
- protocol: SubProtocol,
402
- request: InstanceType<SubProtocolMap[SubProtocol]['request']>,
403
- ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined> {
404
- return this.reqresp.sendRequest(protocol, request);
405
- }
406
-
407
579
  /**
408
580
  * Send a batch of requests to peers, and return the responses
409
581
  * @param protocol - The request response protocol to use
@@ -413,8 +585,9 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
413
585
  sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
414
586
  protocol: SubProtocol,
415
587
  requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
416
- ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[] | undefined> {
417
- return this.reqresp.sendBatchRequest(protocol, requests);
588
+ pinnedPeerId: PeerId | undefined,
589
+ ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
590
+ return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
418
591
  }
419
592
 
420
593
  /**
@@ -425,9 +598,8 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
425
598
  return this.peerDiscoveryService.getEnr();
426
599
  }
427
600
 
428
- public registerBlockReceivedCallback(callback: (block: BlockProposal) => Promise<BlockAttestation | undefined>) {
601
+ public registerBlockReceivedCallback(callback: P2PBlockReceivedCallback) {
429
602
  this.blockReceivedCallback = callback;
430
- this.logger.verbose('Block received callback registered');
431
603
  }
432
604
 
433
605
  /**
@@ -444,160 +616,358 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
444
616
  /**
445
617
  * Publishes data to a topic.
446
618
  * @param topic - The topic to publish to.
447
- * @param data - The data to publish.
619
+ * @param data - The message to publish.
448
620
  * @returns The number of recipients the data was sent to.
449
621
  */
450
- private async publishToTopic(topic: string, data: Uint8Array) {
622
+ private async publishToTopic(topic: string, message: Gossipable) {
451
623
  if (!this.node.services.pubsub) {
452
624
  throw new Error('Pubsub service not available.');
453
625
  }
454
- const result = await this.node.services.pubsub.publish(topic, data);
455
-
626
+ const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages);
627
+ const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
456
628
  return result.recipients.length;
457
629
  }
458
630
 
631
+ /**
632
+ * Checks if this message has already been seen, based on its msgId computed from hashing the message data.
633
+ * Note that we do not rely on the seenCache from gossipsub since we want to keep a longer history of seen
634
+ * messages to avoid tx echoes across the network.
635
+ */
636
+ protected preValidateReceivedMessage(
637
+ msg: Message,
638
+ msgId: string,
639
+ source: PeerId,
640
+ ): { result: boolean; topicType?: TopicType } {
641
+ let topicType: TopicType | undefined;
642
+
643
+ switch (msg.topic) {
644
+ case this.topicStrings[TopicType.tx]:
645
+ topicType = TopicType.tx;
646
+ break;
647
+ case this.topicStrings[TopicType.block_attestation]:
648
+ topicType = TopicType.block_attestation;
649
+ break;
650
+ case this.topicStrings[TopicType.block_proposal]:
651
+ topicType = TopicType.block_proposal;
652
+ break;
653
+ default:
654
+ this.logger.error(`Received message on unknown topic: ${msg.topic}`);
655
+ break;
656
+ }
657
+
658
+ const validator = topicType ? this.msgIdSeenValidators[topicType] : undefined;
659
+
660
+ if (!validator || !validator.addMessage(msgId)) {
661
+ this.instrumentation.incMessagePrevalidationStatus(false, topicType);
662
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
663
+ return { result: false, topicType };
664
+ }
665
+
666
+ this.instrumentation.incMessagePrevalidationStatus(true, topicType);
667
+
668
+ return { result: true, topicType };
669
+ }
670
+
671
+ /**
672
+ * Safely deserializes a P2PMessage from raw message data.
673
+ * @param msgId - The message ID.
674
+ * @param source - The peer ID of the message source.
675
+ * @param data - The raw message data.
676
+ * @returns The deserialized P2PMessage or undefined if deserialization fails.
677
+ */
678
+ private safelyDeserializeP2PMessage(msgId: string, source: PeerId, data: Uint8Array): P2PMessage | undefined {
679
+ try {
680
+ return P2PMessage.fromMessageData(Buffer.from(data), this.config.debugP2PInstrumentMessages);
681
+ } catch (err) {
682
+ this.logger.error(`Error deserializing P2PMessage`, err, {
683
+ msgId,
684
+ source: source.toString(),
685
+ });
686
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Reject);
687
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
688
+ return undefined;
689
+ }
690
+ }
691
+
459
692
  /**
460
693
  * Handles a new gossip message that was received by the client.
461
694
  * @param topic - The message's topic.
462
695
  * @param data - The message data
463
696
  */
464
- private async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
465
- if (msg.topic === Tx.p2pTopic) {
466
- await this.handleGossipedTx(msg, msgId, source);
697
+ protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
698
+ const msgReceivedTime = Date.now();
699
+ let topicType: TopicType | undefined;
700
+ const p2pMessage = this.safelyDeserializeP2PMessage(msgId, source, msg.data);
701
+ if (!p2pMessage) {
702
+ return;
703
+ }
704
+
705
+ const preValidationResult = this.preValidateReceivedMessage(msg, msgId, source);
706
+
707
+ if (!preValidationResult.result) {
708
+ return;
709
+ }
710
+
711
+ if (msg.topic === this.topicStrings[TopicType.tx]) {
712
+ topicType = TopicType.tx;
713
+ await this.handleGossipedTx(p2pMessage.payload, msgId, source);
467
714
  }
468
- if (msg.topic === BlockAttestation.p2pTopic && this.clientType === P2PClientType.Full) {
469
- await this.processAttestationFromPeer(msg, msgId, source);
715
+ if (msg.topic === this.topicStrings[TopicType.block_attestation]) {
716
+ topicType = TopicType.block_attestation;
717
+ if (this.clientType === P2PClientType.Full) {
718
+ await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
719
+ }
720
+ }
721
+ if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
722
+ topicType = TopicType.block_proposal;
723
+ await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
470
724
  }
471
- if (msg.topic == BlockProposal.p2pTopic) {
472
- await this.processBlockFromPeer(msg, msgId, source);
725
+
726
+ if (p2pMessage.timestamp !== undefined && topicType !== undefined) {
727
+ const latency = msgReceivedTime - p2pMessage.timestamp.getTime();
728
+ this.instrumentation.recordMessageLatency(topicType, latency);
473
729
  }
474
730
 
475
731
  return;
476
732
  }
477
733
 
478
- private async validateReceivedMessage<T>(
479
- validationFunc: () => Promise<{ result: boolean; obj: T }>,
734
+ protected async validateReceivedMessage<T>(
735
+ validationFunc: () => Promise<ReceivedMessageValidationResult<T>>,
480
736
  msgId: string,
481
737
  source: PeerId,
482
- ): Promise<{ result: boolean; obj: T | undefined }> {
483
- let resultAndObj: { result: boolean; obj: T | undefined } = { result: false, obj: undefined };
738
+ topicType: TopicType,
739
+ ): Promise<ReceivedMessageValidationResult<T>> {
740
+ let resultAndObj: ReceivedMessageValidationResult<T> = { result: TopicValidatorResult.Reject };
741
+ const timer = new Timer();
484
742
  try {
485
743
  resultAndObj = await validationFunc();
486
744
  } catch (err) {
487
- this.logger.error(`Error deserialising and validating message `, err);
745
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
746
+ this.logger.error(`Error deserializing and validating gossipsub message`, err, {
747
+ msgId,
748
+ source: source.toString(),
749
+ topicType,
750
+ });
488
751
  }
489
752
 
490
- this.node.services.pubsub.reportMessageValidationResult(
491
- msgId,
492
- source.toString(),
493
- resultAndObj.result && resultAndObj.obj ? TopicValidatorResult.Accept : TopicValidatorResult.Reject,
494
- );
753
+ if (resultAndObj.result === TopicValidatorResult.Accept) {
754
+ this.instrumentation.recordMessageValidation(topicType, timer);
755
+ }
756
+
757
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
495
758
  return resultAndObj;
496
759
  }
497
760
 
498
- private async handleGossipedTx(msg: Message, msgId: string, source: PeerId) {
499
- const validationFunc = async () => {
500
- const tx = Tx.fromBuffer(Buffer.from(msg.data));
501
- const result = await this.validatePropagatedTx(tx, source);
502
- return { result, obj: tx };
761
+ protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
762
+ const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
763
+ const tx = Tx.fromBuffer(payloadData);
764
+ const isValid = await this.validatePropagatedTx(tx, source);
765
+ const exists = isValid && (await this.mempools.txPool.hasTx(tx.getTxHash()));
766
+
767
+ this.logger.trace(`Validate propagated tx`, {
768
+ isValid,
769
+ exists,
770
+ [Attributes.P2P_ID]: source.toString(),
771
+ });
772
+
773
+ if (!isValid) {
774
+ return { result: TopicValidatorResult.Reject };
775
+ } else if (exists) {
776
+ return { result: TopicValidatorResult.Ignore, obj: tx };
777
+ } else {
778
+ return { result: TopicValidatorResult.Accept, obj: tx };
779
+ }
503
780
  };
504
781
 
505
- const { result, obj: tx } = await this.validateReceivedMessage<Tx>(validationFunc, msgId, source);
506
- if (!result || !tx) {
782
+ const { result, obj: tx } = await this.validateReceivedMessage<Tx>(validationFunc, msgId, source, TopicType.tx);
783
+ if (result !== TopicValidatorResult.Accept || !tx) {
507
784
  return;
508
785
  }
509
- const txHash = await tx.getTxHash();
786
+
787
+ const txHash = tx.getTxHash();
510
788
  const txHashString = txHash.toString();
511
- this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()}.`);
789
+ this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()} via gossip`, {
790
+ source: source.toString(),
791
+ txHash: txHashString,
792
+ });
793
+
794
+ if (this.config.dropTransactions && randomInt(1000) < this.config.dropTransactionsProbability * 1000) {
795
+ this.logger.warn(`Intentionally dropping tx ${txHashString} (probability rule)`);
796
+ return;
797
+ }
798
+
799
+ this.instrumentation.incrementTxReceived(1);
512
800
  await this.mempools.txPool.addTxs([tx]);
513
801
  }
514
802
 
515
- /**Process Attestation From Peer
803
+ /**
804
+ * Process Attestation From Peer
516
805
  * When a proposal is received from a peer, we add it to the attestation pool, so it can be accessed by other services.
517
806
  *
518
807
  * @param attestation - The attestation to process.
519
808
  */
520
- private async processAttestationFromPeer(msg: Message, msgId: string, source: PeerId): Promise<void> {
521
- const validationFunc = async () => {
522
- const attestation = BlockAttestation.fromBuffer(Buffer.from(msg.data));
523
- const result = await this.validateAttestation(source, attestation);
524
- this.logger.trace(`validatePropagatedAttestation: ${result}`, {
525
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toString(),
809
+ private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
810
+ const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockAttestation>> = async () => {
811
+ const attestation = BlockAttestation.fromBuffer(payloadData);
812
+ const pool = this.mempools.attestationPool!;
813
+ const isValid = await this.validateAttestation(source, attestation);
814
+ const exists = isValid && (await pool.hasAttestation(attestation));
815
+
816
+ let canAdd = true;
817
+ if (isValid && !exists) {
818
+ const slot = attestation.payload.header.slotNumber;
819
+ const { committee } = await this.epochCache.getCommittee(slot);
820
+ const committeeSize = committee?.length ?? 0;
821
+ canAdd = await pool.canAddAttestation(attestation, committeeSize);
822
+ }
823
+
824
+ this.logger.trace(`Validate propagated block attestation`, {
825
+ isValid,
826
+ exists,
827
+ canAdd,
828
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
526
829
  [Attributes.P2P_ID]: source.toString(),
527
830
  });
528
- return { result, obj: attestation };
831
+
832
+ if (!isValid) {
833
+ return { result: TopicValidatorResult.Reject };
834
+ } else if (exists) {
835
+ return { result: TopicValidatorResult.Ignore, obj: attestation };
836
+ } else if (!canAdd) {
837
+ this.logger.warn(`Dropping block attestation due to per-(slot, proposalId) attestation cap`, {
838
+ slot: attestation.payload.header.slotNumber.toString(),
839
+ archive: attestation.archive.toString(),
840
+ source: source.toString(),
841
+ });
842
+ return { result: TopicValidatorResult.Ignore, obj: attestation };
843
+ } else {
844
+ return { result: TopicValidatorResult.Accept, obj: attestation };
845
+ }
529
846
  };
530
847
 
531
848
  const { result, obj: attestation } = await this.validateReceivedMessage<BlockAttestation>(
532
849
  validationFunc,
533
850
  msgId,
534
851
  source,
852
+ TopicType.block_attestation,
535
853
  );
536
- if (!result || !attestation) {
854
+
855
+ if (result !== TopicValidatorResult.Accept || !attestation) {
537
856
  return;
538
857
  }
858
+
539
859
  this.logger.debug(
540
- `Received attestation for block ${attestation.blockNumber.toNumber()} slot ${attestation.slotNumber.toNumber()} from external peer.`,
860
+ `Received attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
541
861
  {
542
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
543
- slot: attestation.slotNumber.toNumber(),
862
+ p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
863
+ slot: attestation.slotNumber,
544
864
  archive: attestation.archive.toString(),
545
- block: attestation.blockNumber.toNumber(),
865
+ source: source.toString(),
546
866
  },
547
867
  );
868
+
548
869
  await this.mempools.attestationPool!.addAttestations([attestation]);
549
870
  }
550
871
 
551
- private async processBlockFromPeer(msg: Message, msgId: string, source: PeerId): Promise<void> {
552
- const validationFunc = async () => {
553
- const block = BlockProposal.fromBuffer(Buffer.from(msg.data));
554
- const result = await this.validateBlockProposal(source, block);
555
- this.logger.trace(`validatePropagatedBlock: ${result}`, {
556
- [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString(),
872
+ private async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
873
+ const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
874
+ const block = BlockProposal.fromBuffer(payloadData);
875
+ const isValid = await this.validateBlockProposal(source, block);
876
+ const pool = this.mempools.attestationPool;
877
+
878
+ // Note that we dont have an attestation pool if we're a prover node, but we still
879
+ // subscribe to block proposal topics in order to prevent their txs from being cleared.
880
+ const exists = isValid && (await pool?.hasBlockProposal(block));
881
+ const canAdd = isValid && (await pool?.canAddProposal(block));
882
+
883
+ this.logger.trace(`Validate propagated block proposal`, {
884
+ isValid,
885
+ exists,
886
+ canAdd,
887
+ [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
557
888
  [Attributes.P2P_ID]: source.toString(),
558
889
  });
559
- return { result, obj: block };
890
+
891
+ if (!isValid) {
892
+ return { result: TopicValidatorResult.Reject };
893
+ } else if (exists) {
894
+ return { result: TopicValidatorResult.Ignore, obj: block };
895
+ } else if (!canAdd) {
896
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
897
+ this.logger.warn(`Penalizing peer for block proposal exceeding per-slot cap`, {
898
+ slot: block.slotNumber.toString(),
899
+ archive: block.archive.toString(),
900
+ source: source.toString(),
901
+ });
902
+ return { result: TopicValidatorResult.Reject };
903
+ } else {
904
+ return { result: TopicValidatorResult.Accept, obj: block };
905
+ }
560
906
  };
561
907
 
562
- const { result, obj: block } = await this.validateReceivedMessage<BlockProposal>(validationFunc, msgId, source);
908
+ const { result, obj: block } = await this.validateReceivedMessage<BlockProposal>(
909
+ validationFunc,
910
+ msgId,
911
+ source,
912
+ TopicType.block_proposal,
913
+ );
914
+
563
915
  if (!result || !block) {
564
916
  return;
565
917
  }
566
- await this.processValidBlockProposal(block);
918
+
919
+ await this.processValidBlockProposal(block, source);
567
920
  }
568
921
 
569
922
  // REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
570
923
  @trackSpan('Libp2pService.processValidBlockProposal', async block => ({
571
- [Attributes.BLOCK_NUMBER]: block.blockNumber.toNumber(),
572
- [Attributes.SLOT_NUMBER]: block.slotNumber.toNumber(),
924
+ [Attributes.SLOT_NUMBER]: block.slotNumber,
573
925
  [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
574
- [Attributes.P2P_ID]: await block.p2pMessageIdentifier().then(i => i.toString()),
926
+ [Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
575
927
  }))
576
- private async processValidBlockProposal(block: BlockProposal) {
577
- this.logger.verbose(
578
- `Received block ${block.blockNumber.toNumber()} for slot ${block.slotNumber.toNumber()} from external peer.`,
579
- {
580
- p2pMessageIdentifier: await block.p2pMessageIdentifier(),
581
- slot: block.slotNumber.toNumber(),
582
- archive: block.archive.toString(),
583
- block: block.blockNumber.toNumber(),
584
- },
585
- );
586
- const attestation = await this.blockReceivedCallback(block);
928
+ private async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
929
+ const slot = block.slotNumber;
930
+ const previousSlot = SlotNumber(slot - 1);
931
+ this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
932
+ p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
933
+ slot: block.slotNumber,
934
+ archive: block.archive.toString(),
935
+ source: sender.toString(),
936
+ });
937
+ const attestationsForPreviousSlot = await this.mempools.attestationPool?.getAttestationsForSlot(previousSlot);
938
+ if (attestationsForPreviousSlot !== undefined) {
939
+ this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
940
+ }
941
+
942
+ // Attempt to add proposal, then mark the txs in this proposal as non-evictable
943
+ try {
944
+ await this.mempools.attestationPool?.addBlockProposal(block);
945
+ } catch (err: unknown) {
946
+ // Drop proposals if we hit per-slot cap in the attestation pool; rethrow unknown errors
947
+ if (err instanceof ProposalSlotCapExceededError) {
948
+ this.logger.warn(`Dropping block proposal due to per-slot proposal cap`, {
949
+ slot: String(slot),
950
+ archive: block.archive.toString(),
951
+ error: (err as Error).message,
952
+ });
953
+ return;
954
+ }
955
+ throw err;
956
+ }
957
+ await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
958
+ const attestations = await this.blockReceivedCallback(block, sender);
587
959
 
588
960
  // TODO: fix up this pattern - the abstraction is not nice
589
- // The attestation can be undefined if no handler is registered / the validator deems the block invalid
590
- if (attestation != undefined) {
591
- this.logger.verbose(
592
- `Broadcasting attestation for block ${attestation.blockNumber.toNumber()} slot ${attestation.slotNumber.toNumber()}`,
593
- {
594
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
595
- slot: attestation.slotNumber.toNumber(),
961
+ // The attestation can be undefined if no handler is registered / the validator deems the block invalid / in fisherman mode
962
+ if (attestations?.length) {
963
+ for (const attestation of attestations) {
964
+ this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber}`, {
965
+ p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
966
+ slot: attestation.slotNumber,
596
967
  archive: attestation.archive.toString(),
597
- block: attestation.blockNumber.toNumber(),
598
- },
599
- );
600
- await this.broadcastAttestation(attestation);
968
+ });
969
+ await this.broadcastAttestation(attestation);
970
+ }
601
971
  }
602
972
  }
603
973
 
@@ -606,10 +976,9 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
606
976
  * @param attestation - The attestation to broadcast.
607
977
  */
608
978
  @trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
609
- [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
610
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
979
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
611
980
  [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
612
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
981
+ [Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
613
982
  }))
614
983
  private async broadcastAttestation(attestation: BlockAttestation) {
615
984
  await this.propagate(attestation);
@@ -620,111 +989,312 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
620
989
  * @param message - The message to propagate.
621
990
  */
622
991
  public async propagate<T extends Gossipable>(message: T) {
623
- const p2pMessageIdentifier = await message.p2pMessageIdentifier();
992
+ const p2pMessageIdentifier = await message.p2pMessageLoggingIdentifier();
624
993
  this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
625
- void this.jobQueue
626
- .put(async () => {
627
- await this.sendToPeers(message);
628
- })
629
- .catch(error => {
630
- this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
631
- });
994
+ void this.sendToPeers(message).catch(error => {
995
+ this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
996
+ });
632
997
  }
633
998
 
634
999
  /**
635
- * Validate a tx that has been requested from a peer.
1000
+ * Validate the requested block transactions. Allow partial returns.
1001
+ * @param request - The block transactions request.
1002
+ * @param response - The block transactions response.
1003
+ * @param peerId - The ID of the peer that made the request.
1004
+ * @returns True if the requested block transactions are valid, false otherwise.
1005
+ */
1006
+ @trackSpan('Libp2pService.validateRequestedBlockTxs', request => ({
1007
+ [Attributes.BLOCK_HASH]: request.blockHash.toString(),
1008
+ }))
1009
+ private async validateRequestedBlockTxs(
1010
+ request: BlockTxsRequest,
1011
+ response: BlockTxsResponse,
1012
+ peerId: PeerId,
1013
+ ): Promise<boolean> {
1014
+ const requestedTxValidator = this.createRequestedTxValidator();
1015
+
1016
+ try {
1017
+ if (!response.blockHash.equals(request.blockHash)) {
1018
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1019
+ throw new ValidationError(
1020
+ `Received block txs for unexpected block: expected ${request.blockHash.toString()}, got ${response.blockHash.toString()}`,
1021
+ );
1022
+ }
1023
+
1024
+ if (response.txIndices.getLength() !== request.txIndices.getLength()) {
1025
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1026
+ throw new ValidationError(
1027
+ `Received block txs with mismatched bitvector length: expected ${request.txIndices.getLength()}, got ${response.txIndices.getLength()}`,
1028
+ );
1029
+ }
1030
+
1031
+ // Check no duplicates and not exceeding returnable count
1032
+ const requestedIndices = new Set(request.txIndices.getTrueIndices());
1033
+ const availableIndices = new Set(response.txIndices.getTrueIndices());
1034
+ const maxReturnable = [...requestedIndices].filter(i => availableIndices.has(i)).length;
1035
+
1036
+ const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
1037
+ const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
1038
+ if (uniqueReturned.size !== returnedHashes.length) {
1039
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1040
+ throw new ValidationError(`Received duplicate txs in block txs response`);
1041
+ }
1042
+ if (response.txs.length > maxReturnable) {
1043
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1044
+ throw new ValidationError(
1045
+ `Received more txs (${response.txs.length}) than requested-and-available (${maxReturnable})`,
1046
+ );
1047
+ }
1048
+
1049
+ // Given proposal (should have locally), ensure returned txs are valid subset and match request indices
1050
+ const proposal = await this.mempools.attestationPool?.getBlockProposal(request.blockHash.toString());
1051
+ if (proposal) {
1052
+ // Build intersected indices
1053
+ const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
1054
+
1055
+ // Enforce subset membership and preserve increasing order by index.
1056
+ const hashToIndexInProposal = new Map<string, number>(
1057
+ proposal.txHashes.map((h, i) => [h.toString(), i] as [string, number]),
1058
+ );
1059
+ const allowedIndexSet = new Set(intersectIdx);
1060
+ const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
1061
+ const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
1062
+ const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
1063
+ if (!allAllowed || !strictlyIncreasing) {
1064
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1065
+ throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
1066
+ }
1067
+ } else {
1068
+ // No local proposal, cannot check the membership/order of the returned txs
1069
+ this.logger.warn(
1070
+ `Block proposal not found for block hash ${request.blockHash.toString()}; cannot validate membership/order of returned txs`,
1071
+ );
1072
+ return false;
1073
+ }
1074
+
1075
+ await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
1076
+ return true;
1077
+ } catch (e: any) {
1078
+ if (e instanceof ValidationError) {
1079
+ this.logger.warn(`Failed validation for requested block txs from peer ${peerId.toString()}`);
1080
+ } else {
1081
+ this.logger.error(`Error during validation of requested block txs`, e);
1082
+ }
1083
+
1084
+ return false;
1085
+ }
1086
+ }
1087
+
1088
+ /**
1089
+ * Validate a collection of txs that has been requested from a peer.
636
1090
  *
637
- * The core component of this validator is that the tx hash MUST match the requested tx hash,
1091
+ * The core component of this validator is that each tx hash MUST match the requested tx hash,
638
1092
  * In order to perform this check, the tx proof must be verified.
639
1093
  *
640
1094
  * Note: This function is called from within `ReqResp.sendRequest` as part of the
641
1095
  * ReqRespSubProtocol.TX subprotocol validation.
642
1096
  *
643
- * @param requestedTxHash - The hash of the tx that was requested.
644
- * @param responseTx - The tx that was received as a response to the request.
1097
+ * @param requestedTxHash - The collection of the txs that was requested.
1098
+ * @param responseTx - The collection of txs that was received as a response to the request.
645
1099
  * @param peerId - The peer ID of the peer that sent the tx.
646
- * @returns True if the tx is valid, false otherwise.
1100
+ * @returns True if the whole collection of txs is valid, false otherwise.
647
1101
  */
648
1102
  @trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
649
1103
  [Attributes.TX_HASH]: requestedTxHash.toString(),
650
1104
  }))
651
- private async validateRequestedTx(requestedTxHash: TxHash, responseTx: Tx, peerId: PeerId): Promise<boolean> {
652
- const proofValidator = new TxProofValidator(this.proofVerifier);
653
- const validProof = await proofValidator.validateTx(responseTx);
654
-
655
- // If the node returns the wrong data, we penalize it
656
- if (!requestedTxHash.equals(await responseTx.getTxHash())) {
657
- // Returning the wrong data is a low tolerance error
658
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1105
+ private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
1106
+ const requested = new Set(requestedTxHash.map(h => h.toString()));
1107
+ const requestedTxValidator = this.createRequestedTxValidator();
1108
+
1109
+ //TODO: (mralj) - this is somewhat naive implementation, if single tx is invlid we consider the whole response invalid.
1110
+ // I think we should still extract the valid txs and return them, so that we can still use the response.
1111
+ try {
1112
+ await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
1113
+ return true;
1114
+ } catch (e: any) {
1115
+ if (e instanceof ValidationError) {
1116
+ this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
1117
+ } else {
1118
+ this.logger.error(`Error during validation of requested txs`, e);
1119
+ }
1120
+
659
1121
  return false;
660
1122
  }
1123
+ }
1124
+
1125
+ /**
1126
+ * Validates a BLOCK response.
1127
+ *
1128
+ * If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
1129
+ * Penalizes on block number mismatch or hash mismatch.
1130
+ *
1131
+ * @param requestedBlockNumber - The requested block number.
1132
+ * @param responseBlock - The block returned by the peer.
1133
+ * @param peerId - The peer that returned the block.
1134
+ * @returns True if the response is valid, false otherwise.
1135
+ */
1136
+ @trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
1137
+ [Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
1138
+ }))
1139
+ private async validateRequestedBlock(
1140
+ requestedBlockNumber: Fr,
1141
+ responseBlock: L2Block,
1142
+ peerId: PeerId,
1143
+ ): Promise<boolean> {
1144
+ try {
1145
+ const reqNum = Number(requestedBlockNumber.toString());
1146
+ if (responseBlock.number !== reqNum) {
1147
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1148
+ return false;
1149
+ }
1150
+
1151
+ const local = await this.archiver.getBlock(reqNum);
1152
+ if (!local) {
1153
+ // We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
1154
+ // TODO: Consider extending this validator to accept an expected hash or
1155
+ // performing quorum-based checks when using P2P syncing prior to L1 sync.
1156
+ this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
1157
+ return false;
1158
+ }
1159
+ const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
1160
+ if (!localHash.equals(respHash)) {
1161
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1162
+ return false;
1163
+ }
661
1164
 
662
- if (validProof.result === 'invalid') {
663
- // If the proof is invalid, but the txHash is correct, then this is an active attack and we severly punish
664
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1165
+ return true;
1166
+ } catch (e) {
1167
+ this.logger.warn(`Error validating requested block`, e);
665
1168
  return false;
666
1169
  }
1170
+ }
667
1171
 
668
- return true;
1172
+ private createRequestedTxValidator(): TxValidator {
1173
+ return new AggregateTxValidator(
1174
+ new DataTxValidator(),
1175
+ new MetadataTxValidator({
1176
+ l1ChainId: new Fr(this.config.l1ChainId),
1177
+ rollupVersion: new Fr(this.config.rollupVersion),
1178
+ protocolContractsHash,
1179
+ vkTreeRoot: getVKTreeRoot(),
1180
+ }),
1181
+ new TxProofValidator(this.proofVerifier),
1182
+ );
669
1183
  }
670
1184
 
671
- @trackSpan('Libp2pService.validatePropagatedTx', async tx => ({
672
- [Attributes.TX_HASH]: (await tx.getTxHash()).toString(),
1185
+ private async validateRequestedTx(tx: Tx, peerId: PeerId, txValidator: TxValidator, requested?: Set<`0x${string}`>) {
1186
+ const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
1187
+
1188
+ if (!(await tx.validateTxHash())) {
1189
+ penalize(PeerErrorSeverity.MidToleranceError);
1190
+ throw new ValidationError(`Received tx with invalid hash ${tx.getTxHash().toString()}.`);
1191
+ }
1192
+
1193
+ if (requested && !requested.has(tx.getTxHash().toString())) {
1194
+ penalize(PeerErrorSeverity.MidToleranceError);
1195
+ throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
1196
+ }
1197
+
1198
+ const { result } = await txValidator.validateTx(tx);
1199
+ if (result === 'invalid') {
1200
+ penalize(PeerErrorSeverity.LowToleranceError);
1201
+ throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
1202
+ }
1203
+ }
1204
+
1205
+ @trackSpan('Libp2pService.validatePropagatedTx', tx => ({
1206
+ [Attributes.TX_HASH]: tx.getTxHash().toString(),
673
1207
  }))
674
1208
  private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
675
- const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1;
676
- const messageValidators = this.createMessageValidators(blockNumber);
677
- const outcome = await this.runValidations(tx, messageValidators);
1209
+ const currentBlockNumber = await this.archiver.getBlockNumber();
678
1210
 
679
- if (outcome.allPassed) {
680
- return true;
1211
+ // We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
1212
+ const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1213
+ const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
1214
+
1215
+ for (const validator of messageValidators) {
1216
+ const outcome = await this.runValidations(tx, validator);
1217
+
1218
+ if (outcome.allPassed) {
1219
+ continue;
1220
+ }
1221
+ const { name } = outcome.failure;
1222
+ let { severity } = outcome.failure;
1223
+
1224
+ // Double spend validator has a special case handler
1225
+ if (name === 'doubleSpendValidator') {
1226
+ const txBlockNumber = currentBlockNumber + 1; // tx is expected to be in the next block
1227
+ severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
1228
+ }
1229
+
1230
+ this.peerManager.penalizePeer(peerId, severity);
1231
+ return false;
681
1232
  }
682
- const { name } = outcome.failure;
683
- let { severity } = outcome.failure;
1233
+ return true;
1234
+ }
684
1235
 
685
- // Double spend validator has a special case handler
686
- if (name === 'doubleSpendValidator') {
687
- severity = await this.handleDoubleSpendFailure(tx, blockNumber);
1236
+ private async getGasFees(blockNumber: number): Promise<GasFees> {
1237
+ if (blockNumber === this.feesCache?.blockNumber) {
1238
+ return this.feesCache.gasFees;
688
1239
  }
689
1240
 
690
- this.peerManager.penalizePeer(peerId, severity);
691
- return false;
1241
+ const header = await this.archiver.getBlockHeader(blockNumber);
1242
+ const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
1243
+ this.feesCache = { blockNumber, gasFees };
1244
+ return gasFees;
1245
+ }
1246
+
1247
+ public async validate(txs: Tx[]): Promise<void> {
1248
+ const currentBlockNumber = await this.archiver.getBlockNumber();
1249
+
1250
+ // We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
1251
+ const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1252
+ const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
1253
+
1254
+ await Promise.all(
1255
+ txs.map(async tx => {
1256
+ for (const validator of messageValidators) {
1257
+ const outcome = await this.runValidations(tx, validator);
1258
+ if (!outcome.allPassed) {
1259
+ throw new Error('Invalid tx detected', { cause: { outcome } });
1260
+ }
1261
+ }
1262
+ }),
1263
+ );
692
1264
  }
693
1265
 
694
1266
  /**
695
- * Create message validators for the given block number.
1267
+ * Create message validators for the given block number and timestamp.
696
1268
  *
697
1269
  * Each validator is a pair of a validator and a severity.
698
1270
  * If a validator fails, the peer is penalized with the severity of the validator.
699
1271
  *
700
- * @param blockNumber - The block number to create validators for.
1272
+ * @param currentBlockNumber - The current synced block number.
1273
+ * @param nextSlotTimestamp - The timestamp of the next slot (used to validate txs are not expired).
701
1274
  * @returns The message validators.
702
1275
  */
703
- private createMessageValidators(blockNumber: number): Record<string, MessageValidator> {
704
- return {
705
- dataValidator: {
706
- validator: new DataTxValidator(),
707
- severity: PeerErrorSeverity.HighToleranceError,
708
- },
709
- metadataValidator: {
710
- validator: new MetadataTxValidator(new Fr(this.config.l1ChainId), new Fr(blockNumber)),
711
- severity: PeerErrorSeverity.HighToleranceError,
712
- },
713
- proofValidator: {
714
- validator: new TxProofValidator(this.proofVerifier),
715
- severity: PeerErrorSeverity.MidToleranceError,
716
- },
717
- doubleSpendValidator: {
718
- validator: new DoubleSpendTxValidator({
719
- nullifiersExist: async (nullifiers: Buffer[]) => {
720
- const merkleTree = this.worldStateSynchronizer.getCommitted();
721
- const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
722
- return indices.map(index => index !== undefined);
723
- },
724
- }),
725
- severity: PeerErrorSeverity.HighToleranceError,
726
- },
727
- };
1276
+ private async createMessageValidators(
1277
+ currentBlockNumber: number,
1278
+ nextSlotTimestamp: UInt64,
1279
+ ): Promise<Record<string, MessageValidator>[]> {
1280
+ const gasFees = await this.getGasFees(currentBlockNumber);
1281
+ const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
1282
+
1283
+ const blockNumberInWhichTheTxIsConsideredToBeIncluded = currentBlockNumber + 1;
1284
+
1285
+ return createTxMessageValidators(
1286
+ nextSlotTimestamp,
1287
+ blockNumberInWhichTheTxIsConsideredToBeIncluded,
1288
+ this.worldStateSynchronizer,
1289
+ gasFees,
1290
+ this.config.l1ChainId,
1291
+ this.config.rollupVersion,
1292
+ protocolContractsHash,
1293
+ this.archiver,
1294
+ this.proofVerifier,
1295
+ !this.config.disableTransactions,
1296
+ allowedInSetup,
1297
+ );
728
1298
  }
729
1299
 
730
1300
  /**
@@ -739,28 +1309,26 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
739
1309
  ): Promise<ValidationOutcome> {
740
1310
  const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
741
1311
  const { result } = await validator.validateTx(tx);
742
- return { name, isValid: result === 'valid', severity };
1312
+ return { name, isValid: result !== 'invalid', severity };
743
1313
  });
744
1314
 
745
1315
  // A promise that resolves when all validations have been run
746
- const allValidations = Promise.all(validationPromises);
747
-
748
- // A promise that resolves when the first validation fails
749
- const firstFailure = Promise.race(
750
- validationPromises.map(async promise => {
751
- const result = await promise;
752
- return result.isValid ? new Promise(() => {}) : result;
753
- }),
754
- );
755
-
756
- // Wait for the first validation to fail or all validations to pass
757
- const result = await Promise.race([
758
- allValidations.then(() => ({ allPassed: true as const })),
759
- firstFailure.then(failure => ({ allPassed: false as const, failure: failure as ValidationResult })),
760
- ]);
761
-
762
- // If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail
763
- return result;
1316
+ const allValidations = await Promise.all(validationPromises);
1317
+ const failed = allValidations.find(x => !x.isValid);
1318
+ if (failed) {
1319
+ return {
1320
+ allPassed: false,
1321
+ failure: {
1322
+ isValid: { result: 'invalid' as const, reason: ['Failed validation'] },
1323
+ name: failed.name,
1324
+ severity: failed.severity,
1325
+ },
1326
+ };
1327
+ } else {
1328
+ return {
1329
+ allPassed: true,
1330
+ };
1331
+ }
764
1332
  }
765
1333
 
766
1334
  /**
@@ -804,10 +1372,9 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
804
1372
  * @returns True if the attestation is valid, false otherwise.
805
1373
  */
806
1374
  @trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
807
- [Attributes.BLOCK_NUMBER]: attestation.payload.header.globalVariables.blockNumber.toNumber(),
808
- [Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
1375
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
809
1376
  [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
810
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
1377
+ [Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
811
1378
  }))
812
1379
  public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
813
1380
  const severity = await this.attestationValidator.validate(attestation);
@@ -826,11 +1393,12 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
826
1393
  * @returns True if the block proposal is valid, false otherwise.
827
1394
  */
828
1395
  @trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
829
- [Attributes.SLOT_NUMBER]: block.payload.header.globalVariables.slotNumber.toString(),
1396
+ [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
830
1397
  }))
831
1398
  public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<boolean> {
832
1399
  const severity = await this.blockProposalValidator.validate(block);
833
1400
  if (severity) {
1401
+ this.logger.debug(`Penalizing peer ${peerId} for block proposal validation failure`);
834
1402
  this.peerManager.penalizePeer(peerId, severity);
835
1403
  return false;
836
1404
  }
@@ -842,13 +1410,17 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
842
1410
  return this.node.services.pubsub.score.score(peerId.toString());
843
1411
  }
844
1412
 
1413
+ public handleAuthRequestFromPeer(authRequest: AuthRequest, peerId: PeerId): Promise<StatusMessage> {
1414
+ return this.peerManager.handleAuthRequestFromPeer(authRequest, peerId);
1415
+ }
1416
+
845
1417
  private async sendToPeers<T extends Gossipable>(message: T) {
846
1418
  const parent = message.constructor as typeof Gossipable;
847
1419
 
848
- const identifier = await message.p2pMessageIdentifier().then(i => i.toString());
1420
+ const identifier = await message.p2pMessageLoggingIdentifier().then(i => i.toString());
849
1421
  this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
850
1422
 
851
- const recipientsNum = await this.publishToTopic(parent.p2pTopic, message.toBuffer());
1423
+ const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);
852
1424
  this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, {
853
1425
  p2pMessageIdentifier: identifier,
854
1426
  sourcePeer: this.node.peerId.toString(),