@aztec/p2p 0.0.0-test.1 → 0.0.1-commit.0b941701

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