@aztec/p2p 0.0.1-commit.9b94fc1 → 0.0.1-commit.9badcec54

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 (563) hide show
  1. package/README.md +129 -3
  2. package/dest/bootstrap/bootstrap.d.ts +4 -3
  3. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  4. package/dest/bootstrap/bootstrap.js +4 -4
  5. package/dest/client/factory.d.ts +11 -11
  6. package/dest/client/factory.d.ts.map +1 -1
  7. package/dest/client/factory.js +60 -20
  8. package/dest/client/interface.d.ts +68 -35
  9. package/dest/client/interface.d.ts.map +1 -1
  10. package/dest/client/p2p_client.d.ts +53 -65
  11. package/dest/client/p2p_client.d.ts.map +1 -1
  12. package/dest/client/p2p_client.js +632 -335
  13. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +2 -0
  14. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +1 -0
  15. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +315 -0
  16. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +73 -0
  17. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +1 -0
  18. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +8 -0
  19. package/dest/config.d.ts +137 -87
  20. package/dest/config.d.ts.map +1 -1
  21. package/dest/config.js +124 -54
  22. package/dest/errors/p2p-service.error.d.ts +9 -0
  23. package/dest/errors/p2p-service.error.d.ts.map +1 -0
  24. package/dest/errors/p2p-service.error.js +10 -0
  25. package/dest/errors/tx-pool.error.d.ts +8 -0
  26. package/dest/errors/tx-pool.error.d.ts.map +1 -0
  27. package/dest/errors/tx-pool.error.js +9 -0
  28. package/dest/index.d.ts +2 -2
  29. package/dest/index.d.ts.map +1 -1
  30. package/dest/index.js +1 -1
  31. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +113 -76
  32. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  33. package/dest/mem_pools/attestation_pool/attestation_pool.js +448 -3
  34. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
  35. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  36. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +529 -289
  37. package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
  38. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
  39. package/dest/mem_pools/attestation_pool/index.js +1 -2
  40. package/dest/mem_pools/attestation_pool/mocks.d.ts +11 -8
  41. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  42. package/dest/mem_pools/attestation_pool/mocks.js +17 -13
  43. package/dest/mem_pools/index.d.ts +3 -3
  44. package/dest/mem_pools/index.d.ts.map +1 -1
  45. package/dest/mem_pools/index.js +1 -1
  46. package/dest/mem_pools/instrumentation.d.ts +9 -1
  47. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  48. package/dest/mem_pools/instrumentation.js +33 -13
  49. package/dest/mem_pools/interface.d.ts +6 -7
  50. package/dest/mem_pools/interface.d.ts.map +1 -1
  51. package/dest/mem_pools/tx_pool_v2/archive/index.d.ts +2 -0
  52. package/dest/mem_pools/tx_pool_v2/archive/index.d.ts.map +1 -0
  53. package/dest/mem_pools/tx_pool_v2/archive/index.js +1 -0
  54. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +43 -0
  55. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -0
  56. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +103 -0
  57. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +104 -0
  58. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
  59. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +251 -0
  60. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +47 -0
  61. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -0
  62. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +128 -0
  63. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +17 -0
  64. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -0
  65. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +94 -0
  66. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +19 -0
  67. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -0
  68. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +97 -0
  69. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +10 -0
  70. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -0
  71. package/dest/mem_pools/tx_pool_v2/eviction/index.js +11 -0
  72. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +180 -0
  73. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -0
  74. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +25 -0
  75. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +15 -0
  76. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -0
  77. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +65 -0
  78. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +17 -0
  79. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -0
  80. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +93 -0
  81. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +16 -0
  82. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -0
  83. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +78 -0
  84. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +20 -0
  85. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -0
  86. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +75 -0
  87. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +15 -0
  88. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -0
  89. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +19 -0
  90. package/dest/mem_pools/tx_pool_v2/index.d.ts +6 -0
  91. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -0
  92. package/dest/mem_pools/tx_pool_v2/index.js +5 -0
  93. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +15 -0
  94. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
  95. package/dest/mem_pools/tx_pool_v2/instrumentation.js +43 -0
  96. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +215 -0
  97. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -0
  98. package/dest/mem_pools/tx_pool_v2/interfaces.js +10 -0
  99. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +134 -0
  100. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -0
  101. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +220 -0
  102. package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts +26 -0
  103. package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts.map +1 -0
  104. package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.js +70 -0
  105. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +108 -0
  106. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
  107. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +337 -0
  108. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +62 -0
  109. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -0
  110. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +167 -0
  111. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +78 -0
  112. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -0
  113. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +923 -0
  114. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +8 -5
  115. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  116. package/dest/msg_validators/attestation_validator/attestation_validator.js +63 -21
  117. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +9 -7
  118. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  119. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +24 -15
  120. package/dest/msg_validators/clock_tolerance.d.ts +32 -0
  121. package/dest/msg_validators/clock_tolerance.d.ts.map +1 -0
  122. package/dest/msg_validators/clock_tolerance.js +88 -0
  123. package/dest/msg_validators/index.d.ts +2 -2
  124. package/dest/msg_validators/index.d.ts.map +1 -1
  125. package/dest/msg_validators/index.js +1 -1
  126. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +12 -0
  127. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -0
  128. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +14 -0
  129. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +12 -0
  130. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -0
  131. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +20 -0
  132. package/dest/msg_validators/proposal_validator/index.d.ts +4 -0
  133. package/dest/msg_validators/proposal_validator/index.d.ts.map +1 -0
  134. package/dest/msg_validators/proposal_validator/index.js +3 -0
  135. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +20 -0
  136. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -0
  137. package/dest/msg_validators/proposal_validator/proposal_validator.js +124 -0
  138. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +4 -4
  139. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  140. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +3 -3
  141. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
  142. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
  143. package/dest/msg_validators/tx_validator/allowed_public_setup.js +24 -20
  144. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
  145. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
  146. package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
  147. package/dest/msg_validators/tx_validator/archive_cache.d.ts +3 -3
  148. package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -1
  149. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +20 -6
  150. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  151. package/dest/msg_validators/tx_validator/block_header_validator.js +4 -3
  152. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
  153. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
  154. package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
  155. package/dest/msg_validators/tx_validator/data_validator.d.ts +3 -1
  156. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  157. package/dest/msg_validators/tx_validator/data_validator.js +39 -3
  158. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +15 -4
  159. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  160. package/dest/msg_validators/tx_validator/double_spend_validator.js +7 -6
  161. package/dest/msg_validators/tx_validator/factory.d.ts +139 -6
  162. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  163. package/dest/msg_validators/tx_validator/factory.js +255 -58
  164. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +10 -0
  165. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -0
  166. package/dest/msg_validators/tx_validator/fee_payer_balance.js +24 -0
  167. package/dest/msg_validators/tx_validator/gas_validator.d.ts +68 -3
  168. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  169. package/dest/msg_validators/tx_validator/gas_validator.js +122 -58
  170. package/dest/msg_validators/tx_validator/index.d.ts +4 -1
  171. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  172. package/dest/msg_validators/tx_validator/index.js +3 -0
  173. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +4 -3
  174. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  175. package/dest/msg_validators/tx_validator/metadata_validator.js +6 -6
  176. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
  177. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
  178. package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
  179. package/dest/msg_validators/tx_validator/phases_validator.d.ts +24 -3
  180. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  181. package/dest/msg_validators/tx_validator/phases_validator.js +75 -27
  182. package/dest/msg_validators/tx_validator/size_validator.d.ts +8 -0
  183. package/dest/msg_validators/tx_validator/size_validator.d.ts.map +1 -0
  184. package/dest/msg_validators/tx_validator/size_validator.js +23 -0
  185. package/dest/msg_validators/tx_validator/test_utils.d.ts +2 -2
  186. package/dest/msg_validators/tx_validator/test_utils.d.ts.map +1 -1
  187. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +23 -5
  188. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  189. package/dest/msg_validators/tx_validator/timestamp_validator.js +8 -8
  190. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts +3 -2
  191. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -1
  192. package/dest/msg_validators/tx_validator/tx_permitted_validator.js +2 -2
  193. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +3 -2
  194. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  195. package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -2
  196. package/dest/services/data_store.d.ts +1 -1
  197. package/dest/services/data_store.d.ts.map +1 -1
  198. package/dest/services/data_store.js +14 -10
  199. package/dest/services/discv5/discV5_service.js +1 -1
  200. package/dest/services/dummy_service.d.ts +31 -4
  201. package/dest/services/dummy_service.d.ts.map +1 -1
  202. package/dest/services/dummy_service.js +54 -1
  203. package/dest/services/encoding.d.ts +7 -3
  204. package/dest/services/encoding.d.ts.map +1 -1
  205. package/dest/services/encoding.js +23 -15
  206. package/dest/services/gossipsub/index.d.ts +3 -0
  207. package/dest/services/gossipsub/index.d.ts.map +1 -0
  208. package/dest/services/gossipsub/index.js +2 -0
  209. package/dest/services/gossipsub/scoring.d.ts +21 -3
  210. package/dest/services/gossipsub/scoring.d.ts.map +1 -1
  211. package/dest/services/gossipsub/scoring.js +24 -7
  212. package/dest/services/gossipsub/topic_score_params.d.ts +184 -0
  213. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
  214. package/dest/services/gossipsub/topic_score_params.js +363 -0
  215. package/dest/services/index.d.ts +2 -1
  216. package/dest/services/index.d.ts.map +1 -1
  217. package/dest/services/index.js +1 -0
  218. package/dest/services/libp2p/instrumentation.d.ts +3 -1
  219. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  220. package/dest/services/libp2p/instrumentation.js +43 -71
  221. package/dest/services/libp2p/libp2p_service.d.ts +109 -44
  222. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  223. package/dest/services/libp2p/libp2p_service.js +1128 -431
  224. package/dest/services/peer-manager/metrics.d.ts +9 -2
  225. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  226. package/dest/services/peer-manager/metrics.js +39 -21
  227. package/dest/services/peer-manager/peer_manager.d.ts +7 -3
  228. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  229. package/dest/services/peer-manager/peer_manager.js +43 -23
  230. package/dest/services/peer-manager/peer_scoring.d.ts +7 -2
  231. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  232. package/dest/services/peer-manager/peer_scoring.js +64 -16
  233. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +51 -0
  234. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -0
  235. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +543 -0
  236. package/dest/services/reqresp/batch-tx-requester/config.d.ts +17 -0
  237. package/dest/services/reqresp/batch-tx-requester/config.d.ts.map +1 -0
  238. package/dest/services/reqresp/batch-tx-requester/config.js +27 -0
  239. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +47 -0
  240. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -0
  241. package/dest/services/reqresp/batch-tx-requester/interface.js +1 -0
  242. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +35 -0
  243. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -0
  244. package/dest/services/reqresp/batch-tx-requester/missing_txs.js +136 -0
  245. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +62 -0
  246. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -0
  247. package/dest/services/reqresp/batch-tx-requester/peer_collection.js +176 -0
  248. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +20 -0
  249. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -0
  250. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +21 -0
  251. package/dest/services/reqresp/config.d.ts +3 -3
  252. package/dest/services/reqresp/config.d.ts.map +1 -1
  253. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +22 -3
  254. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
  255. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +63 -4
  256. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +2 -1
  257. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  258. package/dest/services/reqresp/connection-sampler/connection_sampler.js +12 -0
  259. package/dest/services/reqresp/constants.d.ts +12 -0
  260. package/dest/services/reqresp/constants.d.ts.map +1 -0
  261. package/dest/services/reqresp/constants.js +7 -0
  262. package/dest/services/reqresp/interface.d.ts +25 -9
  263. package/dest/services/reqresp/interface.d.ts.map +1 -1
  264. package/dest/services/reqresp/interface.js +23 -10
  265. package/dest/services/reqresp/metrics.d.ts +6 -5
  266. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  267. package/dest/services/reqresp/metrics.js +16 -21
  268. package/dest/services/reqresp/protocols/auth.d.ts +2 -2
  269. package/dest/services/reqresp/protocols/auth.d.ts.map +1 -1
  270. package/dest/services/reqresp/protocols/auth.js +2 -2
  271. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +5 -1
  272. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  273. package/dest/services/reqresp/protocols/block_txs/bitvector.js +12 -0
  274. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
  275. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  276. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +27 -9
  277. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +30 -7
  278. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  279. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +60 -14
  280. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  281. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  282. package/dest/services/reqresp/protocols/index.js +0 -1
  283. package/dest/services/reqresp/protocols/status.d.ts +5 -4
  284. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  285. package/dest/services/reqresp/protocols/status.js +7 -3
  286. package/dest/services/reqresp/protocols/tx.d.ts +8 -3
  287. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  288. package/dest/services/reqresp/protocols/tx.js +21 -3
  289. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
  290. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  291. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
  292. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  293. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  294. package/dest/services/reqresp/rate-limiter/rate_limits.js +0 -10
  295. package/dest/services/reqresp/reqresp.d.ts +9 -2
  296. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  297. package/dest/services/reqresp/reqresp.js +497 -58
  298. package/dest/services/service.d.ts +59 -4
  299. package/dest/services/service.d.ts.map +1 -1
  300. package/dest/services/tx_collection/config.d.ts +22 -1
  301. package/dest/services/tx_collection/config.d.ts.map +1 -1
  302. package/dest/services/tx_collection/config.js +56 -2
  303. package/dest/services/tx_collection/fast_tx_collection.d.ts +10 -9
  304. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  305. package/dest/services/tx_collection/fast_tx_collection.js +95 -84
  306. package/dest/services/tx_collection/file_store_tx_collection.d.ts +53 -0
  307. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  308. package/dest/services/tx_collection/file_store_tx_collection.js +167 -0
  309. package/dest/services/tx_collection/file_store_tx_source.d.ts +38 -0
  310. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  311. package/dest/services/tx_collection/file_store_tx_source.js +100 -0
  312. package/dest/services/tx_collection/index.d.ts +3 -1
  313. package/dest/services/tx_collection/index.d.ts.map +1 -1
  314. package/dest/services/tx_collection/index.js +2 -0
  315. package/dest/services/tx_collection/instrumentation.d.ts +1 -1
  316. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
  317. package/dest/services/tx_collection/instrumentation.js +11 -13
  318. package/dest/services/tx_collection/proposal_tx_collector.d.ts +48 -0
  319. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -0
  320. package/dest/services/tx_collection/proposal_tx_collector.js +50 -0
  321. package/dest/services/tx_collection/request_tracker.d.ts +53 -0
  322. package/dest/services/tx_collection/request_tracker.d.ts.map +1 -0
  323. package/dest/services/tx_collection/request_tracker.js +84 -0
  324. package/dest/services/tx_collection/slow_tx_collection.d.ts +9 -4
  325. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  326. package/dest/services/tx_collection/slow_tx_collection.js +60 -26
  327. package/dest/services/tx_collection/tx_collection.d.ts +31 -20
  328. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  329. package/dest/services/tx_collection/tx_collection.js +79 -7
  330. package/dest/services/tx_collection/tx_collection_sink.d.ts +18 -8
  331. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  332. package/dest/services/tx_collection/tx_collection_sink.js +26 -29
  333. package/dest/services/tx_collection/tx_source.d.ts +13 -7
  334. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  335. package/dest/services/tx_collection/tx_source.js +26 -7
  336. package/dest/services/tx_file_store/config.d.ts +16 -0
  337. package/dest/services/tx_file_store/config.d.ts.map +1 -0
  338. package/dest/services/tx_file_store/config.js +22 -0
  339. package/dest/services/tx_file_store/index.d.ts +4 -0
  340. package/dest/services/tx_file_store/index.d.ts.map +1 -0
  341. package/dest/services/tx_file_store/index.js +3 -0
  342. package/dest/services/tx_file_store/instrumentation.d.ts +15 -0
  343. package/dest/services/tx_file_store/instrumentation.d.ts.map +1 -0
  344. package/dest/services/tx_file_store/instrumentation.js +29 -0
  345. package/dest/services/tx_file_store/tx_file_store.d.ts +48 -0
  346. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -0
  347. package/dest/services/tx_file_store/tx_file_store.js +152 -0
  348. package/dest/services/tx_provider.d.ts +7 -5
  349. package/dest/services/tx_provider.d.ts.map +1 -1
  350. package/dest/services/tx_provider.js +20 -10
  351. package/dest/services/tx_provider_instrumentation.d.ts +5 -2
  352. package/dest/services/tx_provider_instrumentation.d.ts.map +1 -1
  353. package/dest/services/tx_provider_instrumentation.js +14 -14
  354. package/dest/test-helpers/index.d.ts +3 -1
  355. package/dest/test-helpers/index.d.ts.map +1 -1
  356. package/dest/test-helpers/index.js +2 -0
  357. package/dest/test-helpers/make-test-p2p-clients.d.ts +7 -8
  358. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  359. package/dest/test-helpers/make-test-p2p-clients.js +1 -2
  360. package/dest/test-helpers/mock-pubsub.d.ts +40 -6
  361. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  362. package/dest/test-helpers/mock-pubsub.js +139 -13
  363. package/dest/test-helpers/mock-tx-helpers.js +1 -1
  364. package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
  365. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  366. package/dest/test-helpers/reqresp-nodes.js +5 -5
  367. package/dest/test-helpers/test_tx_provider.d.ts +40 -0
  368. package/dest/test-helpers/test_tx_provider.d.ts.map +1 -0
  369. package/dest/test-helpers/test_tx_provider.js +41 -0
  370. package/dest/test-helpers/testbench-utils.d.ts +163 -0
  371. package/dest/test-helpers/testbench-utils.d.ts.map +1 -0
  372. package/dest/test-helpers/testbench-utils.js +386 -0
  373. package/dest/testbench/p2p_client_testbench_worker.d.ts +28 -2
  374. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
  375. package/dest/testbench/p2p_client_testbench_worker.js +281 -134
  376. package/dest/testbench/worker_client_manager.d.ts +60 -6
  377. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  378. package/dest/testbench/worker_client_manager.js +280 -41
  379. package/dest/util.d.ts +3 -3
  380. package/dest/util.d.ts.map +1 -1
  381. package/package.json +18 -18
  382. package/src/bootstrap/bootstrap.ts +7 -4
  383. package/src/client/factory.ts +116 -45
  384. package/src/client/interface.ts +81 -36
  385. package/src/client/p2p_client.ts +299 -396
  386. package/src/client/test/tx_proposal_collector/README.md +227 -0
  387. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +355 -0
  388. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +43 -0
  389. package/src/config.ts +196 -64
  390. package/src/errors/p2p-service.error.ts +11 -0
  391. package/src/errors/tx-pool.error.ts +12 -0
  392. package/src/index.ts +1 -1
  393. package/src/mem_pools/attestation_pool/attestation_pool.ts +515 -78
  394. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +614 -322
  395. package/src/mem_pools/attestation_pool/index.ts +9 -2
  396. package/src/mem_pools/attestation_pool/mocks.ts +22 -15
  397. package/src/mem_pools/index.ts +2 -2
  398. package/src/mem_pools/instrumentation.ts +43 -14
  399. package/src/mem_pools/interface.ts +5 -7
  400. package/src/mem_pools/tx_pool_v2/README.md +283 -0
  401. package/src/mem_pools/tx_pool_v2/archive/index.ts +1 -0
  402. package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +120 -0
  403. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +321 -0
  404. package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +160 -0
  405. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +122 -0
  406. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +125 -0
  407. package/src/mem_pools/tx_pool_v2/eviction/index.ts +27 -0
  408. package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +219 -0
  409. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +74 -0
  410. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +101 -0
  411. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +91 -0
  412. package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +99 -0
  413. package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +32 -0
  414. package/src/mem_pools/tx_pool_v2/index.ts +12 -0
  415. package/src/mem_pools/tx_pool_v2/instrumentation.ts +69 -0
  416. package/src/mem_pools/tx_pool_v2/interfaces.ts +247 -0
  417. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +343 -0
  418. package/src/mem_pools/tx_pool_v2/tx_pool_bench_metrics.ts +77 -0
  419. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +430 -0
  420. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +238 -0
  421. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +1105 -0
  422. package/src/msg_validators/attestation_validator/README.md +49 -0
  423. package/src/msg_validators/attestation_validator/attestation_validator.ts +51 -24
  424. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +25 -19
  425. package/src/msg_validators/clock_tolerance.ts +120 -0
  426. package/src/msg_validators/index.ts +1 -1
  427. package/src/msg_validators/proposal_validator/README.md +123 -0
  428. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +23 -0
  429. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +29 -0
  430. package/src/msg_validators/proposal_validator/index.ts +3 -0
  431. package/src/msg_validators/proposal_validator/proposal_validator.ts +122 -0
  432. package/src/msg_validators/tx_validator/README.md +119 -0
  433. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +5 -5
  434. package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
  435. package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
  436. package/src/msg_validators/tx_validator/archive_cache.ts +2 -2
  437. package/src/msg_validators/tx_validator/block_header_validator.ts +21 -8
  438. package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
  439. package/src/msg_validators/tx_validator/data_validator.ts +60 -7
  440. package/src/msg_validators/tx_validator/double_spend_validator.ts +15 -9
  441. package/src/msg_validators/tx_validator/factory.ts +416 -58
  442. package/src/msg_validators/tx_validator/fee_payer_balance.ts +44 -0
  443. package/src/msg_validators/tx_validator/gas_validator.ts +162 -61
  444. package/src/msg_validators/tx_validator/index.ts +3 -0
  445. package/src/msg_validators/tx_validator/metadata_validator.ts +31 -12
  446. package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
  447. package/src/msg_validators/tx_validator/phases_validator.ts +87 -30
  448. package/src/msg_validators/tx_validator/size_validator.ts +22 -0
  449. package/src/msg_validators/tx_validator/test_utils.ts +1 -1
  450. package/src/msg_validators/tx_validator/timestamp_validator.ts +30 -19
  451. package/src/msg_validators/tx_validator/tx_permitted_validator.ts +8 -3
  452. package/src/msg_validators/tx_validator/tx_proof_validator.ts +8 -3
  453. package/src/services/data_store.ts +14 -19
  454. package/src/services/discv5/discV5_service.ts +1 -1
  455. package/src/services/dummy_service.ts +71 -2
  456. package/src/services/encoding.ts +22 -13
  457. package/src/services/gossipsub/README.md +641 -0
  458. package/src/services/gossipsub/index.ts +2 -0
  459. package/src/services/gossipsub/scoring.ts +29 -5
  460. package/src/services/gossipsub/topic_score_params.ts +519 -0
  461. package/src/services/index.ts +1 -0
  462. package/src/services/libp2p/instrumentation.ts +48 -75
  463. package/src/services/libp2p/libp2p_service.ts +827 -461
  464. package/src/services/peer-manager/metrics.ts +46 -21
  465. package/src/services/peer-manager/peer_manager.ts +50 -15
  466. package/src/services/peer-manager/peer_scoring.ts +55 -9
  467. package/src/services/reqresp/README.md +229 -0
  468. package/src/services/reqresp/batch-tx-requester/README.md +344 -0
  469. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +673 -0
  470. package/src/services/reqresp/batch-tx-requester/config.ts +40 -0
  471. package/src/services/reqresp/batch-tx-requester/interface.ts +54 -0
  472. package/src/services/reqresp/batch-tx-requester/missing_txs.ts +168 -0
  473. package/src/services/reqresp/batch-tx-requester/peer_collection.ts +249 -0
  474. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +37 -0
  475. package/src/services/reqresp/config.ts +2 -2
  476. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +65 -4
  477. package/src/services/reqresp/connection-sampler/connection_sampler.ts +19 -1
  478. package/src/services/reqresp/constants.ts +14 -0
  479. package/src/services/reqresp/interface.ts +48 -10
  480. package/src/services/reqresp/metrics.ts +35 -27
  481. package/src/services/reqresp/protocols/auth.ts +2 -2
  482. package/src/services/reqresp/protocols/block_txs/bitvector.ts +16 -0
  483. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +35 -12
  484. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +75 -10
  485. package/src/services/reqresp/protocols/index.ts +0 -1
  486. package/src/services/reqresp/protocols/status.ts +16 -12
  487. package/src/services/reqresp/protocols/tx.ts +24 -5
  488. package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
  489. package/src/services/reqresp/rate-limiter/rate_limits.ts +0 -10
  490. package/src/services/reqresp/reqresp.ts +116 -32
  491. package/src/services/service.ts +78 -5
  492. package/src/services/tx_collection/config.ts +84 -2
  493. package/src/services/tx_collection/fast_tx_collection.ts +114 -93
  494. package/src/services/tx_collection/file_store_tx_collection.ts +202 -0
  495. package/src/services/tx_collection/file_store_tx_source.ts +129 -0
  496. package/src/services/tx_collection/index.ts +6 -0
  497. package/src/services/tx_collection/instrumentation.ts +11 -13
  498. package/src/services/tx_collection/proposal_tx_collector.ts +108 -0
  499. package/src/services/tx_collection/request_tracker.ts +127 -0
  500. package/src/services/tx_collection/slow_tx_collection.ts +69 -36
  501. package/src/services/tx_collection/tx_collection.ts +123 -27
  502. package/src/services/tx_collection/tx_collection_sink.ts +30 -34
  503. package/src/services/tx_collection/tx_source.ts +28 -8
  504. package/src/services/tx_file_store/config.ts +37 -0
  505. package/src/services/tx_file_store/index.ts +3 -0
  506. package/src/services/tx_file_store/instrumentation.ts +36 -0
  507. package/src/services/tx_file_store/tx_file_store.ts +175 -0
  508. package/src/services/tx_provider.ts +29 -12
  509. package/src/services/tx_provider_instrumentation.ts +24 -14
  510. package/src/test-helpers/index.ts +2 -0
  511. package/src/test-helpers/make-test-p2p-clients.ts +4 -6
  512. package/src/test-helpers/mock-pubsub.ts +178 -15
  513. package/src/test-helpers/mock-tx-helpers.ts +1 -1
  514. package/src/test-helpers/reqresp-nodes.ts +8 -10
  515. package/src/test-helpers/test_tx_provider.ts +64 -0
  516. package/src/test-helpers/testbench-utils.ts +457 -0
  517. package/src/testbench/p2p_client_testbench_worker.ts +410 -131
  518. package/src/testbench/worker_client_manager.ts +367 -43
  519. package/src/util.ts +8 -2
  520. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -37
  521. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  522. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -213
  523. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -30
  524. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  525. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -219
  526. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -115
  527. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  528. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -617
  529. package/dest/mem_pools/tx_pool/index.d.ts +0 -4
  530. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  531. package/dest/mem_pools/tx_pool/index.js +0 -3
  532. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +0 -80
  533. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +0 -1
  534. package/dest/mem_pools/tx_pool/memory_tx_pool.js +0 -238
  535. package/dest/mem_pools/tx_pool/priority.d.ts +0 -8
  536. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  537. package/dest/mem_pools/tx_pool/priority.js +0 -10
  538. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -122
  539. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  540. package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
  541. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  542. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  543. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -394
  544. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +0 -12
  545. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +0 -1
  546. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +0 -82
  547. package/dest/msg_validators/block_proposal_validator/index.d.ts +0 -2
  548. package/dest/msg_validators/block_proposal_validator/index.d.ts.map +0 -1
  549. package/dest/msg_validators/block_proposal_validator/index.js +0 -1
  550. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  551. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  552. package/dest/services/reqresp/protocols/block.js +0 -31
  553. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -298
  554. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -287
  555. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -750
  556. package/src/mem_pools/tx_pool/index.ts +0 -3
  557. package/src/mem_pools/tx_pool/memory_tx_pool.ts +0 -283
  558. package/src/mem_pools/tx_pool/priority.ts +0 -13
  559. package/src/mem_pools/tx_pool/tx_pool.ts +0 -135
  560. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -313
  561. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +0 -97
  562. package/src/msg_validators/block_proposal_validator/index.ts +0 -1
  563. package/src/services/reqresp/protocols/block.ts +0 -36
