@aztec/p2p 0.0.1-commit.b655e406 → 0.0.1-commit.b8a057fa

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