@aztec/p2p 0.0.1-commit.5daedc8 → 0.0.1-commit.6201a7b05

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