@@ -1,34 +1,41 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
- import { SlotNumber } from '@aztec/foundation/branded-types';
3
- import { randomInt } from '@aztec/foundation/crypto';
4
- import { Fr } from '@aztec/foundation/fields';
2
+ import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { maxBy } from '@aztec/foundation/collection';
5
4
  import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
6
5
  import { RunningPromise } from '@aztec/foundation/running-promise';
7
6
  import { Timer } from '@aztec/foundation/timer';
8
7
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
9
- import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
10
8
  import { protocolContractsHash } from '@aztec/protocol-contracts';
11
- import type { EthAddress, L2Block, L2BlockSource } from '@aztec/stdlib/block';
9
+ import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block';
12
10
  import type { ContractDataSource } from '@aztec/stdlib/contract';
13
11
  import { GasFees } from '@aztec/stdlib/gas';
14
12
  import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
15
13
  import {
16
- BlockAttestation,
17
14
  BlockProposal,
15
+ CheckpointAttestation,
16
+ CheckpointProposal,
17
+ type CheckpointProposalCore,
18
18
  type Gossipable,
19
- P2PClientType,
20
19
  P2PMessage,
21
20
  PeerErrorSeverity,
21
+ PeerErrorSeverityByHarshness,
22
22
  TopicType,
23
23
  createTopicString,
24
- getTopicsForClientAndConfig,
24
+ getTopicsForConfig,
25
25
  metricsTopicStrToLabels,
26
26
  } from '@aztec/stdlib/p2p';
