@aztec/p2p 0.0.1-commit.03f7ef2 → 0.0.1-commit.04d373f

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