27
27
  import { MerkleTreeId } from '@aztec/stdlib/trees';
28
28
  import { Tx, type TxHash, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
29
29
  import type { UInt64 } from '@aztec/stdlib/types';
30
30
  import { compressComponentVersions } from '@aztec/stdlib/versioning';
31
- import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
31
+ import {
32
+ Attributes,
33
+ OtelMetricsAdapter,
34
+ SpanStatusCode,
35
+ type TelemetryClient,
36
+ WithTracer,
37
+ trackSpan,
38
+ } from '@aztec/telemetry-client';
32
39
 
33
40
  import {
34
41
  type GossipSub,
@@ -36,7 +43,7 @@ import {
36
43
  type GossipsubMessage,
37
44
  gossipsub,
38
45
  } from '@chainsafe/libp2p-gossipsub';
39
- import { createPeerScoreParams, createTopicScoreParams } from '@chainsafe/libp2p-gossipsub/score';
46
+ import { createPeerScoreParams } from '@chainsafe/libp2p-gossipsub/score';
40
47
  import { SignaturePolicy } from '@chainsafe/libp2p-gossipsub/types';
41
48
  import { noise } from '@chainsafe/libp2p-noise';
42
49
  import { yamux } from '@chainsafe/libp2p-yamux';
@@ -50,58 +57,65 @@ import { ENR } from '@nethermindeth/enr';
50
57
  import { createLibp2p } from 'libp2p';
51
58
 
52
59
  import type { P2PConfig } from '../../config.js';
53
- import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
60
+ import { CheckpointProposalReceivedCallbackNotRegisteredError } from '../../errors/p2p-service.error.js';
54
61
  import type { MemPools } from '../../mem_pools/interface.js';
55
62
  import {
56
- AttestationValidator,
57
63
  BlockProposalValidator,
64
+ CheckpointAttestationValidator,
65
+ CheckpointProposalValidator,
66
+ DoubleSpendTxValidator,
58
67
  FishermanAttestationValidator,
68
+ getDefaultAllowedSetupFunctions,
59
69
  } from '../../msg_validators/index.js';
60
70
  import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
61
- import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
62
- import { type MessageValidator, createTxMessageValidators } from '../../msg_validators/tx_validator/factory.js';
63
71
  import {
64
- AggregateTxValidator,
65
- DataTxValidator,
66
- DoubleSpendTxValidator,
67
- MetadataTxValidator,
68
- TxProofValidator,
69
- } from '../../msg_validators/tx_validator/index.js';
72
+ type TransactionValidator,
73
+ createFirstStageTxValidationsForGossipedTransactions,
74
+ createSecondStageTxValidationsForGossipedTransactions,
75
+ createTxValidatorForBlockProposalReceivedTxs,
76
+ createTxValidatorForReqResponseReceivedTxs,
77
+ } from '../../msg_validators/tx_validator/factory.js';
70
78
  import { GossipSubEvent } from '../../types/index.js';
71
79
  import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
72
80
  import { getVersions } from '../../versioning.js';
73
81
  import { AztecDatastore } from '../data_store.js';
74
82
  import { DiscV5Service } from '../discv5/discV5_service.js';
75
83
  import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
76
- import { gossipScoreThresholds } from '../gossipsub/scoring.js';
84
+ import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
85
+ import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
77
86
  import type { PeerManagerInterface } from '../peer-manager/interface.js';
78
87
  import { PeerManager } from '../peer-manager/peer_manager.js';
79
88
  import { PeerScoring } from '../peer-manager/peer_scoring.js';
89
+ import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
80
90
  import type { P2PReqRespConfig } from '../reqresp/config.js';
81
91
  import {
92
+ AuthRequest,
93
+ BlockTxsRequest,
94
+ BlockTxsResponse,
82
95
  DEFAULT_SUB_PROTOCOL_VALIDATORS,
83
96
  type ReqRespInterface,
97
+ type ReqRespResponse,
84
98
  ReqRespSubProtocol,
85
99
  type ReqRespSubProtocolHandler,
86
100
  type ReqRespSubProtocolHandlers,
87
101
  type ReqRespSubProtocolValidators,
102
+ StatusMessage,
88
103
  type SubProtocolMap,
89
104
  ValidationError,
90
- } from '../reqresp/interface.js';
91
- import { reqRespBlockTxsHandler } from '../reqresp/protocols/block_txs/block_txs_handler.js';
92
- import { reqGoodbyeHandler } from '../reqresp/protocols/goodbye.js';
93
- import {
94
- AuthRequest,
95
- BlockTxsRequest,
96
- BlockTxsResponse,
97
- StatusMessage,
98
105
  pingHandler,
99
- reqRespBlockHandler,
106
+ reqGoodbyeHandler,
107
+ reqRespBlockTxsHandler,
100
108
  reqRespStatusHandler,
101
109
  reqRespTxHandler,
102
- } from '../reqresp/protocols/index.js';
110
+ } from '../reqresp/index.js';
103
111
  import { ReqResp } from '../reqresp/reqresp.js';
104
- import type { P2PBlockReceivedCallback, P2PService, PeerDiscoveryService } from '../service.js';
112
+ import type {
113
+ P2PBlockReceivedCallback,
114
+ P2PCheckpointReceivedCallback,
115
+ P2PDuplicateAttestationCallback,
116
+ P2PService,
117
+ PeerDiscoveryService,
118
+ } from '../service.js';
105
119
  import { P2PInstrumentation } from './instrumentation.js';
106
120
 
107
121
  interface ValidationResult {
@@ -113,25 +127,36 @@ interface ValidationResult {
113
127
  type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
114
128
 
115
129
  // REFACTOR: Unify with the type above
116
- type ReceivedMessageValidationResult<T> =
117
- | { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject> }
118
- | { obj?: undefined; result: TopicValidatorResult.Reject };
130
+ type ReceivedMessageValidationResult<T, M = undefined> =
131
+ | { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject>; metadata?: M }
132
+ | { obj?: T; result: TopicValidatorResult.Reject; metadata?: M; severity: PeerErrorSeverity };
119
133
 
120
134
  /**
121
135
  * Lib P2P implementation of the P2PService interface.
122
136
  */
123
- export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
137
+ export class LibP2PService extends WithTracer implements P2PService {
124
138
  private discoveryRunningPromise?: RunningPromise;
125
139
  private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
126
140
 
127
141
  // Message validators
128
- private attestationValidator: AttestationValidator;
129
142
  private blockProposalValidator: BlockProposalValidator;
143
+ private checkpointProposalValidator: CheckpointProposalValidator;
144
+ private checkpointAttestationValidator: CheckpointAttestationValidator;
130
145
 
131
146
  private protocolVersion = '';
132
147
  private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
133
148
 
134
- private feesCache: { blockNumber: number; gasFees: GasFees } | undefined;
149
+ private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
150
+
151
+ /** Callback invoked when a duplicate proposal is detected (triggers slashing). */
152
+ private duplicateProposalCallback?: (info: {
153
+ slot: SlotNumber;
154
+ proposer: EthAddress;
155
+ type: 'checkpoint' | 'block';
156
+ }) => void;
157
+
158
+ /** Callback invoked when a duplicate attestation is detected (triggers slashing). */
159
+ private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
135
160
 
136
161
  /**
137
162
  * Callback for when a block is received from a peer.
@@ -140,21 +165,35 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
140
165
  */
141
166
  private blockReceivedCallback: P2PBlockReceivedCallback;
142
167
 
168
+ /**
169
+ * Callback for when a checkpoint proposal is received from a peer.
170
+ * @param checkpoint - The checkpoint proposal received from the peer.
171
+ * @returns The attestations for the checkpoint, if any.
172
+ */
173
+ private allNodesCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
174
+ /**
175
+ * Callback for when a checkpoint proposal is received - specifically for validators - from a peer.
176
+ * @param checkpoint - The checkpoint proposal received from the peer.
177
+ * @returns The attestations for the checkpoint, if any.
178
+ */
179
+ private validatorCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
180
+
143
181
  private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
144
182
 
145
183
  private instrumentation: P2PInstrumentation;
146
184
 
185
+ private telemetry: TelemetryClient;
186
+
147
187
  protected logger: Logger;
148
188
 
149
189
  constructor(
150
- private clientType: T,
151
190
  private config: P2PConfig,
152
191
  protected node: PubSubLibp2p,
153
192
  private peerDiscoveryService: PeerDiscoveryService,
154
193
  private reqresp: ReqRespInterface,
155
- private peerManager: PeerManagerInterface,
156
- protected mempools: MemPools<T>,
157
- private archiver: L2BlockSource & ContractDataSource,
194
+ protected peerManager: PeerManagerInterface,
195
+ protected mempools: MemPools,
196
+ protected archiver: L2BlockSource & ContractDataSource,
158
197
  private epochCache: EpochCacheInterface,
159
198
  private proofVerifier: ClientProtocolCircuitVerifier,
160
199
  private worldStateSynchronizer: WorldStateSynchronizer,
@@ -162,6 +201,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
162
201
  logger: Logger = createLogger('p2p:libp2p_service'),
163
202
  ) {
164
203
  super(telemetry, 'LibP2PService');
204
+ this.telemetry = telemetry;
165
205
 
166
206
  // Create child logger with fisherman prefix if in fisherman mode
167
207
  this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
@@ -170,7 +210,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
170
210
 
171
211
  this.msgIdSeenValidators[TopicType.tx] = new MessageSeenValidator(config.seenMessageCacheSize);
172
212
  this.msgIdSeenValidators[TopicType.block_proposal] = new MessageSeenValidator(config.seenMessageCacheSize);
173
- this.msgIdSeenValidators[TopicType.block_attestation] = new MessageSeenValidator(config.seenMessageCacheSize);
213
+ this.msgIdSeenValidators[TopicType.checkpoint_proposal] = new MessageSeenValidator(config.seenMessageCacheSize);
214
+ this.msgIdSeenValidators[TopicType.checkpoint_attestation] = new MessageSeenValidator(config.seenMessageCacheSize);
174
215
 
175
216
  const versions = getVersions(config);
176
217
  this.protocolVersion = compressComponentVersions(versions);
@@ -178,25 +219,49 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
178
219
 
179
220
  this.topicStrings[TopicType.tx] = createTopicString(TopicType.tx, this.protocolVersion);
180
221
  this.topicStrings[TopicType.block_proposal] = createTopicString(TopicType.block_proposal, this.protocolVersion);
181
- this.topicStrings[TopicType.block_attestation] = createTopicString(
182
- TopicType.block_attestation,
222
+ this.topicStrings[TopicType.checkpoint_proposal] = createTopicString(
223
+ TopicType.checkpoint_proposal,
224
+ this.protocolVersion,
225
+ );
226
+ this.topicStrings[TopicType.checkpoint_attestation] = createTopicString(
227
+ TopicType.checkpoint_attestation,
183
228
  this.protocolVersion,
184
229
  );
185
230
 
186
- // Use FishermanAttestationValidator in fisherman mode to validate attestation payloads against proposals
187
- this.attestationValidator = config.fishermanMode
188
- ? new FishermanAttestationValidator(epochCache, mempools.attestationPool!, telemetry)
189
- : new AttestationValidator(epochCache);
190
- this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions });
231
+ const p2pPropagationTime = config.attestationPropagationTime;
232
+ const proposalValidatorOpts = {
233
+ txsPermitted: !config.disableTransactions,
234
+ maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
235
+ p2pPropagationTime,
236
+ };
237
+ this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
238
+ this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
239
+ this.checkpointAttestationValidator = config.fishermanMode
240
+ ? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry, {
241
+ l1PublishingTime: config.l1PublishingTime,
242
+ })
243
+ : new CheckpointAttestationValidator(epochCache, { l1PublishingTime: config.l1PublishingTime });
191
244
 
192
245
  this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
193
246
 
194
- this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation[] | undefined> => {
195
- this.logger.debug(
196
- `Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber} from peer.`,
247
+ this.blockReceivedCallback = async (block: BlockProposal): Promise<boolean> => {
248
+ this.logger.warn(
249
+ `Handler for block received not yet registered on P2P service. Received block ${block.blockNumber} for slot ${block.slotNumber} from peer.`,
197
250
  { p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier() },
198
251
  );
199
- return undefined;
252
+ return true;
253
+ };
254
+
255
+ this.allNodesCheckpointReceivedCallback = (
256
+ _checkpoint: CheckpointProposalCore,
257
+ ): Promise<CheckpointAttestation[] | undefined> => {
258
+ throw new CheckpointProposalReceivedCallbackNotRegisteredError();
259
+ };
260
+
261
+ this.validatorCheckpointReceivedCallback = (
262
+ _checkpoint: CheckpointProposalCore,
263
+ ): Promise<CheckpointAttestation[] | undefined> => {
264
+ return Promise.resolve(undefined);
200
265
  };
201
266
  }
202
267
 
@@ -210,12 +275,11 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
210
275
  * @param txPool - The transaction pool to be accessed by the service.
211
276
  * @returns The new service.
212
277
  */
213
- public static async new<T extends P2PClientType>(
214
- clientType: T,
278
+ public static async new(
215
279
  config: P2PConfig,
216
280
  peerId: PeerId,
217
281
  deps: {
218
- mempools: MemPools<T>;
282
+ mempools: MemPools;
219
283
  l2BlockSource: L2BlockSource & ContractDataSource;
220
284
  epochCache: EpochCacheInterface;
221
285
  proofVerifier: ClientProtocolCircuitVerifier;
@@ -242,14 +306,14 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
242
306
 
243
307
  const datastore = new AztecDatastore(peerStore);
244
308
 
245
- const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
309
+ const otelMetricsAdapter = new OtelMetricsAdapter(telemetry, logger.getBindings());
246
310
 
247
311
  const peerDiscoveryService = new DiscV5Service(
248
312
  peerId,
249
313
  config,
250
314
  packageVersion,
251
315
  telemetry,
252
- createLogger(`${logger.module}:discv5_service`),
316
+ createLogger(`${logger.module}:discv5_service`, logger.getBindings()),
253
317
  );
254
318
 
255
319
  // Seed libp2p's bootstrap discovery with private and trusted peers
@@ -263,10 +327,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
263
327
  const versions = getVersions(config);
264
328
  const protocolVersion = compressComponentVersions(versions);
265
329
 
266
- const txTopic = createTopicString(TopicType.tx, protocolVersion);
267
- const blockProposalTopic = createTopicString(TopicType.block_proposal, protocolVersion);
268
- const blockAttestationTopic = createTopicString(TopicType.block_attestation, protocolVersion);
269
-
270
330
  const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
271
331
  const directPeers = (
272
332
  await Promise.all(
@@ -286,6 +346,19 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
286
346
 
287
347
  const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
288
348
 
349
+ // Create dynamic topic score params based on network configuration
350
+ const l1Constants = epochCache.getL1Constants();
351
+ const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
352
+ slotDurationMs: l1Constants.slotDuration * 1000,
353
+ ethereumSlotDuration: l1Constants.ethereumSlotDuration,
354
+ heartbeatIntervalMs: config.gossipsubInterval,
355
+ targetCommitteeSize: l1Constants.targetCommitteeSize,
356
+ blockDurationMs: config.blockDurationMs,
357
+ l1PublishingTime: config.l1PublishingTime,
358
+ p2pPropagationTime: config.attestationPropagationTime,
359
+ expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
360
+ });
361
+
289
362
  const node = await createLibp2p({
290
363
  start: false,
291
364
  peerId,
@@ -381,30 +454,14 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
381
454
  scoreParams: createPeerScoreParams({
382
455
  // IPColocation factor can be disabled for local testing - default to -5
383
456
  IPColocationFactorWeight: config.debugDisableColocationPenalty ? 0 : -5.0,
384
- topics: {
385
- [txTopic]: createTopicScoreParams({
386
- topicWeight: 1,
387
- invalidMessageDeliveriesWeight: -20,
388
- invalidMessageDeliveriesDecay: 0.5,
389
- }),
390
- [blockAttestationTopic]: createTopicScoreParams({
391
- topicWeight: 1,
392
- invalidMessageDeliveriesWeight: -20,
393
- invalidMessageDeliveriesDecay: 0.5,
394
- }),
395
- [blockProposalTopic]: createTopicScoreParams({
396
- topicWeight: 1,
397
- invalidMessageDeliveriesWeight: -20,
398
- invalidMessageDeliveriesDecay: 0.5,
399
- }),
400
- },
457
+ topics: topicScoreParams,
401
458
  }),
402
459
  }) as (components: GossipSubComponents) => GossipSub,
403
460
  components: (components: { connectionManager: ConnectionManager }) => ({
404
461
  connectionManager: components.connectionManager,
405
462
  }),
406
463
  },
407
- logger: createLibp2pComponentLogger(logger.module),
464
+ logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
408
465
  });
409
466
 
410
467
  const peerScoring = new PeerScoring(config, telemetry);
@@ -423,13 +480,19 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
423
480
  epochCache,
424
481
  );
425
482
 
426
- // Update gossipsub score params
427
- node.services.pubsub.score.params.appSpecificWeight = 10;
483
+ // Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
484
+ reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
485
+
486
+ // Configure application-specific scoring for gossipsub.
487
+ // The weight scales app score to align with gossipsub thresholds:
488
+ // - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
489
+ // - Ban (-100) × 10 = -1000 = publishThreshold (cannot publish)
490
+ // Note: positive topic scores can offset penalties, so alignment is best-effort.
491
+ node.services.pubsub.score.params.appSpecificWeight = APP_SPECIFIC_WEIGHT;
428
492
  node.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
429
493
  peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
430
494
 
431
495
  return new LibP2PService(
432
- clientType,
433
496
  config,
434
497
  node,
435
498
  peerDiscoveryService,
@@ -462,33 +525,23 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
462
525
  }
463
526
  const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
464
527
 
465
- await this.peerManager.initializePeers();
466
- if (!this.config.p2pDiscoveryDisabled) {
467
- await this.peerDiscoveryService.start();
468
- }
469
- await this.node.start();
470
-
471
- // Subscribe to standard GossipSub topics by default
472
- for (const topic of getTopicsForClientAndConfig(this.clientType, this.config.disableTransactions)) {
473
- this.subscribeToTopic(this.topicStrings[topic]);
474
- }
475
-
476
528
  // Create request response protocol handlers
477
529
  const txHandler = reqRespTxHandler(this.mempools);
478
530
  const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
479
- const blockHandler = reqRespBlockHandler(this.archiver);
480
531
  const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
481
532
 
482
533
  const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
483
534
  [ReqRespSubProtocol.PING]: pingHandler,
484
535
  [ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
485
536
  [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
486
- [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
487
537
  };
488
538
 
489
- // Only handle block transactions request if attestation pool is available to the client
490
- if (this.mempools.attestationPool && !this.config.disableTransactions) {
491
- const blockTxsHandler = reqRespBlockTxsHandler(this.mempools.attestationPool, this.mempools.txPool);
539
+ if (!this.config.disableTransactions) {
540
+ const blockTxsHandler = reqRespBlockTxsHandler(
541
+ this.mempools.attestationPool,
542
+ this.archiver,
543
+ this.mempools.txPool,
544
+ );
492
545
  requestResponseHandlers[ReqRespSubProtocol.BLOCK_TXS] = blockTxsHandler.bind(this);
493
546
  }
494
547
 
@@ -496,10 +549,31 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
496
549
  requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
497
550
  }
498
551
 
552
+ // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
553
+ const reqrespSubProtocolValidators = {
554
+ ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
555
+ [ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
556
+ [ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
557
+ };
558
+
559
+ await this.peerManager.initializePeers();
560
+
561
+ await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
562
+
563
+ await this.node.start();
564
+
565
+ // Subscribe to standard GossipSub topics by default
566
+ for (const topic of getTopicsForConfig(this.config.disableTransactions)) {
567
+ this.subscribeToTopic(this.topicStrings[topic]);
568
+ }
569
+
499
570
  // add GossipSub listener
500
571
  this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
501
572
 
502
573
  // Start running promise for peer discovery and metrics collection
574
+ if (!this.config.p2pDiscoveryDisabled) {
575
+ await this.peerDiscoveryService.start();
576
+ }
503
577
  this.discoveryRunningPromise = new RunningPromise(
504
578
  async () => {
505
579
  await this.peerManager.heartbeat();
@@ -509,14 +583,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
509
583
  );
510
584
  this.discoveryRunningPromise.start();
511
585
 
512
- // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
513
- const reqrespSubProtocolValidators = {
514
- ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
515
- [ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
516
- [ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
517
- [ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
518
- };
519
- await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
520
586
  this.logger.info(`Started P2P service`, {
521
587
  listen: this.config.listenAddress,
522
588
  port: this.config.p2pPort,
@@ -563,6 +629,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
563
629
  return this.peerManager.getPeers(includePending);
564
630
  }
565
631
 
632
+ public getGossipMeshPeerCount(topicType: TopicType): number {
633
+ return this.node.services.pubsub.getMeshPeers(this.topicStrings[topicType]).length;
634
+ }
635
+
566
636
  private handleGossipSubEvent(e: CustomEvent<GossipsubMessage>) {
567
637
  this.logger.trace(`Received PUBSUB message.`);
568
638
 
@@ -590,6 +660,15 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
590
660
  return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
591
661
  }
592
662
 
663
+ public sendRequestToPeer(
664
+ peerId: PeerId,
665
+ subProtocol: ReqRespSubProtocol,
666
+ payload: Buffer,
667
+ dialTimeout?: number,
668
+ ): Promise<ReqRespResponse> {
669
+ return this.reqresp.sendRequestToPeer(peerId, subProtocol, payload, dialTimeout);
670
+ }
671
+
593
672
  /**
594
673
  * Get the ENR of the node
595
674
  * @returns The ENR of the node
@@ -602,6 +681,37 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
602
681
  this.blockReceivedCallback = callback;
603
682
  }
604
683
 
684
+ public registerValidatorCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
685
+ this.validatorCheckpointReceivedCallback = callback;
686
+ }
687
+
688
+ public registerAllNodesCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
689
+ this.allNodesCheckpointReceivedCallback = callback;
690
+ }
691
+
692
+ public async notifyOwnCheckpointProposal(checkpoint: CheckpointProposalCore): Promise<void> {
693
+ await this.allNodesCheckpointReceivedCallback(checkpoint, this.node.peerId);
694
+ }
695
+
696
+ /**
697
+ * Registers a callback to be invoked when a duplicate proposal is detected.
698
+ * This callback is triggered on the first duplicate (when count goes from 1 to 2).
699
+ */
700
+ public registerDuplicateProposalCallback(
701
+ callback: (info: { slot: SlotNumber; proposer: EthAddress; type: 'checkpoint' | 'block' }) => void,
702
+ ): void {
703
+ this.duplicateProposalCallback = callback;
704
+ }
705
+
706
+ /**
707
+ * Registers a callback to be invoked when a duplicate attestation is detected.
708
+ * A validator signing attestations for different proposals at the same slot.
709
+ * This callback is triggered on the first duplicate (when count goes from 1 to 2).
710
+ */
711
+ public registerDuplicateAttestationCallback(callback: P2PDuplicateAttestationCallback): void {
712
+ this.duplicateAttestationCallback = callback;
713
+ }
714
+
605
715
  /**
606
716
  * Subscribes to a topic.
607
717
  * @param topic - The topic to subscribe to.
@@ -623,7 +733,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
623
733
  if (!this.node.services.pubsub) {
624
734
  throw new Error('Pubsub service not available.');
625
735
  }
626
- const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages);
736
+ const isBlockProposal = topic === this.topicStrings[TopicType.block_proposal];
737
+ const traceContext =
738
+ this.config.debugP2PInstrumentMessages && isBlockProposal ? this.telemetry.getTraceContext() : undefined;
739
+ const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages, traceContext);
627
740
  const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
628
741
  return result.recipients.length;
629
742
  }
@@ -644,12 +757,15 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
644
757
  case this.topicStrings[TopicType.tx]:
645
758
  topicType = TopicType.tx;
646
759
  break;
647
- case this.topicStrings[TopicType.block_attestation]:
648
- topicType = TopicType.block_attestation;
649
- break;
650
760
  case this.topicStrings[TopicType.block_proposal]:
651
761
  topicType = TopicType.block_proposal;
652
762
  break;
763
+ case this.topicStrings[TopicType.checkpoint_proposal]:
764
+ topicType = TopicType.checkpoint_proposal;
765
+ break;
766
+ case this.topicStrings[TopicType.checkpoint_attestation]:
767
+ topicType = TopicType.checkpoint_attestation;
768
+ break;
653
769
  default:
654
770
  this.logger.error(`Received message on unknown topic: ${msg.topic}`);
655
771
  break;
@@ -660,6 +776,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
660
776
  if (!validator || !validator.addMessage(msgId)) {
661
777
  this.instrumentation.incMessagePrevalidationStatus(false, topicType);
662
778
  this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
779
+ if (topicType === TopicType.tx) {
780
+ this.logger.verbose(`Ignoring already-seen tx gossip message`, { msgId, source: source.toString() });
781
+ }
663
782
  return { result: false, topicType };
664
783
  }
665
784
 
@@ -708,74 +827,217 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
708
827
  return;
709
828
  }
710
829
 
830
+ // Determine topic type for attributes
711
831
  if (msg.topic === this.topicStrings[TopicType.tx]) {
712
832
  topicType = TopicType.tx;
713
- await this.handleGossipedTx(p2pMessage.payload, msgId, source);
833
+ } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
834
+ topicType = TopicType.checkpoint_attestation;
835
+ } else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
836
+ topicType = TopicType.block_proposal;
837
+ } else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
838
+ topicType = TopicType.checkpoint_proposal;
714
839
  }
715
- if (msg.topic === this.topicStrings[TopicType.block_attestation]) {
716
- topicType = TopicType.block_attestation;
717
- if (this.clientType === P2PClientType.Full) {
718
- await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
840
+
841
+ // Process the message, optionally within a linked span for trace propagation
842
+ const processMessage = async () => {
843
+ if (msg.topic === this.topicStrings[TopicType.tx]) {
844
+ await this.handleGossipedTx(p2pMessage.payload, msgId, source);
845
+ } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
846
+ await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
847
+ } else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
848
+ await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
849
+ } else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
850
+ await this.handleGossipedCheckpointProposal(p2pMessage.payload, msgId, source);
851
+ } else {
852
+ this.logger.error(`Received message on unknown topic: ${msg.topic}`);
719
853
  }
720
- }
721
- if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
722
- topicType = TopicType.block_proposal;
723
- await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
854
+ };
855
+
856
+ const latency = p2pMessage.timestamp !== undefined ? msgReceivedTime - p2pMessage.timestamp.getTime() : undefined;
857
+ const propagatedContext = p2pMessage.traceContext
858
+ ? this.telemetry.extractPropagatedContext(p2pMessage.traceContext)
859
+ : undefined;
860
+
861
+ if (propagatedContext) {
862
+ await this.tracer.startActiveSpan(
863
+ 'LibP2PService.processMessage',
864
+ {
865
+ attributes: {
866
+ [Attributes.TOPIC_NAME]: topicType!,
867
+ [Attributes.PEER_ID]: source.toString(),
868
+ },
869
+ },
870
+ propagatedContext,
871
+ async span => {
872
+ try {
873
+ await processMessage();
874
+ span.setStatus({
875
+ code: SpanStatusCode.OK,
876
+ });
877
+ } catch (err) {
878
+ span.setStatus({
879
+ code: SpanStatusCode.ERROR,
880
+ message: String(err),
881
+ });
882
+ if (typeof err === 'string' || (err && err instanceof Error)) {
883
+ span.recordException(err);
884
+ }
885
+ throw err;
886
+ } finally {
887
+ span.end();
888
+ }
889
+ },
890
+ );
891
+ } else {
892
+ await processMessage();
724
893
  }
725
894
 
726
- if (p2pMessage.timestamp !== undefined && topicType !== undefined) {
727
- const latency = msgReceivedTime - p2pMessage.timestamp.getTime();
895
+ if (latency !== undefined && topicType !== undefined) {
728
896
  this.instrumentation.recordMessageLatency(topicType, latency);
729
897
  }
730
898
 
731
899
  return;
732
900
  }
733
901
 
734
- protected async validateReceivedMessage<T>(
735
- validationFunc: () => Promise<ReceivedMessageValidationResult<T>>,
902
+ protected async validateReceivedMessage<T, M = undefined>(
903
+ validationFunc: () => Promise<ReceivedMessageValidationResult<T, M>>,
736
904
  msgId: string,
737
905
  source: PeerId,
738
906
  topicType: TopicType,
739
- ): Promise<ReceivedMessageValidationResult<T>> {
740
- let resultAndObj: ReceivedMessageValidationResult<T> = { result: TopicValidatorResult.Reject };
907
+ ): Promise<ReceivedMessageValidationResult<T, M>> {
908
+ // Default to reject result with a penalty if validation function throws an error
909
+ let resultAndObj: ReceivedMessageValidationResult<T, M> = {
910
+ result: TopicValidatorResult.Reject,
911
+ severity: PeerErrorSeverity.MidToleranceError,
912
+ };
741
913
  const timer = new Timer();
742
914
  try {
743
915
  resultAndObj = await validationFunc();
744
916
  } catch (err) {
745
- this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
746
- this.logger.error(`Error deserializing and validating gossipsub message`, err, {
747
- msgId,
748
- source: source.toString(),
749
- topicType,
750
- });
917
+ this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
918
+ }
919
+
920
+ const validationTimeMs = timer.ms();
921
+ const mcacheWindowMs = this.config.gossipsubMcacheLength * this.config.gossipsubInterval;
922
+ if (validationTimeMs > mcacheWindowMs * 0.75) {
923
+ this.instrumentation.incSlowValidation(topicType);
924
+ this.logger.warn(
925
+ `Gossip validation for ${topicType} took ${validationTimeMs}ms, approaching mcache eviction window of ${mcacheWindowMs}ms. ` +
926
+ `Message forwarding may be skipped if validation exceeds the window.`,
927
+ { msgId, source: source.toString(), topicType, validationTimeMs, mcacheWindowMs },
928
+ );
751
929
  }
752
930
 
753
931
  if (resultAndObj.result === TopicValidatorResult.Accept) {
932
+ this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
754
933
  this.instrumentation.recordMessageValidation(topicType, timer);
934
+ } else if (resultAndObj.result === TopicValidatorResult.Reject) {
935
+ this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
936
+ msgId,
937
+ source: source.toString(),
938
+ topicType,
939
+ severity: resultAndObj.severity,
940
+ });
941
+ this.peerManager.penalizePeer(source, resultAndObj.severity);
942
+ } else {
943
+ this.logger.trace(`Message ${topicType} ignored by validator`, { msgId, source: source.toString(), topicType });
755
944
  }
756
945
 
757
946
  this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
758
947
  return resultAndObj;
759
948
  }
760
949
 
950
+ private tryDeserialize<T>(deserializeFunc: () => T, msgId: string, source: PeerId): T | undefined {
951
+ try {
952
+ return deserializeFunc();
953
+ } catch (err) {
954
+ this.logger.warn(`Failed to deserialize gossipsub message from buffer`, {
955
+ err,
956
+ msgId,
957
+ source: source.toString(),
958
+ });
959
+ return undefined;
960
+ }
961
+ }
962
+
761
963
  protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
762
964
  const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
763
- const tx = Tx.fromBuffer(payloadData);
764
- const isValid = await this.validatePropagatedTx(tx, source);
765
- const exists = isValid && (await this.mempools.txPool.hasTx(tx.getTxHash()));
965
+ const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
966
+ if (!tx) {
967
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
968
+ }
766
969
 
767
- this.logger.trace(`Validate propagated tx`, {
768
- isValid,
769
- exists,
970
+ const currentBlockNumber = await this.archiver.getBlockNumber();
971
+ const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
972
+
973
+ // Stage 1: fast validators (metadata, data, timestamps, double-spend, gas, phases, block header)
974
+ const firstStageValidators = await this.createFirstStageMessageValidators(currentBlockNumber, nextSlotTimestamp);
975
+ const firstStageOutcome = await this.runValidations(tx, firstStageValidators);
976
+ if (!firstStageOutcome.allPassed) {
977
+ const { name } = firstStageOutcome.failure;
978
+ let { severity } = firstStageOutcome.failure;
979
+
980
+ // Double spend validator has a special case handler. We perform more detailed examination
981
+ // as to how recently the nullifier was entered into the tree and if the transaction should
982
+ // have 'known' the nullifier existed. This determines the severity of the penalty applied to the peer.
983
+ if (name === 'doubleSpendValidator') {
984
+ const txBlockNumber = BlockNumber(currentBlockNumber + 1);
985
+ severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
986
+ }
987
+
988
+ this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 1 validation failed`, {
989
+ validator: name,
990
+ severity,
991
+ source: source.toString(),
992
+ });
993
+ return { result: TopicValidatorResult.Reject, severity };
994
+ }
995
+
996
+ // Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
997
+ const canAdd = await this.mempools.txPool.canAddPendingTx(tx);
998
+ if (canAdd === 'ignored') {
999
+ this.logger.verbose(`Ignoring gossiped tx ${tx.getTxHash().toString()}: pool pre-check returned ignored`, {
1000
+ source: source.toString(),
1001
+ });
1002
+ return { result: TopicValidatorResult.Ignore, obj: tx };
1003
+ }
1004
+
1005
+ // Stage 2: expensive proof verification
1006
+ const secondStageValidators = this.createSecondStageMessageValidators();
1007
+ const secondStageOutcome = await this.runValidations(tx, secondStageValidators);
1008
+ if (!secondStageOutcome.allPassed) {
1009
+ const { severity, name } = secondStageOutcome.failure;
1010
+ this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 2 validation failed`, {
1011
+ validator: name,
1012
+ severity,
1013
+ source: source.toString(),
1014
+ });
1015
+ return { result: TopicValidatorResult.Reject, severity };
1016
+ }
1017
+
1018
+ // Pool add: persist the tx
1019
+ const txHash = tx.getTxHash();
1020
+ const addResult = await this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' });
1021
+
1022
+ const wasAccepted = addResult.accepted.some(h => h.equals(txHash));
1023
+ const wasIgnored = addResult.ignored.some(h => h.equals(txHash));
1024
+
1025
+ this.logger.verbose(`Validate propagated tx ${txHash.toString()}`, {
1026
+ wasAccepted,
1027
+ wasIgnored,
770
1028
  [Attributes.P2P_ID]: source.toString(),
771
1029
  });
772
1030
 
773
- if (!isValid) {
774
- return { result: TopicValidatorResult.Reject };
775
- } else if (exists) {
1031
+ if (wasAccepted) {
1032
+ return { result: TopicValidatorResult.Accept, obj: tx };
1033
+ } else if (wasIgnored) {
776
1034
  return { result: TopicValidatorResult.Ignore, obj: tx };
777
1035
  } else {
778
- return { result: TopicValidatorResult.Accept, obj: tx };
1036
+ this.logger.warn(`Gossiped tx ${txHash.toString()} unexpectedly rejected by pool`, {
1037
+ source: source.toString(),
1038
+ txHash: txHash.toString(),
1039
+ });
1040
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
779
1041
  }
780
1042
  };
781
1043
 
@@ -784,6 +1046,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
784
1046
  return;
785
1047
  }
786
1048
 
1049
+ // Tx was accepted into pool and will be propagated - just log and record metrics
787
1050
  const txHash = tx.getTxHash();
788
1051
  const txHashString = txHash.toString();
789
1052
  this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()} via gossip`, {
@@ -791,73 +1054,40 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
791
1054
  txHash: txHashString,
792
1055
  });
793
1056
 
794
- if (this.config.dropTransactions && randomInt(1000) < this.config.dropTransactionsProbability * 1000) {
795
- this.logger.warn(`Intentionally dropping tx ${txHashString} (probability rule)`);
796
- return;
797
- }
798
-
799
1057
  this.instrumentation.incrementTxReceived(1);
800
- await this.mempools.txPool.addTxs([tx]);
801
1058
  }
802
1059
 
803
1060
  /**
804
- * Process Attestation From Peer
805
- * When a proposal is received from a peer, we add it to the attestation pool, so it can be accessed by other services.
806
- *
807
- * @param attestation - The attestation to process.
1061
+ * Process a checkpoint attestation from a peer.
1062
+ * Validates the attestation and adds it to the pool.
808
1063
  */
809
- private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
810
- const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockAttestation>> = async () => {
811
- const attestation = BlockAttestation.fromBuffer(payloadData);
812
- const pool = this.mempools.attestationPool!;
813
- const isValid = await this.validateAttestation(source, attestation);
814
- const exists = isValid && (await pool.hasAttestation(attestation));
815
-
816
- let canAdd = true;
817
- if (isValid && !exists) {
818
- const slot = attestation.payload.header.slotNumber;
819
- const { committee } = await this.epochCache.getCommittee(slot);
820
- const committeeSize = committee?.length ?? 0;
821
- canAdd = await pool.canAddAttestation(attestation, committeeSize);
822
- }
823
-
824
- this.logger.trace(`Validate propagated block attestation`, {
825
- isValid,
826
- exists,
827
- canAdd,
828
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
829
- [Attributes.P2P_ID]: source.toString(),
830
- });
831
-
832
- if (!isValid) {
833
- return { result: TopicValidatorResult.Reject };
834
- } else if (exists) {
835
- return { result: TopicValidatorResult.Ignore, obj: attestation };
836
- } else if (!canAdd) {
837
- this.logger.warn(`Dropping block attestation due to per-(slot, proposalId) attestation cap`, {
838
- slot: attestation.payload.header.slotNumber.toString(),
839
- archive: attestation.archive.toString(),
840
- source: source.toString(),
841
- });
842
- return { result: TopicValidatorResult.Ignore, obj: attestation };
843
- } else {
844
- return { result: TopicValidatorResult.Accept, obj: attestation };
845
- }
846
- };
847
-
848
- const { result, obj: attestation } = await this.validateReceivedMessage<BlockAttestation>(
849
- validationFunc,
1064
+ private async processCheckpointAttestationFromPeer(
1065
+ payloadData: Buffer,
1066
+ msgId: string,
1067
+ source: PeerId,
1068
+ ): Promise<void> {
1069
+ const { result, obj: attestation } = await this.validateReceivedMessage<CheckpointAttestation>(
1070
+ () => {
1071
+ const attestation = this.tryDeserialize(() => CheckpointAttestation.fromBuffer(payloadData), msgId, source);
1072
+ if (!attestation) {
1073
+ return Promise.resolve({
1074
+ result: TopicValidatorResult.Reject,
1075
+ severity: PeerErrorSeverity.LowToleranceError,
1076
+ });
1077
+ }
1078
+ return this.validateAndStoreCheckpointAttestation(source, attestation);
1079
+ },
850
1080
  msgId,
851
1081
  source,
852
- TopicType.block_attestation,
1082
+ TopicType.checkpoint_attestation,
853
1083
  );
854
1084
 
855
1085
  if (result !== TopicValidatorResult.Accept || !attestation) {
856
1086
  return;
857
1087
  }
858
1088
 
859
- this.logger.debug(
860
- `Received attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
1089
+ this.logger.verbose(
1090
+ `Received valid checkpoint attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
861
1091
  {
862
1092
  p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
863
1093
  slot: attestation.slotNumber,
@@ -865,123 +1095,361 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
865
1095
  source: source.toString(),
866
1096
  },
867
1097
  );
868
-
869
- await this.mempools.attestationPool!.addAttestations([attestation]);
870
1098
  }
871
1099
 
872
- private async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
873
- const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
874
- const block = BlockProposal.fromBuffer(payloadData);
875
- const isValid = await this.validateBlockProposal(source, block);
876
- const pool = this.mempools.attestationPool;
877
-
878
- // Note that we dont have an attestation pool if we're a prover node, but we still
879
- // subscribe to block proposal topics in order to prevent their txs from being cleared.
880
- const exists = isValid && (await pool?.hasBlockProposal(block));
881
- const canAdd = isValid && (await pool?.canAddProposal(block));
882
-
883
- this.logger.trace(`Validate propagated block proposal`, {
884
- isValid,
885
- exists,
886
- canAdd,
887
- [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
888
- [Attributes.P2P_ID]: source.toString(),
1100
+ /** Validates a checkpoint attestation and adds it to the pool. Penalizes the peer if validation fails. */
1101
+ @trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId, attestation) => ({
1102
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
1103
+ }))
1104
+ protected async validateAndStoreCheckpointAttestation(
1105
+ peerId: PeerId,
1106
+ attestation: CheckpointAttestation,
1107
+ ): Promise<ReceivedMessageValidationResult<CheckpointAttestation>> {
1108
+ const validationResult = await this.checkpointAttestationValidator.validate(attestation);
1109
+
1110
+ if (validationResult.result === 'reject') {
1111
+ this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
1112
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1113
+ }
1114
+
1115
+ if (validationResult.result === 'ignore') {
1116
+ return { result: TopicValidatorResult.Ignore, obj: attestation };
1117
+ }
1118
+
1119
+ // Try to add the attestation: this handles existence check, cap check, and adding in one call
1120
+ // count is the number of attestations by this signer for this slot (for duplicate detection)
1121
+ const slot = attestation.payload.header.slotNumber;
1122
+ const { added, alreadyExists, count } =
1123
+ await this.mempools.attestationPool.tryAddCheckpointAttestation(attestation);
1124
+
1125
+ this.logger.trace(`Validate propagated checkpoint attestation`, {
1126
+ added,
1127
+ alreadyExists,
1128
+ count,
1129
+ [Attributes.SLOT_NUMBER]: slot.toString(),
1130
+ [Attributes.P2P_ID]: peerId.toString(),
1131
+ });
1132
+
1133
+ // Exact same attestation received, no need to re-broadcast
1134
+ if (alreadyExists) {
1135
+ return { result: TopicValidatorResult.Ignore, obj: attestation };
1136
+ }
1137
+
1138
+ // Could not add (cap reached for signer), penalize and do not re-broadcast
1139
+ if (!added) {
1140
+ this.logger.warn(`Rejecting checkpoint attestation due to cap`, {
1141
+ slot: slot.toString(),
1142
+ archive: attestation.archive.toString(),
1143
+ source: peerId.toString(),
1144
+ attester: attestation.getSender()?.toString(),
1145
+ count,
889
1146
  });
1147
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
1148
+ }
890
1149
 
891
- if (!isValid) {
892
- return { result: TopicValidatorResult.Reject };
893
- } else if (exists) {
894
- return { result: TopicValidatorResult.Ignore, obj: block };
895
- } else if (!canAdd) {
896
- this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
897
- this.logger.warn(`Penalizing peer for block proposal exceeding per-slot cap`, {
898
- slot: block.slotNumber.toString(),
899
- archive: block.archive.toString(),
900
- source: source.toString(),
1150
+ // Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
1151
+ // count is the number of attestations by this signer for this slot
1152
+ if (count === 2) {
1153
+ const attester = attestation.getSender();
1154
+ if (attester) {
1155
+ this.logger.warn(`Detected duplicate attestation (equivocation) at slot ${slot}`, {
1156
+ slot: slot.toString(),
1157
+ archive: attestation.archive.toString(),
1158
+ source: peerId.toString(),
1159
+ attester: attester.toString(),
901
1160
  });
902
- return { result: TopicValidatorResult.Reject };
903
- } else {
904
- return { result: TopicValidatorResult.Accept, obj: block };
1161
+ this.duplicateAttestationCallback?.({ slot, attester });
905
1162
  }
906
- };
1163
+ }
907
1164
 
908
- const { result, obj: block } = await this.validateReceivedMessage<BlockProposal>(
909
- validationFunc,
1165
+ // Attestation was added successfully - accept it so other nodes can also detect the equivocation
1166
+ return { result: TopicValidatorResult.Accept, obj: attestation };
1167
+ }
1168
+
1169
+ protected async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
1170
+ const {
1171
+ result,
1172
+ obj: block,
1173
+ metadata: { isEquivocated } = {},
1174
+ } = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean }>(
1175
+ () => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
910
1176
  msgId,
911
1177
  source,
912
1178
  TopicType.block_proposal,
913
1179
  );
914
1180
 
915
- if (!result || !block) {
1181
+ // If not accepted or equivocated, return
1182
+ if (result !== TopicValidatorResult.Accept || !block || isEquivocated) {
916
1183
  return;
917
1184
  }
918
1185
 
919
1186
  await this.processValidBlockProposal(block, source);
920
1187
  }
921
1188
 
922
- // REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
1189
+ /** Validates a block proposal. Triggers a penalization to the peer that sent it if invalid. Adds to the mempool if valid. */
1190
+ @trackSpan('Libp2pService.validateAndStoreBlockProposal', (_peerId, block) => ({
1191
+ [Attributes.BLOCK_NUMBER]: block.blockNumber.toString(),
1192
+ [Attributes.SLOT_NUMBER]: block.slotNumber.toString(),
1193
+ }))
1194
+ protected async validateAndStoreBlockProposal(
1195
+ peerId: PeerId,
1196
+ block: BlockProposal,
1197
+ ): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean }>> {
1198
+ const validationResult = await this.blockProposalValidator.validate(block);
1199
+
1200
+ if (validationResult.result === 'reject') {
1201
+ this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`);
1202
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1203
+ }
1204
+
1205
+ if (validationResult.result === 'ignore') {
1206
+ return { result: TopicValidatorResult.Ignore, obj: block };
1207
+ }
1208
+
1209
+ // Try to add the proposal: this handles existence check, cap check, and adding in one call
1210
+ const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
1211
+ const isEquivocated = count !== undefined && count > 1;
1212
+
1213
+ // Duplicate proposal received, no need to re-broadcast
1214
+ if (alreadyExists) {
1215
+ this.logger.debug(`Ignoring duplicate block proposal received`, {
1216
+ ...block.toBlockInfo(),
1217
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
1218
+ proposer: block.getSender()?.toString(),
1219
+ source: peerId.toString(),
1220
+ });
1221
+ return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated } };
1222
+ }
1223
+
1224
+ // Too many blocks received for this slot and index, penalize peer and do not re-broadcast
1225
+ if (!added) {
1226
+ this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, {
1227
+ ...block.toBlockInfo(),
1228
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
1229
+ count,
1230
+ proposer: block.getSender()?.toString(),
1231
+ source: peerId.toString(),
1232
+ });
1233
+ return {
1234
+ result: TopicValidatorResult.Reject,
1235
+ metadata: { isEquivocated },
1236
+ severity: PeerErrorSeverity.HighToleranceError,
1237
+ };
1238
+ }
1239
+
1240
+ // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
1241
+ // and do re-broadcast it so other nodes in the network know to slash the proposer
1242
+ if (isEquivocated) {
1243
+ const proposer = block.getSender();
1244
+ this.logger.warn(`Detected duplicate block proposal (equivocation) at slot ${block.slotNumber}`, {
1245
+ ...block.toBlockInfo(),
1246
+ source: peerId.toString(),
1247
+ proposer: proposer?.toString(),
1248
+ });
1249
+ // Invoke the duplicate callback on the first duplicate spotted only
1250
+ if (proposer && count === 2) {
1251
+ this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
1252
+ }
1253
+ return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated } };
1254
+ }
1255
+
1256
+ // Otherwise, we're good to go!
1257
+ return { result: TopicValidatorResult.Accept, obj: block };
1258
+ }
1259
+
1260
+ // REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
1261
+ // should not be here as it does not deal with p2p networking.
923
1262
  @trackSpan('Libp2pService.processValidBlockProposal', async block => ({
924
1263
  [Attributes.SLOT_NUMBER]: block.slotNumber,
925
1264
  [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
926
1265
  [Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
927
1266
  }))
928
- private async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
1267
+ protected async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
929
1268
  const slot = block.slotNumber;
930
- const previousSlot = SlotNumber(slot - 1);
931
1269
  this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
932
1270
  p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
933
- slot: block.slotNumber,
934
- archive: block.archive.toString(),
935
1271
  source: sender.toString(),
1272
+ ...block.toBlockInfo(),
936
1273
  });
937
- const attestationsForPreviousSlot = await this.mempools.attestationPool?.getAttestationsForSlot(previousSlot);
938
- if (attestationsForPreviousSlot !== undefined) {
939
- this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
1274
+
1275
+ // Mark the txs in this proposal as protected
1276
+ await this.mempools.txPool.protectTxs(block.txHashes, block.blockHeader);
1277
+
1278
+ // Call the block received callback to validate the proposal.
1279
+ // Note: Validators do NOT attest to individual blocks, only to checkpoint proposals.
1280
+ const isValid = await this.blockReceivedCallback(block, sender);
1281
+ if (!isValid) {
1282
+ this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
940
1283
  }
1284
+ }
941
1285
 
942
- // Attempt to add proposal, then mark the txs in this proposal as non-evictable
943
- try {
944
- await this.mempools.attestationPool?.addBlockProposal(block);
945
- } catch (err: unknown) {
946
- // Drop proposals if we hit per-slot cap in the attestation pool; rethrow unknown errors
947
- if (err instanceof ProposalSlotCapExceededError) {
948
- this.logger.warn(`Dropping block proposal due to per-slot proposal cap`, {
949
- slot: String(slot),
950
- archive: block.archive.toString(),
951
- error: (err as Error).message,
1286
+ /**
1287
+ * Handle a gossiped checkpoint proposal.
1288
+ * Validates and processes the checkpoint proposal, then triggers the callback for attestation.
1289
+ */
1290
+ protected async handleGossipedCheckpointProposal(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
1291
+ const {
1292
+ result,
1293
+ obj: checkpoint,
1294
+ metadata: { isEquivocated, processBlock } = {},
1295
+ } = await this.validateReceivedMessage<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>(
1296
+ () => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
1297
+ msgId,
1298
+ source,
1299
+ TopicType.checkpoint_proposal,
1300
+ );
1301
+
1302
+ // If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
1303
+ // TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
1304
+ if (processBlock && checkpoint?.getBlockProposal()) {
1305
+ await this.processValidBlockProposal(checkpoint.getBlockProposal()!, source);
1306
+ }
1307
+
1308
+ if (result !== TopicValidatorResult.Accept || !checkpoint || isEquivocated) {
1309
+ return;
1310
+ }
1311
+
1312
+ await this.processValidCheckpointProposal(checkpoint.toCore(), source);
1313
+ }
1314
+
1315
+ /**
1316
+ * Validates a checkpoint proposal. Penalizes peer if validation fails. Adds the checkpoint and
1317
+ * its last block (if present) to the mempool if valid. Triggers equivocation detection on both.
1318
+ */
1319
+ @trackSpan('Libp2pService.validateAndStoreCheckpointProposal', (_peerId, checkpoint) => ({
1320
+ [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1321
+ }))
1322
+ protected async validateAndStoreCheckpointProposal(
1323
+ peerId: PeerId,
1324
+ checkpoint: CheckpointProposal,
1325
+ ): Promise<ReceivedMessageValidationResult<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>> {
1326
+ const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
1327
+
1328
+ if (validationResult.result === 'reject') {
1329
+ this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
1330
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1331
+ }
1332
+
1333
+ if (validationResult.result === 'ignore') {
1334
+ return { result: TopicValidatorResult.Ignore, obj: checkpoint };
1335
+ }
1336
+
1337
+ // Extract and try to add the block proposal first if present
1338
+ const blockProposal = checkpoint.getBlockProposal();
1339
+ let processBlock = false;
1340
+ if (blockProposal) {
1341
+ this.logger.debug(`Validating block proposal from propagated checkpoint`, {
1342
+ [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1343
+ [Attributes.P2P_ID]: peerId.toString(),
1344
+ });
1345
+ const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
1346
+ const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
1347
+ if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
1348
+ this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
1349
+ [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1350
+ [Attributes.P2P_ID]: peerId.toString(),
1351
+ isEquivocated,
1352
+ result: blockProposalResult.result,
952
1353
  });
953
- return;
1354
+ return {
1355
+ result: TopicValidatorResult.Reject,
1356
+ severity:
1357
+ 'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
1358
+ };
1359
+ } else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) {
1360
+ processBlock = true;
954
1361
  }
955
- throw err;
956
1362
  }
957
- await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
958
- const attestations = await this.blockReceivedCallback(block, sender);
959
1363
 
960
- // TODO: fix up this pattern - the abstraction is not nice
961
- // The attestation can be undefined if no handler is registered / the validator deems the block invalid / in fisherman mode
962
- if (attestations?.length) {
963
- for (const attestation of attestations) {
964
- this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber}`, {
965
- p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
966
- slot: attestation.slotNumber,
967
- archive: attestation.archive.toString(),
968
- });
969
- await this.broadcastAttestation(attestation);
1364
+ // Try to add the checkpoint proposal core: this handles existence check, cap check, and adding in one call
1365
+ const checkpointCore = checkpoint.toCore();
1366
+ const tryAddResult = await this.mempools.attestationPool.tryAddCheckpointProposal(checkpointCore);
1367
+ const { added, alreadyExists, count } = tryAddResult;
1368
+ const isEquivocated = count !== undefined && count > 1;
1369
+
1370
+ // Duplicate proposal received, do not re-broadcast
1371
+ if (alreadyExists) {
1372
+ this.logger.debug(`Ignoring duplicate checkpoint proposal received`, {
1373
+ ...checkpoint.toCheckpointInfo(),
1374
+ source: peerId.toString(),
1375
+ });
1376
+ return {
1377
+ result: TopicValidatorResult.Ignore,
1378
+ obj: checkpoint,
1379
+ metadata: { isEquivocated, processBlock },
1380
+ };
1381
+ }
1382
+
1383
+ // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
1384
+ // Note: We still return the checkpoint obj so the lastBlock can be processed if valid
1385
+ if (!added) {
1386
+ this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
1387
+ ...checkpoint.toCheckpointInfo(),
1388
+ count,
1389
+ source: peerId.toString(),
1390
+ });
1391
+ return {
1392
+ result: TopicValidatorResult.Reject,
1393
+ obj: checkpoint,
1394
+ metadata: { isEquivocated, processBlock },
1395
+ severity: PeerErrorSeverity.HighToleranceError,
1396
+ };
1397
+ }
1398
+
1399
+ // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
1400
+ // and do re-broadcast it so other nodes in the network know to slash the proposer
1401
+ if (isEquivocated) {
1402
+ const proposer = checkpoint.getSender();
1403
+ this.logger.warn(`Detected duplicate checkpoint proposal (equivocation) at slot ${checkpoint.slotNumber}`, {
1404
+ ...checkpoint.toCheckpointInfo(),
1405
+ source: peerId.toString(),
1406
+ proposer: proposer?.toString(),
1407
+ });
1408
+ // Invoke the duplicate callback on the first duplicate spotted only
1409
+ if (proposer && count === 2) {
1410
+ this.duplicateProposalCallback?.({ slot: checkpoint.slotNumber, proposer, type: 'checkpoint' });
970
1411
  }
1412
+ return {
1413
+ result: TopicValidatorResult.Accept,
1414
+ obj: checkpoint,
1415
+ metadata: { isEquivocated, processBlock },
1416
+ };
971
1417
  }
1418
+
1419
+ // Otherwise, we're good to go!
1420
+ return { result: TopicValidatorResult.Accept, obj: checkpoint, metadata: { processBlock, isEquivocated } };
972
1421
  }
973
1422
 
974
1423
  /**
975
- * Broadcast an attestation to all peers.
976
- * @param attestation - The attestation to broadcast.
1424
+ * Process a validated checkpoint proposal.
1425
+ * Note: The proposal was already added to the pool by tryAddCheckpointProposal in handleGossipedCheckpointProposal.
977
1426
  */
978
- @trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
979
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
980
- [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
981
- [Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
1427
+ @trackSpan('Libp2pService.processValidCheckpointProposal', async checkpoint => ({
1428
+ [Attributes.SLOT_NUMBER]: checkpoint.slotNumber,
1429
+ [Attributes.BLOCK_ARCHIVE]: checkpoint.archive.toString(),
1430
+ [Attributes.P2P_ID]: await checkpoint.p2pMessageLoggingIdentifier().then(i => i.toString()),
982
1431
  }))
983
- private async broadcastAttestation(attestation: BlockAttestation) {
984
- await this.propagate(attestation);
1432
+ protected async processValidCheckpointProposal(checkpoint: CheckpointProposalCore, sender: PeerId) {
1433
+ const slot = checkpoint.slotNumber;
1434
+ this.logger.verbose(`Received checkpoint proposal for slot ${slot} from external peer ${sender.toString()}.`, {
1435
+ p2pMessageIdentifier: await checkpoint.p2pMessageLoggingIdentifier(),
1436
+ slot: checkpoint.slotNumber,
1437
+ archive: checkpoint.archive.toString(),
1438
+ source: sender.toString(),
1439
+ });
1440
+
1441
+ await this.allNodesCheckpointReceivedCallback(checkpoint, sender);
1442
+
1443
+ // Call the checkpoint received callback with the core version (without lastBlock)
1444
+ // to validate and potentially generate attestations
1445
+ const attestations = await this.validatorCheckpointReceivedCallback(checkpoint, sender);
1446
+ if (attestations && attestations.length > 0) {
1447
+ // If the callback returned attestations, add them to the pool and propagate them
1448
+ await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
1449
+ for (const attestation of attestations) {
1450
+ await this.propagate(attestation);
1451
+ }
1452
+ }
985
1453
  }
986
1454
 
987
1455
  /**
@@ -1004,9 +1472,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1004
1472
  * @returns True if the requested block transactions are valid, false otherwise.
1005
1473
  */
1006
1474
  @trackSpan('Libp2pService.validateRequestedBlockTxs', request => ({
1007
- [Attributes.BLOCK_HASH]: request.blockHash.toString(),
1475
+ [Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
1008
1476
  }))
1009
- private async validateRequestedBlockTxs(
1477
+ protected async validateRequestedBlockTxs(
1010
1478
  request: BlockTxsRequest,
1011
1479
  response: BlockTxsResponse,
1012
1480
  peerId: PeerId,
@@ -1014,10 +1482,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1014
1482
  const requestedTxValidator = this.createRequestedTxValidator();
1015
1483
 
1016
1484
  try {
1017
- if (!response.blockHash.equals(request.blockHash)) {
1485
+ if (!response.archiveRoot.equals(request.archiveRoot)) {
1018
1486
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1019
1487
  throw new ValidationError(
1020
- `Received block txs for unexpected block: expected ${request.blockHash.toString()}, got ${response.blockHash.toString()}`,
1488
+ `Received block txs for unexpected archive root: expected ${request.archiveRoot.toString()}, got ${response.archiveRoot.toString()}`,
1021
1489
  );
1022
1490
  }
1023
1491
 
@@ -1047,7 +1515,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1047
1515
  }
1048
1516
 
1049
1517
  // Given proposal (should have locally), ensure returned txs are valid subset and match request indices
1050
- const proposal = await this.mempools.attestationPool?.getBlockProposal(request.blockHash.toString());
1518
+ const proposal = await this.mempools.attestationPool.getBlockProposal(request.archiveRoot.toString());
1051
1519
  if (proposal) {
1052
1520
  // Build intersected indices
1053
1521
  const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
@@ -1067,7 +1535,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1067
1535
  } else {
1068
1536
  // No local proposal, cannot check the membership/order of the returned txs
1069
1537
  this.logger.warn(
1070
- `Block proposal not found for block hash ${request.blockHash.toString()}; cannot validate membership/order of returned txs`,
1538
+ `Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`,
1071
1539
  );
1072
1540
  return false;
1073
1541
  }
@@ -1106,7 +1574,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1106
1574
  const requested = new Set(requestedTxHash.map(h => h.toString()));
1107
1575
  const requestedTxValidator = this.createRequestedTxValidator();
1108
1576
 
1109
- //TODO: (mralj) - this is somewhat naive implementation, if single tx is invlid we consider the whole response invalid.
1577
+ //TODO: (mralj) - this is somewhat naive implementation, if single tx is invalid we consider the whole response invalid.
1110
1578
  // I think we should still extract the valid txs and return them, so that we can still use the response.
1111
1579
  try {
1112
1580
  await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
@@ -1122,74 +1590,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1122
1590
  }
1123
1591
  }
1124
1592
 
1125
- /**
1126
- * Validates a BLOCK response.
1127
- *
1128
- * If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
1129
- * Penalizes on block number mismatch or hash mismatch.
1130
- *
1131
- * @param requestedBlockNumber - The requested block number.
1132
- * @param responseBlock - The block returned by the peer.
1133
- * @param peerId - The peer that returned the block.
1134
- * @returns True if the response is valid, false otherwise.
1135
- */
1136
- @trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
1137
- [Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
1138
- }))
1139
- private async validateRequestedBlock(
1140
- requestedBlockNumber: Fr,
1141
- responseBlock: L2Block,
1593
+ protected async validateRequestedTx(
1594
+ tx: Tx,
1142
1595
  peerId: PeerId,
1143
- ): Promise<boolean> {
1144
- try {
1145
- const reqNum = Number(requestedBlockNumber.toString());
1146
- if (responseBlock.number !== reqNum) {
1147
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1148
- return false;
1149
- }
1150
-
1151
- const local = await this.archiver.getBlock(reqNum);
1152
- if (!local) {
1153
- // We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
1154
- // TODO: Consider extending this validator to accept an expected hash or
1155
- // performing quorum-based checks when using P2P syncing prior to L1 sync.
1156
- this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
1157
- return false;
1158
- }
1159
- const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
1160
- if (!localHash.equals(respHash)) {
1161
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1162
- return false;
1163
- }
1164
-
1165
- return true;
1166
- } catch (e) {
1167
- this.logger.warn(`Error validating requested block`, e);
1168
- return false;
1169
- }
1170
- }
1171
-
1172
- private createRequestedTxValidator(): TxValidator {
1173
- return new AggregateTxValidator(
1174
- new DataTxValidator(),
1175
- new MetadataTxValidator({
1176
- l1ChainId: new Fr(this.config.l1ChainId),
1177
- rollupVersion: new Fr(this.config.rollupVersion),
1178
- protocolContractsHash,
1179
- vkTreeRoot: getVKTreeRoot(),
1180
- }),
1181
- new TxProofValidator(this.proofVerifier),
1182
- );
1183
- }
1184
-
1185
- private async validateRequestedTx(tx: Tx, peerId: PeerId, txValidator: TxValidator, requested?: Set<`0x${string}`>) {
1596
+ txValidator: TxValidator,
1597
+ requested?: Set<`0x${string}`>,
1598
+ ) {
1186
1599
  const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
1187
-
1188
- if (!(await tx.validateTxHash())) {
1189
- penalize(PeerErrorSeverity.MidToleranceError);
1190
- throw new ValidationError(`Received tx with invalid hash ${tx.getTxHash().toString()}.`);
1191
- }
1192
-
1193
1600
  if (requested && !requested.has(tx.getTxHash().toString())) {
1194
1601
  penalize(PeerErrorSeverity.MidToleranceError);
1195
1602
  throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
@@ -1202,38 +1609,14 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1202
1609
  }
1203
1610
  }
1204
1611
 
1205
- @trackSpan('Libp2pService.validatePropagatedTx', tx => ({
1206
- [Attributes.TX_HASH]: tx.getTxHash().toString(),
1207
- }))
1208
- private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
1209
- const currentBlockNumber = await this.archiver.getBlockNumber();
1210
-
1211
- // We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
1212
- const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1213
- const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
1214
-
1215
- for (const validator of messageValidators) {
1216
- const outcome = await this.runValidations(tx, validator);
1217
-
1218
- if (outcome.allPassed) {
1219
- continue;
1220
- }
1221
- const { name } = outcome.failure;
1222
- let { severity } = outcome.failure;
1223
-
1224
- // Double spend validator has a special case handler
1225
- if (name === 'doubleSpendValidator') {
1226
- const txBlockNumber = currentBlockNumber + 1; // tx is expected to be in the next block
1227
- severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
1228
- }
1229
-
1230
- this.peerManager.penalizePeer(peerId, severity);
1231
- return false;
1232
- }
1233
- return true;
1612
+ protected createRequestedTxValidator(): TxValidator {
1613
+ return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
1614
+ l1ChainId: this.config.l1ChainId,
1615
+ rollupVersion: this.config.rollupVersion,
1616
+ });
1234
1617
  }
1235
1618
 
1236
- private async getGasFees(blockNumber: number): Promise<GasFees> {
1619
+ private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
1237
1620
  if (blockNumber === this.feesCache?.blockNumber) {
1238
1621
  return this.feesCache.gasFees;
1239
1622
  }
@@ -1244,59 +1627,78 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1244
1627
  return gasFees;
1245
1628
  }
1246
1629
 
1247
- public async validate(txs: Tx[]): Promise<void> {
1248
- const currentBlockNumber = await this.archiver.getBlockNumber();
1630
+ /**
1631
+ * Get the BatchTxRequesterLibP2PService dependencies for creating BatchTxRequester instances
1632
+ */
1633
+ public getBatchTxRequesterService(): BatchTxRequesterLibP2PService {
1634
+ return {
1635
+ reqResp: this.reqresp,
1636
+ connectionSampler: this.reqresp.getConnectionSampler(),
1637
+ txValidatorConfig: {
1638
+ l1ChainId: this.config.l1ChainId,
1639
+ rollupVersion: this.config.rollupVersion,
1640
+ proofVerifier: this.proofVerifier,
1641
+ },
1642
+ peerScoring: this.peerManager,
1643
+ };
1644
+ }
1249
1645
 
1250
- // We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
1251
- const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1252
- const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
1646
+ public async validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void> {
1647
+ const validator = createTxValidatorForBlockProposalReceivedTxs(
1648
+ this.proofVerifier,
1649
+ { l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
1650
+ this.logger.getBindings(),
1651
+ );
1253
1652
 
1254
- await Promise.all(
1653
+ const results = await Promise.all(
1255
1654
  txs.map(async tx => {
1256
- for (const validator of messageValidators) {
1257
- const outcome = await this.runValidations(tx, validator);
1258
- if (!outcome.allPassed) {
1259
- throw new Error('Invalid tx detected', { cause: { outcome } });
1260
- }
1261
- }
1655
+ const result = await validator.validateTx(tx);
1656
+ return result.result !== 'invalid';
1262
1657
  }),
1263
1658
  );
1659
+ if (results.some(value => value === false)) {
1660
+ throw new Error('Invalid tx detected');
1661
+ }
1264
1662
  }
1265
1663
 
1266
- /**
1267
- * Create message validators for the given block number and timestamp.
1268
- *
1269
- * Each validator is a pair of a validator and a severity.
1270
- * If a validator fails, the peer is penalized with the severity of the validator.
1271
- *
1272
- * @param currentBlockNumber - The current synced block number.
1273
- * @param nextSlotTimestamp - The timestamp of the next slot (used to validate txs are not expired).
1274
- * @returns The message validators.
1275
- */
1276
- private async createMessageValidators(
1277
- currentBlockNumber: number,
1664
+ /** Creates the first stage (fast) validators for gossiped transactions. */
1665
+ protected async createFirstStageMessageValidators(
1666
+ currentBlockNumber: BlockNumber,
1278
1667
  nextSlotTimestamp: UInt64,
1279
- ): Promise<Record<string, MessageValidator>[]> {
1668
+ ): Promise<Record<string, TransactionValidator>> {
1280
1669
  const gasFees = await this.getGasFees(currentBlockNumber);
1281
- const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
1282
-
1283
- const blockNumberInWhichTheTxIsConsideredToBeIncluded = currentBlockNumber + 1;
1284
-
1285
- return createTxMessageValidators(
1670
+ const allowedInSetup = [
1671
+ ...(await getDefaultAllowedSetupFunctions()),
1672
+ ...(this.config.txPublicSetupAllowListExtend ?? []),
1673
+ ];
1674
+ const blockNumber = BlockNumber(currentBlockNumber + 1);
1675
+ const l1Constants = await this.archiver.getL1Constants();
1676
+
1677
+ return createFirstStageTxValidationsForGossipedTransactions(
1286
1678
  nextSlotTimestamp,
1287
- blockNumberInWhichTheTxIsConsideredToBeIncluded,
1679
+ blockNumber,
1288
1680
  this.worldStateSynchronizer,
1289
1681
  gasFees,
1290
1682
  this.config.l1ChainId,
1291
1683
  this.config.rollupVersion,
1292
1684
  protocolContractsHash,
1293
1685
  this.archiver,
1294
- this.proofVerifier,
1295
1686
  !this.config.disableTransactions,
1296
1687
  allowedInSetup,
1688
+ this.logger.getBindings(),
1689
+ {
1690
+ rollupManaLimit: l1Constants.rollupManaLimit,
1691
+ maxBlockL2Gas: this.config.validateMaxL2BlockGas,
1692
+ maxBlockDAGas: this.config.validateMaxDABlockGas,
1693
+ },
1297
1694
  );
1298
1695
  }
1299
1696
 
1697
+ /** Creates the second stage (expensive proof verification) validators for gossiped transactions. */
1698
+ protected createSecondStageMessageValidators(): Record<string, TransactionValidator> {
1699
+ return createSecondStageTxValidationsForGossipedTransactions(this.proofVerifier, this.logger.getBindings());
1700
+ }
1701
+
1300
1702
  /**
1301
1703
  * Run validations on a tx.
1302
1704
  * @param tx - The tx to validate.
@@ -1305,7 +1707,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1305
1707
  */
1306
1708
  private async runValidations(
1307
1709
  tx: Tx,
1308
- messageValidators: Record<string, MessageValidator>,
1710
+ messageValidators: Record<string, TransactionValidator>,
1309
1711
  ): Promise<ValidationOutcome> {
1310
1712
  const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
1311
1713
  const { result } = await validator.validateTx(tx);
@@ -1314,8 +1716,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1314
1716
 
1315
1717
  // A promise that resolves when all validations have been run
1316
1718
  const allValidations = await Promise.all(validationPromises);
1317
- const failed = allValidations.find(x => !x.isValid);
1318
- if (failed) {
1719
+ const failures = allValidations.filter(x => !x.isValid);
1720
+ if (failures.length > 0) {
1721
+ // Pick the most severe failure (lowest tolerance = harshest penalty)
1722
+ const failed = maxBy(failures, f => PeerErrorSeverityByHarshness.indexOf(f.severity))!;
1319
1723
  return {
1320
1724
  allPassed: false,
1321
1725
  failure: {
@@ -1342,20 +1746,23 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1342
1746
  * @param peerId - The peer ID of the peer that sent the tx.
1343
1747
  * @returns Severity
1344
1748
  */
1345
- private async handleDoubleSpendFailure(tx: Tx, blockNumber: number): Promise<PeerErrorSeverity> {
1749
+ private async handleDoubleSpendFailure(tx: Tx, blockNumber: BlockNumber): Promise<PeerErrorSeverity> {
1346
1750
  if (blockNumber <= this.config.doubleSpendSeverePeerPenaltyWindow) {
1347
1751
  return PeerErrorSeverity.HighToleranceError;
1348
1752
  }
1349
1753
 
1350
- const snapshotValidator = new DoubleSpendTxValidator({
1351
- nullifiersExist: async (nullifiers: Buffer[]) => {
1352
- const merkleTree = this.worldStateSynchronizer.getSnapshot(
1353
- blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow,
1354
- );
1355
- const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
1356
- return indices.map(index => index !== undefined);
1754
+ const snapshotValidator = new DoubleSpendTxValidator(
1755
+ {
1756
+ nullifiersExist: async (nullifiers: Buffer[]) => {
1757
+ const merkleTree = this.worldStateSynchronizer.getSnapshot(
1758
+ BlockNumber(blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow),
1759
+ );
1760
+ const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
1761
+ return indices.map(index => index !== undefined);
1762
+ },
1357
1763
  },
1358
- });
1764
+ this.logger.getBindings(),
1765
+ );
1359
1766
 
1360
1767
  const validSnapshot = await snapshotValidator.validateTx(tx);
1361
1768
  if (validSnapshot.result !== 'valid') {
@@ -1365,47 +1772,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1365
1772
  return PeerErrorSeverity.HighToleranceError;
1366
1773
  }
1367
1774
 
1368
- /**
1369
- * Validate an attestation.
1370
- *
1371
- * @param attestation - The attestation to validate.
1372
- * @returns True if the attestation is valid, false otherwise.
1373
- */
1374
- @trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
1375
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
1376
- [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
1377
- [Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
1378
- }))
1379
- public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
1380
- const severity = await this.attestationValidator.validate(attestation);
1381
- if (severity) {
1382
- this.peerManager.penalizePeer(peerId, severity);
1383
- return false;
1384
- }
1385
-
1386
- return true;
1387
- }
1388
-
1389
- /**
1390
- * Validate a block proposal.
1391
- *
1392
- * @param block - The block proposal to validate.
1393
- * @returns True if the block proposal is valid, false otherwise.
1394
- */
1395
- @trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
1396
- [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
1397
- }))
1398
- public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<boolean> {
1399
- const severity = await this.blockProposalValidator.validate(block);
1400
- if (severity) {
1401
- this.logger.debug(`Penalizing peer ${peerId} for block proposal validation failure`);
1402
- this.peerManager.penalizePeer(peerId, severity);
1403
- return false;
1404
- }
1405
-
1406
- return true;
1407
- }
1408
-
1409
1775
  public getPeerScore(peerId: PeerId): number {
1410
1776
  return this.node.services.pubsub.score.score(peerId.toString());
1411
1777
  }